├── demo ├── core │ ├── data │ │ ├── demo.css │ │ ├── demo.html │ │ ├── demo.details │ │ └── demo.js │ ├── context │ │ ├── demo.css │ │ ├── demo.html │ │ ├── demo.details │ │ └── demo.js │ ├── nodata │ │ ├── demo.css │ │ ├── demo.html │ │ ├── demo.details │ │ └── demo.js │ └── priority │ │ ├── demo.css │ │ ├── demo.html │ │ ├── demo.details │ │ └── demo.js └── store │ ├── implicit │ ├── demo.css │ ├── demo.html │ ├── demo.details │ └── demo.js │ └── sessionstorage │ ├── demo.css │ ├── demo.html │ ├── demo.details │ └── demo.js ├── src ├── amplify.core.js ├── amplify.store.js ├── amplify.request.js ├── amplify.js ├── core-header.js ├── request-header.js ├── store-header.js ├── core.js ├── amplify-vsdoc.js ├── store.js └── request.js ├── test ├── request │ ├── static │ │ ├── data.json │ │ ├── jsend-error.json │ │ ├── jsend-fail.json │ │ ├── jsend.json │ │ └── jsend-error2.json │ ├── routes │ │ ├── headers.js │ │ ├── delay-route.js │ │ ├── index.js │ │ ├── echo-route.js │ │ ├── headers-route.js │ │ ├── echoraw-route.js │ │ └── jsonp-route.js │ └── test.html ├── store │ ├── other-page.html │ ├── routes │ │ └── index.js │ ├── test.html │ └── unit.js ├── testswarm.js ├── core │ ├── routes │ │ └── index.js │ ├── test.html │ └── unit.js ├── jquery.js └── index.html ├── .gitignore ├── header.js ├── external ├── qunit-composite.css ├── qunit-composite.js ├── qunit.css └── json2.js ├── .jshintrc ├── bower.json ├── test-server.js ├── readme.md ├── package.json ├── MIT-LICENSE.txt ├── lib ├── amplify.core.min.js ├── amplify.store.min.js ├── amplify.core.js ├── amplify.request.min.js ├── amplify-vsdoc.js ├── amplify.min.js ├── amplify.store.js └── amplify.request.js ├── testswarm.json ├── test-jquery-versions.js ├── docs ├── amplify.core.md ├── amplify.store.md └── amplify.request.md └── GPL-LICENSE.txt /demo/core/data/demo.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/core/context/demo.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/core/nodata/demo.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/core/priority/demo.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/store/implicit/demo.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/core/data/demo.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /demo/core/nodata/demo.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/store/sessionstorage/demo.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/core/context/demo.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/core/priority/demo.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/store/implicit/demo.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/amplify.core.js: -------------------------------------------------------------------------------- 1 | // import("core.js") -------------------------------------------------------------------------------- /src/amplify.store.js: -------------------------------------------------------------------------------- 1 | // import("store.js") -------------------------------------------------------------------------------- /demo/store/sessionstorage/demo.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/amplify.request.js: -------------------------------------------------------------------------------- 1 | // import "request.js" -------------------------------------------------------------------------------- /test/request/static/data.json: -------------------------------------------------------------------------------- 1 | { "foo": "bar" } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .anvil 4 | .tmp 5 | -------------------------------------------------------------------------------- /src/amplify.js: -------------------------------------------------------------------------------- 1 | // import "core.js" 2 | // import "store.js" 3 | // import "request.js" -------------------------------------------------------------------------------- /test/request/static/jsend-error.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "error", 3 | "message": "it broke" 4 | } -------------------------------------------------------------------------------- /test/request/static/jsend-fail.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "fail", 3 | "data": { 4 | "broken": true 5 | } 6 | } -------------------------------------------------------------------------------- /test/request/static/jsend.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "unwrapped": true 5 | } 6 | } -------------------------------------------------------------------------------- /test/request/static/jsend-error2.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "error", 3 | "message": "it broke", 4 | "code": 15, 5 | "data": { 6 | "you": "broke", 7 | "it": "bad" 8 | } 9 | } -------------------------------------------------------------------------------- /test/request/routes/headers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | json: {'Content-Type':'application/json'}, 4 | text: {'Content-Type':'text/plain'}, 5 | html: {'Content-Type':'text/html'} 6 | }; -------------------------------------------------------------------------------- /demo/core/nodata/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: amplify.core pub/sub 3 | description: basic pub/sub demo. 4 | authors: 5 | - The appendTo Team 6 | resources: 7 | - http://github.com/appendto/amplify/raw/master/lib/amplify.core.js 8 | ... 9 | -------------------------------------------------------------------------------- /demo/core/data/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: amplify.core pub/sub with data. 3 | description: amplify.core pub/sub with data. 4 | authors: 5 | - The appendTo Team 6 | resources: 7 | - http://github.com/appendto/amplify/raw/master/lib/amplify.core.js 8 | ... 9 | -------------------------------------------------------------------------------- /header.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Amplify {{{version}}} 3 | * 4 | * Copyright 2011 - 2013 appendTo LLC. (http://appendto.com/team) 5 | * Dual licensed under the MIT or GPL licenses. 6 | * http://appendto.com/open-source-licenses 7 | * 8 | * http://amplifyjs.com 9 | */ -------------------------------------------------------------------------------- /demo/core/context/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: amplify.core pub/sub with context. 3 | description: amplify.core pub/sub with context. 4 | authors: 5 | - The appendTo Team 6 | resources: 7 | - http://github.com/appendto/amplify/raw/master/lib/amplify.core.js 8 | ... 9 | -------------------------------------------------------------------------------- /src/core-header.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Amplify Core {{{version}}} 3 | * 4 | * Copyright 2011 - 2013 appendTo LLC. (http://appendto.com/team) 5 | * Dual licensed under the MIT or GPL licenses. 6 | * http://appendto.com/open-source-licenses 7 | * 8 | * http://amplifyjs.com 9 | */ -------------------------------------------------------------------------------- /src/request-header.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Amplify Request {{{version}}} 3 | * 4 | * Copyright 2011 - 2013 appendTo LLC. (http://appendto.com/team) 5 | * Dual licensed under the MIT or GPL licenses. 6 | * http://appendto.com/open-source-licenses 7 | * 8 | * http://amplifyjs.com 9 | */ -------------------------------------------------------------------------------- /demo/core/priority/demo.details: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | name: amplify.core pub/sub with priority 4 | description: amplify.core pub/sub with priority 5 | authors: 6 | - The appendTo Team 7 | resources: 8 | - http://github.com/appendto/amplify/raw/master/lib/amplify.core.js 9 | ... 10 | -------------------------------------------------------------------------------- /external/qunit-composite.css: -------------------------------------------------------------------------------- 1 | .qunit-composite-suite { 2 | position: fixed; 3 | bottom: 0; 4 | left: 0; 5 | 6 | margin: 0; 7 | padding: 0; 8 | border-width: 1px 0 0; 9 | height: 45%; 10 | width: 100%; 11 | 12 | background: #fff; 13 | } 14 | -------------------------------------------------------------------------------- /src/store-header.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Amplify Store - Persistent Client-Side Storage {{{version}}} 3 | * 4 | * Copyright 2011 - 2013 appendTo LLC. (http://appendto.com/team) 5 | * Dual licensed under the MIT or GPL licenses. 6 | * http://appendto.com/open-source-licenses 7 | * 8 | * http://amplifyjs.com 9 | */ -------------------------------------------------------------------------------- /test/store/other-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |amplify.unsubscribe( string topic, function callback )
25 |
26 | Remove a subscription.
27 |
28 | * `topic`: The topic being unsubscribed from.
29 | * [`context`]: If provided, only subscriptions with a matching context will be unsubscribed.
30 | * `callback`: The callback that was originally subscribed.
31 |
32 | amplify.publish( string topic, ... )
33 |
34 | Publish a message.
35 |
36 | * `topic`: The name of the message to publish.
37 | * Any additional parameters will be passed to the subscriptions.
38 |
39 | > `amplify.publish` returns a boolean indicating whether any subscriptions returned `false`.
40 | > The return value is `true` if none of the subscriptions returned `false`, and `false` otherwise. Note that only one subscription can return `false` because doing so will prevent additional subscriptions from being invoked.
41 |
42 | ## Examples
43 |
44 | ### Subscribe and publish with no data
45 |
46 | Subscribing to a topic with no data allows a generic message to be
47 | published on the bus for any event (user or code related) that needs communicated but no data
48 | is needed along with the event.
49 |
50 | amplify.subscribe( "nodataexample", function() {
51 | alert( "nodataexample topic published!" );
52 | });
53 |
54 | //...
55 |
56 | amplify.publish( "nodataexample" );
57 |
58 | ### Subscribe and publish with data
59 |
60 | The majority usage of a pub/sub system includes passing data from the
61 | publisher to any subscriptions listening to the topic.
62 |
63 | amplify.subscribe( "dataexample", function( data ) {
64 | alert( data.foo ); // bar
65 | });
66 |
67 | //...
68 |
69 | amplify.publish( "dataexample", { foo: "bar" } );
70 |
71 | You can choose to pass multiple parameters to any subscriber as well.
72 |
73 | amplify.subscribe( "dataexample2", function( param1, param2 ) {
74 | alert( param1 + param2 ); // barbaz
75 | });
76 |
77 | //...
78 |
79 | amplify.publish( "dataexample2", "bar", "baz" );
80 |
81 | ### Subscribe and publish with context and data
82 |
83 | Often there is a need to operate within a context for a subscription
84 | callback. It can be a reasonable strategy to have the context be set to
85 | a jQuery object that will be used inside of the subscription, or even a
86 | native DOM element.
87 |
88 | > Note: the following example assumes jQuery is already loaded on the
89 | > page, and assumes at least one paragraph exists within the body of the page.
90 |
91 | amplify.subscribe( "datacontextexample", $( "p:first" ), function( data ) {
92 | this.text( data.exampleText ); // first p element would have "foo bar baz" as text
93 | });
94 |
95 | //...
96 |
97 | amplify.publish( "datacontextexample", { exampleText: "foo bar baz" } );
98 |
99 | ### Subscribe to a topic with high priority
100 |
101 | Subscribing to a topic with high priority can be useful as an error
102 | handler or anytime data may need to be checked or augmented before
103 | proceeding.
104 |
105 | amplify.subscribe( "priorityexample", function( data ) {
106 | alert( data.foo );
107 | });
108 |
109 | amplify.subscribe( "priorityexample", function( data ) {
110 | if ( data.foo === "oops" ) {
111 | return false;
112 | }
113 | }, 1 );
114 |
115 | //...
116 |
117 | amplify.publish( "priorityexample", { foo: "bar" } );
118 | amplify.publish( "priorityexample", { foo: "oops" } );
--------------------------------------------------------------------------------
/docs/amplify.store.md:
--------------------------------------------------------------------------------
1 | # amplify.store
2 |
3 | `amplify.store` is a wrapper for various persistent client-side storage systems.
4 | `amplify.store` supports IE 5+, Firefox 2+, Safari 4+, Chrome, Opera 10.5+, iPhone 2+, Android 2+
5 | and provides a consistent API to handle storage cross-browser.
6 |
7 | `amplify.store` is meant to allow you to utilize all the latest storage
8 | technologies for those browsers that have them, while gracefully
9 | degrading for those without support. `amplify.store` allows you to be
10 | passive or explicit in the storage technologies used. With no storage
11 | type explicitly specified, `amplify.store` will go through a series of
12 | technologies and pick an appropriate storage technology through feature
13 | detection. `amplify.store` also handles serializing to and from a JavaScript object
14 | using JSON serialization where necessary.
15 |
16 | Note: Because of the JSON dependency, you need to add [json2.js](https://github.com/douglascrockford/JSON-js) for support in browsers without native JSON support, including IE 5, IE 6, IE 7, Firefox 2.0 and Firefox 3.0.
17 |
18 | ## Usage
19 |
20 | amplify.store( string key, mixed value [, hash options ] )
21 |
22 | Stores a value for a given key using the default storage type.
23 |
24 | * `key`: Identifier for the value being stored.
25 | * `value`: The value to store. The value can be anything that can be serialized as JSON.
26 | * [`options`]: A set of key/value pairs that relate to settings for storing the value.
27 |
28 | amplify.store( string key )
29 |
30 | Gets a stored value based on the key.
31 |
32 | amplify.store()
33 |
34 | Gets a hash of all stored values.
35 |
36 | amplify.store( string key, null )
37 |
38 | Clears key/value pair from the store.
39 |
40 | amplify.store.storageType( string key, mixed value [, hash options ] )
41 |
42 | Stores a value for a given key using an explicit storage type, where `storageType`
43 | is one of the available storage types through amplify.store. The storage
44 | types available by default are listed below.
45 |
46 | amplify.store.storageType( string key )
47 |
48 | Gets a stored value based upon key for the explicit storage type.
49 |
50 | amplify.store.storageType()
51 |
52 | Gets a hash of all stored values which were stored through `amplify.store`.
53 |
54 |
55 | ### Options
56 |
57 | * `expires`: Duration in milliseconds that the value should be cached.
58 |
59 | ## Storage Types
60 |
61 | Support for the following storage types are built into `amplify.store` and are
62 | detected in the order listed. The first available storage type will become the
63 | default storage type when using `amplify.store()`.
64 |
65 | ### localStorage
66 |
67 | * IE 8+
68 | * Firefox 3.5+
69 | * Safari 4+
70 | * Chrome
71 | * Opera 10.5+
72 | * iPhone 2+
73 | * Android 2+
74 |
75 | ### sessionStorage
76 |
77 | * IE 8+
78 | * Firefox 2+
79 | * Safari 4+
80 | * Chrome
81 | * Opera 10.5+
82 | * iPhone 2+
83 | * Android 2+
84 |
85 | ### globalStorage
86 |
87 | * Firefox 2+
88 |
89 | ### userData
90 |
91 | * IE 5 - 7
92 | * __userData stores are available only in the same directory used to store the data.__
93 | * _userData exists in newer versions of IE as well, but due to quirks in IE 9's implementation, we don't register userData if localStorage is supported._
94 |
95 | ### memory
96 |
97 | An in-memory store is provided as a fallback if none of the other storage types are available.
98 |
99 | ## Examples
100 |
101 | ### Store data with amplify storage picking the default storage technology:
102 |
103 | amplify.store( "storeExample1", { foo: "bar" } );
104 | amplify.store( "storeExample2", "baz" );
105 | // retrieve the data later via the key
106 | var myStoredValue = amplify.store( "storeExample1" ),
107 | myStoredValue2 = amplify.store( "storeExample2" ),
108 | myStoredValues = amplify.store();
109 | myStoredValue.foo; // bar
110 | myStoredValue2; // baz
111 | myStoredValues.storeExample1.foo; // bar
112 | myStoredValues.storeExample2; // baz
113 |
114 |
115 | [Open this example in jsFiddle](http://jsfiddle.net/FKuJJ/)
116 |
117 | ### Store data explicitly with session storage
118 |
119 | amplify.store.sessionStorage( "explicitExample", { foo2: "baz" } );
120 | // retrieve the data later via the key
121 | var myStoredValue2 = amplify.store.sessionStorage( "explicitExample" );
122 | myStoredValue2.foo2; // baz
123 |
124 |
125 | [Open this example in jsFiddle](http://jsfiddle.net/DWa92/)
126 |
127 | ## Known Issues
128 |
129 | ### userData (IE 5 - 7) key names are not preserved ###
130 |
131 | Keys for userData stores are restricted to valid XML names.
132 | As a result, invalid keys are sanitized prior to setting or getting a value.
133 | This generally works as expected, but if you store a value using an invalid key
134 | then getting the full store via `amplify.store()` will return the sanitized keys
135 | instead of the original keys.
136 | See [Names and Tokens, W3C XML Naming](http://www.w3.org/TR/REC-xml/#NT-Name)
137 | for full details on valid XML names.
138 |
--------------------------------------------------------------------------------
/external/qunit.css:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit v1.10.0 - A JavaScript Unit Testing Framework
3 | *
4 | * http://qunitjs.com
5 | *
6 | * Copyright 2012 jQuery Foundation and other contributors
7 | * Released under the MIT license.
8 | * http://jquery.org/license
9 | */
10 |
11 | /** Font Family and Sizes */
12 |
13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
15 | }
16 |
17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
18 | #qunit-tests { font-size: smaller; }
19 |
20 |
21 | /** Resets */
22 |
23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
24 | margin: 0;
25 | padding: 0;
26 | }
27 |
28 |
29 | /** Header */
30 |
31 | #qunit-header {
32 | padding: 0.5em 0 0.5em 1em;
33 |
34 | color: #8699a4;
35 | background-color: #0d3349;
36 |
37 | font-size: 1.5em;
38 | line-height: 1em;
39 | font-weight: normal;
40 |
41 | border-radius: 5px 5px 0 0;
42 | -moz-border-radius: 5px 5px 0 0;
43 | -webkit-border-top-right-radius: 5px;
44 | -webkit-border-top-left-radius: 5px;
45 | }
46 |
47 | #qunit-header a {
48 | text-decoration: none;
49 | color: #c2ccd1;
50 | }
51 |
52 | #qunit-header a:hover,
53 | #qunit-header a:focus {
54 | color: #fff;
55 | }
56 |
57 | #qunit-testrunner-toolbar label {
58 | display: inline-block;
59 | padding: 0 .5em 0 .1em;
60 | }
61 |
62 | #qunit-banner {
63 | height: 5px;
64 | }
65 |
66 | #qunit-testrunner-toolbar {
67 | padding: 0.5em 0 0.5em 2em;
68 | color: #5E740B;
69 | background-color: #eee;
70 | overflow: hidden;
71 | }
72 |
73 | #qunit-userAgent {
74 | padding: 0.5em 0 0.5em 2.5em;
75 | background-color: #2b81af;
76 | color: #fff;
77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
78 | }
79 |
80 | #qunit-modulefilter-container {
81 | float: right;
82 | }
83 |
84 | /** Tests: Pass/Fail */
85 |
86 | #qunit-tests {
87 | list-style-position: inside;
88 | }
89 |
90 | #qunit-tests li {
91 | padding: 0.4em 0.5em 0.4em 2.5em;
92 | border-bottom: 1px solid #fff;
93 | list-style-position: inside;
94 | }
95 |
96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
97 | display: none;
98 | }
99 |
100 | #qunit-tests li strong {
101 | cursor: pointer;
102 | }
103 |
104 | #qunit-tests li a {
105 | padding: 0.5em;
106 | color: #c2ccd1;
107 | text-decoration: none;
108 | }
109 | #qunit-tests li a:hover,
110 | #qunit-tests li a:focus {
111 | color: #000;
112 | }
113 |
114 | #qunit-tests ol {
115 | margin-top: 0.5em;
116 | padding: 0.5em;
117 |
118 | background-color: #fff;
119 |
120 | border-radius: 5px;
121 | -moz-border-radius: 5px;
122 | -webkit-border-radius: 5px;
123 | }
124 |
125 | #qunit-tests table {
126 | border-collapse: collapse;
127 | margin-top: .2em;
128 | }
129 |
130 | #qunit-tests th {
131 | text-align: right;
132 | vertical-align: top;
133 | padding: 0 .5em 0 0;
134 | }
135 |
136 | #qunit-tests td {
137 | vertical-align: top;
138 | }
139 |
140 | #qunit-tests pre {
141 | margin: 0;
142 | white-space: pre-wrap;
143 | word-wrap: break-word;
144 | }
145 |
146 | #qunit-tests del {
147 | background-color: #e0f2be;
148 | color: #374e0c;
149 | text-decoration: none;
150 | }
151 |
152 | #qunit-tests ins {
153 | background-color: #ffcaca;
154 | color: #500;
155 | text-decoration: none;
156 | }
157 |
158 | /*** Test Counts */
159 |
160 | #qunit-tests b.counts { color: black; }
161 | #qunit-tests b.passed { color: #5E740B; }
162 | #qunit-tests b.failed { color: #710909; }
163 |
164 | #qunit-tests li li {
165 | padding: 5px;
166 | background-color: #fff;
167 | border-bottom: none;
168 | list-style-position: inside;
169 | }
170 |
171 | /*** Passing Styles */
172 |
173 | #qunit-tests li li.pass {
174 | color: #3c510c;
175 | background-color: #fff;
176 | border-left: 10px solid #C6E746;
177 | }
178 |
179 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
180 | #qunit-tests .pass .test-name { color: #366097; }
181 |
182 | #qunit-tests .pass .test-actual,
183 | #qunit-tests .pass .test-expected { color: #999999; }
184 |
185 | #qunit-banner.qunit-pass { background-color: #C6E746; }
186 |
187 | /*** Failing Styles */
188 |
189 | #qunit-tests li li.fail {
190 | color: #710909;
191 | background-color: #fff;
192 | border-left: 10px solid #EE5757;
193 | white-space: pre;
194 | }
195 |
196 | #qunit-tests > li:last-child {
197 | border-radius: 0 0 5px 5px;
198 | -moz-border-radius: 0 0 5px 5px;
199 | -webkit-border-bottom-right-radius: 5px;
200 | -webkit-border-bottom-left-radius: 5px;
201 | }
202 |
203 | #qunit-tests .fail { color: #000000; background-color: #EE5757; }
204 | #qunit-tests .fail .test-name,
205 | #qunit-tests .fail .module-name { color: #000000; }
206 |
207 | #qunit-tests .fail .test-actual { color: #EE5757; }
208 | #qunit-tests .fail .test-expected { color: green; }
209 |
210 | #qunit-banner.qunit-fail { background-color: #EE5757; }
211 |
212 |
213 | /** Result */
214 |
215 | #qunit-testresult {
216 | padding: 0.5em 0.5em 0.5em 2.5em;
217 |
218 | color: #2b81af;
219 | background-color: #D2E0E6;
220 |
221 | border-bottom: 1px solid white;
222 | }
223 | #qunit-testresult .module-name {
224 | font-weight: bold;
225 | }
226 |
227 | /** Fixture */
228 |
229 | #qunit-fixture {
230 | position: absolute;
231 | top: -10000px;
232 | left: -10000px;
233 | width: 1000px;
234 | height: 1000px;
235 | }
236 |
--------------------------------------------------------------------------------
/src/amplify-vsdoc.js:
--------------------------------------------------------------------------------
1 | /// >> amplify.request
2 |
3 | var amplify = {
4 |
5 | request: function( resourceId ) {
6 | /// amplify.request( hash settings )
34 |
35 | Request a resource.
36 |
37 | * `settings`: A set of key/value pairs of settings for the request.
38 | * `resourceId`: Identifier string for the resource.
39 | * `data` (optional): Data associated with the request.
40 | * `success` (optional): Function to invoke on success.
41 | * `error` (optional): Function to invoke on error.
42 |
43 | amplify.request.define(
44 | string resourceId, string requestType [, hash settings ] )
45 |
46 | Define a resource.
47 |
48 | * `resourceId`: Identifier string for the resource.
49 | * `requestType`: The type of data retrieval method from the server.
50 | See the [request types][requestTypes] sections for more information.
51 | * `settings`: A set of key/value pairs that relate to the server
52 | communication technology. The following settings are available:
53 | * Any settings found in jQuery.ajax().
54 | * `cache`: See the [cache][cache] section for more details.
55 | * `decoder`: See the [decoder][decoder] section for more details.
56 |
57 | amplify.request.define( string resourceId, function resource )
58 |
59 | Define a custom request.
60 |
61 | * `resourceId`: Identifier string for the resource.
62 | * `resource`: Function to handle requests. Receives a hash with the following properties:
63 | * `resourceId`: Identifier string for the resource.
64 | * `data`: Data provided by the user.
65 | * `success`: Callback to invoke on success.
66 | * `error`: Callback to invoke on error.
67 |
68 | ## Request Types
69 |
70 | ### Built-in Types
71 |
72 | `amplify.request` comes with a built-in `ajax` type.
73 |
74 | **NOTE:** The `ajax` request type depends on the use of jQuery 1.4 or higher.
75 |
76 | ### Custom Types
77 |
78 | You can choose to create additional types by adding to the `amplify.request.types` hash.
79 | There is also an option to define custom one-off types for single requests.
80 |
81 | ## Data Handling
82 |
83 | ### Pre-defined Data
84 |
85 | When defining an ajax request, you can provide data in the definition.
86 | This data will be merged (via a deep extend) with any data provided with the request.
87 | Data provided with the request will override data provided in the definition.
88 |
89 | ### Data Mapping
90 |
91 | Data maps allow you to modify the data for an ajax request before the data is sent.
92 | You can use a hash to map keys or you can use a function for complete flexibility.
93 |
94 | ### URL substitution/routing
95 |
96 | You can define variables in the URL of an ajax request by wrapping the variable in curly braces, e.g., `"/user/{id}"`.
97 | The variable will be replaced by the respective value from the data provided in the request.
98 | Whenever a variable is replaced, the value is removed from the data (not submitted as GET/POST data).
99 | If there are variables that are not replaced, they will remain in the URL.
100 |
101 | ### Decoders
102 |
103 | Decoders allow you to parse an ajax response before calling the success or error callback.
104 | This allows you to return data marked with a status and react accordingly.
105 | This also allows you to manipulate the data any way you want before passing the data along to the callback.
106 |
107 | #### Built-in Decoders
108 |
109 | [JSend](http://labs.omniti.com/labs/jsend) is a built in decoder
110 | provided with the library.
111 |
112 | #### Custom decoders
113 |
114 | You can define new decoders by adding to the `amplify.request.decoders` hash.
115 | A popular use case for decoders is when you have a JSON envelope that
116 | must be unpacked with each response from the server.
117 |
118 | You can also define custom one-off decoders for single requests, which
119 | is specified as a function in the settings hash for
120 | `amplify.request.define`.
121 |
122 | amplify.request.decoders.decoderName =
123 | function( hash data, string status, object xhr,
124 | function success, function error )
125 |
126 | Define a decoder. `decoderName` should be replaced with the decoder name
127 | of your choosing.
128 |
129 | * `data`: Data returned from the ajax request.
130 | * `status`: Status of the ajax request. See the [handling status][handlingstatus] section below.
131 | * `xhr`: A wrapped xhr object used to make the request.
132 | * `success`: Callback to invoke on success.
133 | * `error`: Callback to invoke on error.
134 |
135 | ## Cache
136 |
137 | ### In-memory Cache
138 |
139 | There is a built-in memory cache. You can pass a boolean to enable caching of a request, e.g., `cache: true`.
140 | You can also pass a number to specify that the response should be cached for a certain amount of time.
141 | For example, `cache: 30` will cache the response for 30 milliseconds.
142 |
143 | * `cache: boolean` Cache the data in memory for the remainder of this page load.
144 | * `cache: number` Cache the data in memory for the specified number of milliseconds.
145 |
146 | ### Named Caches
147 |
148 | * `cache: string` Cache the data using a pre-defined caching mechanism.
149 |
150 | cache: {
151 | type: string,
152 | expires: number
153 | }
154 |
155 |
156 | Cache the data using a pre-defined caching mechanism for the specified number
157 | of milliseconds.
158 |
159 | #### Persistent Client-side Cache
160 |
161 | You can also persist a cache if `amplify.store` has been loaded.
162 | You can specify `cache: "persist"` to cache in the default store
163 | or you can specify any of the specific stores available, e.g., `cache: "localStorage"`.
164 | You can also use the hash form listed above to store in a persistent cache with
165 | an expiration.
166 |
167 | _Note: You must include `amplify.store.js` before `amplify.request.js` to use persistent caches._
168 |
169 | ### Custom Cache
170 |
171 | You can also create additional cache types by adding to the `amplify.request.cache` hash.
172 |
173 | amplify.request.cache.customCacheName =
174 | function( hash resource, hash settings, hash ajaxSettings )
175 |
176 | Definition for a caching mechanism. `customCacheName` should be replaced
177 | with the custom name of your choosing.
178 |
179 | * `resource`: The definition of the resource being requested.
180 | * `settings`: The settings for the request.
181 | * `ajaxSettings`: The settings that will be passed to `jQuery.ajax()`.
182 |
183 | ## Examples
184 |
185 | The examples assume that the request location returns the following as
186 | json unless specified otherwise:
187 |
188 | {
189 | "foo" : "bar",
190 | "baz" : "qux"
191 | }
192 |
193 | ### Set up and use a request utilizing Ajax
194 |
195 | amplify.request.define( "ajaxExample1", "ajax", {
196 | url: "/myApiUrl",
197 | dataType: "json",
198 | type: "GET"
199 | });
200 |
201 | // later in code
202 | amplify.request( "ajaxExample1", function( data ) {
203 | data.foo; // bar
204 | });
205 |
206 | ### Set up and use a request utilizing Ajax and Caching
207 |
208 | amplify.request.define( "ajaxExample2", "ajax", {
209 | url: "/myApiUrl",
210 | dataType: "json",
211 | type: "GET",
212 | cache: "persist"
213 | });
214 |
215 | // later in code
216 | amplify.request( "ajaxExample2", function( data ) {
217 | data.foo; // bar
218 | });
219 |
220 | // a second call will result in pulling from the cache
221 | amplify.request( "ajaxExample2", function( data ) {
222 | data.baz; // qux
223 | })
224 |
225 | ### Set up and use a RESTful request utilizing Ajax
226 |
227 | amplify.request.define( "ajaxRESTFulExample", "ajax", {
228 | url: "/myRestFulApi/{type}/{id}",
229 | type: "GET"
230 | })
231 |
232 | // later in code
233 | amplify.request( "ajaxRESTFulExample",
234 | {
235 | type: "foo",
236 | id: "bar"
237 | },
238 | function( data ) {
239 | // /myRESTFulApi/foo/bar was the URL used
240 | data.foo; // bar
241 | }
242 | );
243 |
244 | ### POST data with Ajax
245 |
246 | amplify.request.define( "ajaxPostExample", "ajax", {
247 | url: "/myRestFulApi",
248 | type: "POST"
249 | })
250 |
251 | // later in code
252 | amplify.request( "ajaxPostExample",
253 | {
254 | type: "foo",
255 | id: "bar"
256 | },
257 | function( data ) {
258 | data.foo; // bar
259 | }
260 | );
261 |
262 | ### Using data maps
263 |
264 | When searching Twitter, the key for the search phrase is `q`.
265 | If we want a more descriptive name, such as `term`, we can use a data map:
266 |
267 | amplify.request.define( "twitter-search", "ajax", {
268 | url: "http://search.twitter.com/search.json",
269 | dataType: "jsonp",
270 | dataMap: {
271 | term: "q"
272 | }
273 | });
274 |
275 | amplify.request( "twitter-search", { term: "amplifyjs" }, ... );
276 |
277 | Similarly, we can create a request that searches for mentions, by accepting a username:
278 |
279 | amplify.request.define( "twitter-mentions", "ajax", {
280 | url: "http://search.twitter.com/search.json",
281 | dataType: "jsonp",
282 | dataMap: function( data ) {
283 | return {
284 | q: "@" + data.user
285 | };
286 | }
287 | });
288 |
289 | amplify.request( "twitter-mentions", { user: "amplifyjs" }, ... );
290 |
291 | ### Setting up and using decoders
292 |
293 | This example assumes the following envelope format:
294 |
295 | **Success:**
296 |
297 | {
298 | "status": "success",
299 | "data" : {
300 | "foo": "bar",
301 | "baz": "qux"
302 | }
303 | }
304 |
305 | **Fail ( or Error ):**
306 |
307 | {
308 | "status": "fail",
309 | "message": "failure message."
310 | }
311 |
312 | **Example:**
313 |
314 | amplify.request.decoders.appEnvelope =
315 | function ( data, status, xhr, success, error ) {
316 | if ( data.status === "success" ) {
317 | success( data.data );
318 | } else if ( data.status === "fail" || data.status === "error" ) {
319 | error( data.message, data.status );
320 | } else {
321 | error( data.message , "fatal" );
322 | }
323 | };
324 |
325 | amplify.request.define( "decoderExample", "ajax", {
326 | url: "/myAjaxUrl",
327 | type: "POST",
328 | decoder: "appEnvelope"
329 | });
330 |
331 | amplify.request({
332 | resourceId: "decoderExample",
333 | success: function( data ) {
334 | data.foo; // bar
335 | },
336 | error: function( message, level ) {
337 | alert( "always handle errors with alerts." );
338 | }
339 | });
340 |
341 | ### POST with caching and single-use decoder
342 |
343 | This example assumes the following envelope format:
344 |
345 | **Success:**
346 |
347 | {
348 | "status": "success",
349 | "data" : {
350 | "foo": "bar",
351 | "baz": "qux"
352 | }
353 | }
354 |
355 | **Fail ( or Error ):**
356 |
357 | {
358 | "status": "fail",
359 | "message": "failure message."
360 | }
361 |
362 | **Example:**
363 |
364 | amplify.request.define( "decoderSingleExample", "ajax", {
365 | url: "/myAjaxUrl",
366 | type: "POST",
367 | decoder: function ( data, status, xhr, success, error ) {
368 | if ( data.status === "success" ) {
369 | success( data.data );
370 | } else if ( data.status === "fail" || data.status === "error" ) {
371 | error( data.message, data.status );
372 | } else {
373 | error( data.message , "fatal" );
374 | }
375 | }
376 | });
377 |
378 | amplify.request({
379 | resourceId: "decoderSingleExample",
380 | success: function( data ) {
381 | data.foo; // bar
382 | },
383 | error: function( message, level ) {
384 | alert( "always handle errors with alerts." );
385 | }
386 | });
387 |
388 | ## Handling Status
389 |
390 |
391 | ### Status in Success and Error Callbacks
392 |
393 | `amplify.request` comes with built in support for status. The status
394 | parameter appears in the default success or error callbacks when using
395 | an ajax definition.
396 |
397 | amplify.request.define( "statusExample1", "ajax", {
398 | //...
399 | });
400 |
401 | amplify.request({
402 | resourceId: "statusExample1",
403 | success: function( data, status ) {
404 | },
405 | error: function( data, status ) {
406 | }
407 | });
408 |
409 | With the success callback, the only default status is `success`.
410 | With the error callback two default statuses are possible: `error` and
411 | `abort`.
412 |
413 | ### Status Codes and Decoders
414 |
415 | When specifying a custom decoder for request definition a status code
416 | will be passed in as a parameter. You can determine results from a request based on
417 | this status. When a success or error callback is executed, the
418 | appropriate status will be set by `amplify.request`.
419 |
420 | **A basic decoder example:**
421 |
422 | amplify.request.define( "statusExample2", "ajax", {
423 | decoder: function( data, status, xhr, success, error ) {
424 | if ( status === "success" ) {
425 | success( data );
426 | } else {
427 | error( data );
428 | }
429 | }
430 | });
431 |
432 | amplify.request({
433 | resourceId: "statusExample2",
434 | success: function( data, status ) {
435 | // status will be "success"
436 | },
437 | error: function( data, status ) {
438 | // status could be "error" or "abort"
439 | }
440 | });
441 |
442 | **A request is aborted by using the object returned by a request call:**
443 |
444 | amplify.request.define( "statusExample3", "ajax", {
445 | //...
446 | });
447 |
448 | var myRequest = amplify.request({
449 | resourceId: "statusExample3",
450 | success: function( data, status ) {
451 | // status will be "success"
452 | },
453 | error: function( data, status ) {
454 | // status could be "abort"
455 | }
456 | });
457 |
458 | // sometime later in code
459 | myRequest.abort();
460 |
461 | ### Subscribing to status events
462 |
463 | For an alternative to handling issues and statuses you can subscribe to a series of
464 | globally available messages that are published during the request process.
465 |
466 | subscribe( "request.error", function callback );
467 |
468 | Subscribe a function to be executed when any error callback is invoked
469 | for any request.
470 |
471 | subscribe( "request.success", function callback );
472 |
473 | Subscribe a function to be executed when any success callback is invoked
474 | for any request.
475 |
476 | subscribe( "request.complete", function callback );
477 |
478 | Subscribe a function to be executed when any request complete,
479 | regardless of error or success.
480 |
481 | The subscriptions and statuses can be used to create issue handlers:
482 |
483 | subscribe( "request.error", function( settings, data, status ) {
484 | if ( status === "abort" ) {
485 | // deal with explicit abort of request
486 | } else {
487 | // deal with normal error
488 | }
489 | });
490 |
491 | ### Statuses with jsend
492 |
493 | The jsend request type has an extra default status. The [jsend
494 | spec](http://labs.omniti.com/labs/jsend) includes a fail status. If a
495 | jsend fail occurs, the error callback ( and appropriate error
496 | subscriptions ) will be called with a status of `fail`.
497 |
498 | ### Customizing statuses
499 |
500 | When calling a success or error callback through a decoder you can
501 | specify a custom status to be sent to the callback as the second
502 | parameter for the callback function.
503 |
504 | **An example with a success callback:**
505 |
506 | amplify.request.define( "customStatusExample", "ajax", {
507 | decoder: function( data, status, xhr, success, error ) {
508 | var customStatus = status;
509 | if ( someWarningCondition ) {
510 | customStatus = "warning";
511 | }
512 | success( data, "warning" )
513 | }
514 | });
515 |
516 | amplify.request({
517 | resourceId: "customStatusExample",
518 | success: function( data, status ) {
519 | // status could be "success" or "warning"
520 | }
521 | });
522 |
523 |
524 | **An example with an error callback:**
525 |
526 | amplify.request.define( "customStatusExample2", "ajax", {
527 | decoder: function( data, status, xhr, success, error ) {
528 | var customStatus = status;
529 | if ( status === "error" && someCriticalCondition ) {
530 | customStatus = "zomg";
531 | }
532 | if ( status != "success" ) {
533 | error( data, customStatus );
534 | }
535 | }
536 | });
537 |
538 | amplify.request({
539 | resourceId: "customStatusExample2",
540 | error: function( data, status ) {
541 | // status could be "error", "abort", or "zomg"
542 | }
543 | });
544 |
545 |
546 | [requestTypes]: #request_types "request types"
547 | [cache]: #cache "caching"
548 | [decoder]: #decoders "decoders"
549 | [examples]: #examples "examples"
550 | [handlingstatus]: #handling_status "handling status"
--------------------------------------------------------------------------------
/external/json2.js:
--------------------------------------------------------------------------------
1 | /*
2 | http://www.JSON.org/json2.js
3 | 2010-08-25
4 |
5 | Public Domain.
6 |
7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 |
9 | See http://www.JSON.org/js.html
10 |
11 |
12 | This code should be minified before deployment.
13 | See http://javascript.crockford.com/jsmin.html
14 |
15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
16 | NOT CONTROL.
17 |
18 |
19 | This file creates a global JSON object containing two methods: stringify
20 | and parse.
21 |
22 | JSON.stringify(value, replacer, space)
23 | value any JavaScript value, usually an object or array.
24 |
25 | replacer an optional parameter that determines how object
26 | values are stringified for objects. It can be a
27 | function or an array of strings.
28 |
29 | space an optional parameter that specifies the indentation
30 | of nested structures. If it is omitted, the text will
31 | be packed without extra whitespace. If it is a number,
32 | it will specify the number of spaces to indent at each
33 | level. If it is a string (such as '\t' or ' '),
34 | it contains the characters used to indent at each level.
35 |
36 | This method produces a JSON text from a JavaScript value.
37 |
38 | When an object value is found, if the object contains a toJSON
39 | method, its toJSON method will be called and the result will be
40 | stringified. A toJSON method does not serialize: it returns the
41 | value represented by the name/value pair that should be serialized,
42 | or undefined if nothing should be serialized. The toJSON method
43 | will be passed the key associated with the value, and this will be
44 | bound to the value
45 |
46 | For example, this would serialize Dates as ISO strings.
47 |
48 | Date.prototype.toJSON = function (key) {
49 | function f(n) {
50 | // Format integers to have at least two digits.
51 | return n < 10 ? '0' + n : n;
52 | }
53 |
54 | return this.getUTCFullYear() + '-' +
55 | f(this.getUTCMonth() + 1) + '-' +
56 | f(this.getUTCDate()) + 'T' +
57 | f(this.getUTCHours()) + ':' +
58 | f(this.getUTCMinutes()) + ':' +
59 | f(this.getUTCSeconds()) + 'Z';
60 | };
61 |
62 | You can provide an optional replacer method. It will be passed the
63 | key and value of each member, with this bound to the containing
64 | object. The value that is returned from your method will be
65 | serialized. If your method returns undefined, then the member will
66 | be excluded from the serialization.
67 |
68 | If the replacer parameter is an array of strings, then it will be
69 | used to select the members to be serialized. It filters the results
70 | such that only members with keys listed in the replacer array are
71 | stringified.
72 |
73 | Values that do not have JSON representations, such as undefined or
74 | functions, will not be serialized. Such values in objects will be
75 | dropped; in arrays they will be replaced with null. You can use
76 | a replacer function to replace those with JSON values.
77 | JSON.stringify(undefined) returns undefined.
78 |
79 | The optional space parameter produces a stringification of the
80 | value that is filled with line breaks and indentation to make it
81 | easier to read.
82 |
83 | If the space parameter is a non-empty string, then that string will
84 | be used for indentation. If the space parameter is a number, then
85 | the indentation will be that many spaces.
86 |
87 | Example:
88 |
89 | text = JSON.stringify(['e', {pluribus: 'unum'}]);
90 | // text is '["e",{"pluribus":"unum"}]'
91 |
92 |
93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
95 |
96 | text = JSON.stringify([new Date()], function (key, value) {
97 | return this[key] instanceof Date ?
98 | 'Date(' + this[key] + ')' : value;
99 | });
100 | // text is '["Date(---current time---)"]'
101 |
102 |
103 | JSON.parse(text, reviver)
104 | This method parses a JSON text to produce an object or array.
105 | It can throw a SyntaxError exception.
106 |
107 | The optional reviver parameter is a function that can filter and
108 | transform the results. It receives each of the keys and values,
109 | and its return value is used instead of the original value.
110 | If it returns what it received, then the structure is not modified.
111 | If it returns undefined then the member is deleted.
112 |
113 | Example:
114 |
115 | // Parse the text. Values that look like ISO date strings will
116 | // be converted to Date objects.
117 |
118 | myData = JSON.parse(text, function (key, value) {
119 | var a;
120 | if (typeof value === 'string') {
121 | a =
122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
123 | if (a) {
124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
125 | +a[5], +a[6]));
126 | }
127 | }
128 | return value;
129 | });
130 |
131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
132 | var d;
133 | if (typeof value === 'string' &&
134 | value.slice(0, 5) === 'Date(' &&
135 | value.slice(-1) === ')') {
136 | d = new Date(value.slice(5, -1));
137 | if (d) {
138 | return d;
139 | }
140 | }
141 | return value;
142 | });
143 |
144 |
145 | This is a reference implementation. You are free to copy, modify, or
146 | redistribute.
147 | */
148 |
149 | /*jslint evil: true, strict: false */
150 |
151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
154 | lastIndex, length, parse, prototype, push, replace, slice, stringify,
155 | test, toJSON, toString, valueOf
156 | */
157 |
158 |
159 | // Create a JSON object only if one does not already exist. We create the
160 | // methods in a closure to avoid creating global variables.
161 |
162 | if (!this.JSON) {
163 | this.JSON = {};
164 | }
165 |
166 | (function () {
167 |
168 | function f(n) {
169 | // Format integers to have at least two digits.
170 | return n < 10 ? '0' + n : n;
171 | }
172 |
173 | if (typeof Date.prototype.toJSON !== 'function') {
174 |
175 | Date.prototype.toJSON = function (key) {
176 |
177 | return isFinite(this.valueOf()) ?
178 | this.getUTCFullYear() + '-' +
179 | f(this.getUTCMonth() + 1) + '-' +
180 | f(this.getUTCDate()) + 'T' +
181 | f(this.getUTCHours()) + ':' +
182 | f(this.getUTCMinutes()) + ':' +
183 | f(this.getUTCSeconds()) + 'Z' : null;
184 | };
185 |
186 | String.prototype.toJSON =
187 | Number.prototype.toJSON =
188 | Boolean.prototype.toJSON = function (key) {
189 | return this.valueOf();
190 | };
191 | }
192 |
193 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
194 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
195 | gap,
196 | indent,
197 | meta = { // table of character substitutions
198 | '\b': '\\b',
199 | '\t': '\\t',
200 | '\n': '\\n',
201 | '\f': '\\f',
202 | '\r': '\\r',
203 | '"' : '\\"',
204 | '\\': '\\\\'
205 | },
206 | rep;
207 |
208 |
209 | function quote(string) {
210 |
211 | // If the string contains no control characters, no quote characters, and no
212 | // backslash characters, then we can safely slap some quotes around it.
213 | // Otherwise we must also replace the offending characters with safe escape
214 | // sequences.
215 |
216 | escapable.lastIndex = 0;
217 | return escapable.test(string) ?
218 | '"' + string.replace(escapable, function (a) {
219 | var c = meta[a];
220 | return typeof c === 'string' ? c :
221 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
222 | }) + '"' :
223 | '"' + string + '"';
224 | }
225 |
226 |
227 | function str(key, holder) {
228 |
229 | // Produce a string from holder[key].
230 |
231 | var i, // The loop counter.
232 | k, // The member key.
233 | v, // The member value.
234 | length,
235 | mind = gap,
236 | partial,
237 | value = holder[key];
238 |
239 | // If the value has a toJSON method, call it to obtain a replacement value.
240 |
241 | if (value && typeof value === 'object' &&
242 | typeof value.toJSON === 'function') {
243 | value = value.toJSON(key);
244 | }
245 |
246 | // If we were called with a replacer function, then call the replacer to
247 | // obtain a replacement value.
248 |
249 | if (typeof rep === 'function') {
250 | value = rep.call(holder, key, value);
251 | }
252 |
253 | // What happens next depends on the value's type.
254 |
255 | switch (typeof value) {
256 | case 'string':
257 | return quote(value);
258 |
259 | case 'number':
260 |
261 | // JSON numbers must be finite. Encode non-finite numbers as null.
262 |
263 | return isFinite(value) ? String(value) : 'null';
264 |
265 | case 'boolean':
266 | case 'null':
267 |
268 | // If the value is a boolean or null, convert it to a string. Note:
269 | // typeof null does not produce 'null'. The case is included here in
270 | // the remote chance that this gets fixed someday.
271 |
272 | return String(value);
273 |
274 | // If the type is 'object', we might be dealing with an object or an array or
275 | // null.
276 |
277 | case 'object':
278 |
279 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
280 | // so watch out for that case.
281 |
282 | if (!value) {
283 | return 'null';
284 | }
285 |
286 | // Make an array to hold the partial results of stringifying this object value.
287 |
288 | gap += indent;
289 | partial = [];
290 |
291 | // Is the value an array?
292 |
293 | if (Object.prototype.toString.apply(value) === '[object Array]') {
294 |
295 | // The value is an array. Stringify every element. Use null as a placeholder
296 | // for non-JSON values.
297 |
298 | length = value.length;
299 | for (i = 0; i < length; i += 1) {
300 | partial[i] = str(i, value) || 'null';
301 | }
302 |
303 | // Join all of the elements together, separated with commas, and wrap them in
304 | // brackets.
305 |
306 | v = partial.length === 0 ? '[]' :
307 | gap ? '[\n' + gap +
308 | partial.join(',\n' + gap) + '\n' +
309 | mind + ']' :
310 | '[' + partial.join(',') + ']';
311 | gap = mind;
312 | return v;
313 | }
314 |
315 | // If the replacer is an array, use it to select the members to be stringified.
316 |
317 | if (rep && typeof rep === 'object') {
318 | length = rep.length;
319 | for (i = 0; i < length; i += 1) {
320 | k = rep[i];
321 | if (typeof k === 'string') {
322 | v = str(k, value);
323 | if (v) {
324 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
325 | }
326 | }
327 | }
328 | } else {
329 |
330 | // Otherwise, iterate through all of the keys in the object.
331 |
332 | for (k in value) {
333 | if (Object.hasOwnProperty.call(value, k)) {
334 | v = str(k, value);
335 | if (v) {
336 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
337 | }
338 | }
339 | }
340 | }
341 |
342 | // Join all of the member texts together, separated with commas,
343 | // and wrap them in braces.
344 |
345 | v = partial.length === 0 ? '{}' :
346 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
347 | mind + '}' : '{' + partial.join(',') + '}';
348 | gap = mind;
349 | return v;
350 | }
351 | }
352 |
353 | // If the JSON object does not yet have a stringify method, give it one.
354 |
355 | if (typeof JSON.stringify !== 'function') {
356 | JSON.stringify = function (value, replacer, space) {
357 |
358 | // The stringify method takes a value and an optional replacer, and an optional
359 | // space parameter, and returns a JSON text. The replacer can be a function
360 | // that can replace values, or an array of strings that will select the keys.
361 | // A default replacer method can be provided. Use of the space parameter can
362 | // produce text that is more easily readable.
363 |
364 | var i;
365 | gap = '';
366 | indent = '';
367 |
368 | // If the space parameter is a number, make an indent string containing that
369 | // many spaces.
370 |
371 | if (typeof space === 'number') {
372 | for (i = 0; i < space; i += 1) {
373 | indent += ' ';
374 | }
375 |
376 | // If the space parameter is a string, it will be used as the indent string.
377 |
378 | } else if (typeof space === 'string') {
379 | indent = space;
380 | }
381 |
382 | // If there is a replacer, it must be a function or an array.
383 | // Otherwise, throw an error.
384 |
385 | rep = replacer;
386 | if (replacer && typeof replacer !== 'function' &&
387 | (typeof replacer !== 'object' ||
388 | typeof replacer.length !== 'number')) {
389 | throw new Error('JSON.stringify');
390 | }
391 |
392 | // Make a fake root object containing our value under the key of ''.
393 | // Return the result of stringifying the value.
394 |
395 | return str('', {'': value});
396 | };
397 | }
398 |
399 |
400 | // If the JSON object does not yet have a parse method, give it one.
401 |
402 | if (typeof JSON.parse !== 'function') {
403 | JSON.parse = function (text, reviver) {
404 |
405 | // The parse method takes a text and an optional reviver function, and returns
406 | // a JavaScript value if the text is a valid JSON text.
407 |
408 | var j;
409 |
410 | function walk(holder, key) {
411 |
412 | // The walk method is used to recursively walk the resulting structure so
413 | // that modifications can be made.
414 |
415 | var k, v, value = holder[key];
416 | if (value && typeof value === 'object') {
417 | for (k in value) {
418 | if (Object.hasOwnProperty.call(value, k)) {
419 | v = walk(value, k);
420 | if (v !== undefined) {
421 | value[k] = v;
422 | } else {
423 | delete value[k];
424 | }
425 | }
426 | }
427 | }
428 | return reviver.call(holder, key, value);
429 | }
430 |
431 |
432 | // Parsing happens in four stages. In the first stage, we replace certain
433 | // Unicode characters with escape sequences. JavaScript handles many characters
434 | // incorrectly, either silently deleting them, or treating them as line endings.
435 |
436 | text = String(text);
437 | cx.lastIndex = 0;
438 | if (cx.test(text)) {
439 | text = text.replace(cx, function (a) {
440 | return '\\u' +
441 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
442 | });
443 | }
444 |
445 | // In the second stage, we run the text against regular expressions that look
446 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
447 | // because they can cause invocation, and '=' because it can cause mutation.
448 | // But just to be safe, we want to reject all unexpected forms.
449 |
450 | // We split the second stage into 4 regexp operations in order to work around
451 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
452 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
453 | // replace all simple value tokens with ']' characters. Third, we delete all
454 | // open brackets that follow a colon or comma or that begin the text. Finally,
455 | // we look to see that the remaining characters are only whitespace or ']' or
456 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
457 |
458 | if (/^[\],:{}\s]*$/
459 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
460 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
461 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
462 |
463 | // In the third stage we use the eval function to compile the text into a
464 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
465 | // in JavaScript: it can begin a block or an object literal. We wrap the text
466 | // in parens to eliminate the ambiguity.
467 |
468 | j = eval('(' + text + ')');
469 |
470 | // In the optional fourth stage, we recursively walk the new structure, passing
471 | // each name/value pair to a reviver function for possible transformation.
472 |
473 | return typeof reviver === 'function' ?
474 | walk({'': j}, '') : j;
475 | }
476 |
477 | // If the text is not JSON parseable, then a SyntaxError is thrown.
478 |
479 | throw new SyntaxError('JSON.parse');
480 | };
481 | }
482 | }());
483 |
--------------------------------------------------------------------------------