├── .eslintrc.js ├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── bower.json ├── jquery.serializejson.js ├── jquery.serializejson.min.js ├── package-lock.json ├── package.json └── spec ├── lib ├── jasmine-2.0.0 │ ├── boot.js │ ├── console.js │ ├── jasmine-html.js │ ├── jasmine.css │ ├── jasmine.js │ └── jasmine_favicon.png ├── jquery-2.1.3.js └── zepto-1.1.6.js ├── spec.js ├── spec_runner_jquery.html └── spec_runner_zepto.html /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 2 | /* global module */ 3 | module.exports = { 4 | "env": { 5 | "browser": true, 6 | "jquery": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "ecmaVersion": 5 11 | }, 12 | "rules": { 13 | "indent": [ 14 | "error", 15 | 4 16 | ], 17 | "linebreak-style": [ 18 | "error", 19 | "unix" 20 | ], 21 | "quotes": [ 22 | "error", 23 | "double" 24 | ], 25 | "semi": [ 26 | "error", 27 | "always" 28 | ] 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | --------- 3 | 4 | * *3.2.1* (Feb 24, 2021): Fix sync issue between Github tag and npm. 5 | * *3.2.0* (Feb 24, 2021): Reimplement jQuery's serializeArray function, with the ability to include unchecked checkboxes, and returning the DOM element. This allows to simplify the code, fixing an issue with repeated input names used for arrays (Fixes #67), and allows custom type functions to receive the DOM elemnt (Fixes #109). 6 | * *3.1.1* (Dec 30, 2020): Update #114 (Allow to use new versions of jQuery by avoiding calls to the deprecated method `jQuery.isArray`). 7 | * *3.1.1* (Nov 09, 2020): Bugfix #110 (Allow unindexed arrays with multiple levels of nested objects). 8 | * *3.1.0* (Sep 13, 2020): Rename option `disableColonTypes` that was mistakenly named `disableSemicolonTypes`. Fix typos in README. 9 | * *3.0.0* (Sep 06, 2020): Improve types (PR #105) and remove parsing options (PR #104). The type system with `:type` suffixes, `data-value-type` attributes, and a combination of the options `customTypes`, `disableColonTypes` and `defaultType`, are safer and easier to use than the previous options `parseNumbers`, `parseAll`, etc. Thanks [Laykou](https://github.com/Laykou) for suggesting [PR #102] that pointed the problems of inputs with colons in their names. 10 | * *2.9.0* (Jan 12, 2018): Overrides to `customTypes.string` function now also apply to fields with no type, because `:string` is the default implicit type. Thanks [JocaPC](https://github.com/JocaPC) for reporting the [issue #83](https://github.com/marioizquierdo/jquery.serializeJSON/issues/83). 11 | * *2.8.1* (Dec 09, 2016): Identify issue #67 and throw a descriptive error with a link to the issue, that explains why nested arrays of objects with checkboxes with unchecked values are not supported. 12 | * *2.8.0* (Dec 09, 2016): Add options `skipFalsyValuesForFields`, `skipFalsyValuesForTypes` and attr `data-skip-falsy` to easily skip falsy values (which includes empty strings). Thanks to [milkaknap](https://github.com/milkaknap). 13 | * *2.7.2* (Dec 19, 2015): Bugfix #55 (Allow data types with the `data-value-type` attribute to use brackets in names). Thanks to [stricte](https://github.com/stricte). 14 | * *2.7.1* (Dec 12, 2015): Bugfix #54 (`data-value-type` attribute only works with input elements). Thanks to [madrabaz](https://github.com/madrabaz). 15 | * *2.7.0* (Nov 28, 2015): Allow to define custom types with the `data-value-type` attribute. Thanks to [madrabaz](https://github.com/madrabaz). 16 | * *2.6.2* (Oct 24, 2015): Add support for AMD/CommonJS/Browserify modules. Thanks to [jisaacks](https://github.com/jisaacks). 17 | * *2.6.1* (May 13, 2015): Bugfix #43 (Fix IE 8 compatibility). Thanks to [rywall](https://github.com/rywall). 18 | * *2.6.0* (Apr 24, 2015): Allow to define custom types with the option `customTypes` and inspect/override default types with the option `defaultTypes`. Thanks to [tygriffin](https://github.com/tygriffin) for the [pull request](https://github.com/marioizquierdo/jquery.serializeJSON/pull/40). 19 | * *2.5.0* (Mar 11, 2015): Override serialized properties if using the same name, even for nested values, instead of crashing the script, fixing issue#29. Also fix a crash when using Zepto and the data-unchecked-value option. 20 | * *2.4.2* (Feb 04, 2015): Ignore disabled checkboxes with "data-unchecked-value". Thanks to [skarr](https://github.com/skarr) for the [pull request](https://github.com/marioizquierdo/jquery.serializeJSON/pull/33). 21 | * *2.4.1* (Oct 12, 2014): Add `:auto` type, that works like the `parseAll` option, but targeted to a single input. 22 | * *2.4.0* (Oct 12, 2014): Implement :types. Types allow to easily specify how to parse each input. 23 | * *2.3.2* (Oct 11, 2014): Bugfix #27 (parsing error on nested keys like name="foo[inn[bar]]"). Thanks to [danlo](https://github.com/danlo) for finding the issue. 24 | * *2.3.1* (Oct 06, 2014): Bugfix #22 (ignore checkboxes with no name when doing `checkboxUncheckedValue`). Thanks to [KATT](https://github.com/KATT) for finding and fixing the issue. 25 | * *2.3.0* (Sep 25, 2014): Properly spell "data-unckecked-value", change for "data-unchecked-value" 26 | * *2.2.0* (Sep 17, 2014): Add option `checkboxUncheckedValue` and attribute `data-unckecked-value` to allow parsing unchecked checkboxes. 27 | * *2.1.0* (Jun 08, 2014): Add option `parseWithFunction` to allow custom parsers. And fix issue #14: empty strings were parsed as a zero when `parseNumbers` option was true. 28 | * *2.0.0* (May 04, 2014): Nested keys are always object attributes by default (discussed on issue #12). Set option `$.serializeJSON.defaultOptions.useIntKeysAsArrayIndex = true;` for backwards compatibility (see **Options** section). Thanks to [joshuajabbour](https://github.com/joshuajabbour) for finding the issue. 29 | * *1.3.0* (May 03, 2014): Accept options {parseBooleans, parseNumbers, parseNulls, parseAll} to modify what type to values are interpreted from the strings. Thanks to [diaswrd](https://github.com/diaswrd) for finding the issue. 30 | * *1.2.3* (Apr 12, 2014): Lowercase filenames. 31 | * *1.2.2* (Apr 03, 2014): Now also works with [Zepto.js](http://zeptojs.com/). 32 | * *1.2.1* (Mar 17, 2014): Refactor, cleanup, lint code and improve test coverage. 33 | * *1.2.0* (Mar 11, 2014): Arrays with empty index and objects with empty values are added and not overriden. Thanks to [kotas](https://github.com/kotas). 34 | * *1.1.1* (Feb 16, 2014): Only unsigned integers are used to create arrays. Alphanumeric keys are always for objects. Thanks to [Nicocin](https://github.com/Nicocin). 35 | * *1.0.2* (Jan 07, 2014): Tag to be on the jQuery plugin registry. 36 | * *1.0.1* (Aug 20, 2012): Bugfix: ensure that generated arrays are being displayed when parsed with JSON.stringify 37 | * *1.0.0* (Aug 20, 2012): Initial release 38 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2021 Mario Izquierdo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jquery.serializeJSON 2 | ==================== 3 | 4 | Adds the method `.serializeJSON()` to [jQuery](http://jquery.com/) to serializes a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails. 5 | 6 | Install 7 | ------- 8 | 9 | Install with [bower](http://bower.io/) `bower install jquery.serializeJSON`, or [npm](https://www.npmjs.com/) `npm install jquery-serializejson`, or just download the [jquery.serializejson.js](https://raw.githubusercontent.com/marioizquierdo/jquery.serializeJSON/master/jquery.serializejson.js) script. 10 | 11 | And make sure it is included after jQuery, for example: 12 | ```html 13 | 14 | 15 | ``` 16 | 17 | Usage Example 18 | ------------- 19 | 20 | HTML form: 21 | ```html 22 |
23 | 24 | 25 | 26 |
27 | ``` 28 | 29 | JavaScript: 30 | ```javascript 31 | $('form').serializeJSON(); 32 | 33 | // returns => 34 | { 35 | title: "Dune", 36 | author: { 37 | name: "Frank Herbert", 38 | period: "1945–1986" 39 | } 40 | } 41 | ``` 42 | 43 | Nested attributes and arrays can be specified by naming fields with the syntax: `name="attr[nested][nested]"`. 44 | 45 | HTML form: 46 | ```html 47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 77 | 78 | 79 | 84 |
85 | 86 | ``` 87 | 88 | JavaScript: 89 | 90 | ```javascript 91 | $('#my-profile').serializeJSON(); 92 | 93 | // returns => 94 | { 95 | fullName: "Mario", 96 | 97 | address: { 98 | city: "San Francisco", 99 | state: { 100 | name: "California", 101 | abbr: "CA" 102 | } 103 | }, 104 | 105 | jobbies: ["code", "climbing"], 106 | 107 | projects: { 108 | '0': { name: "serializeJSON", language: "javascript", popular: "1" }, 109 | '1': { name: "tinytest.js", language: "javascript", popular: "0" } 110 | }, 111 | 112 | selectOne: "rock", 113 | selectMultiple: ["red", "blue"] 114 | } 115 | ``` 116 | 117 | The `serializeJSON` function returns a JavaScript object, not a JSON String. The plugin should probably have been called `serializeObject` or similar, but that plugin name was already taken. 118 | 119 | To convert into a JSON String, use the `JSON.stringify` method, that is available on all major [new browsers](http://caniuse.com/json). 120 | If you need to support very old browsers, just include the [json2.js](https://github.com/douglascrockford/JSON-js) polyfill (as described on [stackoverfow](http://stackoverflow.com/questions/191881/serializing-to-json-in-jquery)). 121 | 122 | ```javascript 123 | var obj = $('form').serializeJSON(); 124 | var jsonString = JSON.stringify(obj); 125 | ``` 126 | 127 | The plugin serializes the same inputs supported by [.serializeArray()](https://api.jquery.com/serializeArray/), following the standard W3C rules for [successful controls](http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2). In particular, the included elements **cannot be disabled** and must contain a **name attribute**. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized. 128 | 129 | 130 | Parse values with :types 131 | ------------------------ 132 | 133 | Fields values are `:string` by default. But can be parsed with types by appending a `:type` suffix to the field name: 134 | 135 | ```html 136 |
137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
158 | ``` 159 | 160 | ```javascript 161 | $('form').serializeJSON(); 162 | 163 | // returns => 164 | { 165 | "default": ":string is the default", 166 | "text": "some text string", 167 | // excluded:skip is ignored in the output 168 | 169 | "numbers": { 170 | "1": 1, 171 | "1.1": 1.1, 172 | "other": NaN, // <-- "other" is parsed as NaN 173 | }, 174 | "bools": { 175 | "true": true, 176 | "false": false, 177 | "0": false, // <-- "false", "null", "undefined", "", "0" are parsed as false 178 | }, 179 | "nulls": { 180 | "null": null, // <-- "false", "null", "undefined", "", "0" are parsed as null 181 | "other": "other" // <-- if not null, the type is a string 182 | }, 183 | "arrays": { // <-- uses JSON.parse 184 | "empty": [], 185 | "not empty": [1,2,3] 186 | }, 187 | "objects": { // <-- uses JSON.parse 188 | "empty": {}, 189 | "not empty": {"my": "stuff"} 190 | } 191 | } 192 | ``` 193 | 194 | Types can also be specified with the attribute `data-value-type`, instead of adding the `:type` suffix in the field name: 195 | 196 | ```html 197 |
198 | 199 | 200 | 201 | 202 |
203 | ``` 204 | 205 | If your field names contain colons (e.g. `name="article[my::key][active]"`) the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type `:string` (e.g. `name="article[my::key][active]:string"`), or to use the attribute `data-value-type="string"`. Data attributes have precedence over `:type` name suffixes. It is also possible to disable parsing `:type` suffixes with the option `{ disableColonTypes: true }`. 206 | 207 | 208 | ### Custom Types 209 | 210 | Use the `customTypes` option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input. 211 | 212 | ```html 213 |
214 | 215 | 216 | 217 |
218 | ``` 219 | 220 | ```javascript 221 | $('form').serializeJSON({ 222 | customTypes: { 223 | alwaysBoo: (strVal, el) => { 224 | // strVal: is the input value as a string 225 | // el: is the dom element. $(el) would be the jQuery element 226 | return "boo"; // value returned in the serialization of this type 227 | }, 228 | } 229 | }); 230 | 231 | // returns => 232 | { 233 | "scary": "boo", // <-- parsed with custom type "alwaysBoo" 234 | "str": "str", 235 | "five": 5, 236 | } 237 | ``` 238 | 239 | The provided `customTypes` can include one of the `detaultTypes` to override the default behavior: 240 | 241 | ```javascript 242 | $('form').serializeJSON({ 243 | customTypes: { 244 | alwaysBoo: (strVal) => { return "boo"; }, 245 | string: (strVal) => { return strVal + "-OVERDRIVE"; }, 246 | } 247 | }); 248 | 249 | // returns => 250 | { 251 | "scary": "boo", // <-- parsed with custom type "alwaysBoo" 252 | "str": "str-OVERDRIVE", // <-- parsed with custom override "string" 253 | "five": 5, // <-- parsed with default type "number" 254 | } 255 | ``` 256 | 257 | Default types used by the plugin are defined in `$.serializeJSON.defaultOptions.defaultTypes`. 258 | 259 | 260 | Options 261 | ------- 262 | 263 | With no options, `.serializeJSON()` returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular: 264 | 265 | * Values are **strings** (unless appending a `:type` to the input name) 266 | * Unchecked checkboxes are ignored (as defined in the W3C rules for [successful controls](http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2)). 267 | * Disabled elements are ignored (W3C rules) 268 | * Keys (input names) are always **strings** (nested params are objects by default) 269 | 270 | Available options: 271 | 272 | * **checkboxUncheckedValue: string**, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: `{checkboxUncheckedValue: ""}` returns an empty string. If the field has a `:type`, the returned value will be properly parsed; for example if the field type is `:boolean`, it returns `false` instead of an empty string. 273 | * **useIntKeysAsArrayIndex: true**, when using integers as keys (i.e. ``), serialize as an array (`{"foods": ["banana"]}`) instead of an object (`{"foods": {"0": "banana"}`). 274 | * **skipFalsyValuesForFields: []**, skip given fields (by name) with falsy values. You can use `data-skip-falsy="true"` input attribute as well. Falsy values are determined after converting to a given type, note that `"0"` as `:string` (default) is still truthy, but `0` as `:number` is falsy. 275 | * **skipFalsyValuesForTypes: []**, skip given fields (by :type) with falsy values (i.e. `skipFalsyValuesForTypes: ["string", "number"]` would skip `""` for `:string` fields, and `0` for `:number` fields). 276 | * **customTypes: {}**, define your own `:type` functions. Defined as an object like `{ type: function(value){...} }`. For example: `{customTypes: {nullable: function(str){ return str || null; }}`. Custom types extend defaultTypes. 277 | * **defaultTypes: {defaults}**, contains the orignal type functions `string`, `number`, `boolean`, `null`, `array`, `object` and `skip`. 278 | * **defaultType: "string"**, fields that have no `:type` suffix and no `data-value-type` attribute are parsed with the `string` type function by default, but it could be changed to use a different type function instead. 279 | * **disableColonTypes: true**, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the `data-value-type` attribute. For example `` will be parsed as a number. 280 | 281 | More details about these options in the sections below. 282 | 283 | ## Include unchecked checkboxes 284 | 285 | One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked. 286 | 287 | To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values: 288 | 289 | ```html 290 | 291 | 292 | 293 | ``` 294 | 295 | This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled. 296 | 297 | But, to make things easier, `serializeJSON` includes the option `checkboxUncheckedValue` and the possibility to add the attribute `data-unchecked-value` to the checkboxes: 298 | 299 | ```html 300 |
301 | 302 | 303 | 304 |
305 | ``` 306 | 307 | Serializes like this by default: 308 | 309 | ```javascript 310 | $('form').serializeJSON(); 311 | 312 | // returns => 313 | {check1: 'true'} // check2 and check3 are ignored 314 | ``` 315 | 316 | To include all checkboxes, use the `checkboxUncheckedValue` option: 317 | 318 | ```javascript 319 | $('form').serializeJSON({checkboxUncheckedValue: "false"}); 320 | 321 | // returns => 322 | {check1: "true", check2: "false", check3: "false"} 323 | ``` 324 | 325 | The `data-unchecked-value` HTML attribute can be used to targed specific values per field: 326 | 327 | ```html 328 |
329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 |
337 | ``` 338 | 339 | ```javascript 340 | $('form#checkboxes').serializeJSON(); // No option is needed if the data attribute is used 341 | 342 | // returns => 343 | { 344 | 'checked': { 345 | 'b': true, 346 | 'numb': '1', 347 | 'cool': 'YUP' 348 | }, 349 | 'unchecked': { 350 | 'bool': false, 351 | 'bin': '0' 352 | // 'cool' is not included, because it doesn't use data-unchecked-value 353 | } 354 | } 355 | ``` 356 | 357 | You can use both the option `checkboxUncheckedValue` and the attribute `data-unchecked-value` at the same time, in which case the option is used as default value (the data attribute has precedence). 358 | 359 | ```javascript 360 | $('form#checkboxes').serializeJSON({checkboxUncheckedValue: 'NOPE'}); 361 | 362 | // returns => 363 | { 364 | 'checked': { 365 | 'b': true, 366 | 'numb': '1', 367 | 'cool': 'YUP' 368 | }, 369 | 'unchecked': { 370 | 'bool': false, // value from data-unchecked-value attribute, and parsed with type "boolean" 371 | 'bin': '0', // value from data-unchecked-value attribute 372 | 'cool': 'NOPE' // value from checkboxUncheckedValue option 373 | } 374 | } 375 | ``` 376 | 377 | ## Ignore Empty Form Fields 378 | 379 | You can use the option `.serializeJSON({skipFalsyValuesForTypes: ["string"]})`, which ignores any string field with an empty value (default type is :string, and empty strings are falsy). 380 | 381 | Another option, since `serializeJSON()` is called on a jQuery object, is to just use the proper jQuery selector to skip empty values (see [Issue #28](https://github.com/marioizquierdo/jquery.serializeJSON/issues/28) for more info): 382 | 383 | ```javascript 384 | // Select only imputs that have a non-empty value 385 | $('form :input[value!=""]').serializeJSON(); 386 | 387 | // Or filter them from the form 388 | obj = $('form').find('input').not('[value=""]').serializeJSON(); 389 | 390 | // For more complicated filtering, you can use a function 391 | obj = $form.find(':input').filter(function () { 392 | return $.trim(this.value).length > 0 393 | }).serializeJSON(); 394 | ``` 395 | 396 | 397 | ## Ignore Fields With Falsy Values 398 | 399 | When using :types, you can also skip falsy values (`false, "", 0, null, undefined, NaN`) by using the option `skipFalsyValuesForFields: ["fullName", "address[city]"]` or `skipFalsyValuesForTypes: ["string", "null"]`. 400 | 401 | Or setting a data attribute `data-skip-falsy="true"` on the inputs that should be ignored. Note that `data-skip-falsy` is aware of field :types, so it knows how to skip a non-empty input like this `` (Note that `"0"` as a string is not falsy, but `0` as number is falsy)). 402 | 403 | 404 | ## Use integer keys as array indexes 405 | 406 | By default, all serialized keys are **strings**, this includes keys that look like numbers like this: 407 | 408 | ```html 409 |
410 | 411 | 412 | 413 |
414 | ``` 415 | 416 | ```javascript 417 | $('form').serializeJSON(); 418 | 419 | // arr is an object => 420 | {'arr': {'0': 'foo', '1': 'var', '5': 'inn' }} 421 | ``` 422 | 423 | Which is how Rack [parse_nested_query](http://codefol.io/posts/How-Does-Rack-Parse-Query-Params-With-parse-nested-query) behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method. 424 | 425 | Use the option `useIntKeysAsArrayIndex` to interpret integers as array indexes: 426 | 427 | ```javascript 428 | $('form').serializeJSON({useIntKeysAsArrayIndex: true}); 429 | 430 | // arr is an array => 431 | {'arr': ['foo', 'var', undefined, undefined, undefined, 'inn']} 432 | ``` 433 | 434 | **Note**: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility. 435 | 436 | 437 | ## Option Defaults 438 | 439 | All options defaults are defined in `$.serializeJSON.defaultOptions`. You can just modify it to avoid setting the option on every call to `serializeJSON`. For example: 440 | 441 | ```javascript 442 | $.serializeJSON.defaultOptions.checkboxUncheckedValue = ""; // include unckecked checkboxes as empty strings 443 | $.serializeJSON.defaultOptions.customTypes.foo = (str) => { return str + "-foo"; }; // define global custom type ":foo" 444 | ``` 445 | 446 | 447 | Alternatives 448 | ------------ 449 | 450 | Other plugins solve the same problem in similar ways: 451 | 452 | * https://github.com/macek/jquery-serialize-object 453 | * https://github.com/hongymagic/jQuery.serializeObject 454 | * https://github.com/danheberden/jquery-serializeForm 455 | * https://github.com/maxatwork/form2js (plain js, no jQuery) 456 | * https://github.com/serbanghita/formToObject.js (plain js, no jQuery) 457 | * https://gist.github.com/shiawuen/2634143 (simpler but small) 458 | 459 | None of them did what I needed at the time `serializeJSON` was created. Factors that differentiate `serializeJSON` from the alternatives: 460 | 461 | * Simple and small code base. The minimified version is < 1Kb. 462 | * Yet flexible enough with features like nested objects, unchecked-checkboxes and custom types. 463 | * Implementation follows the same rules as the jQuery method `serializeArray`, that creates a JavaScript array of objects, ready to be encoded as a JSON string. Taking into account the W3C rules for [successful controls](http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2) for better compatibility. 464 | * The format for the input field names is the same used by Rails (from [Rack::Utils.parse_nested_query](http://codefol.io/posts/How-Does-Rack-Parse-Query-Params-With-parse-nested-query)), that is successfully used by many backend systems and already well understood by many front end developers. 465 | * Exaustive test suite helps iterate on new releases and bugfixes with confidence. 466 | * Compatible with [bower](https://github.com/bower/bower), [zepto.js](http://zeptojs.com/) and pretty much every version of [jQuery](https://jquery.com/). 467 | 468 | 469 | Contributions 470 | ------------- 471 | 472 | Contributions are awesome. Feature branch *pull requests* are the preferred method. Just make sure to add tests for it. To run the jasmine specs, just open `spec/spec_runner_jquery.html` in your browser. 473 | 474 | Changelog 475 | --------- 476 | 477 | See [CHANGELOG.md](./CHANGELOG.md) 478 | 479 | Author 480 | ------- 481 | 482 | Written and maintained by [Mario Izquierdo](https://github.com/marioizquierdo) 483 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery.serializeJSON", 3 | "main": "jquery.serializejson.js", 4 | "version": "3.2.1", 5 | "description": "jQuery or Zepto plugin to serialize a form into a JavaScript Object, using the same format as the default Ruby on Rails request params", 6 | "homepage": "https://github.com/marioizquierdo/jquery.serializeJSON", 7 | "authors": ["Mario Izquierdo "], 8 | "keywords": ["form", "serialize", "json", "helper", "jquery"], 9 | "license": "MIT", 10 | "ignore": ["**/.*", "node_modules", "bower_components", "test", "tests"], 11 | "dependencies": { 12 | "jquery": "*" 13 | }, 14 | "devDependencies": { 15 | "jasmine": "2.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /jquery.serializejson.js: -------------------------------------------------------------------------------- 1 | /*! 2 | SerializeJSON jQuery plugin. 3 | https://github.com/marioizquierdo/jquery.serializeJSON 4 | version 3.2.1 (Feb, 2021) 5 | 6 | Copyright (c) 2012-2021 Mario Izquierdo 7 | Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 8 | and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 9 | */ 10 | (function (factory) { 11 | /* global define, require, module */ 12 | if (typeof define === "function" && define.amd) { // AMD. Register as an anonymous module. 13 | define(["jquery"], factory); 14 | } else if (typeof exports === "object") { // Node/CommonJS 15 | var jQuery = require("jquery"); 16 | module.exports = factory(jQuery); 17 | } else { // Browser globals (zepto supported) 18 | factory(window.jQuery || window.Zepto || window.$); // Zepto supported on browsers as well 19 | } 20 | 21 | }(function ($) { 22 | "use strict"; 23 | 24 | var rCRLF = /\r?\n/g; 25 | var rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i; 26 | var rsubmittable = /^(?:input|select|textarea|keygen)/i; 27 | var rcheckableType = /^(?:checkbox|radio)$/i; 28 | 29 | $.fn.serializeJSON = function (options) { 30 | var f = $.serializeJSON; 31 | var $form = this; // NOTE: the set of matched elements is most likely a form, but it could also be a group of inputs 32 | var opts = f.setupOpts(options); // validate options and apply defaults 33 | var typeFunctions = $.extend({}, opts.defaultTypes, opts.customTypes); 34 | 35 | // Make a list with {name, value, el} for each input element 36 | var serializedArray = f.serializeArray($form, opts); 37 | 38 | // Convert the serializedArray into a serializedObject with nested keys 39 | var serializedObject = {}; 40 | $.each(serializedArray, function (_i, obj) { 41 | 42 | var nameSansType = obj.name; 43 | var type = $(obj.el).attr("data-value-type"); 44 | 45 | if (!type && !opts.disableColonTypes) { // try getting the type from the input name 46 | var p = f.splitType(obj.name); // "foo:string" => ["foo", "string"] 47 | nameSansType = p[0]; 48 | type = p[1]; 49 | } 50 | if (type === "skip") { 51 | return; // ignore fields with type skip 52 | } 53 | if (!type) { 54 | type = opts.defaultType; // "string" by default 55 | } 56 | 57 | var typedValue = f.applyTypeFunc(obj.name, obj.value, type, obj.el, typeFunctions); // Parse type as string, number, etc. 58 | 59 | if (!typedValue && f.shouldSkipFalsy(obj.name, nameSansType, type, obj.el, opts)) { 60 | return; // ignore falsy inputs if specified in the options 61 | } 62 | 63 | var keys = f.splitInputNameIntoKeysArray(nameSansType); 64 | f.deepSet(serializedObject, keys, typedValue, opts); 65 | }); 66 | return serializedObject; 67 | }; 68 | 69 | // Use $.serializeJSON as namespace for the auxiliar functions 70 | // and to define defaults 71 | $.serializeJSON = { 72 | defaultOptions: {}, // reassign to override option defaults for all serializeJSON calls 73 | 74 | defaultBaseOptions: { // do not modify, use defaultOptions instead 75 | checkboxUncheckedValue: undefined, // to include that value for unchecked checkboxes (instead of ignoring them) 76 | useIntKeysAsArrayIndex: false, // name="foo[2]" value="v" => {foo: [null, null, "v"]}, instead of {foo: ["2": "v"]} 77 | 78 | skipFalsyValuesForTypes: [], // skip serialization of falsy values for listed value types 79 | skipFalsyValuesForFields: [], // skip serialization of falsy values for listed field names 80 | 81 | disableColonTypes: false, // do not interpret ":type" suffix as a type 82 | customTypes: {}, // extends defaultTypes 83 | defaultTypes: { 84 | "string": function(str) { return String(str); }, 85 | "number": function(str) { return Number(str); }, 86 | "boolean": function(str) { var falses = ["false", "null", "undefined", "", "0"]; return falses.indexOf(str) === -1; }, 87 | "null": function(str) { var falses = ["false", "null", "undefined", "", "0"]; return falses.indexOf(str) === -1 ? str : null; }, 88 | "array": function(str) { return JSON.parse(str); }, 89 | "object": function(str) { return JSON.parse(str); }, 90 | "skip": null // skip is a special type used to ignore fields 91 | }, 92 | defaultType: "string", 93 | }, 94 | 95 | // Validate and set defaults 96 | setupOpts: function(options) { 97 | if (options == null) options = {}; 98 | var f = $.serializeJSON; 99 | 100 | // Validate 101 | var validOpts = [ 102 | "checkboxUncheckedValue", 103 | "useIntKeysAsArrayIndex", 104 | 105 | "skipFalsyValuesForTypes", 106 | "skipFalsyValuesForFields", 107 | 108 | "disableColonTypes", 109 | "customTypes", 110 | "defaultTypes", 111 | "defaultType" 112 | ]; 113 | for (var opt in options) { 114 | if (validOpts.indexOf(opt) === -1) { 115 | throw new Error("serializeJSON ERROR: invalid option '" + opt + "'. Please use one of " + validOpts.join(", ")); 116 | } 117 | } 118 | 119 | // Helper to get options or defaults 120 | return $.extend({}, f.defaultBaseOptions, f.defaultOptions, options); 121 | }, 122 | 123 | // Just like jQuery's serializeArray method, returns an array of objects with name and value. 124 | // but also includes the dom element (el) and is handles unchecked checkboxes if the option or data attribute are provided. 125 | serializeArray: function($form, opts) { 126 | if (opts == null) { opts = {}; } 127 | var f = $.serializeJSON; 128 | 129 | return $form.map(function() { 130 | var elements = $.prop(this, "elements"); // handle propHook "elements" to filter or add form elements 131 | return elements ? $.makeArray(elements) : this; 132 | 133 | }).filter(function() { 134 | var $el = $(this); 135 | var type = this.type; 136 | 137 | // Filter with the standard W3C rules for successful controls: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2 138 | return this.name && // must contain a name attribute 139 | !$el.is(":disabled") && // must not be disable (use .is(":disabled") so that fieldset[disabled] works) 140 | rsubmittable.test(this.nodeName) && !rsubmitterTypes.test(type) && // only serialize submittable fields (and not buttons) 141 | (this.checked || !rcheckableType.test(type) || f.getCheckboxUncheckedValue($el, opts) != null); // skip unchecked checkboxes (unless using opts) 142 | 143 | }).map(function(_i, el) { 144 | var $el = $(this); 145 | var val = $el.val(); 146 | var type = this.type; // "input", "select", "textarea", "checkbox", etc. 147 | 148 | if (val == null) { 149 | return null; 150 | } 151 | 152 | if (rcheckableType.test(type) && !this.checked) { 153 | val = f.getCheckboxUncheckedValue($el, opts); 154 | } 155 | 156 | if (isArray(val)) { 157 | return $.map(val, function(val) { 158 | return { name: el.name, value: val.replace(rCRLF, "\r\n"), el: el }; 159 | } ); 160 | } 161 | 162 | return { name: el.name, value: val.replace(rCRLF, "\r\n"), el: el }; 163 | 164 | }).get(); 165 | }, 166 | 167 | getCheckboxUncheckedValue: function($el, opts) { 168 | var val = $el.attr("data-unchecked-value"); 169 | if (val == null) { 170 | val = opts.checkboxUncheckedValue; 171 | } 172 | return val; 173 | }, 174 | 175 | // Parse value with type function 176 | applyTypeFunc: function(name, strVal, type, el, typeFunctions) { 177 | var typeFunc = typeFunctions[type]; 178 | if (!typeFunc) { // quick feedback to user if there is a typo or missconfiguration 179 | throw new Error("serializeJSON ERROR: Invalid type " + type + " found in input name '" + name + "', please use one of " + objectKeys(typeFunctions).join(", ")); 180 | } 181 | return typeFunc(strVal, el); 182 | }, 183 | 184 | // Splits a field name into the name and the type. Examples: 185 | // "foo" => ["foo", ""] 186 | // "foo:boolean" => ["foo", "boolean"] 187 | // "foo[bar]:null" => ["foo[bar]", "null"] 188 | splitType : function(name) { 189 | var parts = name.split(":"); 190 | if (parts.length > 1) { 191 | var t = parts.pop(); 192 | return [parts.join(":"), t]; 193 | } else { 194 | return [name, ""]; 195 | } 196 | }, 197 | 198 | // Check if this input should be skipped when it has a falsy value, 199 | // depending on the options to skip values by name or type, and the data-skip-falsy attribute. 200 | shouldSkipFalsy: function(name, nameSansType, type, el, opts) { 201 | var skipFromDataAttr = $(el).attr("data-skip-falsy"); 202 | if (skipFromDataAttr != null) { 203 | return skipFromDataAttr !== "false"; // any value is true, except the string "false" 204 | } 205 | 206 | var optForFields = opts.skipFalsyValuesForFields; 207 | if (optForFields && (optForFields.indexOf(nameSansType) !== -1 || optForFields.indexOf(name) !== -1)) { 208 | return true; 209 | } 210 | 211 | var optForTypes = opts.skipFalsyValuesForTypes; 212 | if (optForTypes && optForTypes.indexOf(type) !== -1) { 213 | return true; 214 | } 215 | 216 | return false; 217 | }, 218 | 219 | // Split the input name in programatically readable keys. 220 | // Examples: 221 | // "foo" => ["foo"] 222 | // "[foo]" => ["foo"] 223 | // "foo[inn][bar]" => ["foo", "inn", "bar"] 224 | // "foo[inn[bar]]" => ["foo", "inn", "bar"] 225 | // "foo[inn][arr][0]" => ["foo", "inn", "arr", "0"] 226 | // "arr[][val]" => ["arr", "", "val"] 227 | splitInputNameIntoKeysArray: function(nameWithNoType) { 228 | var keys = nameWithNoType.split("["); // split string into array 229 | keys = $.map(keys, function (key) { return key.replace(/\]/g, ""); }); // remove closing brackets 230 | if (keys[0] === "") { keys.shift(); } // ensure no opening bracket ("[foo][inn]" should be same as "foo[inn]") 231 | return keys; 232 | }, 233 | 234 | // Set a value in an object or array, using multiple keys to set in a nested object or array. 235 | // This is the main function of the script, that allows serializeJSON to use nested keys. 236 | // Examples: 237 | // 238 | // deepSet(obj, ["foo"], v) // obj["foo"] = v 239 | // deepSet(obj, ["foo", "inn"], v) // obj["foo"]["inn"] = v // Create the inner obj["foo"] object, if needed 240 | // deepSet(obj, ["foo", "inn", "123"], v) // obj["foo"]["arr"]["123"] = v // 241 | // 242 | // deepSet(obj, ["0"], v) // obj["0"] = v 243 | // deepSet(arr, ["0"], v, {useIntKeysAsArrayIndex: true}) // arr[0] = v 244 | // deepSet(arr, [""], v) // arr.push(v) 245 | // deepSet(obj, ["arr", ""], v) // obj["arr"].push(v) 246 | // 247 | // arr = []; 248 | // deepSet(arr, ["", v] // arr => [v] 249 | // deepSet(arr, ["", "foo"], v) // arr => [v, {foo: v}] 250 | // deepSet(arr, ["", "bar"], v) // arr => [v, {foo: v, bar: v}] 251 | // deepSet(arr, ["", "bar"], v) // arr => [v, {foo: v, bar: v}, {bar: v}] 252 | // 253 | deepSet: function (o, keys, value, opts) { 254 | if (opts == null) { opts = {}; } 255 | var f = $.serializeJSON; 256 | if (isUndefined(o)) { throw new Error("ArgumentError: param 'o' expected to be an object or array, found undefined"); } 257 | if (!keys || keys.length === 0) { throw new Error("ArgumentError: param 'keys' expected to be an array with least one element"); } 258 | 259 | var key = keys[0]; 260 | 261 | // Only one key, then it's not a deepSet, just assign the value in the object or add it to the array. 262 | if (keys.length === 1) { 263 | if (key === "") { // push values into an array (o must be an array) 264 | o.push(value); 265 | } else { 266 | o[key] = value; // keys can be object keys (strings) or array indexes (numbers) 267 | } 268 | return; 269 | } 270 | 271 | var nextKey = keys[1]; // nested key 272 | var tailKeys = keys.slice(1); // list of all other nested keys (nextKey is first) 273 | 274 | if (key === "") { // push nested objects into an array (o must be an array) 275 | var lastIdx = o.length - 1; 276 | var lastVal = o[lastIdx]; 277 | 278 | // if the last value is an object or array, and the new key is not set yet 279 | if (isObject(lastVal) && isUndefined(f.deepGet(lastVal, tailKeys))) { 280 | key = lastIdx; // then set the new value as a new attribute of the same object 281 | } else { 282 | key = lastIdx + 1; // otherwise, add a new element in the array 283 | } 284 | } 285 | 286 | if (nextKey === "") { // "" is used to push values into the nested array "array[]" 287 | if (isUndefined(o[key]) || !isArray(o[key])) { 288 | o[key] = []; // define (or override) as array to push values 289 | } 290 | } else { 291 | if (opts.useIntKeysAsArrayIndex && isValidArrayIndex(nextKey)) { // if 1, 2, 3 ... then use an array, where nextKey is the index 292 | if (isUndefined(o[key]) || !isArray(o[key])) { 293 | o[key] = []; // define (or override) as array, to insert values using int keys as array indexes 294 | } 295 | } else { // nextKey is going to be the nested object's attribute 296 | if (isUndefined(o[key]) || !isObject(o[key])) { 297 | o[key] = {}; // define (or override) as object, to set nested properties 298 | } 299 | } 300 | } 301 | 302 | // Recursively set the inner object 303 | f.deepSet(o[key], tailKeys, value, opts); 304 | }, 305 | 306 | deepGet: function (o, keys) { 307 | var f = $.serializeJSON; 308 | if (isUndefined(o) || isUndefined(keys) || keys.length === 0 || (!isObject(o) && !isArray(o))) { 309 | return o; 310 | } 311 | var key = keys[0]; 312 | if (key === "") { // "" means next array index (used by deepSet) 313 | return undefined; 314 | } 315 | if (keys.length === 1) { 316 | return o[key]; 317 | } 318 | var tailKeys = keys.slice(1); 319 | return f.deepGet(o[key], tailKeys); 320 | } 321 | }; 322 | 323 | // polyfill Object.keys to get option keys in IE<9 324 | var objectKeys = function(obj) { 325 | if (Object.keys) { 326 | return Object.keys(obj); 327 | } else { 328 | var key, keys = []; 329 | for (key in obj) { keys.push(key); } 330 | return keys; 331 | } 332 | }; 333 | 334 | var isObject = function(obj) { return obj === Object(obj); }; // true for Objects and Arrays 335 | var isUndefined = function(obj) { return obj === void 0; }; // safe check for undefined values 336 | var isValidArrayIndex = function(val) { return /^[0-9]+$/.test(String(val)); }; // 1,2,3,4 ... are valid array indexes 337 | var isArray = Array.isArray || function(obj) { return Object.prototype.toString.call(obj) === "[object Array]"; }; 338 | })); 339 | -------------------------------------------------------------------------------- /jquery.serializejson.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | SerializeJSON jQuery plugin. 3 | https://github.com/marioizquierdo/jquery.serializeJSON 4 | version 3.2.1 (Feb, 2021) 5 | 6 | Copyright (c) 2012-2021 Mario Izquierdo 7 | Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 8 | and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 9 | */ 10 | !function(e){if("function"==typeof define&&define.amd)define(["jquery"],e);else if("object"==typeof exports){var n=require("jquery");module.exports=e(n)}else e(window.jQuery||window.Zepto||window.$)}(function(e){"use strict";var n=/\r?\n/g,r=/^(?:submit|button|image|reset|file)$/i,t=/^(?:input|select|textarea|keygen)/i,i=/^(?:checkbox|radio)$/i;e.fn.serializeJSON=function(n){var r=e.serializeJSON,t=r.setupOpts(n),i=e.extend({},t.defaultTypes,t.customTypes),a=r.serializeArray(this,t),u={};return e.each(a,function(n,a){var s=a.name,l=e(a.el).attr("data-value-type");if(!l&&!t.disableColonTypes){var o=r.splitType(a.name);s=o[0],l=o[1]}if("skip"!==l){l||(l=t.defaultType);var p=r.applyTypeFunc(a.name,a.value,l,a.el,i);if(p||!r.shouldSkipFalsy(a.name,s,l,a.el,t)){var f=r.splitInputNameIntoKeysArray(s);r.deepSet(u,f,p,t)}}}),u},e.serializeJSON={defaultOptions:{},defaultBaseOptions:{checkboxUncheckedValue:void 0,useIntKeysAsArrayIndex:!1,skipFalsyValuesForTypes:[],skipFalsyValuesForFields:[],disableColonTypes:!1,customTypes:{},defaultTypes:{string:function(e){return String(e)},number:function(e){return Number(e)},boolean:function(e){return-1===["false","null","undefined","","0"].indexOf(e)},null:function(e){return-1===["false","null","undefined","","0"].indexOf(e)?e:null},array:function(e){return JSON.parse(e)},object:function(e){return JSON.parse(e)},skip:null},defaultType:"string"},setupOpts:function(n){null==n&&(n={});var r=e.serializeJSON,t=["checkboxUncheckedValue","useIntKeysAsArrayIndex","skipFalsyValuesForTypes","skipFalsyValuesForFields","disableColonTypes","customTypes","defaultTypes","defaultType"];for(var i in n)if(-1===t.indexOf(i))throw new Error("serializeJSON ERROR: invalid option '"+i+"'. Please use one of "+t.join(", "));return e.extend({},r.defaultBaseOptions,r.defaultOptions,n)},serializeArray:function(a,u){null==u&&(u={});var s=e.serializeJSON;return a.map(function(){var n=e.prop(this,"elements");return n?e.makeArray(n):this}).filter(function(){var n=e(this),a=this.type;return this.name&&!n.is(":disabled")&&t.test(this.nodeName)&&!r.test(a)&&(this.checked||!i.test(a)||null!=s.getCheckboxUncheckedValue(n,u))}).map(function(r,t){var a=e(this),l=a.val(),p=this.type;return null==l?null:(i.test(p)&&!this.checked&&(l=s.getCheckboxUncheckedValue(a,u)),o(l)?e.map(l,function(e){return{name:t.name,value:e.replace(n,"\r\n"),el:t}}):{name:t.name,value:l.replace(n,"\r\n"),el:t})}).get()},getCheckboxUncheckedValue:function(e,n){var r=e.attr("data-unchecked-value");return null==r&&(r=n.checkboxUncheckedValue),r},applyTypeFunc:function(e,n,r,t,i){var u=i[r];if(!u)throw new Error("serializeJSON ERROR: Invalid type "+r+" found in input name '"+e+"', please use one of "+a(i).join(", "));return u(n,t)},splitType:function(e){var n=e.split(":");if(n.length>1){var r=n.pop();return[n.join(":"),r]}return[e,""]},shouldSkipFalsy:function(n,r,t,i,a){var u=e(i).attr("data-skip-falsy");if(null!=u)return"false"!==u;var s=a.skipFalsyValuesForFields;if(s&&(-1!==s.indexOf(r)||-1!==s.indexOf(n)))return!0;var l=a.skipFalsyValuesForTypes;return!(!l||-1===l.indexOf(t))},splitInputNameIntoKeysArray:function(n){var r=n.split("[");return""===(r=e.map(r,function(e){return e.replace(/\]/g,"")}))[0]&&r.shift(),r},deepSet:function(n,r,t,i){null==i&&(i={});var a=e.serializeJSON;if(s(n))throw new Error("ArgumentError: param 'o' expected to be an object or array, found undefined");if(!r||0===r.length)throw new Error("ArgumentError: param 'keys' expected to be an array with least one element");var p=r[0];if(1!==r.length){var f=r[1],c=r.slice(1);if(""===p){var d=n.length-1,y=n[d];p=u(y)&&s(a.deepGet(y,c))?d:d+1}""===f?!s(n[p])&&o(n[p])||(n[p]=[]):i.useIntKeysAsArrayIndex&&l(f)?!s(n[p])&&o(n[p])||(n[p]=[]):!s(n[p])&&u(n[p])||(n[p]={}),a.deepSet(n[p],c,t,i)}else""===p?n.push(t):n[p]=t},deepGet:function(n,r){var t=e.serializeJSON;if(s(n)||s(r)||0===r.length||!u(n)&&!o(n))return n;var i=r[0];if(""!==i){if(1===r.length)return n[i];var a=r.slice(1);return t.deepGet(n[i],a)}}};var a=function(e){if(Object.keys)return Object.keys(e);var n,r=[];for(n in e)r.push(n);return r},u=function(e){return e===Object(e)},s=function(e){return void 0===e},l=function(e){return/^[0-9]+$/.test(String(e))},o=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}}); 11 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-serializejson", 3 | "version": "3.1.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.4", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 19 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "chalk": { 34 | "version": "2.4.2", 35 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 36 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 37 | "dev": true, 38 | "requires": { 39 | "ansi-styles": "^3.2.1", 40 | "escape-string-regexp": "^1.0.5", 41 | "supports-color": "^5.3.0" 42 | } 43 | } 44 | } 45 | }, 46 | "@eslint/eslintrc": { 47 | "version": "0.1.3", 48 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", 49 | "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", 50 | "dev": true, 51 | "requires": { 52 | "ajv": "^6.12.4", 53 | "debug": "^4.1.1", 54 | "espree": "^7.3.0", 55 | "globals": "^12.1.0", 56 | "ignore": "^4.0.6", 57 | "import-fresh": "^3.2.1", 58 | "js-yaml": "^3.13.1", 59 | "lodash": "^4.17.19", 60 | "minimatch": "^3.0.4", 61 | "strip-json-comments": "^3.1.1" 62 | } 63 | }, 64 | "@types/color-name": { 65 | "version": "1.1.1", 66 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 67 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", 68 | "dev": true 69 | }, 70 | "acorn": { 71 | "version": "7.4.0", 72 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", 73 | "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", 74 | "dev": true 75 | }, 76 | "acorn-jsx": { 77 | "version": "5.2.0", 78 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", 79 | "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", 80 | "dev": true 81 | }, 82 | "ajv": { 83 | "version": "6.12.4", 84 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", 85 | "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", 86 | "dev": true, 87 | "requires": { 88 | "fast-deep-equal": "^3.1.1", 89 | "fast-json-stable-stringify": "^2.0.0", 90 | "json-schema-traverse": "^0.4.1", 91 | "uri-js": "^4.2.2" 92 | } 93 | }, 94 | "ansi-colors": { 95 | "version": "4.1.1", 96 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 97 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 98 | "dev": true 99 | }, 100 | "ansi-regex": { 101 | "version": "5.0.0", 102 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 103 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 104 | "dev": true 105 | }, 106 | "ansi-styles": { 107 | "version": "3.2.1", 108 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 109 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 110 | "dev": true, 111 | "requires": { 112 | "color-convert": "^1.9.0" 113 | } 114 | }, 115 | "argparse": { 116 | "version": "1.0.10", 117 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 118 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 119 | "dev": true, 120 | "requires": { 121 | "sprintf-js": "~1.0.2" 122 | } 123 | }, 124 | "astral-regex": { 125 | "version": "1.0.0", 126 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", 127 | "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", 128 | "dev": true 129 | }, 130 | "balanced-match": { 131 | "version": "1.0.0", 132 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 133 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 134 | "dev": true 135 | }, 136 | "brace-expansion": { 137 | "version": "1.1.11", 138 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 139 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 140 | "dev": true, 141 | "requires": { 142 | "balanced-match": "^1.0.0", 143 | "concat-map": "0.0.1" 144 | } 145 | }, 146 | "callsites": { 147 | "version": "3.1.0", 148 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 149 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 150 | "dev": true 151 | }, 152 | "chalk": { 153 | "version": "4.1.0", 154 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 155 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 156 | "dev": true, 157 | "requires": { 158 | "ansi-styles": "^4.1.0", 159 | "supports-color": "^7.1.0" 160 | }, 161 | "dependencies": { 162 | "ansi-styles": { 163 | "version": "4.2.1", 164 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", 165 | "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", 166 | "dev": true, 167 | "requires": { 168 | "@types/color-name": "^1.1.1", 169 | "color-convert": "^2.0.1" 170 | } 171 | }, 172 | "color-convert": { 173 | "version": "2.0.1", 174 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 175 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 176 | "dev": true, 177 | "requires": { 178 | "color-name": "~1.1.4" 179 | } 180 | }, 181 | "color-name": { 182 | "version": "1.1.4", 183 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 184 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 185 | "dev": true 186 | }, 187 | "has-flag": { 188 | "version": "4.0.0", 189 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 190 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 191 | "dev": true 192 | }, 193 | "supports-color": { 194 | "version": "7.2.0", 195 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 196 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 197 | "dev": true, 198 | "requires": { 199 | "has-flag": "^4.0.0" 200 | } 201 | } 202 | } 203 | }, 204 | "color-convert": { 205 | "version": "1.9.3", 206 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 207 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 208 | "dev": true, 209 | "requires": { 210 | "color-name": "1.1.3" 211 | } 212 | }, 213 | "color-name": { 214 | "version": "1.1.3", 215 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 216 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 217 | "dev": true 218 | }, 219 | "concat-map": { 220 | "version": "0.0.1", 221 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 222 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 223 | "dev": true 224 | }, 225 | "cross-spawn": { 226 | "version": "7.0.3", 227 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 228 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 229 | "dev": true, 230 | "requires": { 231 | "path-key": "^3.1.0", 232 | "shebang-command": "^2.0.0", 233 | "which": "^2.0.1" 234 | } 235 | }, 236 | "debug": { 237 | "version": "4.1.1", 238 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 239 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 240 | "dev": true, 241 | "requires": { 242 | "ms": "^2.1.1" 243 | } 244 | }, 245 | "deep-is": { 246 | "version": "0.1.3", 247 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 248 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 249 | "dev": true 250 | }, 251 | "doctrine": { 252 | "version": "3.0.0", 253 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 254 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 255 | "dev": true, 256 | "requires": { 257 | "esutils": "^2.0.2" 258 | } 259 | }, 260 | "emoji-regex": { 261 | "version": "7.0.3", 262 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 263 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 264 | "dev": true 265 | }, 266 | "enquirer": { 267 | "version": "2.3.6", 268 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 269 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 270 | "dev": true, 271 | "requires": { 272 | "ansi-colors": "^4.1.1" 273 | } 274 | }, 275 | "escape-string-regexp": { 276 | "version": "1.0.5", 277 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 278 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 279 | "dev": true 280 | }, 281 | "eslint": { 282 | "version": "7.8.1", 283 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz", 284 | "integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==", 285 | "dev": true, 286 | "requires": { 287 | "@babel/code-frame": "^7.0.0", 288 | "@eslint/eslintrc": "^0.1.3", 289 | "ajv": "^6.10.0", 290 | "chalk": "^4.0.0", 291 | "cross-spawn": "^7.0.2", 292 | "debug": "^4.0.1", 293 | "doctrine": "^3.0.0", 294 | "enquirer": "^2.3.5", 295 | "eslint-scope": "^5.1.0", 296 | "eslint-utils": "^2.1.0", 297 | "eslint-visitor-keys": "^1.3.0", 298 | "espree": "^7.3.0", 299 | "esquery": "^1.2.0", 300 | "esutils": "^2.0.2", 301 | "file-entry-cache": "^5.0.1", 302 | "functional-red-black-tree": "^1.0.1", 303 | "glob-parent": "^5.0.0", 304 | "globals": "^12.1.0", 305 | "ignore": "^4.0.6", 306 | "import-fresh": "^3.0.0", 307 | "imurmurhash": "^0.1.4", 308 | "is-glob": "^4.0.0", 309 | "js-yaml": "^3.13.1", 310 | "json-stable-stringify-without-jsonify": "^1.0.1", 311 | "levn": "^0.4.1", 312 | "lodash": "^4.17.19", 313 | "minimatch": "^3.0.4", 314 | "natural-compare": "^1.4.0", 315 | "optionator": "^0.9.1", 316 | "progress": "^2.0.0", 317 | "regexpp": "^3.1.0", 318 | "semver": "^7.2.1", 319 | "strip-ansi": "^6.0.0", 320 | "strip-json-comments": "^3.1.0", 321 | "table": "^5.2.3", 322 | "text-table": "^0.2.0", 323 | "v8-compile-cache": "^2.0.3" 324 | } 325 | }, 326 | "eslint-scope": { 327 | "version": "5.1.0", 328 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", 329 | "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", 330 | "dev": true, 331 | "requires": { 332 | "esrecurse": "^4.1.0", 333 | "estraverse": "^4.1.1" 334 | } 335 | }, 336 | "eslint-utils": { 337 | "version": "2.1.0", 338 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 339 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 340 | "dev": true, 341 | "requires": { 342 | "eslint-visitor-keys": "^1.1.0" 343 | } 344 | }, 345 | "eslint-visitor-keys": { 346 | "version": "1.3.0", 347 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 348 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 349 | "dev": true 350 | }, 351 | "espree": { 352 | "version": "7.3.0", 353 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", 354 | "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", 355 | "dev": true, 356 | "requires": { 357 | "acorn": "^7.4.0", 358 | "acorn-jsx": "^5.2.0", 359 | "eslint-visitor-keys": "^1.3.0" 360 | } 361 | }, 362 | "esprima": { 363 | "version": "4.0.1", 364 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 365 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 366 | "dev": true 367 | }, 368 | "esquery": { 369 | "version": "1.3.1", 370 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", 371 | "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", 372 | "dev": true, 373 | "requires": { 374 | "estraverse": "^5.1.0" 375 | }, 376 | "dependencies": { 377 | "estraverse": { 378 | "version": "5.2.0", 379 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 380 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 381 | "dev": true 382 | } 383 | } 384 | }, 385 | "esrecurse": { 386 | "version": "4.3.0", 387 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 388 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 389 | "dev": true, 390 | "requires": { 391 | "estraverse": "^5.2.0" 392 | }, 393 | "dependencies": { 394 | "estraverse": { 395 | "version": "5.2.0", 396 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 397 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 398 | "dev": true 399 | } 400 | } 401 | }, 402 | "estraverse": { 403 | "version": "4.3.0", 404 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 405 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 406 | "dev": true 407 | }, 408 | "esutils": { 409 | "version": "2.0.3", 410 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 411 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 412 | "dev": true 413 | }, 414 | "fast-deep-equal": { 415 | "version": "3.1.3", 416 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 417 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 418 | "dev": true 419 | }, 420 | "fast-json-stable-stringify": { 421 | "version": "2.1.0", 422 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 423 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 424 | "dev": true 425 | }, 426 | "fast-levenshtein": { 427 | "version": "2.0.6", 428 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 429 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 430 | "dev": true 431 | }, 432 | "file-entry-cache": { 433 | "version": "5.0.1", 434 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", 435 | "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", 436 | "dev": true, 437 | "requires": { 438 | "flat-cache": "^2.0.1" 439 | } 440 | }, 441 | "flat-cache": { 442 | "version": "2.0.1", 443 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", 444 | "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", 445 | "dev": true, 446 | "requires": { 447 | "flatted": "^2.0.0", 448 | "rimraf": "2.6.3", 449 | "write": "1.0.3" 450 | } 451 | }, 452 | "flatted": { 453 | "version": "2.0.2", 454 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", 455 | "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", 456 | "dev": true 457 | }, 458 | "fs.realpath": { 459 | "version": "1.0.0", 460 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 461 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 462 | "dev": true 463 | }, 464 | "functional-red-black-tree": { 465 | "version": "1.0.1", 466 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 467 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 468 | "dev": true 469 | }, 470 | "glob": { 471 | "version": "7.1.6", 472 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 473 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 474 | "dev": true, 475 | "requires": { 476 | "fs.realpath": "^1.0.0", 477 | "inflight": "^1.0.4", 478 | "inherits": "2", 479 | "minimatch": "^3.0.4", 480 | "once": "^1.3.0", 481 | "path-is-absolute": "^1.0.0" 482 | } 483 | }, 484 | "glob-parent": { 485 | "version": "5.1.1", 486 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 487 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 488 | "dev": true, 489 | "requires": { 490 | "is-glob": "^4.0.1" 491 | } 492 | }, 493 | "globals": { 494 | "version": "12.4.0", 495 | "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", 496 | "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", 497 | "dev": true, 498 | "requires": { 499 | "type-fest": "^0.8.1" 500 | } 501 | }, 502 | "has-flag": { 503 | "version": "3.0.0", 504 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 505 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 506 | "dev": true 507 | }, 508 | "ignore": { 509 | "version": "4.0.6", 510 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 511 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 512 | "dev": true 513 | }, 514 | "import-fresh": { 515 | "version": "3.2.1", 516 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", 517 | "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", 518 | "dev": true, 519 | "requires": { 520 | "parent-module": "^1.0.0", 521 | "resolve-from": "^4.0.0" 522 | } 523 | }, 524 | "imurmurhash": { 525 | "version": "0.1.4", 526 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 527 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 528 | "dev": true 529 | }, 530 | "inflight": { 531 | "version": "1.0.6", 532 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 533 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 534 | "dev": true, 535 | "requires": { 536 | "once": "^1.3.0", 537 | "wrappy": "1" 538 | } 539 | }, 540 | "inherits": { 541 | "version": "2.0.4", 542 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 543 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 544 | "dev": true 545 | }, 546 | "is-extglob": { 547 | "version": "2.1.1", 548 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 549 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 550 | "dev": true 551 | }, 552 | "is-fullwidth-code-point": { 553 | "version": "2.0.0", 554 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 555 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 556 | "dev": true 557 | }, 558 | "is-glob": { 559 | "version": "4.0.1", 560 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 561 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 562 | "dev": true, 563 | "requires": { 564 | "is-extglob": "^2.1.1" 565 | } 566 | }, 567 | "isexe": { 568 | "version": "2.0.0", 569 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 570 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 571 | "dev": true 572 | }, 573 | "js-tokens": { 574 | "version": "4.0.0", 575 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 576 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 577 | "dev": true 578 | }, 579 | "js-yaml": { 580 | "version": "3.14.0", 581 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 582 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 583 | "dev": true, 584 | "requires": { 585 | "argparse": "^1.0.7", 586 | "esprima": "^4.0.0" 587 | } 588 | }, 589 | "json-schema-traverse": { 590 | "version": "0.4.1", 591 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 592 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 593 | "dev": true 594 | }, 595 | "json-stable-stringify-without-jsonify": { 596 | "version": "1.0.1", 597 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 598 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 599 | "dev": true 600 | }, 601 | "levn": { 602 | "version": "0.4.1", 603 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 604 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 605 | "dev": true, 606 | "requires": { 607 | "prelude-ls": "^1.2.1", 608 | "type-check": "~0.4.0" 609 | } 610 | }, 611 | "lodash": { 612 | "version": "4.17.20", 613 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 614 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", 615 | "dev": true 616 | }, 617 | "minimatch": { 618 | "version": "3.0.4", 619 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 620 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 621 | "dev": true, 622 | "requires": { 623 | "brace-expansion": "^1.1.7" 624 | } 625 | }, 626 | "minimist": { 627 | "version": "1.2.5", 628 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 629 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 630 | "dev": true 631 | }, 632 | "mkdirp": { 633 | "version": "0.5.5", 634 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 635 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 636 | "dev": true, 637 | "requires": { 638 | "minimist": "^1.2.5" 639 | } 640 | }, 641 | "ms": { 642 | "version": "2.1.2", 643 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 644 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 645 | "dev": true 646 | }, 647 | "natural-compare": { 648 | "version": "1.4.0", 649 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 650 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 651 | "dev": true 652 | }, 653 | "once": { 654 | "version": "1.4.0", 655 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 656 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 657 | "dev": true, 658 | "requires": { 659 | "wrappy": "1" 660 | } 661 | }, 662 | "optionator": { 663 | "version": "0.9.1", 664 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 665 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 666 | "dev": true, 667 | "requires": { 668 | "deep-is": "^0.1.3", 669 | "fast-levenshtein": "^2.0.6", 670 | "levn": "^0.4.1", 671 | "prelude-ls": "^1.2.1", 672 | "type-check": "^0.4.0", 673 | "word-wrap": "^1.2.3" 674 | } 675 | }, 676 | "parent-module": { 677 | "version": "1.0.1", 678 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 679 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 680 | "dev": true, 681 | "requires": { 682 | "callsites": "^3.0.0" 683 | } 684 | }, 685 | "path-is-absolute": { 686 | "version": "1.0.1", 687 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 688 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 689 | "dev": true 690 | }, 691 | "path-key": { 692 | "version": "3.1.1", 693 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 694 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 695 | "dev": true 696 | }, 697 | "prelude-ls": { 698 | "version": "1.2.1", 699 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 700 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 701 | "dev": true 702 | }, 703 | "progress": { 704 | "version": "2.0.3", 705 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 706 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 707 | "dev": true 708 | }, 709 | "punycode": { 710 | "version": "2.1.1", 711 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 712 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 713 | "dev": true 714 | }, 715 | "regexpp": { 716 | "version": "3.1.0", 717 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", 718 | "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", 719 | "dev": true 720 | }, 721 | "resolve-from": { 722 | "version": "4.0.0", 723 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 724 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 725 | "dev": true 726 | }, 727 | "rimraf": { 728 | "version": "2.6.3", 729 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 730 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 731 | "dev": true, 732 | "requires": { 733 | "glob": "^7.1.3" 734 | } 735 | }, 736 | "semver": { 737 | "version": "7.3.2", 738 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", 739 | "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", 740 | "dev": true 741 | }, 742 | "shebang-command": { 743 | "version": "2.0.0", 744 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 745 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 746 | "dev": true, 747 | "requires": { 748 | "shebang-regex": "^3.0.0" 749 | } 750 | }, 751 | "shebang-regex": { 752 | "version": "3.0.0", 753 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 754 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 755 | "dev": true 756 | }, 757 | "slice-ansi": { 758 | "version": "2.1.0", 759 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", 760 | "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", 761 | "dev": true, 762 | "requires": { 763 | "ansi-styles": "^3.2.0", 764 | "astral-regex": "^1.0.0", 765 | "is-fullwidth-code-point": "^2.0.0" 766 | } 767 | }, 768 | "sprintf-js": { 769 | "version": "1.0.3", 770 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 771 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 772 | "dev": true 773 | }, 774 | "string-width": { 775 | "version": "3.1.0", 776 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 777 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 778 | "dev": true, 779 | "requires": { 780 | "emoji-regex": "^7.0.1", 781 | "is-fullwidth-code-point": "^2.0.0", 782 | "strip-ansi": "^5.1.0" 783 | }, 784 | "dependencies": { 785 | "ansi-regex": { 786 | "version": "4.1.0", 787 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 788 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 789 | "dev": true 790 | }, 791 | "strip-ansi": { 792 | "version": "5.2.0", 793 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 794 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 795 | "dev": true, 796 | "requires": { 797 | "ansi-regex": "^4.1.0" 798 | } 799 | } 800 | } 801 | }, 802 | "strip-ansi": { 803 | "version": "6.0.0", 804 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 805 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 806 | "dev": true, 807 | "requires": { 808 | "ansi-regex": "^5.0.0" 809 | } 810 | }, 811 | "strip-json-comments": { 812 | "version": "3.1.1", 813 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 814 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 815 | "dev": true 816 | }, 817 | "supports-color": { 818 | "version": "5.5.0", 819 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 820 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 821 | "dev": true, 822 | "requires": { 823 | "has-flag": "^3.0.0" 824 | } 825 | }, 826 | "table": { 827 | "version": "5.4.6", 828 | "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", 829 | "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", 830 | "dev": true, 831 | "requires": { 832 | "ajv": "^6.10.2", 833 | "lodash": "^4.17.14", 834 | "slice-ansi": "^2.1.0", 835 | "string-width": "^3.0.0" 836 | } 837 | }, 838 | "text-table": { 839 | "version": "0.2.0", 840 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 841 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 842 | "dev": true 843 | }, 844 | "type-check": { 845 | "version": "0.4.0", 846 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 847 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 848 | "dev": true, 849 | "requires": { 850 | "prelude-ls": "^1.2.1" 851 | } 852 | }, 853 | "type-fest": { 854 | "version": "0.8.1", 855 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 856 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 857 | "dev": true 858 | }, 859 | "uri-js": { 860 | "version": "4.4.0", 861 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", 862 | "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", 863 | "dev": true, 864 | "requires": { 865 | "punycode": "^2.1.0" 866 | } 867 | }, 868 | "v8-compile-cache": { 869 | "version": "2.1.1", 870 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", 871 | "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", 872 | "dev": true 873 | }, 874 | "which": { 875 | "version": "2.0.2", 876 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 877 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 878 | "dev": true, 879 | "requires": { 880 | "isexe": "^2.0.0" 881 | } 882 | }, 883 | "word-wrap": { 884 | "version": "1.2.3", 885 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 886 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 887 | "dev": true 888 | }, 889 | "wrappy": { 890 | "version": "1.0.2", 891 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 892 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 893 | "dev": true 894 | }, 895 | "write": { 896 | "version": "1.0.3", 897 | "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", 898 | "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", 899 | "dev": true, 900 | "requires": { 901 | "mkdirp": "^0.5.1" 902 | } 903 | } 904 | } 905 | } 906 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-serializejson", 3 | "main": "jquery.serializejson.js", 4 | "version": "3.2.1", 5 | "description": "jQuery or Zepto plugin to serialize a form into a JavaScript Object, using the same format as the default Ruby on Rails request params", 6 | "homepage": "https://github.com/marioizquierdo/jquery.serializeJSON", 7 | "author": "Mario Izquierdo", 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "echo \"Open /spec/spec_runner_jquery.html or /spec/spec_runner_zepto.html to run the jasmine specs in your browser\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/marioizquierdo/jquery.serializeJSON.git" 15 | }, 16 | "keywords": [ 17 | "jquery-plugin", 18 | "ecosystem:jquery", 19 | "form-serialization", 20 | "form-to-json", 21 | "html-to-json", 22 | "form", 23 | "json" 24 | ], 25 | "bugs": { 26 | "url": "https://github.com/marioizquierdo/jquery.serializeJSON/issues" 27 | }, 28 | "dependencies": {}, 29 | "devDependencies": { 30 | "eslint": "^7.8.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spec/lib/jasmine-2.0.0/boot.js: -------------------------------------------------------------------------------- 1 | /** 2 | Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. 3 | 4 | If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. 5 | 6 | The location of `boot.js` can be specified and/or overridden in `jasmine.yml`. 7 | 8 | [jasmine-gem]: http://github.com/pivotal/jasmine-gem 9 | */ 10 | 11 | (function() { 12 | 13 | /** 14 | * ## Require & Instantiate 15 | * 16 | * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. 17 | */ 18 | window.jasmine = jasmineRequire.core(jasmineRequire); 19 | 20 | /** 21 | * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. 22 | */ 23 | jasmineRequire.html(jasmine); 24 | 25 | /** 26 | * Create the Jasmine environment. This is used to run all specs in a project. 27 | */ 28 | var env = jasmine.getEnv(); 29 | 30 | /** 31 | * ## The Global Interface 32 | * 33 | * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. 34 | */ 35 | var jasmineInterface = { 36 | describe: function(description, specDefinitions) { 37 | return env.describe(description, specDefinitions); 38 | }, 39 | 40 | xdescribe: function(description, specDefinitions) { 41 | return env.xdescribe(description, specDefinitions); 42 | }, 43 | 44 | it: function(desc, func) { 45 | return env.it(desc, func); 46 | }, 47 | 48 | xit: function(desc, func) { 49 | return env.xit(desc, func); 50 | }, 51 | 52 | beforeEach: function(beforeEachFunction) { 53 | return env.beforeEach(beforeEachFunction); 54 | }, 55 | 56 | afterEach: function(afterEachFunction) { 57 | return env.afterEach(afterEachFunction); 58 | }, 59 | 60 | expect: function(actual) { 61 | return env.expect(actual); 62 | }, 63 | 64 | pending: function() { 65 | return env.pending(); 66 | }, 67 | 68 | spyOn: function(obj, methodName) { 69 | return env.spyOn(obj, methodName); 70 | }, 71 | 72 | jsApiReporter: new jasmine.JsApiReporter({ 73 | timer: new jasmine.Timer() 74 | }) 75 | }; 76 | 77 | /** 78 | * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. 79 | */ 80 | if (typeof window == "undefined" && typeof exports == "object") { 81 | extend(exports, jasmineInterface); 82 | } else { 83 | extend(window, jasmineInterface); 84 | } 85 | 86 | /** 87 | * Expose the interface for adding custom equality testers. 88 | */ 89 | jasmine.addCustomEqualityTester = function(tester) { 90 | env.addCustomEqualityTester(tester); 91 | }; 92 | 93 | /** 94 | * Expose the interface for adding custom expectation matchers 95 | */ 96 | jasmine.addMatchers = function(matchers) { 97 | return env.addMatchers(matchers); 98 | }; 99 | 100 | /** 101 | * Expose the mock interface for the JavaScript timeout functions 102 | */ 103 | jasmine.clock = function() { 104 | return env.clock; 105 | }; 106 | 107 | /** 108 | * ## Runner Parameters 109 | * 110 | * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. 111 | */ 112 | 113 | var queryString = new jasmine.QueryString({ 114 | getWindowLocation: function() { return window.location; } 115 | }); 116 | 117 | var catchingExceptions = queryString.getParam("catch"); 118 | env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); 119 | 120 | /** 121 | * ## Reporters 122 | * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). 123 | */ 124 | var htmlReporter = new jasmine.HtmlReporter({ 125 | env: env, 126 | onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); }, 127 | getContainer: function() { return document.body; }, 128 | createElement: function() { return document.createElement.apply(document, arguments); }, 129 | createTextNode: function() { return document.createTextNode.apply(document, arguments); }, 130 | timer: new jasmine.Timer() 131 | }); 132 | 133 | /** 134 | * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. 135 | */ 136 | env.addReporter(jasmineInterface.jsApiReporter); 137 | env.addReporter(htmlReporter); 138 | 139 | /** 140 | * Filter which specs will be run by matching the start of the full name against the `spec` query param. 141 | */ 142 | var specFilter = new jasmine.HtmlSpecFilter({ 143 | filterString: function() { return queryString.getParam("spec"); } 144 | }); 145 | 146 | env.specFilter = function(spec) { 147 | return specFilter.matches(spec.getFullName()); 148 | }; 149 | 150 | /** 151 | * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. 152 | */ 153 | window.setTimeout = window.setTimeout; 154 | window.setInterval = window.setInterval; 155 | window.clearTimeout = window.clearTimeout; 156 | window.clearInterval = window.clearInterval; 157 | 158 | /** 159 | * ## Execution 160 | * 161 | * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. 162 | */ 163 | var currentWindowOnload = window.onload; 164 | 165 | window.onload = function() { 166 | if (currentWindowOnload) { 167 | currentWindowOnload(); 168 | } 169 | htmlReporter.initialize(); 170 | env.execute(); 171 | }; 172 | 173 | /** 174 | * Helper function for readability above. 175 | */ 176 | function extend(destination, source) { 177 | for (var property in source) destination[property] = source[property]; 178 | return destination; 179 | } 180 | 181 | }()); 182 | -------------------------------------------------------------------------------- /spec/lib/jasmine-2.0.0/console.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2013 Pivotal Labs 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | function getJasmineRequireObj() { 24 | if (typeof module !== "undefined" && module.exports) { 25 | return exports; 26 | } else { 27 | window.jasmineRequire = window.jasmineRequire || {}; 28 | return window.jasmineRequire; 29 | } 30 | } 31 | 32 | getJasmineRequireObj().console = function(jRequire, j$) { 33 | j$.ConsoleReporter = jRequire.ConsoleReporter(); 34 | }; 35 | 36 | getJasmineRequireObj().ConsoleReporter = function() { 37 | 38 | var noopTimer = { 39 | start: function(){}, 40 | elapsed: function(){ return 0; } 41 | }; 42 | 43 | function ConsoleReporter(options) { 44 | var print = options.print, 45 | showColors = options.showColors || false, 46 | onComplete = options.onComplete || function() {}, 47 | timer = options.timer || noopTimer, 48 | specCount, 49 | failureCount, 50 | failedSpecs = [], 51 | pendingCount, 52 | ansi = { 53 | green: '\x1B[32m', 54 | red: '\x1B[31m', 55 | yellow: '\x1B[33m', 56 | none: '\x1B[0m' 57 | }; 58 | 59 | this.jasmineStarted = function() { 60 | specCount = 0; 61 | failureCount = 0; 62 | pendingCount = 0; 63 | print("Started"); 64 | printNewline(); 65 | timer.start(); 66 | }; 67 | 68 | this.jasmineDone = function() { 69 | printNewline(); 70 | for (var i = 0; i < failedSpecs.length; i++) { 71 | specFailureDetails(failedSpecs[i]); 72 | } 73 | 74 | printNewline(); 75 | var specCounts = specCount + " " + plural("spec", specCount) + ", " + 76 | failureCount + " " + plural("failure", failureCount); 77 | 78 | if (pendingCount) { 79 | specCounts += ", " + pendingCount + " pending " + plural("spec", pendingCount); 80 | } 81 | 82 | print(specCounts); 83 | 84 | printNewline(); 85 | var seconds = timer.elapsed() / 1000; 86 | print("Finished in " + seconds + " " + plural("second", seconds)); 87 | 88 | printNewline(); 89 | 90 | onComplete(failureCount === 0); 91 | }; 92 | 93 | this.specDone = function(result) { 94 | specCount++; 95 | 96 | if (result.status == "pending") { 97 | pendingCount++; 98 | print(colored("yellow", "*")); 99 | return; 100 | } 101 | 102 | if (result.status == "passed") { 103 | print(colored("green", '.')); 104 | return; 105 | } 106 | 107 | if (result.status == "failed") { 108 | failureCount++; 109 | failedSpecs.push(result); 110 | print(colored("red", 'F')); 111 | } 112 | }; 113 | 114 | return this; 115 | 116 | function printNewline() { 117 | print("\n"); 118 | } 119 | 120 | function colored(color, str) { 121 | return showColors ? (ansi[color] + str + ansi.none) : str; 122 | } 123 | 124 | function plural(str, count) { 125 | return count == 1 ? str : str + "s"; 126 | } 127 | 128 | function repeat(thing, times) { 129 | var arr = []; 130 | for (var i = 0; i < times; i++) { 131 | arr.push(thing); 132 | } 133 | return arr; 134 | } 135 | 136 | function indent(str, spaces) { 137 | var lines = (str || '').split("\n"); 138 | var newArr = []; 139 | for (var i = 0; i < lines.length; i++) { 140 | newArr.push(repeat(" ", spaces).join("") + lines[i]); 141 | } 142 | return newArr.join("\n"); 143 | } 144 | 145 | function specFailureDetails(result) { 146 | printNewline(); 147 | print(result.fullName); 148 | 149 | for (var i = 0; i < result.failedExpectations.length; i++) { 150 | var failedExpectation = result.failedExpectations[i]; 151 | printNewline(); 152 | print(indent(failedExpectation.stack, 2)); 153 | } 154 | 155 | printNewline(); 156 | } 157 | } 158 | 159 | return ConsoleReporter; 160 | }; 161 | -------------------------------------------------------------------------------- /spec/lib/jasmine-2.0.0/jasmine-html.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2013 Pivotal Labs 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | jasmineRequire.html = function(j$) { 24 | j$.ResultsNode = jasmineRequire.ResultsNode(); 25 | j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); 26 | j$.QueryString = jasmineRequire.QueryString(); 27 | j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); 28 | }; 29 | 30 | jasmineRequire.HtmlReporter = function(j$) { 31 | 32 | var noopTimer = { 33 | start: function() {}, 34 | elapsed: function() { return 0; } 35 | }; 36 | 37 | function HtmlReporter(options) { 38 | var env = options.env || {}, 39 | getContainer = options.getContainer, 40 | createElement = options.createElement, 41 | createTextNode = options.createTextNode, 42 | onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, 43 | timer = options.timer || noopTimer, 44 | results = [], 45 | specsExecuted = 0, 46 | failureCount = 0, 47 | pendingSpecCount = 0, 48 | htmlReporterMain, 49 | symbols; 50 | 51 | this.initialize = function() { 52 | htmlReporterMain = createDom("div", {className: "html-reporter"}, 53 | createDom("div", {className: "banner"}, 54 | createDom("span", {className: "title"}, "Jasmine"), 55 | createDom("span", {className: "version"}, j$.version) 56 | ), 57 | createDom("ul", {className: "symbol-summary"}), 58 | createDom("div", {className: "alert"}), 59 | createDom("div", {className: "results"}, 60 | createDom("div", {className: "failures"}) 61 | ) 62 | ); 63 | getContainer().appendChild(htmlReporterMain); 64 | 65 | symbols = find(".symbol-summary"); 66 | }; 67 | 68 | var totalSpecsDefined; 69 | this.jasmineStarted = function(options) { 70 | totalSpecsDefined = options.totalSpecsDefined || 0; 71 | timer.start(); 72 | }; 73 | 74 | var summary = createDom("div", {className: "summary"}); 75 | 76 | var topResults = new j$.ResultsNode({}, "", null), 77 | currentParent = topResults; 78 | 79 | this.suiteStarted = function(result) { 80 | currentParent.addChild(result, "suite"); 81 | currentParent = currentParent.last(); 82 | }; 83 | 84 | this.suiteDone = function(result) { 85 | if (currentParent == topResults) { 86 | return; 87 | } 88 | 89 | currentParent = currentParent.parent; 90 | }; 91 | 92 | this.specStarted = function(result) { 93 | currentParent.addChild(result, "spec"); 94 | }; 95 | 96 | var failures = []; 97 | this.specDone = function(result) { 98 | if (result.status != "disabled") { 99 | specsExecuted++; 100 | } 101 | 102 | symbols.appendChild(createDom("li", { 103 | className: result.status, 104 | id: "spec_" + result.id, 105 | title: result.fullName 106 | } 107 | )); 108 | 109 | if (result.status == "failed") { 110 | failureCount++; 111 | 112 | var failure = 113 | createDom("div", {className: "spec-detail failed"}, 114 | createDom("div", {className: "description"}, 115 | createDom("a", {title: result.fullName, href: specHref(result)}, result.fullName) 116 | ), 117 | createDom("div", {className: "messages"}) 118 | ); 119 | var messages = failure.childNodes[1]; 120 | 121 | for (var i = 0; i < result.failedExpectations.length; i++) { 122 | var expectation = result.failedExpectations[i]; 123 | messages.appendChild(createDom("div", {className: "result-message"}, expectation.message)); 124 | messages.appendChild(createDom("div", {className: "stack-trace"}, expectation.stack)); 125 | } 126 | 127 | failures.push(failure); 128 | } 129 | 130 | if (result.status == "pending") { 131 | pendingSpecCount++; 132 | } 133 | }; 134 | 135 | this.jasmineDone = function() { 136 | var banner = find(".banner"); 137 | banner.appendChild(createDom("span", {className: "duration"}, "finished in " + timer.elapsed() / 1000 + "s")); 138 | 139 | var alert = find(".alert"); 140 | 141 | alert.appendChild(createDom("span", { className: "exceptions" }, 142 | createDom("label", { className: "label", 'for': "raise-exceptions" }, "raise exceptions"), 143 | createDom("input", { 144 | className: "raise", 145 | id: "raise-exceptions", 146 | type: "checkbox" 147 | }) 148 | )); 149 | var checkbox = find("input"); 150 | 151 | checkbox.checked = !env.catchingExceptions(); 152 | checkbox.onclick = onRaiseExceptionsClick; 153 | 154 | if (specsExecuted < totalSpecsDefined) { 155 | var skippedMessage = "Ran " + specsExecuted + " of " + totalSpecsDefined + " specs - run all"; 156 | alert.appendChild( 157 | createDom("span", {className: "bar skipped"}, 158 | createDom("a", {href: "?", title: "Run all specs"}, skippedMessage) 159 | ) 160 | ); 161 | } 162 | var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount); 163 | if (pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); } 164 | 165 | var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); 166 | alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage)); 167 | 168 | var results = find(".results"); 169 | results.appendChild(summary); 170 | 171 | summaryList(topResults, summary); 172 | 173 | function summaryList(resultsTree, domParent) { 174 | var specListNode; 175 | for (var i = 0; i < resultsTree.children.length; i++) { 176 | var resultNode = resultsTree.children[i]; 177 | if (resultNode.type == "suite") { 178 | var suiteListNode = createDom("ul", {className: "suite", id: "suite-" + resultNode.result.id}, 179 | createDom("li", {className: "suite-detail"}, 180 | createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) 181 | ) 182 | ); 183 | 184 | summaryList(resultNode, suiteListNode); 185 | domParent.appendChild(suiteListNode); 186 | } 187 | if (resultNode.type == "spec") { 188 | if (domParent.getAttribute("class") != "specs") { 189 | specListNode = createDom("ul", {className: "specs"}); 190 | domParent.appendChild(specListNode); 191 | } 192 | specListNode.appendChild( 193 | createDom("li", { 194 | className: resultNode.result.status, 195 | id: "spec-" + resultNode.result.id 196 | }, 197 | createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) 198 | ) 199 | ); 200 | } 201 | } 202 | } 203 | 204 | if (failures.length) { 205 | alert.appendChild( 206 | createDom('span', {className: "menu bar spec-list"}, 207 | createDom("span", {}, "Spec List | "), 208 | createDom('a', {className: "failures-menu", href: "#"}, "Failures"))); 209 | alert.appendChild( 210 | createDom('span', {className: "menu bar failure-list"}, 211 | createDom('a', {className: "spec-list-menu", href: "#"}, "Spec List"), 212 | createDom("span", {}, " | Failures "))); 213 | 214 | find(".failures-menu").onclick = function() { 215 | setMenuModeTo('failure-list'); 216 | }; 217 | find(".spec-list-menu").onclick = function() { 218 | setMenuModeTo('spec-list'); 219 | }; 220 | 221 | setMenuModeTo('failure-list'); 222 | 223 | var failureNode = find(".failures"); 224 | for (var i = 0; i < failures.length; i++) { 225 | failureNode.appendChild(failures[i]); 226 | } 227 | } 228 | }; 229 | 230 | return this; 231 | 232 | function find(selector) { 233 | return getContainer().querySelector(selector); 234 | } 235 | 236 | function createDom(type, attrs, childrenVarArgs) { 237 | var el = createElement(type); 238 | 239 | for (var i = 2; i < arguments.length; i++) { 240 | var child = arguments[i]; 241 | 242 | if (typeof child === 'string') { 243 | el.appendChild(createTextNode(child)); 244 | } else { 245 | if (child) { 246 | el.appendChild(child); 247 | } 248 | } 249 | } 250 | 251 | for (var attr in attrs) { 252 | if (attr == "className") { 253 | el[attr] = attrs[attr]; 254 | } else { 255 | el.setAttribute(attr, attrs[attr]); 256 | } 257 | } 258 | 259 | return el; 260 | } 261 | 262 | function pluralize(singular, count) { 263 | var word = (count == 1 ? singular : singular + "s"); 264 | 265 | return "" + count + " " + word; 266 | } 267 | 268 | function specHref(result) { 269 | return "?spec=" + encodeURIComponent(result.fullName); 270 | } 271 | 272 | function setMenuModeTo(mode) { 273 | htmlReporterMain.setAttribute("class", "html-reporter " + mode); 274 | } 275 | } 276 | 277 | return HtmlReporter; 278 | }; 279 | 280 | jasmineRequire.HtmlSpecFilter = function() { 281 | function HtmlSpecFilter(options) { 282 | var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 283 | var filterPattern = new RegExp(filterString); 284 | 285 | this.matches = function(specName) { 286 | return filterPattern.test(specName); 287 | }; 288 | } 289 | 290 | return HtmlSpecFilter; 291 | }; 292 | 293 | jasmineRequire.ResultsNode = function() { 294 | function ResultsNode(result, type, parent) { 295 | this.result = result; 296 | this.type = type; 297 | this.parent = parent; 298 | 299 | this.children = []; 300 | 301 | this.addChild = function(result, type) { 302 | this.children.push(new ResultsNode(result, type, this)); 303 | }; 304 | 305 | this.last = function() { 306 | return this.children[this.children.length - 1]; 307 | }; 308 | } 309 | 310 | return ResultsNode; 311 | }; 312 | 313 | jasmineRequire.QueryString = function() { 314 | function QueryString(options) { 315 | 316 | this.setParam = function(key, value) { 317 | var paramMap = queryStringToParamMap(); 318 | paramMap[key] = value; 319 | options.getWindowLocation().search = toQueryString(paramMap); 320 | }; 321 | 322 | this.getParam = function(key) { 323 | return queryStringToParamMap()[key]; 324 | }; 325 | 326 | return this; 327 | 328 | function toQueryString(paramMap) { 329 | var qStrPairs = []; 330 | for (var prop in paramMap) { 331 | qStrPairs.push(encodeURIComponent(prop) + "=" + encodeURIComponent(paramMap[prop])); 332 | } 333 | return "?" + qStrPairs.join('&'); 334 | } 335 | 336 | function queryStringToParamMap() { 337 | var paramStr = options.getWindowLocation().search.substring(1), 338 | params = [], 339 | paramMap = {}; 340 | 341 | if (paramStr.length > 0) { 342 | params = paramStr.split('&'); 343 | for (var i = 0; i < params.length; i++) { 344 | var p = params[i].split('='); 345 | var value = decodeURIComponent(p[1]); 346 | if (value === "true" || value === "false") { 347 | value = JSON.parse(value); 348 | } 349 | paramMap[decodeURIComponent(p[0])] = value; 350 | } 351 | } 352 | 353 | return paramMap; 354 | } 355 | 356 | } 357 | 358 | return QueryString; 359 | }; 360 | -------------------------------------------------------------------------------- /spec/lib/jasmine-2.0.0/jasmine.css: -------------------------------------------------------------------------------- 1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } 2 | 3 | .html-reporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } 4 | .html-reporter a { text-decoration: none; } 5 | .html-reporter a:hover { text-decoration: underline; } 6 | .html-reporter p, .html-reporter h1, .html-reporter h2, .html-reporter h3, .html-reporter h4, .html-reporter h5, .html-reporter h6 { margin: 0; line-height: 14px; } 7 | .html-reporter .banner, .html-reporter .symbol-summary, .html-reporter .summary, .html-reporter .result-message, .html-reporter .spec .description, .html-reporter .spec-detail .description, .html-reporter .alert .bar, .html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; } 8 | .html-reporter .banner .version { margin-left: 14px; } 9 | .html-reporter #jasmine_content { position: fixed; right: 100%; } 10 | .html-reporter .version { color: #aaaaaa; } 11 | .html-reporter .banner { margin-top: 14px; } 12 | .html-reporter .duration { color: #aaaaaa; float: right; } 13 | .html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } 14 | .html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; } 15 | .html-reporter .symbol-summary li.passed { font-size: 14px; } 16 | .html-reporter .symbol-summary li.passed:before { color: #5e7d00; content: "\02022"; } 17 | .html-reporter .symbol-summary li.failed { line-height: 9px; } 18 | .html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } 19 | .html-reporter .symbol-summary li.disabled { font-size: 14px; } 20 | .html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } 21 | .html-reporter .symbol-summary li.pending { line-height: 17px; } 22 | .html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } 23 | .html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } 24 | .html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 25 | .html-reporter .bar.failed { background-color: #b03911; } 26 | .html-reporter .bar.passed { background-color: #a6b779; } 27 | .html-reporter .bar.skipped { background-color: #bababa; } 28 | .html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; } 29 | .html-reporter .bar.menu a { color: #333333; } 30 | .html-reporter .bar a { color: white; } 31 | .html-reporter.spec-list .bar.menu.failure-list, .html-reporter.spec-list .results .failures { display: none; } 32 | .html-reporter.failure-list .bar.menu.spec-list, .html-reporter.failure-list .summary { display: none; } 33 | .html-reporter .running-alert { background-color: #666666; } 34 | .html-reporter .results { margin-top: 14px; } 35 | .html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } 36 | .html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } 37 | .html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } 38 | .html-reporter.showDetails .summary { display: none; } 39 | .html-reporter.showDetails #details { display: block; } 40 | .html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } 41 | .html-reporter .summary { margin-top: 14px; } 42 | .html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } 43 | .html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } 44 | .html-reporter .summary li.passed a { color: #5e7d00; } 45 | .html-reporter .summary li.failed a { color: #b03911; } 46 | .html-reporter .summary li.pending a { color: #ba9d37; } 47 | .html-reporter .description + .suite { margin-top: 0; } 48 | .html-reporter .suite { margin-top: 14px; } 49 | .html-reporter .suite a { color: #333333; } 50 | .html-reporter .failures .spec-detail { margin-bottom: 28px; } 51 | .html-reporter .failures .spec-detail .description { background-color: #b03911; } 52 | .html-reporter .failures .spec-detail .description a { color: white; } 53 | .html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; } 54 | .html-reporter .result-message span.result { display: block; } 55 | .html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } 56 | -------------------------------------------------------------------------------- /spec/lib/jasmine-2.0.0/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioizquierdo/jquery.serializeJSON/8a2f2cfebd928b65509181bcdf01f8de243ed573/spec/lib/jasmine-2.0.0/jasmine_favicon.png -------------------------------------------------------------------------------- /spec/lib/zepto-1.1.6.js: -------------------------------------------------------------------------------- 1 | /* Zepto v1.1.6 - zepto event ajax form ie - zeptojs.com/license */ 2 | 3 | var Zepto = (function() { 4 | var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter, 5 | document = window.document, 6 | elementDisplay = {}, classCache = {}, 7 | cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, 8 | fragmentRE = /^\s*<(\w+|!)[^>]*>/, 9 | singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, 10 | tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, 11 | rootNodeRE = /^(?:body|html)$/i, 12 | capitalRE = /([A-Z])/g, 13 | 14 | // special attributes that should be get/set via method calls 15 | methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], 16 | 17 | adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], 18 | table = document.createElement('table'), 19 | tableRow = document.createElement('tr'), 20 | containers = { 21 | 'tr': document.createElement('tbody'), 22 | 'tbody': table, 'thead': table, 'tfoot': table, 23 | 'td': tableRow, 'th': tableRow, 24 | '*': document.createElement('div') 25 | }, 26 | readyRE = /complete|loaded|interactive/, 27 | simpleSelectorRE = /^[\w-]*$/, 28 | class2type = {}, 29 | toString = class2type.toString, 30 | zepto = {}, 31 | camelize, uniq, 32 | tempParent = document.createElement('div'), 33 | propMap = { 34 | 'tabindex': 'tabIndex', 35 | 'readonly': 'readOnly', 36 | 'for': 'htmlFor', 37 | 'class': 'className', 38 | 'maxlength': 'maxLength', 39 | 'cellspacing': 'cellSpacing', 40 | 'cellpadding': 'cellPadding', 41 | 'rowspan': 'rowSpan', 42 | 'colspan': 'colSpan', 43 | 'usemap': 'useMap', 44 | 'frameborder': 'frameBorder', 45 | 'contenteditable': 'contentEditable' 46 | }, 47 | isArray = Array.isArray || 48 | function(object){ return object instanceof Array } 49 | 50 | zepto.matches = function(element, selector) { 51 | if (!selector || !element || element.nodeType !== 1) return false 52 | var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || 53 | element.oMatchesSelector || element.matchesSelector 54 | if (matchesSelector) return matchesSelector.call(element, selector) 55 | // fall back to performing a selector: 56 | var match, parent = element.parentNode, temp = !parent 57 | if (temp) (parent = tempParent).appendChild(element) 58 | match = ~zepto.qsa(parent, selector).indexOf(element) 59 | temp && tempParent.removeChild(element) 60 | return match 61 | } 62 | 63 | function type(obj) { 64 | return obj == null ? String(obj) : 65 | class2type[toString.call(obj)] || "object" 66 | } 67 | 68 | function isFunction(value) { return type(value) == "function" } 69 | function isWindow(obj) { return obj != null && obj == obj.window } 70 | function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } 71 | function isObject(obj) { return type(obj) == "object" } 72 | function isPlainObject(obj) { 73 | return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype 74 | } 75 | function likeArray(obj) { return typeof obj.length == 'number' } 76 | 77 | function compact(array) { return filter.call(array, function(item){ return item != null }) } 78 | function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array } 79 | camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } 80 | function dasherize(str) { 81 | return str.replace(/::/g, '/') 82 | .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') 83 | .replace(/([a-z\d])([A-Z])/g, '$1_$2') 84 | .replace(/_/g, '-') 85 | .toLowerCase() 86 | } 87 | uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) } 88 | 89 | function classRE(name) { 90 | return name in classCache ? 91 | classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')) 92 | } 93 | 94 | function maybeAddPx(name, value) { 95 | return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value 96 | } 97 | 98 | function defaultDisplay(nodeName) { 99 | var element, display 100 | if (!elementDisplay[nodeName]) { 101 | element = document.createElement(nodeName) 102 | document.body.appendChild(element) 103 | display = getComputedStyle(element, '').getPropertyValue("display") 104 | element.parentNode.removeChild(element) 105 | display == "none" && (display = "block") 106 | elementDisplay[nodeName] = display 107 | } 108 | return elementDisplay[nodeName] 109 | } 110 | 111 | function children(element) { 112 | return 'children' in element ? 113 | slice.call(element.children) : 114 | $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node }) 115 | } 116 | 117 | // `$.zepto.fragment` takes a html string and an optional tag name 118 | // to generate DOM nodes nodes from the given html string. 119 | // The generated DOM nodes are returned as an array. 120 | // This function can be overriden in plugins for example to make 121 | // it compatible with browsers that don't support the DOM fully. 122 | zepto.fragment = function(html, name, properties) { 123 | var dom, nodes, container 124 | 125 | // A special case optimization for a single tag 126 | if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1)) 127 | 128 | if (!dom) { 129 | if (html.replace) html = html.replace(tagExpanderRE, "<$1>") 130 | if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 131 | if (!(name in containers)) name = '*' 132 | 133 | container = containers[name] 134 | container.innerHTML = '' + html 135 | dom = $.each(slice.call(container.childNodes), function(){ 136 | container.removeChild(this) 137 | }) 138 | } 139 | 140 | if (isPlainObject(properties)) { 141 | nodes = $(dom) 142 | $.each(properties, function(key, value) { 143 | if (methodAttributes.indexOf(key) > -1) nodes[key](value) 144 | else nodes.attr(key, value) 145 | }) 146 | } 147 | 148 | return dom 149 | } 150 | 151 | // `$.zepto.Z` swaps out the prototype of the given `dom` array 152 | // of nodes with `$.fn` and thus supplying all the Zepto functions 153 | // to the array. Note that `__proto__` is not supported on Internet 154 | // Explorer. This method can be overriden in plugins. 155 | zepto.Z = function(dom, selector) { 156 | dom = dom || [] 157 | dom.__proto__ = $.fn 158 | dom.selector = selector || '' 159 | return dom 160 | } 161 | 162 | // `$.zepto.isZ` should return `true` if the given object is a Zepto 163 | // collection. This method can be overriden in plugins. 164 | zepto.isZ = function(object) { 165 | return object instanceof zepto.Z 166 | } 167 | 168 | // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and 169 | // takes a CSS selector and an optional context (and handles various 170 | // special cases). 171 | // This method can be overriden in plugins. 172 | zepto.init = function(selector, context) { 173 | var dom 174 | // If nothing given, return an empty Zepto collection 175 | if (!selector) return zepto.Z() 176 | // Optimize for string selectors 177 | else if (typeof selector == 'string') { 178 | selector = selector.trim() 179 | // If it's a html fragment, create nodes from it 180 | // Note: In both Chrome 21 and Firefox 15, DOM error 12 181 | // is thrown if the fragment doesn't begin with < 182 | if (selector[0] == '<' && fragmentRE.test(selector)) 183 | dom = zepto.fragment(selector, RegExp.$1, context), selector = null 184 | // If there's a context, create a collection on that context first, and select 185 | // nodes from there 186 | else if (context !== undefined) return $(context).find(selector) 187 | // If it's a CSS selector, use it to select nodes. 188 | else dom = zepto.qsa(document, selector) 189 | } 190 | // If a function is given, call it when the DOM is ready 191 | else if (isFunction(selector)) return $(document).ready(selector) 192 | // If a Zepto collection is given, just return it 193 | else if (zepto.isZ(selector)) return selector 194 | else { 195 | // normalize array if an array of nodes is given 196 | if (isArray(selector)) dom = compact(selector) 197 | // Wrap DOM nodes. 198 | else if (isObject(selector)) 199 | dom = [selector], selector = null 200 | // If it's a html fragment, create nodes from it 201 | else if (fragmentRE.test(selector)) 202 | dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null 203 | // If there's a context, create a collection on that context first, and select 204 | // nodes from there 205 | else if (context !== undefined) return $(context).find(selector) 206 | // And last but no least, if it's a CSS selector, use it to select nodes. 207 | else dom = zepto.qsa(document, selector) 208 | } 209 | // create a new Zepto collection from the nodes found 210 | return zepto.Z(dom, selector) 211 | } 212 | 213 | // `$` will be the base `Zepto` object. When calling this 214 | // function just call `$.zepto.init, which makes the implementation 215 | // details of selecting nodes and creating Zepto collections 216 | // patchable in plugins. 217 | $ = function(selector, context){ 218 | return zepto.init(selector, context) 219 | } 220 | 221 | function extend(target, source, deep) { 222 | for (key in source) 223 | if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { 224 | if (isPlainObject(source[key]) && !isPlainObject(target[key])) 225 | target[key] = {} 226 | if (isArray(source[key]) && !isArray(target[key])) 227 | target[key] = [] 228 | extend(target[key], source[key], deep) 229 | } 230 | else if (source[key] !== undefined) target[key] = source[key] 231 | } 232 | 233 | // Copy all but undefined properties from one or more 234 | // objects to the `target` object. 235 | $.extend = function(target){ 236 | var deep, args = slice.call(arguments, 1) 237 | if (typeof target == 'boolean') { 238 | deep = target 239 | target = args.shift() 240 | } 241 | args.forEach(function(arg){ extend(target, arg, deep) }) 242 | return target 243 | } 244 | 245 | // `$.zepto.qsa` is Zepto's CSS selector implementation which 246 | // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`. 247 | // This method can be overriden in plugins. 248 | zepto.qsa = function(element, selector){ 249 | var found, 250 | maybeID = selector[0] == '#', 251 | maybeClass = !maybeID && selector[0] == '.', 252 | nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked 253 | isSimple = simpleSelectorRE.test(nameOnly) 254 | return (isDocument(element) && isSimple && maybeID) ? 255 | ( (found = element.getElementById(nameOnly)) ? [found] : [] ) : 256 | (element.nodeType !== 1 && element.nodeType !== 9) ? [] : 257 | slice.call( 258 | isSimple && !maybeID ? 259 | maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class 260 | element.getElementsByTagName(selector) : // Or a tag 261 | element.querySelectorAll(selector) // Or it's not simple, and we need to query all 262 | ) 263 | } 264 | 265 | function filtered(nodes, selector) { 266 | return selector == null ? $(nodes) : $(nodes).filter(selector) 267 | } 268 | 269 | $.contains = document.documentElement.contains ? 270 | function(parent, node) { 271 | return parent !== node && parent.contains(node) 272 | } : 273 | function(parent, node) { 274 | while (node && (node = node.parentNode)) 275 | if (node === parent) return true 276 | return false 277 | } 278 | 279 | function funcArg(context, arg, idx, payload) { 280 | return isFunction(arg) ? arg.call(context, idx, payload) : arg 281 | } 282 | 283 | function setAttribute(node, name, value) { 284 | value == null ? node.removeAttribute(name) : node.setAttribute(name, value) 285 | } 286 | 287 | // access className property while respecting SVGAnimatedString 288 | function className(node, value){ 289 | var klass = node.className || '', 290 | svg = klass && klass.baseVal !== undefined 291 | 292 | if (value === undefined) return svg ? klass.baseVal : klass 293 | svg ? (klass.baseVal = value) : (node.className = value) 294 | } 295 | 296 | // "true" => true 297 | // "false" => false 298 | // "null" => null 299 | // "42" => 42 300 | // "42.5" => 42.5 301 | // "08" => "08" 302 | // JSON => parse if valid 303 | // String => self 304 | function deserializeValue(value) { 305 | try { 306 | return value ? 307 | value == "true" || 308 | ( value == "false" ? false : 309 | value == "null" ? null : 310 | +value + "" == value ? +value : 311 | /^[\[\{]/.test(value) ? $.parseJSON(value) : 312 | value ) 313 | : value 314 | } catch(e) { 315 | return value 316 | } 317 | } 318 | 319 | $.type = type 320 | $.isFunction = isFunction 321 | $.isWindow = isWindow 322 | $.isArray = isArray 323 | $.isPlainObject = isPlainObject 324 | 325 | $.isEmptyObject = function(obj) { 326 | var name 327 | for (name in obj) return false 328 | return true 329 | } 330 | 331 | $.inArray = function(elem, array, i){ 332 | return emptyArray.indexOf.call(array, elem, i) 333 | } 334 | 335 | $.camelCase = camelize 336 | $.trim = function(str) { 337 | return str == null ? "" : String.prototype.trim.call(str) 338 | } 339 | 340 | // plugin compatibility 341 | $.uuid = 0 342 | $.support = { } 343 | $.expr = { } 344 | 345 | $.map = function(elements, callback){ 346 | var value, values = [], i, key 347 | if (likeArray(elements)) 348 | for (i = 0; i < elements.length; i++) { 349 | value = callback(elements[i], i) 350 | if (value != null) values.push(value) 351 | } 352 | else 353 | for (key in elements) { 354 | value = callback(elements[key], key) 355 | if (value != null) values.push(value) 356 | } 357 | return flatten(values) 358 | } 359 | 360 | $.each = function(elements, callback){ 361 | var i, key 362 | if (likeArray(elements)) { 363 | for (i = 0; i < elements.length; i++) 364 | if (callback.call(elements[i], i, elements[i]) === false) return elements 365 | } else { 366 | for (key in elements) 367 | if (callback.call(elements[key], key, elements[key]) === false) return elements 368 | } 369 | 370 | return elements 371 | } 372 | 373 | $.grep = function(elements, callback){ 374 | return filter.call(elements, callback) 375 | } 376 | 377 | if (window.JSON) $.parseJSON = JSON.parse 378 | 379 | // Populate the class2type map 380 | $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { 381 | class2type[ "[object " + name + "]" ] = name.toLowerCase() 382 | }) 383 | 384 | // Define methods that will be available on all 385 | // Zepto collections 386 | $.fn = { 387 | // Because a collection acts like an array 388 | // copy over these useful array functions. 389 | forEach: emptyArray.forEach, 390 | reduce: emptyArray.reduce, 391 | push: emptyArray.push, 392 | sort: emptyArray.sort, 393 | indexOf: emptyArray.indexOf, 394 | concat: emptyArray.concat, 395 | 396 | // `map` and `slice` in the jQuery API work differently 397 | // from their array counterparts 398 | map: function(fn){ 399 | return $($.map(this, function(el, i){ return fn.call(el, i, el) })) 400 | }, 401 | slice: function(){ 402 | return $(slice.apply(this, arguments)) 403 | }, 404 | 405 | ready: function(callback){ 406 | // need to check if document.body exists for IE as that browser reports 407 | // document ready when it hasn't yet created the body element 408 | if (readyRE.test(document.readyState) && document.body) callback($) 409 | else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false) 410 | return this 411 | }, 412 | get: function(idx){ 413 | return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length] 414 | }, 415 | toArray: function(){ return this.get() }, 416 | size: function(){ 417 | return this.length 418 | }, 419 | remove: function(){ 420 | return this.each(function(){ 421 | if (this.parentNode != null) 422 | this.parentNode.removeChild(this) 423 | }) 424 | }, 425 | each: function(callback){ 426 | emptyArray.every.call(this, function(el, idx){ 427 | return callback.call(el, idx, el) !== false 428 | }) 429 | return this 430 | }, 431 | filter: function(selector){ 432 | if (isFunction(selector)) return this.not(this.not(selector)) 433 | return $(filter.call(this, function(element){ 434 | return zepto.matches(element, selector) 435 | })) 436 | }, 437 | add: function(selector,context){ 438 | return $(uniq(this.concat($(selector,context)))) 439 | }, 440 | is: function(selector){ 441 | return this.length > 0 && zepto.matches(this[0], selector) 442 | }, 443 | not: function(selector){ 444 | var nodes=[] 445 | if (isFunction(selector) && selector.call !== undefined) 446 | this.each(function(idx){ 447 | if (!selector.call(this,idx)) nodes.push(this) 448 | }) 449 | else { 450 | var excludes = typeof selector == 'string' ? this.filter(selector) : 451 | (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) 452 | this.forEach(function(el){ 453 | if (excludes.indexOf(el) < 0) nodes.push(el) 454 | }) 455 | } 456 | return $(nodes) 457 | }, 458 | has: function(selector){ 459 | return this.filter(function(){ 460 | return isObject(selector) ? 461 | $.contains(this, selector) : 462 | $(this).find(selector).size() 463 | }) 464 | }, 465 | eq: function(idx){ 466 | return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1) 467 | }, 468 | first: function(){ 469 | var el = this[0] 470 | return el && !isObject(el) ? el : $(el) 471 | }, 472 | last: function(){ 473 | var el = this[this.length - 1] 474 | return el && !isObject(el) ? el : $(el) 475 | }, 476 | find: function(selector){ 477 | var result, $this = this 478 | if (!selector) result = $() 479 | else if (typeof selector == 'object') 480 | result = $(selector).filter(function(){ 481 | var node = this 482 | return emptyArray.some.call($this, function(parent){ 483 | return $.contains(parent, node) 484 | }) 485 | }) 486 | else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) 487 | else result = this.map(function(){ return zepto.qsa(this, selector) }) 488 | return result 489 | }, 490 | closest: function(selector, context){ 491 | var node = this[0], collection = false 492 | if (typeof selector == 'object') collection = $(selector) 493 | while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) 494 | node = node !== context && !isDocument(node) && node.parentNode 495 | return $(node) 496 | }, 497 | parents: function(selector){ 498 | var ancestors = [], nodes = this 499 | while (nodes.length > 0) 500 | nodes = $.map(nodes, function(node){ 501 | if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { 502 | ancestors.push(node) 503 | return node 504 | } 505 | }) 506 | return filtered(ancestors, selector) 507 | }, 508 | parent: function(selector){ 509 | return filtered(uniq(this.pluck('parentNode')), selector) 510 | }, 511 | children: function(selector){ 512 | return filtered(this.map(function(){ return children(this) }), selector) 513 | }, 514 | contents: function() { 515 | return this.map(function() { return slice.call(this.childNodes) }) 516 | }, 517 | siblings: function(selector){ 518 | return filtered(this.map(function(i, el){ 519 | return filter.call(children(el.parentNode), function(child){ return child!==el }) 520 | }), selector) 521 | }, 522 | empty: function(){ 523 | return this.each(function(){ this.innerHTML = '' }) 524 | }, 525 | // `pluck` is borrowed from Prototype.js 526 | pluck: function(property){ 527 | return $.map(this, function(el){ return el[property] }) 528 | }, 529 | show: function(){ 530 | return this.each(function(){ 531 | this.style.display == "none" && (this.style.display = '') 532 | if (getComputedStyle(this, '').getPropertyValue("display") == "none") 533 | this.style.display = defaultDisplay(this.nodeName) 534 | }) 535 | }, 536 | replaceWith: function(newContent){ 537 | return this.before(newContent).remove() 538 | }, 539 | wrap: function(structure){ 540 | var func = isFunction(structure) 541 | if (this[0] && !func) 542 | var dom = $(structure).get(0), 543 | clone = dom.parentNode || this.length > 1 544 | 545 | return this.each(function(index){ 546 | $(this).wrapAll( 547 | func ? structure.call(this, index) : 548 | clone ? dom.cloneNode(true) : dom 549 | ) 550 | }) 551 | }, 552 | wrapAll: function(structure){ 553 | if (this[0]) { 554 | $(this[0]).before(structure = $(structure)) 555 | var children 556 | // drill down to the inmost element 557 | while ((children = structure.children()).length) structure = children.first() 558 | $(structure).append(this) 559 | } 560 | return this 561 | }, 562 | wrapInner: function(structure){ 563 | var func = isFunction(structure) 564 | return this.each(function(index){ 565 | var self = $(this), contents = self.contents(), 566 | dom = func ? structure.call(this, index) : structure 567 | contents.length ? contents.wrapAll(dom) : self.append(dom) 568 | }) 569 | }, 570 | unwrap: function(){ 571 | this.parent().each(function(){ 572 | $(this).replaceWith($(this).children()) 573 | }) 574 | return this 575 | }, 576 | clone: function(){ 577 | return this.map(function(){ return this.cloneNode(true) }) 578 | }, 579 | hide: function(){ 580 | return this.css("display", "none") 581 | }, 582 | toggle: function(setting){ 583 | return this.each(function(){ 584 | var el = $(this) 585 | ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide() 586 | }) 587 | }, 588 | prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') }, 589 | next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') }, 590 | html: function(html){ 591 | return 0 in arguments ? 592 | this.each(function(idx){ 593 | var originHtml = this.innerHTML 594 | $(this).empty().append( funcArg(this, html, idx, originHtml) ) 595 | }) : 596 | (0 in this ? this[0].innerHTML : null) 597 | }, 598 | text: function(text){ 599 | return 0 in arguments ? 600 | this.each(function(idx){ 601 | var newText = funcArg(this, text, idx, this.textContent) 602 | this.textContent = newText == null ? '' : ''+newText 603 | }) : 604 | (0 in this ? this[0].textContent : null) 605 | }, 606 | attr: function(name, value){ 607 | var result 608 | return (typeof name == 'string' && !(1 in arguments)) ? 609 | (!this.length || this[0].nodeType !== 1 ? undefined : 610 | (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result 611 | ) : 612 | this.each(function(idx){ 613 | if (this.nodeType !== 1) return 614 | if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) 615 | else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) 616 | }) 617 | }, 618 | removeAttr: function(name){ 619 | return this.each(function(){ this.nodeType === 1 && name.split(' ').forEach(function(attribute){ 620 | setAttribute(this, attribute) 621 | }, this)}) 622 | }, 623 | prop: function(name, value){ 624 | name = propMap[name] || name 625 | return (1 in arguments) ? 626 | this.each(function(idx){ 627 | this[name] = funcArg(this, value, idx, this[name]) 628 | }) : 629 | (this[0] && this[0][name]) 630 | }, 631 | data: function(name, value){ 632 | var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase() 633 | 634 | var data = (1 in arguments) ? 635 | this.attr(attrName, value) : 636 | this.attr(attrName) 637 | 638 | return data !== null ? deserializeValue(data) : undefined 639 | }, 640 | val: function(value){ 641 | return 0 in arguments ? 642 | this.each(function(idx){ 643 | this.value = funcArg(this, value, idx, this.value) 644 | }) : 645 | (this[0] && (this[0].multiple ? 646 | $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') : 647 | this[0].value) 648 | ) 649 | }, 650 | offset: function(coordinates){ 651 | if (coordinates) return this.each(function(index){ 652 | var $this = $(this), 653 | coords = funcArg(this, coordinates, index, $this.offset()), 654 | parentOffset = $this.offsetParent().offset(), 655 | props = { 656 | top: coords.top - parentOffset.top, 657 | left: coords.left - parentOffset.left 658 | } 659 | 660 | if ($this.css('position') == 'static') props['position'] = 'relative' 661 | $this.css(props) 662 | }) 663 | if (!this.length) return null 664 | var obj = this[0].getBoundingClientRect() 665 | return { 666 | left: obj.left + window.pageXOffset, 667 | top: obj.top + window.pageYOffset, 668 | width: Math.round(obj.width), 669 | height: Math.round(obj.height) 670 | } 671 | }, 672 | css: function(property, value){ 673 | if (arguments.length < 2) { 674 | var computedStyle, element = this[0] 675 | if(!element) return 676 | computedStyle = getComputedStyle(element, '') 677 | if (typeof property == 'string') 678 | return element.style[camelize(property)] || computedStyle.getPropertyValue(property) 679 | else if (isArray(property)) { 680 | var props = {} 681 | $.each(property, function(_, prop){ 682 | props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop)) 683 | }) 684 | return props 685 | } 686 | } 687 | 688 | var css = '' 689 | if (type(property) == 'string') { 690 | if (!value && value !== 0) 691 | this.each(function(){ this.style.removeProperty(dasherize(property)) }) 692 | else 693 | css = dasherize(property) + ":" + maybeAddPx(property, value) 694 | } else { 695 | for (key in property) 696 | if (!property[key] && property[key] !== 0) 697 | this.each(function(){ this.style.removeProperty(dasherize(key)) }) 698 | else 699 | css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';' 700 | } 701 | 702 | return this.each(function(){ this.style.cssText += ';' + css }) 703 | }, 704 | index: function(element){ 705 | return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) 706 | }, 707 | hasClass: function(name){ 708 | if (!name) return false 709 | return emptyArray.some.call(this, function(el){ 710 | return this.test(className(el)) 711 | }, classRE(name)) 712 | }, 713 | addClass: function(name){ 714 | if (!name) return this 715 | return this.each(function(idx){ 716 | if (!('className' in this)) return 717 | classList = [] 718 | var cls = className(this), newName = funcArg(this, name, idx, cls) 719 | newName.split(/\s+/g).forEach(function(klass){ 720 | if (!$(this).hasClass(klass)) classList.push(klass) 721 | }, this) 722 | classList.length && className(this, cls + (cls ? " " : "") + classList.join(" ")) 723 | }) 724 | }, 725 | removeClass: function(name){ 726 | return this.each(function(idx){ 727 | if (!('className' in this)) return 728 | if (name === undefined) return className(this, '') 729 | classList = className(this) 730 | funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){ 731 | classList = classList.replace(classRE(klass), " ") 732 | }) 733 | className(this, classList.trim()) 734 | }) 735 | }, 736 | toggleClass: function(name, when){ 737 | if (!name) return this 738 | return this.each(function(idx){ 739 | var $this = $(this), names = funcArg(this, name, idx, className(this)) 740 | names.split(/\s+/g).forEach(function(klass){ 741 | (when === undefined ? !$this.hasClass(klass) : when) ? 742 | $this.addClass(klass) : $this.removeClass(klass) 743 | }) 744 | }) 745 | }, 746 | scrollTop: function(value){ 747 | if (!this.length) return 748 | var hasScrollTop = 'scrollTop' in this[0] 749 | if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset 750 | return this.each(hasScrollTop ? 751 | function(){ this.scrollTop = value } : 752 | function(){ this.scrollTo(this.scrollX, value) }) 753 | }, 754 | scrollLeft: function(value){ 755 | if (!this.length) return 756 | var hasScrollLeft = 'scrollLeft' in this[0] 757 | if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset 758 | return this.each(hasScrollLeft ? 759 | function(){ this.scrollLeft = value } : 760 | function(){ this.scrollTo(value, this.scrollY) }) 761 | }, 762 | position: function() { 763 | if (!this.length) return 764 | 765 | var elem = this[0], 766 | // Get *real* offsetParent 767 | offsetParent = this.offsetParent(), 768 | // Get correct offsets 769 | offset = this.offset(), 770 | parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset() 771 | 772 | // Subtract element margins 773 | // note: when an element has margin: auto the offsetLeft and marginLeft 774 | // are the same in Safari causing offset.left to incorrectly be 0 775 | offset.top -= parseFloat( $(elem).css('margin-top') ) || 0 776 | offset.left -= parseFloat( $(elem).css('margin-left') ) || 0 777 | 778 | // Add offsetParent borders 779 | parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0 780 | parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0 781 | 782 | // Subtract the two offsets 783 | return { 784 | top: offset.top - parentOffset.top, 785 | left: offset.left - parentOffset.left 786 | } 787 | }, 788 | offsetParent: function() { 789 | return this.map(function(){ 790 | var parent = this.offsetParent || document.body 791 | while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") 792 | parent = parent.offsetParent 793 | return parent 794 | }) 795 | } 796 | } 797 | 798 | // for now 799 | $.fn.detach = $.fn.remove 800 | 801 | // Generate the `width` and `height` functions 802 | ;['width', 'height'].forEach(function(dimension){ 803 | var dimensionProperty = 804 | dimension.replace(/./, function(m){ return m[0].toUpperCase() }) 805 | 806 | $.fn[dimension] = function(value){ 807 | var offset, el = this[0] 808 | if (value === undefined) return isWindow(el) ? el['inner' + dimensionProperty] : 809 | isDocument(el) ? el.documentElement['scroll' + dimensionProperty] : 810 | (offset = this.offset()) && offset[dimension] 811 | else return this.each(function(idx){ 812 | el = $(this) 813 | el.css(dimension, funcArg(this, value, idx, el[dimension]())) 814 | }) 815 | } 816 | }) 817 | 818 | function traverseNode(node, fun) { 819 | fun(node) 820 | for (var i = 0, len = node.childNodes.length; i < len; i++) 821 | traverseNode(node.childNodes[i], fun) 822 | } 823 | 824 | // Generate the `after`, `prepend`, `before`, `append`, 825 | // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods. 826 | adjacencyOperators.forEach(function(operator, operatorIndex) { 827 | var inside = operatorIndex % 2 //=> prepend, append 828 | 829 | $.fn[operator] = function(){ 830 | // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings 831 | var argType, nodes = $.map(arguments, function(arg) { 832 | argType = type(arg) 833 | return argType == "object" || argType == "array" || arg == null ? 834 | arg : zepto.fragment(arg) 835 | }), 836 | parent, copyByClone = this.length > 1 837 | if (nodes.length < 1) return this 838 | 839 | return this.each(function(_, target){ 840 | parent = inside ? target : target.parentNode 841 | 842 | // convert all methods to a "before" operation 843 | target = operatorIndex == 0 ? target.nextSibling : 844 | operatorIndex == 1 ? target.firstChild : 845 | operatorIndex == 2 ? target : 846 | null 847 | 848 | var parentInDocument = $.contains(document.documentElement, parent) 849 | 850 | nodes.forEach(function(node){ 851 | if (copyByClone) node = node.cloneNode(true) 852 | else if (!parent) return $(node).remove() 853 | 854 | parent.insertBefore(node, target) 855 | if (parentInDocument) traverseNode(node, function(el){ 856 | if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && 857 | (!el.type || el.type === 'text/javascript') && !el.src) 858 | window['eval'].call(window, el.innerHTML) 859 | }) 860 | }) 861 | }) 862 | } 863 | 864 | // after => insertAfter 865 | // prepend => prependTo 866 | // before => insertBefore 867 | // append => appendTo 868 | $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){ 869 | $(html)[operator](this) 870 | return this 871 | } 872 | }) 873 | 874 | zepto.Z.prototype = $.fn 875 | 876 | // Export internal API functions in the `$.zepto` namespace 877 | zepto.uniq = uniq 878 | zepto.deserializeValue = deserializeValue 879 | $.zepto = zepto 880 | 881 | return $ 882 | })() 883 | 884 | window.Zepto = Zepto 885 | window.$ === undefined && (window.$ = Zepto) 886 | 887 | ;(function($){ 888 | var _zid = 1, undefined, 889 | slice = Array.prototype.slice, 890 | isFunction = $.isFunction, 891 | isString = function(obj){ return typeof obj == 'string' }, 892 | handlers = {}, 893 | specialEvents={}, 894 | focusinSupported = 'onfocusin' in window, 895 | focus = { focus: 'focusin', blur: 'focusout' }, 896 | hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } 897 | 898 | specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' 899 | 900 | function zid(element) { 901 | return element._zid || (element._zid = _zid++) 902 | } 903 | function findHandlers(element, event, fn, selector) { 904 | event = parse(event) 905 | if (event.ns) var matcher = matcherFor(event.ns) 906 | return (handlers[zid(element)] || []).filter(function(handler) { 907 | return handler 908 | && (!event.e || handler.e == event.e) 909 | && (!event.ns || matcher.test(handler.ns)) 910 | && (!fn || zid(handler.fn) === zid(fn)) 911 | && (!selector || handler.sel == selector) 912 | }) 913 | } 914 | function parse(event) { 915 | var parts = ('' + event).split('.') 916 | return {e: parts[0], ns: parts.slice(1).sort().join(' ')} 917 | } 918 | function matcherFor(ns) { 919 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') 920 | } 921 | 922 | function eventCapture(handler, captureSetting) { 923 | return handler.del && 924 | (!focusinSupported && (handler.e in focus)) || 925 | !!captureSetting 926 | } 927 | 928 | function realEvent(type) { 929 | return hover[type] || (focusinSupported && focus[type]) || type 930 | } 931 | 932 | function add(element, events, fn, data, selector, delegator, capture){ 933 | var id = zid(element), set = (handlers[id] || (handlers[id] = [])) 934 | events.split(/\s/).forEach(function(event){ 935 | if (event == 'ready') return $(document).ready(fn) 936 | var handler = parse(event) 937 | handler.fn = fn 938 | handler.sel = selector 939 | // emulate mouseenter, mouseleave 940 | if (handler.e in hover) fn = function(e){ 941 | var related = e.relatedTarget 942 | if (!related || (related !== this && !$.contains(this, related))) 943 | return handler.fn.apply(this, arguments) 944 | } 945 | handler.del = delegator 946 | var callback = delegator || fn 947 | handler.proxy = function(e){ 948 | e = compatible(e) 949 | if (e.isImmediatePropagationStopped()) return 950 | e.data = data 951 | var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) 952 | if (result === false) e.preventDefault(), e.stopPropagation() 953 | return result 954 | } 955 | handler.i = set.length 956 | set.push(handler) 957 | if ('addEventListener' in element) 958 | element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 959 | }) 960 | } 961 | function remove(element, events, fn, selector, capture){ 962 | var id = zid(element) 963 | ;(events || '').split(/\s/).forEach(function(event){ 964 | findHandlers(element, event, fn, selector).forEach(function(handler){ 965 | delete handlers[id][handler.i] 966 | if ('removeEventListener' in element) 967 | element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 968 | }) 969 | }) 970 | } 971 | 972 | $.event = { add: add, remove: remove } 973 | 974 | $.proxy = function(fn, context) { 975 | var args = (2 in arguments) && slice.call(arguments, 2) 976 | if (isFunction(fn)) { 977 | var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) } 978 | proxyFn._zid = zid(fn) 979 | return proxyFn 980 | } else if (isString(context)) { 981 | if (args) { 982 | args.unshift(fn[context], fn) 983 | return $.proxy.apply(null, args) 984 | } else { 985 | return $.proxy(fn[context], fn) 986 | } 987 | } else { 988 | throw new TypeError("expected function") 989 | } 990 | } 991 | 992 | $.fn.bind = function(event, data, callback){ 993 | return this.on(event, data, callback) 994 | } 995 | $.fn.unbind = function(event, callback){ 996 | return this.off(event, callback) 997 | } 998 | $.fn.one = function(event, selector, data, callback){ 999 | return this.on(event, selector, data, callback, 1) 1000 | } 1001 | 1002 | var returnTrue = function(){return true}, 1003 | returnFalse = function(){return false}, 1004 | ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/, 1005 | eventMethods = { 1006 | preventDefault: 'isDefaultPrevented', 1007 | stopImmediatePropagation: 'isImmediatePropagationStopped', 1008 | stopPropagation: 'isPropagationStopped' 1009 | } 1010 | 1011 | function compatible(event, source) { 1012 | if (source || !event.isDefaultPrevented) { 1013 | source || (source = event) 1014 | 1015 | $.each(eventMethods, function(name, predicate) { 1016 | var sourceMethod = source[name] 1017 | event[name] = function(){ 1018 | this[predicate] = returnTrue 1019 | return sourceMethod && sourceMethod.apply(source, arguments) 1020 | } 1021 | event[predicate] = returnFalse 1022 | }) 1023 | 1024 | if (source.defaultPrevented !== undefined ? source.defaultPrevented : 1025 | 'returnValue' in source ? source.returnValue === false : 1026 | source.getPreventDefault && source.getPreventDefault()) 1027 | event.isDefaultPrevented = returnTrue 1028 | } 1029 | return event 1030 | } 1031 | 1032 | function createProxy(event) { 1033 | var key, proxy = { originalEvent: event } 1034 | for (key in event) 1035 | if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] 1036 | 1037 | return compatible(proxy, event) 1038 | } 1039 | 1040 | $.fn.delegate = function(selector, event, callback){ 1041 | return this.on(event, selector, callback) 1042 | } 1043 | $.fn.undelegate = function(selector, event, callback){ 1044 | return this.off(event, selector, callback) 1045 | } 1046 | 1047 | $.fn.live = function(event, callback){ 1048 | $(document.body).delegate(this.selector, event, callback) 1049 | return this 1050 | } 1051 | $.fn.die = function(event, callback){ 1052 | $(document.body).undelegate(this.selector, event, callback) 1053 | return this 1054 | } 1055 | 1056 | $.fn.on = function(event, selector, data, callback, one){ 1057 | var autoRemove, delegator, $this = this 1058 | if (event && !isString(event)) { 1059 | $.each(event, function(type, fn){ 1060 | $this.on(type, selector, data, fn, one) 1061 | }) 1062 | return $this 1063 | } 1064 | 1065 | if (!isString(selector) && !isFunction(callback) && callback !== false) 1066 | callback = data, data = selector, selector = undefined 1067 | if (isFunction(data) || data === false) 1068 | callback = data, data = undefined 1069 | 1070 | if (callback === false) callback = returnFalse 1071 | 1072 | return $this.each(function(_, element){ 1073 | if (one) autoRemove = function(e){ 1074 | remove(element, e.type, callback) 1075 | return callback.apply(this, arguments) 1076 | } 1077 | 1078 | if (selector) delegator = function(e){ 1079 | var evt, match = $(e.target).closest(selector, element).get(0) 1080 | if (match && match !== element) { 1081 | evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) 1082 | return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) 1083 | } 1084 | } 1085 | 1086 | add(element, event, callback, data, selector, delegator || autoRemove) 1087 | }) 1088 | } 1089 | $.fn.off = function(event, selector, callback){ 1090 | var $this = this 1091 | if (event && !isString(event)) { 1092 | $.each(event, function(type, fn){ 1093 | $this.off(type, selector, fn) 1094 | }) 1095 | return $this 1096 | } 1097 | 1098 | if (!isString(selector) && !isFunction(callback) && callback !== false) 1099 | callback = selector, selector = undefined 1100 | 1101 | if (callback === false) callback = returnFalse 1102 | 1103 | return $this.each(function(){ 1104 | remove(this, event, callback, selector) 1105 | }) 1106 | } 1107 | 1108 | $.fn.trigger = function(event, args){ 1109 | event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) 1110 | event._args = args 1111 | return this.each(function(){ 1112 | // handle focus(), blur() by calling them directly 1113 | if (event.type in focus && typeof this[event.type] == "function") this[event.type]() 1114 | // items in the collection might not be DOM elements 1115 | else if ('dispatchEvent' in this) this.dispatchEvent(event) 1116 | else $(this).triggerHandler(event, args) 1117 | }) 1118 | } 1119 | 1120 | // triggers event handlers on current element just as if an event occurred, 1121 | // doesn't trigger an actual event, doesn't bubble 1122 | $.fn.triggerHandler = function(event, args){ 1123 | var e, result 1124 | this.each(function(i, element){ 1125 | e = createProxy(isString(event) ? $.Event(event) : event) 1126 | e._args = args 1127 | e.target = element 1128 | $.each(findHandlers(element, event.type || event), function(i, handler){ 1129 | result = handler.proxy(e) 1130 | if (e.isImmediatePropagationStopped()) return false 1131 | }) 1132 | }) 1133 | return result 1134 | } 1135 | 1136 | // shortcut methods for `.bind(event, fn)` for each event type 1137 | ;('focusin focusout focus blur load resize scroll unload click dblclick '+ 1138 | 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ 1139 | 'change select keydown keypress keyup error').split(' ').forEach(function(event) { 1140 | $.fn[event] = function(callback) { 1141 | return (0 in arguments) ? 1142 | this.bind(event, callback) : 1143 | this.trigger(event) 1144 | } 1145 | }) 1146 | 1147 | $.Event = function(type, props) { 1148 | if (!isString(type)) props = type, type = props.type 1149 | var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true 1150 | if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) 1151 | event.initEvent(type, bubbles, true) 1152 | return compatible(event) 1153 | } 1154 | 1155 | })(Zepto) 1156 | 1157 | ;(function($){ 1158 | var jsonpID = 0, 1159 | document = window.document, 1160 | key, 1161 | name, 1162 | rscript = /)<[^<]*)*<\/script>/gi, 1163 | scriptTypeRE = /^(?:text|application)\/javascript/i, 1164 | xmlTypeRE = /^(?:text|application)\/xml/i, 1165 | jsonType = 'application/json', 1166 | htmlType = 'text/html', 1167 | blankRE = /^\s*$/, 1168 | originAnchor = document.createElement('a') 1169 | 1170 | originAnchor.href = window.location.href 1171 | 1172 | // trigger a custom event and return false if it was cancelled 1173 | function triggerAndReturn(context, eventName, data) { 1174 | var event = $.Event(eventName) 1175 | $(context).trigger(event, data) 1176 | return !event.isDefaultPrevented() 1177 | } 1178 | 1179 | // trigger an Ajax "global" event 1180 | function triggerGlobal(settings, context, eventName, data) { 1181 | if (settings.global) return triggerAndReturn(context || document, eventName, data) 1182 | } 1183 | 1184 | // Number of active Ajax requests 1185 | $.active = 0 1186 | 1187 | function ajaxStart(settings) { 1188 | if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart') 1189 | } 1190 | function ajaxStop(settings) { 1191 | if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop') 1192 | } 1193 | 1194 | // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable 1195 | function ajaxBeforeSend(xhr, settings) { 1196 | var context = settings.context 1197 | if (settings.beforeSend.call(context, xhr, settings) === false || 1198 | triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) 1199 | return false 1200 | 1201 | triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]) 1202 | } 1203 | function ajaxSuccess(data, xhr, settings, deferred) { 1204 | var context = settings.context, status = 'success' 1205 | settings.success.call(context, data, status, xhr) 1206 | if (deferred) deferred.resolveWith(context, [data, status, xhr]) 1207 | triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]) 1208 | ajaxComplete(status, xhr, settings) 1209 | } 1210 | // type: "timeout", "error", "abort", "parsererror" 1211 | function ajaxError(error, type, xhr, settings, deferred) { 1212 | var context = settings.context 1213 | settings.error.call(context, xhr, type, error) 1214 | if (deferred) deferred.rejectWith(context, [xhr, type, error]) 1215 | triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type]) 1216 | ajaxComplete(type, xhr, settings) 1217 | } 1218 | // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" 1219 | function ajaxComplete(status, xhr, settings) { 1220 | var context = settings.context 1221 | settings.complete.call(context, xhr, status) 1222 | triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]) 1223 | ajaxStop(settings) 1224 | } 1225 | 1226 | // Empty function, used as default callback 1227 | function empty() {} 1228 | 1229 | $.ajaxJSONP = function(options, deferred){ 1230 | if (!('type' in options)) return $.ajax(options) 1231 | 1232 | var _callbackName = options.jsonpCallback, 1233 | callbackName = ($.isFunction(_callbackName) ? 1234 | _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)), 1235 | script = document.createElement('script'), 1236 | originalCallback = window[callbackName], 1237 | responseData, 1238 | abort = function(errorType) { 1239 | $(script).triggerHandler('error', errorType || 'abort') 1240 | }, 1241 | xhr = { abort: abort }, abortTimeout 1242 | 1243 | if (deferred) deferred.promise(xhr) 1244 | 1245 | $(script).on('load error', function(e, errorType){ 1246 | clearTimeout(abortTimeout) 1247 | $(script).off().remove() 1248 | 1249 | if (e.type == 'error' || !responseData) { 1250 | ajaxError(null, errorType || 'error', xhr, options, deferred) 1251 | } else { 1252 | ajaxSuccess(responseData[0], xhr, options, deferred) 1253 | } 1254 | 1255 | window[callbackName] = originalCallback 1256 | if (responseData && $.isFunction(originalCallback)) 1257 | originalCallback(responseData[0]) 1258 | 1259 | originalCallback = responseData = undefined 1260 | }) 1261 | 1262 | if (ajaxBeforeSend(xhr, options) === false) { 1263 | abort('abort') 1264 | return xhr 1265 | } 1266 | 1267 | window[callbackName] = function(){ 1268 | responseData = arguments 1269 | } 1270 | 1271 | script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName) 1272 | document.head.appendChild(script) 1273 | 1274 | if (options.timeout > 0) abortTimeout = setTimeout(function(){ 1275 | abort('timeout') 1276 | }, options.timeout) 1277 | 1278 | return xhr 1279 | } 1280 | 1281 | $.ajaxSettings = { 1282 | // Default type of request 1283 | type: 'GET', 1284 | // Callback that is executed before request 1285 | beforeSend: empty, 1286 | // Callback that is executed if the request succeeds 1287 | success: empty, 1288 | // Callback that is executed the the server drops error 1289 | error: empty, 1290 | // Callback that is executed on request complete (both: error and success) 1291 | complete: empty, 1292 | // The context for the callbacks 1293 | context: null, 1294 | // Whether to trigger "global" Ajax events 1295 | global: true, 1296 | // Transport 1297 | xhr: function () { 1298 | return new window.XMLHttpRequest() 1299 | }, 1300 | // MIME types mapping 1301 | // IIS returns Javascript as "application/x-javascript" 1302 | accepts: { 1303 | script: 'text/javascript, application/javascript, application/x-javascript', 1304 | json: jsonType, 1305 | xml: 'application/xml, text/xml', 1306 | html: htmlType, 1307 | text: 'text/plain' 1308 | }, 1309 | // Whether the request is to another domain 1310 | crossDomain: false, 1311 | // Default timeout 1312 | timeout: 0, 1313 | // Whether data should be serialized to string 1314 | processData: true, 1315 | // Whether the browser should be allowed to cache GET responses 1316 | cache: true 1317 | } 1318 | 1319 | function mimeToDataType(mime) { 1320 | if (mime) mime = mime.split(';', 2)[0] 1321 | return mime && ( mime == htmlType ? 'html' : 1322 | mime == jsonType ? 'json' : 1323 | scriptTypeRE.test(mime) ? 'script' : 1324 | xmlTypeRE.test(mime) && 'xml' ) || 'text' 1325 | } 1326 | 1327 | function appendQuery(url, query) { 1328 | if (query == '') return url 1329 | return (url + '&' + query).replace(/[&?]{1,2}/, '?') 1330 | } 1331 | 1332 | // serialize payload and append it to the URL for GET requests 1333 | function serializeData(options) { 1334 | if (options.processData && options.data && $.type(options.data) != "string") 1335 | options.data = $.param(options.data, options.traditional) 1336 | if (options.data && (!options.type || options.type.toUpperCase() == 'GET')) 1337 | options.url = appendQuery(options.url, options.data), options.data = undefined 1338 | } 1339 | 1340 | $.ajax = function(options){ 1341 | var settings = $.extend({}, options || {}), 1342 | deferred = $.Deferred && $.Deferred(), 1343 | urlAnchor 1344 | for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] 1345 | 1346 | ajaxStart(settings) 1347 | 1348 | if (!settings.crossDomain) { 1349 | urlAnchor = document.createElement('a') 1350 | urlAnchor.href = settings.url 1351 | urlAnchor.href = urlAnchor.href 1352 | settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host) 1353 | } 1354 | 1355 | if (!settings.url) settings.url = window.location.toString() 1356 | serializeData(settings) 1357 | 1358 | var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url) 1359 | if (hasPlaceholder) dataType = 'jsonp' 1360 | 1361 | if (settings.cache === false || ( 1362 | (!options || options.cache !== true) && 1363 | ('script' == dataType || 'jsonp' == dataType) 1364 | )) 1365 | settings.url = appendQuery(settings.url, '_=' + Date.now()) 1366 | 1367 | if ('jsonp' == dataType) { 1368 | if (!hasPlaceholder) 1369 | settings.url = appendQuery(settings.url, 1370 | settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') 1371 | return $.ajaxJSONP(settings, deferred) 1372 | } 1373 | 1374 | var mime = settings.accepts[dataType], 1375 | headers = { }, 1376 | setHeader = function(name, value) { headers[name.toLowerCase()] = [name, value] }, 1377 | protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, 1378 | xhr = settings.xhr(), 1379 | nativeSetHeader = xhr.setRequestHeader, 1380 | abortTimeout 1381 | 1382 | if (deferred) deferred.promise(xhr) 1383 | 1384 | if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest') 1385 | setHeader('Accept', mime || '*/*') 1386 | if (mime = settings.mimeType || mime) { 1387 | if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] 1388 | xhr.overrideMimeType && xhr.overrideMimeType(mime) 1389 | } 1390 | if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) 1391 | setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded') 1392 | 1393 | if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name]) 1394 | xhr.setRequestHeader = setHeader 1395 | 1396 | xhr.onreadystatechange = function(){ 1397 | if (xhr.readyState == 4) { 1398 | xhr.onreadystatechange = empty 1399 | clearTimeout(abortTimeout) 1400 | var result, error = false 1401 | if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) { 1402 | dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type')) 1403 | result = xhr.responseText 1404 | 1405 | try { 1406 | // http://perfectionkills.com/global-eval-what-are-the-options/ 1407 | if (dataType == 'script') (1,eval)(result) 1408 | else if (dataType == 'xml') result = xhr.responseXML 1409 | else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result) 1410 | } catch (e) { error = e } 1411 | 1412 | if (error) ajaxError(error, 'parsererror', xhr, settings, deferred) 1413 | else ajaxSuccess(result, xhr, settings, deferred) 1414 | } else { 1415 | ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred) 1416 | } 1417 | } 1418 | } 1419 | 1420 | if (ajaxBeforeSend(xhr, settings) === false) { 1421 | xhr.abort() 1422 | ajaxError(null, 'abort', xhr, settings, deferred) 1423 | return xhr 1424 | } 1425 | 1426 | if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name] 1427 | 1428 | var async = 'async' in settings ? settings.async : true 1429 | xhr.open(settings.type, settings.url, async, settings.username, settings.password) 1430 | 1431 | for (name in headers) nativeSetHeader.apply(xhr, headers[name]) 1432 | 1433 | if (settings.timeout > 0) abortTimeout = setTimeout(function(){ 1434 | xhr.onreadystatechange = empty 1435 | xhr.abort() 1436 | ajaxError(null, 'timeout', xhr, settings, deferred) 1437 | }, settings.timeout) 1438 | 1439 | // avoid sending empty string (#319) 1440 | xhr.send(settings.data ? settings.data : null) 1441 | return xhr 1442 | } 1443 | 1444 | // handle optional data/success arguments 1445 | function parseArguments(url, data, success, dataType) { 1446 | if ($.isFunction(data)) dataType = success, success = data, data = undefined 1447 | if (!$.isFunction(success)) dataType = success, success = undefined 1448 | return { 1449 | url: url 1450 | , data: data 1451 | , success: success 1452 | , dataType: dataType 1453 | } 1454 | } 1455 | 1456 | $.get = function(/* url, data, success, dataType */){ 1457 | return $.ajax(parseArguments.apply(null, arguments)) 1458 | } 1459 | 1460 | $.post = function(/* url, data, success, dataType */){ 1461 | var options = parseArguments.apply(null, arguments) 1462 | options.type = 'POST' 1463 | return $.ajax(options) 1464 | } 1465 | 1466 | $.getJSON = function(/* url, data, success */){ 1467 | var options = parseArguments.apply(null, arguments) 1468 | options.dataType = 'json' 1469 | return $.ajax(options) 1470 | } 1471 | 1472 | $.fn.load = function(url, data, success){ 1473 | if (!this.length) return this 1474 | var self = this, parts = url.split(/\s/), selector, 1475 | options = parseArguments(url, data, success), 1476 | callback = options.success 1477 | if (parts.length > 1) options.url = parts[0], selector = parts[1] 1478 | options.success = function(response){ 1479 | self.html(selector ? 1480 | $('
').html(response.replace(rscript, "")).find(selector) 1481 | : response) 1482 | callback && callback.apply(self, arguments) 1483 | } 1484 | $.ajax(options) 1485 | return this 1486 | } 1487 | 1488 | var escape = encodeURIComponent 1489 | 1490 | function serialize(params, obj, traditional, scope){ 1491 | var type, array = $.isArray(obj), hash = $.isPlainObject(obj) 1492 | $.each(obj, function(key, value) { 1493 | type = $.type(value) 1494 | if (scope) key = traditional ? scope : 1495 | scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' 1496 | // handle data in serializeArray() format 1497 | if (!scope && array) params.add(value.name, value.value) 1498 | // recurse into nested objects 1499 | else if (type == "array" || (!traditional && type == "object")) 1500 | serialize(params, value, traditional, key) 1501 | else params.add(key, value) 1502 | }) 1503 | } 1504 | 1505 | $.param = function(obj, traditional){ 1506 | var params = [] 1507 | params.add = function(key, value) { 1508 | if ($.isFunction(value)) value = value() 1509 | if (value == null) value = "" 1510 | this.push(escape(key) + '=' + escape(value)) 1511 | } 1512 | serialize(params, obj, traditional) 1513 | return params.join('&').replace(/%20/g, '+') 1514 | } 1515 | })(Zepto) 1516 | 1517 | ;(function($){ 1518 | $.fn.serializeArray = function() { 1519 | var name, type, result = [], 1520 | add = function(value) { 1521 | if (value.forEach) return value.forEach(add) 1522 | result.push({ name: name, value: value }) 1523 | } 1524 | if (this[0]) $.each(this[0].elements, function(_, field){ 1525 | type = field.type, name = field.name 1526 | if (name && field.nodeName.toLowerCase() != 'fieldset' && 1527 | !field.disabled && type != 'submit' && type != 'reset' && type != 'button' && type != 'file' && 1528 | ((type != 'radio' && type != 'checkbox') || field.checked)) 1529 | add($(field).val()) 1530 | }) 1531 | return result 1532 | } 1533 | 1534 | $.fn.serialize = function(){ 1535 | var result = [] 1536 | this.serializeArray().forEach(function(elm){ 1537 | result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value)) 1538 | }) 1539 | return result.join('&') 1540 | } 1541 | 1542 | $.fn.submit = function(callback) { 1543 | if (0 in arguments) this.bind('submit', callback) 1544 | else if (this.length) { 1545 | var event = $.Event('submit') 1546 | this.eq(0).trigger(event) 1547 | if (!event.isDefaultPrevented()) this.get(0).submit() 1548 | } 1549 | return this 1550 | } 1551 | 1552 | })(Zepto) 1553 | 1554 | ;(function($){ 1555 | // __proto__ doesn't exist on IE<11, so redefine 1556 | // the Z function to use object extension instead 1557 | if (!('__proto__' in {})) { 1558 | $.extend($.zepto, { 1559 | Z: function(dom, selector){ 1560 | dom = dom || [] 1561 | $.extend(dom, $.fn) 1562 | dom.selector = selector || '' 1563 | dom.__Z = true 1564 | return dom 1565 | }, 1566 | // this is a kludge but works 1567 | isZ: function(object){ 1568 | return $.type(object) === 'array' && '__Z' in object 1569 | } 1570 | }) 1571 | } 1572 | 1573 | // getComputedStyle shouldn't freak out when called 1574 | // without a valid element as argument 1575 | try { 1576 | getComputedStyle(undefined) 1577 | } catch(e) { 1578 | var nativeGetComputedStyle = getComputedStyle; 1579 | window.getComputedStyle = function(element){ 1580 | try { 1581 | return nativeGetComputedStyle(element) 1582 | } catch(e) { 1583 | return null 1584 | } 1585 | } 1586 | } 1587 | })(Zepto) -------------------------------------------------------------------------------- /spec/spec_runner_jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SerializeJSON Spec Runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /spec/spec_runner_zepto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SerializeJSON Spec Runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | --------------------------------------------------------------------------------