diff --git a/ajax/libs/amplifyjs/1.1.0/amplify.js b/ajax/libs/amplifyjs/1.1.0/amplify.js new file mode 100644 index 000000000..00252de12 --- /dev/null +++ b/ajax/libs/amplifyjs/1.1.0/amplify.js @@ -0,0 +1,788 @@ +/*! + * AmplifyJS 1.1.0 - Core, Store, Request + * + * Copyright 2011 appendTo LLC. (http://appendto.com/team) + * Dual licensed under the MIT or GPL licenses. + * http://appendto.com/open-source-licenses + * + * http://amplifyjs.com + */ +/*! + * Amplify Core 1.1.0 + * + * Copyright 2011 appendTo LLC. (http://appendto.com/team) + * Dual licensed under the MIT or GPL licenses. + * http://appendto.com/open-source-licenses + * + * http://amplifyjs.com + */ +(function( global, undefined ) { + +var slice = [].slice, + subscriptions = {}; + +var amplify = global.amplify = { + publish: function( topic ) { + var args = slice.call( arguments, 1 ), + topicSubscriptions, + subscription, + length, + i = 0, + ret; + + if ( !subscriptions[ topic ] ) { + return true; + } + + topicSubscriptions = subscriptions[ topic ].slice(); + for ( length = topicSubscriptions.length; i < length; i++ ) { + subscription = topicSubscriptions[ i ]; + ret = subscription.callback.apply( subscription.context, args ); + if ( ret === false ) { + break; + } + } + return ret !== false; + }, + + subscribe: function( topic, context, callback, priority ) { + if ( arguments.length === 3 && typeof callback === "number" ) { + priority = callback; + callback = context; + context = null; + } + if ( arguments.length === 2 ) { + callback = context; + context = null; + } + priority = priority || 10; + + var topicIndex = 0, + topics = topic.split( /\s/ ), + topicLength = topics.length, + added; + for ( ; topicIndex < topicLength; topicIndex++ ) { + topic = topics[ topicIndex ]; + added = false; + if ( !subscriptions[ topic ] ) { + subscriptions[ topic ] = []; + } + + var i = subscriptions[ topic ].length - 1, + subscriptionInfo = { + callback: callback, + context: context, + priority: priority + }; + + for ( ; i >= 0; i-- ) { + if ( subscriptions[ topic ][ i ].priority <= priority ) { + subscriptions[ topic ].splice( i + 1, 0, subscriptionInfo ); + added = true; + break; + } + } + + if ( !added ) { + subscriptions[ topic ].unshift( subscriptionInfo ); + } + } + + return callback; + }, + + unsubscribe: function( topic, callback ) { + if ( !subscriptions[ topic ] ) { + return; + } + + var length = subscriptions[ topic ].length, + i = 0; + + for ( ; i < length; i++ ) { + if ( subscriptions[ topic ][ i ].callback === callback ) { + subscriptions[ topic ].splice( i, 1 ); + break; + } + } + } +}; + +}( this ) ); +/*! + * Amplify Store - Persistent Client-Side Storage 1.1.0 + * + * Copyright 2011 appendTo LLC. (http://appendto.com/team) + * Dual licensed under the MIT or GPL licenses. + * http://appendto.com/open-source-licenses + * + * http://amplifyjs.com + */ +(function( amplify, undefined ) { + +var store = amplify.store = function( key, value, options, type ) { + var type = store.type; + if ( options && options.type && options.type in store.types ) { + type = options.type; + } + return store.types[ type ]( key, value, options || {} ); +}; + +store.types = {}; +store.type = null; +store.addType = function( type, storage ) { + if ( !store.type ) { + store.type = type; + } + + store.types[ type ] = storage; + store[ type ] = function( key, value, options ) { + options = options || {}; + options.type = type; + return store( key, value, options ); + }; +} +store.error = function() { + return "amplify.store quota exceeded"; +}; + +var rprefix = /^__amplify__/; +function createFromStorageInterface( storageType, storage ) { + store.addType( storageType, function( key, value, options ) { + var storedValue, parsed, i, remove, + ret = value, + now = (new Date()).getTime(); + + if ( !key ) { + ret = {}; + remove = []; + i = 0; + try { + // accessing the length property works around a localStorage bug + // in Firefox 4.0 where the keys don't update cross-page + // we assign to key just to avoid Closure Compiler from removing + // the access as "useless code" + // https://bugzilla.mozilla.org/show_bug.cgi?id=662511 + key = storage.length; + + while ( key = storage.key( i++ ) ) { + if ( rprefix.test( key ) ) { + parsed = JSON.parse( storage.getItem( key ) ); + if ( parsed.expires && parsed.expires <= now ) { + remove.push( key ); + } else { + ret[ key.replace( rprefix, "" ) ] = parsed.data; + } + } + } + while ( key = remove.pop() ) { + storage.removeItem( key ); + } + } catch ( error ) {} + return ret; + } + + // protect against name collisions with direct storage + key = "__amplify__" + key; + + if ( value === undefined ) { + storedValue = storage.getItem( key ); + parsed = storedValue ? JSON.parse( storedValue ) : { expires: -1 }; + if ( parsed.expires && parsed.expires <= now ) { + storage.removeItem( key ); + } else { + return parsed.data; + } + } else { + if ( value === null ) { + storage.removeItem( key ); + } else { + parsed = JSON.stringify({ + data: value, + expires: options.expires ? now + options.expires : null + }); + try { + storage.setItem( key, parsed ); + // quota exceeded + } catch( error ) { + // expire old data and try again + store[ storageType ](); + try { + storage.setItem( key, parsed ); + } catch( error ) { + throw store.error(); + } + } + } + } + + return ret; + }); +} + +// localStorage + sessionStorage +// IE 8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+, iPhone 2+, Android 2+ +for ( var webStorageType in { localStorage: 1, sessionStorage: 1 } ) { + // try/catch for file protocol in Firefox + try { + if ( window[ webStorageType ].getItem ) { + createFromStorageInterface( webStorageType, window[ webStorageType ] ); + } + } catch( e ) {} +} + +// globalStorage +// non-standard: Firefox 2+ +// https://developer.mozilla.org/en/dom/storage#globalStorage +if ( window.globalStorage ) { + // try/catch for file protocol in Firefox + try { + createFromStorageInterface( "globalStorage", + window.globalStorage[ window.location.hostname ] ); + // Firefox 2.0 and 3.0 have sessionStorage and globalStorage + // make sure we default to globalStorage + // but don't default to globalStorage in 3.5+ which also has localStorage + if ( store.type === "sessionStorage" ) { + store.type = "globalStorage"; + } + } catch( e ) {} +} + +// userData +// non-standard: IE 5+ +// http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx +(function() { + // IE 9 has quirks in userData that are a huge pain + // rather than finding a way to detect these quirks + // we just don't register userData if we have localStorage + if ( store.types.localStorage ) { + return; + } + + // append to html instead of body so we can do this from the head + var div = document.createElement( "div" ), + attrKey = "amplify"; + div.style.display = "none"; + document.getElementsByTagName( "head" )[ 0 ].appendChild( div ); + + // we can't feature detect userData support + // so just try and see if it fails + // surprisingly, even just adding the behavior isn't enough for a failure + // so we need to load the data as well + try { + div.addBehavior( "#default#userdata" ); + div.load( attrKey ); + } catch( e ) { + div.parentNode.removeChild( div ); + return; + } + + store.addType( "userData", function( key, value, options ) { + div.load( attrKey ); + var attr, parsed, prevValue, i, remove, + ret = value, + now = (new Date()).getTime(); + + if ( !key ) { + ret = {}; + remove = []; + i = 0; + while ( attr = div.XMLDocument.documentElement.attributes[ i++ ] ) { + parsed = JSON.parse( attr.value ); + if ( parsed.expires && parsed.expires <= now ) { + remove.push( attr.name ); + } else { + ret[ attr.name ] = parsed.data; + } + } + while ( key = remove.pop() ) { + div.removeAttribute( key ); + } + div.save( attrKey ); + return ret; + } + + // convert invalid characters to dashes + // http://www.w3.org/TR/REC-xml/#NT-Name + // simplified to assume the starting character is valid + // also removed colon as it is invalid in HTML attribute names + key = key.replace( /[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-" ); + + if ( value === undefined ) { + attr = div.getAttribute( key ); + parsed = attr ? JSON.parse( attr ) : { expires: -1 }; + if ( parsed.expires && parsed.expires <= now ) { + div.removeAttribute( key ); + } else { + return parsed.data; + } + } else { + if ( value === null ) { + div.removeAttribute( key ); + } else { + // we need to get the previous value in case we need to rollback + prevValue = div.getAttribute( key ); + parsed = JSON.stringify({ + data: value, + expires: (options.expires ? (now + options.expires) : null) + }); + div.setAttribute( key, parsed ); + } + } + + try { + div.save( attrKey ); + // quota exceeded + } catch ( error ) { + // roll the value back to the previous value + if ( prevValue === null ) { + div.removeAttribute( key ); + } else { + div.setAttribute( key, prevValue ); + } + + // expire old data and try again + store.userData(); + try { + div.setAttribute( key, parsed ); + div.save( attrKey ); + } catch ( error ) { + // roll the value back to the previous value + if ( prevValue === null ) { + div.removeAttribute( key ); + } else { + div.setAttribute( key, prevValue ); + } + throw store.error(); + } + } + return ret; + }); +}() ); + +// in-memory storage +// fallback for all browsers to enable the API even if we can't persist data +(function() { + var memory = {}, + timeout = {}; + + function copy( obj ) { + return obj === undefined ? undefined : JSON.parse( JSON.stringify( obj ) ); + } + + store.addType( "memory", function( key, value, options ) { + if ( !key ) { + return copy( memory ); + } + + if ( value === undefined ) { + return copy( memory[ key ] ); + } + + if ( timeout[ key ] ) { + clearTimeout( timeout[ key ] ); + delete timeout[ key ]; + } + + if ( value === null ) { + delete memory[ key ]; + return null; + } + + memory[ key ] = value; + if ( options.expires ) { + timeout[ key ] = setTimeout(function() { + delete memory[ key ]; + delete timeout[ key ]; + }, options.expires ); + } + + return value; + }); +}() ); + +}( this.amplify = this.amplify || {} ) ); +/*! + * Amplify Request 1.1.0 + * + * Copyright 2011 appendTo LLC. (http://appendto.com/team) + * Dual licensed under the MIT or GPL licenses. + * http://appendto.com/open-source-licenses + * + * http://amplifyjs.com + */ +(function( amplify, undefined ) { + +function noop() {} +function isFunction( obj ) { + return ({}).toString.call( obj ) === "[object Function]"; +} + +function async( fn ) { + var isAsync = false; + setTimeout(function() { + isAsync = true; + }, 1 ); + return function() { + var that = this, + args = arguments; + if ( isAsync ) { + fn.apply( that, args ); + } else { + setTimeout(function() { + fn.apply( that, args ); + }, 1 ); + } + }; +} + +amplify.request = function( resourceId, data, callback ) { + // default to an empty hash just so we can handle a missing resourceId + // in one place + var settings = resourceId || {}; + + if ( typeof settings === "string" ) { + if ( isFunction( data ) ) { + callback = data; + data = {}; + } + settings = { + resourceId: resourceId, + data: data || {}, + success: callback + }; + } + + var request = { abort: noop }, + resource = amplify.request.resources[ settings.resourceId ], + success = settings.success || noop, + error = settings.error || noop; + settings.success = async( function( data, status ) { + status = status || "success"; + amplify.publish( "request.success", settings, data, status ); + amplify.publish( "request.complete", settings, data, status ); + success( data, status ); + }); + settings.error = async( function( data, status ) { + status = status || "error"; + amplify.publish( "request.error", settings, data, status ); + amplify.publish( "request.complete", settings, data, status ); + error( data, status ); + }); + + if ( !resource ) { + if ( !settings.resourceId ) { + throw "amplify.request: no resourceId provided"; + } + throw "amplify.request: unknown resourceId: " + settings.resourceId; + } + + if ( !amplify.publish( "request.before", settings ) ) { + settings.error( null, "abort" ); + return; + } + + amplify.request.resources[ settings.resourceId ]( settings, request ); + return request; +}; + +amplify.request.types = {}; +amplify.request.resources = {}; +amplify.request.define = function( resourceId, type, settings ) { + if ( typeof type === "string" ) { + if ( !( type in amplify.request.types ) ) { + throw "amplify.request.define: unknown type: " + type; + } + + settings.resourceId = resourceId; + amplify.request.resources[ resourceId ] = + amplify.request.types[ type ]( settings ); + } else { + // no pre-processor or settings for one-off types (don't invoke) + amplify.request.resources[ resourceId ] = type; + } +}; + +}( amplify ) ); + + + + + +(function( amplify, $, undefined ) { + +var xhrProps = [ "status", "statusText", "responseText", "responseXML", "readyState" ], + rurlData = /\{([^\}]+)\}/g; + +amplify.request.types.ajax = function( defnSettings ) { + defnSettings = $.extend({ + type: "GET" + }, defnSettings ); + + return function( settings, request ) { + var xhr, + url = defnSettings.url, + abort = request.abort, + ajaxSettings = $.extend( true, {}, defnSettings, { data: settings.data } ), + aborted = false, + ampXHR = { + readyState: 0, + setRequestHeader: function( name, value ) { + return xhr.setRequestHeader( name, value ); + }, + getAllResponseHeaders: function() { + return xhr.getAllResponseHeaders(); + }, + getResponseHeader: function( key ) { + return xhr.getResponseHeader( key ); + }, + overrideMimeType: function( type ) { + return xhr.overrideMideType( type ); + }, + abort: function() { + aborted = true; + try { + xhr.abort(); + // IE 7 throws an error when trying to abort + } catch( e ) {} + handleResponse( null, "abort" ); + }, + success: function( data, status ) { + settings.success( data, status ); + }, + error: function( data, status ) { + settings.error( data, status ); + } + }; + + amplify.publish( "request.ajax.preprocess", + defnSettings, settings, ajaxSettings, ampXHR ); + + $.extend( ajaxSettings, { + success: function( data, status ) { + handleResponse( data, status ); + }, + error: function( _xhr, status ) { + handleResponse( null, status ); + }, + beforeSend: function( _xhr, _ajaxSettings ) { + xhr = _xhr; + ajaxSettings = _ajaxSettings; + var ret = defnSettings.beforeSend ? + defnSettings.beforeSend.call( this, ampXHR, ajaxSettings ) : true; + return ret && amplify.publish( "request.before.ajax", + defnSettings, settings, ajaxSettings, ampXHR ); + } + }); + $.ajax( ajaxSettings ); + + function handleResponse( data, status ) { + $.each( xhrProps, function( i, key ) { + try { + ampXHR[ key ] = xhr[ key ]; + } catch( e ) {} + }); + // Playbook returns "HTTP/1.1 200 OK" + // TODO: something also returns "OK", what? + if ( /OK$/.test( ampXHR.statusText ) ) { + ampXHR.statusText = "success"; + } + if ( data === undefined ) { + // TODO: add support for ajax errors with data + data = null; + } + if ( aborted ) { + status = "abort"; + } + if ( /timeout|error|abort/.test( status ) ) { + ampXHR.error( data, status ); + } else { + ampXHR.success( data, status ); + } + // avoid handling a response multiple times + // this can happen if a request is aborted + // TODO: figure out if this breaks polling or multi-part responses + handleResponse = $.noop; + } + + request.abort = function() { + ampXHR.abort(); + abort.call( this ); + }; + }; +}; + + + +amplify.subscribe( "request.ajax.preprocess", function( defnSettings, settings, ajaxSettings ) { + var mappedKeys = [], + data = ajaxSettings.data; + + if ( typeof data === "string" ) { + return; + } + + data = $.extend( true, {}, defnSettings.data, data ); + + ajaxSettings.url = ajaxSettings.url.replace( rurlData, function ( m, key ) { + if ( key in data ) { + mappedKeys.push( key ); + return data[ key ]; + } + }); + + // We delete the keys later so duplicates are still replaced + $.each( mappedKeys, function ( i, key ) { + delete data[ key ]; + }); + + ajaxSettings.data = data; +}); + + + +amplify.subscribe( "request.ajax.preprocess", function( defnSettings, settings, ajaxSettings ) { + var data = ajaxSettings.data, + dataMap = defnSettings.dataMap; + + if ( !dataMap || typeof data === "string" ) { + return; + } + + if ( $.isFunction( dataMap ) ) { + ajaxSettings.data = dataMap( data ); + } else { + $.each( defnSettings.dataMap, function( orig, replace ) { + if ( orig in data ) { + data[ replace ] = data[ orig ]; + delete data[ orig ]; + } + }); + ajaxSettings.data = data; + } +}); + + + +var cache = amplify.request.cache = { + _key: function( resourceId, url, data ) { + data = url + data; + var length = data.length, + i = 0, + checksum = chunk(); + + while ( i < length ) { + checksum ^= chunk(); + } + + function chunk() { + return data.charCodeAt( i++ ) << 24 | + data.charCodeAt( i++ ) << 16 | + data.charCodeAt( i++ ) << 8 | + data.charCodeAt( i++ ) << 0; + } + + return "request-" + resourceId + "-" + checksum; + }, + + _default: (function() { + var memoryStore = {}; + return function( resource, settings, ajaxSettings, ampXHR ) { + // data is already converted to a string by the time we get here + var cacheKey = cache._key( settings.resourceId, + ajaxSettings.url, ajaxSettings.data ), + duration = resource.cache; + + if ( cacheKey in memoryStore ) { + ampXHR.success( memoryStore[ cacheKey ] ); + return false; + } + var success = ampXHR.success; + ampXHR.success = function( data ) { + memoryStore[ cacheKey ] = data; + if ( typeof duration === "number" ) { + setTimeout(function() { + delete memoryStore[ cacheKey ]; + }, duration ); + } + success.apply( this, arguments ); + }; + }; + }()) +}; + +if ( amplify.store ) { + $.each( amplify.store.types, function( type ) { + cache[ type ] = function( resource, settings, ajaxSettings, ampXHR ) { + var cacheKey = cache._key( settings.resourceId, + ajaxSettings.url, ajaxSettings.data ), + cached = amplify.store[ type ]( cacheKey ); + + if ( cached ) { + ajaxSettings.success( cached ); + return false; + } + var success = ampXHR.success; + ampXHR.success = function( data ) { + amplify.store[ type ]( cacheKey, data, { expires: resource.cache.expires } ); + success.apply( this, arguments ); + }; + }; + }); + cache.persist = cache[ amplify.store.type ]; +} + +amplify.subscribe( "request.before.ajax", function( resource ) { + var cacheType = resource.cache; + if ( cacheType ) { + // normalize between objects and strings/booleans/numbers + cacheType = cacheType.type || cacheType; + return cache[ cacheType in cache ? cacheType : "_default" ] + .apply( this, arguments ); + } +}); + + + +amplify.request.decoders = { + // http://labs.omniti.com/labs/jsend + jsend: function( data, status, ampXHR, success, error ) { + if ( data.status === "success" ) { + success( data.data ); + } else if ( data.status === "fail" ) { + error( data.data, "fail" ); + } else if ( data.status === "error" ) { + delete data.status; + error( data, "error" ); + } + } +}; + +amplify.subscribe( "request.before.ajax", function( resource, settings, ajaxSettings, ampXHR ) { + var _success = ampXHR.success, + _error = ampXHR.error, + decoder = $.isFunction( resource.decoder ) + ? resource.decoder + : resource.decoder in amplify.request.decoders + ? amplify.request.decoders[ resource.decoder ] + : amplify.request.decoders._default; + + if ( !decoder ) { + return; + } + + function success( data, status ) { + _success( data, status ); + } + function error( data, status ) { + _error( data, status ); + } + ampXHR.success = function( data, status ) { + decoder( data, status, ampXHR, success, error ); + }; + ampXHR.error = function( data, status ) { + decoder( data, status, ampXHR, success, error ); + }; +}); + +}( amplify, jQuery ) ); diff --git a/ajax/libs/amplifyjs/1.1.0/amplify.min.js b/ajax/libs/amplifyjs/1.1.0/amplify.min.js new file mode 100644 index 000000000..8398f85b3 --- /dev/null +++ b/ajax/libs/amplifyjs/1.1.0/amplify.min.js @@ -0,0 +1,10 @@ +/*! + * AmplifyJS 1.1.0 - Core, Store, Request + * + * Copyright 2011 appendTo LLC. (http://appendto.com/team) + * Dual licensed under the MIT or GPL licenses. + * http://appendto.com/open-source-licenses + * + * http://amplifyjs.com + */ +(function(a,b){var c=[].slice,d={},e=a.amplify={publish:function(a){var b=c.call(arguments,1),e,f,g,h=0,i;if(!d[a])return!0;e=d[a].slice();for(g=e.length;h=0;j--)if(d[a][j].priority<=e){d[a].splice(j+1,0,k),i=!0;break}i||d[a].unshift(k)}return c},unsubscribe:function(a,b){if(!!d[a]){var c=d[a].length,e=0;for(;e