From 285cfbfccc4c61d50ee8e0fe6e23695dc663e166 Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Thu, 11 Jun 2015 16:25:36 +0300 Subject: [PATCH 001/925] Build: remove bower.json lint target Ref 26eca143c2dd857b9e3d1c446a467fed16e903d2 --- Gruntfile.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index b5482829b4..d22807bf48 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -71,10 +71,6 @@ module.exports = function( grunt ) { jsonlint: { pkg: { src: [ "package.json" ] - }, - - bower: { - src: [ "bower.json" ] } }, jshint: { From 1556c4661af647e355a9a5c0a814012955e231bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 1 Jun 2015 23:25:38 +0200 Subject: [PATCH 002/925] Build: Update grunt-contrib-jshint --- package.json | 2 +- test/.jshintrc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c2ea7a735..c0ee702811 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "grunt": "0.4.5", "grunt-cli": "0.1.13", "grunt-compare-size": "0.4.0", - "grunt-contrib-jshint": "0.10.0", + "grunt-contrib-jshint": "0.11.2", "grunt-contrib-uglify": "0.7.0", "grunt-contrib-watch": "0.6.1", "grunt-git-authors": "2.0.1", diff --git a/test/.jshintrc b/test/.jshintrc index 7d24cba5de..b55594f4f8 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -21,6 +21,7 @@ "require": false, "define": false, "DOMParser": false, + "Promise": false, "QUnit": false, "ok": false, "equal": false, From 9c8a3ecdc46156afd8f93aa44b6e6aea7c52c049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 1 Jun 2015 23:25:38 +0200 Subject: [PATCH 003/925] Build: Refactor Node smoke tests Utilize the assert module, avoid inline JSHint comments. --- build/tasks/node_smoke_tests.js | 3 ++- test/node_smoke_tests/.jshintrc | 14 +++++++++++++ test/node_smoke_tests/document_missing.js | 20 ++++++------------- test/node_smoke_tests/document_passed.js | 9 +++------ .../document_present_originally.js | 9 +++------ .../lib/ensure_global_not_created.js | 10 ++++------ test/node_smoke_tests/lib/ensure_jquery.js | 10 ++++------ 7 files changed, 36 insertions(+), 39 deletions(-) create mode 100644 test/node_smoke_tests/.jshintrc diff --git a/build/tasks/node_smoke_tests.js b/build/tasks/node_smoke_tests.js index 077745b83f..5c23dae2ba 100644 --- a/build/tasks/node_smoke_tests.js +++ b/build/tasks/node_smoke_tests.js @@ -15,7 +15,8 @@ module.exports = function( grunt ) { fs.readdirSync( testsDir ) .filter( function( testFilePath ) { - return fs.statSync( testsDir + testFilePath ).isFile(); + return fs.statSync( testsDir + testFilePath ).isFile() && + /\.js$/.test( testFilePath ); } ) .forEach( function( testFilePath ) { var taskName = "node_" + testFilePath.replace( /\.js$/, "" ); diff --git a/test/node_smoke_tests/.jshintrc b/test/node_smoke_tests/.jshintrc new file mode 100644 index 0000000000..1445c7b18b --- /dev/null +++ b/test/node_smoke_tests/.jshintrc @@ -0,0 +1,14 @@ +{ + "boss": true, + "curly": true, + "eqeqeq": true, + "eqnull": true, + "expr": true, + "immed": true, + "noarg": true, + "quotmark": "double", + "undef": true, + "unused": true, + + "node": true +} diff --git a/test/node_smoke_tests/document_missing.js b/test/node_smoke_tests/document_missing.js index 0a0bda3503..4a0ad2e2bd 100644 --- a/test/node_smoke_tests/document_missing.js +++ b/test/node_smoke_tests/document_missing.js @@ -1,19 +1,11 @@ -/* jshint node: true */ - "use strict"; -var ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" ), +var assert = require( "assert" ), + ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" ), jQueryFactory = require( "../../dist/jquery.js" ); -try { +assert.throws( function () { jQueryFactory( {} ); - console.error( "The jQuery factory should reject window without a document" ); - process.exit( 1 ); -} catch ( e ) { - if ( e.message === "jQuery requires a window with a document" ) { - ensureGlobalNotCreated( module.exports ); - process.exit( 0 ); - } - console.error( "An unexpected error thrown; message: ", e.message ); - process.exit( 1 ); -} +}, /jQuery requires a window with a document/ ); + +ensureGlobalNotCreated( module.exports ); diff --git a/test/node_smoke_tests/document_passed.js b/test/node_smoke_tests/document_passed.js index 5bbddb7185..5999cc7441 100644 --- a/test/node_smoke_tests/document_passed.js +++ b/test/node_smoke_tests/document_passed.js @@ -1,12 +1,9 @@ -/* jshint node: true */ - "use strict"; +var assert = require( "assert" ); + require( "jsdom" ).env( "", function( errors, window ) { - if ( errors ) { - console.error( errors ); - process.exit( 1 ); - } + assert.ifError( errors ); var ensureJQuery = require( "./lib/ensure_jquery" ), ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" ), diff --git a/test/node_smoke_tests/document_present_originally.js b/test/node_smoke_tests/document_present_originally.js index 76fa88e861..f751487080 100644 --- a/test/node_smoke_tests/document_present_originally.js +++ b/test/node_smoke_tests/document_present_originally.js @@ -1,12 +1,9 @@ -/* jshint node: true */ - "use strict"; +var assert = require( "assert" ); + require( "jsdom" ).env( "", function( errors, window ) { - if ( errors ) { - console.error( errors ); - process.exit( 1 ); - } + assert.ifError( errors ); // Pretend the window is a global. global.window = window; diff --git a/test/node_smoke_tests/lib/ensure_global_not_created.js b/test/node_smoke_tests/lib/ensure_global_not_created.js index e2ce98309c..7cc83b5411 100644 --- a/test/node_smoke_tests/lib/ensure_global_not_created.js +++ b/test/node_smoke_tests/lib/ensure_global_not_created.js @@ -1,7 +1,7 @@ -/* jshint node: true */ - "use strict"; +var assert = require( "assert" ); + // Ensure the jQuery property on global/window/module.exports/etc. was not // created in a CommonJS environment. // `global` is always checked in addition to passed parameters. @@ -9,9 +9,7 @@ module.exports = function ensureGlobalNotCreated() { var args = [].slice.call( arguments ).concat( global ); args.forEach( function( object ) { - if ( object.jQuery ) { - console.error( "A jQuery global was created in a CommonJS environment." ); - process.exit( 1 ); - } + assert.strictEqual( object.jQuery, undefined, + "A jQuery global was created in a CommonJS environment." ); } ); }; diff --git a/test/node_smoke_tests/lib/ensure_jquery.js b/test/node_smoke_tests/lib/ensure_jquery.js index f121f6652f..0933a1d338 100644 --- a/test/node_smoke_tests/lib/ensure_jquery.js +++ b/test/node_smoke_tests/lib/ensure_jquery.js @@ -1,11 +1,9 @@ -/* jshint node: true */ - "use strict"; +var assert = require( "assert" ); + // Check if the object we got is the jQuery object by invoking a basic API. module.exports = function ensureJQuery( jQuery ) { - if ( !/^jQuery/.test( jQuery.expando ) ) { - console.error( "jQuery.expando was not detected, the jQuery bootstrap process has failed" ); - process.exit( 1 ); - } + assert( /^jQuery/.test( jQuery.expando ), + "jQuery.expando was not detected, the jQuery bootstrap process has failed" ); }; From bb026fc12c3c2ad37f47f0919e484bddcdc3d291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 1 Jun 2015 23:25:38 +0200 Subject: [PATCH 004/925] Core: Make jQuery objects iterable Make iterating over jQuery objects possible using ES 2015 for-of: for ( node of $( "
" ) ) { console.log( node.id ); // "narwhal" } Fixes gh-1693 --- .gitignore | 2 ++ .jscsrc | 3 ++- .jshintignore | 1 + Gruntfile.js | 12 +++++++++ build/tasks/node_smoke_tests.js | 2 +- package.json | 2 ++ src/core.js | 10 ++++++++ test/.jshintrc | 1 + .../iterable_with_native_symbol.js | 8 ++++++ .../iterable_with_symbol_polyfill.js | 13 ++++++++++ .../lib/ensure_iterability_es6.js | 25 +++++++++++++++++++ test/unit/core.js | 20 +++++++++++++++ 12 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 test/node_smoke_tests/iterable_with_native_symbol.js create mode 100644 test/node_smoke_tests/iterable_with_symbol_polyfill.js create mode 100644 test/node_smoke_tests/lib/ensure_iterability_es6.js diff --git a/.gitignore b/.gitignore index 463dea4268..eae5df6e6c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ /dist /node_modules + +/test/node_smoke_tests/lib/ensure_iterability.js diff --git a/.jscsrc b/.jscsrc index fa91a9d9e5..14f3491337 100644 --- a/.jscsrc +++ b/.jscsrc @@ -1,5 +1,6 @@ { "preset": "jquery", - "excludeFiles": [ "external", "src/intro.js", "src/outro.js" ] + "excludeFiles": [ "external", "src/intro.js", "src/outro.js", + "test/node_smoke_tests/lib/ensure_iterability.js" ] } diff --git a/.jshintignore b/.jshintignore index 19f1b9c523..1ddafd635a 100644 --- a/.jshintignore +++ b/.jshintignore @@ -9,3 +9,4 @@ test/data/readywaitasset.js test/data/readywaitloader.js test/data/support/csp.js test/data/support/getComputedSupport.js +test/node_smoke_tests/lib/ensure_iterability.js diff --git a/Gruntfile.js b/Gruntfile.js index d22807bf48..0bf20d4542 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -30,6 +30,18 @@ module.exports = function( grunt ) { cache: "build/.sizecache.json" } }, + babel: { + options: { + sourceMap: "inline", + retainLines: true + }, + nodeSmokeTests: { + files: { + "test/node_smoke_tests/lib/ensure_iterability.js": + "test/node_smoke_tests/lib/ensure_iterability_es6.js" + } + } + }, build: { all: { dest: "dist/jquery.js", diff --git a/build/tasks/node_smoke_tests.js b/build/tasks/node_smoke_tests.js index 5c23dae2ba..9334516d9e 100644 --- a/build/tasks/node_smoke_tests.js +++ b/build/tasks/node_smoke_tests.js @@ -5,7 +5,7 @@ module.exports = function( grunt ) { var fs = require( "fs" ), spawnTest = require( "./lib/spawn_test.js" ), testsDir = "./test/node_smoke_tests/", - nodeSmokeTests = [ "jsdom" ]; + nodeSmokeTests = [ "jsdom", "babel:nodeSmokeTests" ]; // Fire up all tests defined in test/node_smoke_tests/*.js in spawned sub-processes. // All the files under test/node_smoke_tests/*.js are supposed to exit with 0 code diff --git a/package.json b/package.json index c0ee702811..5d82995343 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,9 @@ "dependencies": {}, "devDependencies": { "commitplease": "2.0.0", + "core-js": "0.9.17", "grunt": "0.4.5", + "grunt-babel": "5.0.1", "grunt-cli": "0.1.13", "grunt-compare-size": "0.4.0", "grunt-contrib-jshint": "0.11.2", diff --git a/src/core.js b/src/core.js index 88b9a3c5d8..ba6eeceb8f 100644 --- a/src/core.js +++ b/src/core.js @@ -425,6 +425,16 @@ jQuery.extend({ support: support }); +// JSHint would error on this code due to the Symbol not being defined in ES5. +// Defining this global in .jshintrc would create a danger of using the global +// unguarded in another place, it seems safer to just disable JSHint for these +// three lines. +/* jshint ignore: start */ +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} +/* jshint ignore: end */ + // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { diff --git a/test/.jshintrc b/test/.jshintrc index b55594f4f8..7aef665c68 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -22,6 +22,7 @@ "define": false, "DOMParser": false, "Promise": false, + "Symbol": false, "QUnit": false, "ok": false, "equal": false, diff --git a/test/node_smoke_tests/iterable_with_native_symbol.js b/test/node_smoke_tests/iterable_with_native_symbol.js new file mode 100644 index 0000000000..3376ebdc55 --- /dev/null +++ b/test/node_smoke_tests/iterable_with_native_symbol.js @@ -0,0 +1,8 @@ +"use strict"; + +if ( typeof Symbol === "undefined" ) { + console.log( "Symbols not supported, skipping the test..." ); + process.exit(); +} + +require( "./lib/ensure_iterability_es6" )(); diff --git a/test/node_smoke_tests/iterable_with_symbol_polyfill.js b/test/node_smoke_tests/iterable_with_symbol_polyfill.js new file mode 100644 index 0000000000..dd377f19c2 --- /dev/null +++ b/test/node_smoke_tests/iterable_with_symbol_polyfill.js @@ -0,0 +1,13 @@ +/* jshint esnext: true */ + +"use strict"; + +var assert = require( "assert" ); + +delete global.Symbol; +require( "core-js" ); + +assert.strictEqual( typeof Symbol, "function", "Expected Symbol to be a function" ); +assert.notEqual( typeof Symbol.iterator, "symbol", "Expected Symbol.iterator to be polyfilled" ); + +require( "./lib/ensure_iterability" )(); diff --git a/test/node_smoke_tests/lib/ensure_iterability_es6.js b/test/node_smoke_tests/lib/ensure_iterability_es6.js new file mode 100644 index 0000000000..ebe68539e1 --- /dev/null +++ b/test/node_smoke_tests/lib/ensure_iterability_es6.js @@ -0,0 +1,25 @@ +/* jshint esnext: true */ + +"use strict"; + +var assert = require( "assert" ); + +module.exports = function ensureIterability() { + require( "jsdom" ).env( "", function( errors, window ) { + assert.ifError( errors ); + + var i, + ensureJQuery = require( "./ensure_jquery" ), + jQuery = require( "../../../dist/jquery.js" )( window ), + elem = jQuery( "
" ), + result = ""; + + ensureJQuery( jQuery ); + + for ( i of elem ) { + result += i.nodeName; + } + + assert.strictEqual( result, "DIVSPANA", "for-of doesn't work on jQuery objects" ); + } ); +}; diff --git a/test/unit/core.js b/test/unit/core.js index 0a018dea4e..d8370637bb 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -1538,3 +1538,23 @@ testIframeWithCallback( "Don't call window.onready (#14802)", "core/onready.html equal( error, false, "no call to user-defined onready" ); } ); + +test( "Iterability of jQuery objects (gh-1693)", function() { + /* jshint unused: false */ + expect( 1 ); + + var i, elem, result; + + if ( typeof Symbol === "function" ) { + + elem = jQuery( "
" ); + result = ""; + + try { + eval( "for ( i of elem ) { result += i.nodeName; }" ); + } catch ( e ) {} + equal( result, "DIVSPANA", "for-of works on jQuery objects" ); + } else { + ok( true, "The browser doesn't support Symbols" ); + } +} ); From 04a29696e5b176ac66401120e433d52425222f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 1 Jun 2015 21:34:57 +0200 Subject: [PATCH 005/925] Event: Remove an internal argument to the on method Refs gh-2301 --- src/event.js | 106 ++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/src/event.js b/src/event.js index 1e954513ab..04ee403d5d 100644 --- a/src/event.js +++ b/src/event.js @@ -34,6 +34,58 @@ function safeActiveElement() { } catch ( err ) { } } +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); +} + /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. @@ -799,59 +851,11 @@ if ( !support.focusin ) { jQuery.fn.extend({ - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); }, one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); + return on( this, types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; From 349edbd6c53aa93d4fd207d3c0c4c24a7b0314dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 1 Jun 2015 21:35:13 +0200 Subject: [PATCH 006/925] Manipulation: Remove an internal argument to the remove method Fixes gh-2301 Closes gh-2366 --- src/manipulation.js | 54 ++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/manipulation.js b/src/manipulation.js index 6424d8394c..4bac1417fa 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -196,6 +196,27 @@ function domManip( collection, args, callback, ignored ) { return collection; } +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; (node = nodes[i]) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + jQuery.extend({ htmlPrefilter: function( html ) { return html.replace( rxhtmlTag, "<$1>" ); @@ -268,6 +289,14 @@ jQuery.extend({ }); jQuery.fn.extend({ + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + text: function( value ) { return access( this, function( value ) { return value === undefined ? @@ -314,27 +343,6 @@ jQuery.fn.extend({ }); }, - remove: function( selector, keepData /* Internal Use Only */ ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, - empty: function() { var elem, i = 0; @@ -417,10 +425,6 @@ jQuery.fn.extend({ // Force callback invocation }, ignored ); - }, - - detach: function( selector ) { - return this.remove( selector, true ); } }); From c17543fd3c14ff86c448dbb90f9fe1223661a73b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Sun, 14 Jun 2015 00:37:31 +0200 Subject: [PATCH 007/925] Tests: Correct a typo in the regex matching Safari 8 --- test/unit/support.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/support.js b/test/unit/support.js index bf3e114d83..5a96d28a11 100644 --- a/test/unit/support.js +++ b/test/unit/support.js @@ -107,7 +107,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "radioValue": false, "reliableMarginRight": true }; - } else if ( /8.0(\.\d+|) safari/i.test( userAgent ) ) { + } else if ( /8\.0(\.\d+|) safari/i.test( userAgent ) ) { expected = { "ajax": true, "boxSizingReliable": true, From 8e111df641cca3e1b75b31a1971bfc89014b4ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Sun, 14 Jun 2015 00:50:05 +0200 Subject: [PATCH 008/925] Tests: Add Microsoft Edge results (from Windows 10 build 10130) The Microsoft Edge user agent contains "Chrome" so it needs to be checked before Chrome. --- test/unit/support.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/test/unit/support.js b/test/unit/support.js index 5a96d28a11..d1e0d189af 100644 --- a/test/unit/support.js +++ b/test/unit/support.js @@ -51,15 +51,13 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec var expected, userAgent = window.navigator.userAgent; - if ( /chrome/i.test( userAgent ) ) { - // Catches Chrome on Android as well (i.e. the default - // Android browser on Android >= 4.4). + if ( /edge\/12/i.test( userAgent ) ) { expected = { "ajax": true, "boxSizingReliable": true, "checkClone": true, "checkOn": true, - "clearCloneStyle": true, + "clearCloneStyle": false, "cors": true, "createHTMLDocument": true, "focusin": false, @@ -107,6 +105,26 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "radioValue": false, "reliableMarginRight": true }; + } else if ( /chrome/i.test( userAgent ) ) { + // Catches Chrome on Android as well (i.e. the default + // Android browser on Android >= 4.4). + expected = { + "ajax": true, + "boxSizingReliable": true, + "checkClone": true, + "checkOn": true, + "clearCloneStyle": true, + "cors": true, + "createHTMLDocument": true, + "focusin": false, + "noCloneChecked": true, + "optDisabled": true, + "optSelected": true, + "pixelMarginRight": true, + "pixelPosition": true, + "radioValue": true, + "reliableMarginRight": true + }; } else if ( /8\.0(\.\d+|) safari/i.test( userAgent ) ) { expected = { "ajax": true, From 5a1217e40193c8884155ccaf415091d326ddb52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Tue, 16 Jun 2015 14:44:24 +0200 Subject: [PATCH 009/925] Tests: Remove Edge version from the user agent The version will change in the future, matching by /edge\//i is enough Refs 8e111df641cca3e1b75b31a1971bfc89014b4ded --- test/unit/support.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/support.js b/test/unit/support.js index d1e0d189af..46f904efad 100644 --- a/test/unit/support.js +++ b/test/unit/support.js @@ -51,7 +51,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec var expected, userAgent = window.navigator.userAgent; - if ( /edge\/12/i.test( userAgent ) ) { + if ( /edge\//i.test( userAgent ) ) { expected = { "ajax": true, "boxSizingReliable": true, From e831856490d2212bdbaff4cd76137b93ccf26d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Tue, 16 Jun 2015 16:25:16 +0200 Subject: [PATCH 010/925] Tests: Remove test/data/ua.txt The file was used by $.browser tests but $.browser now doesn't exists in Core and this file hasn't been updated for a few years. Fixes gh-2398 --- test/data/ua.txt | 272 ----------------------------------------------- 1 file changed, 272 deletions(-) delete mode 100644 test/data/ua.txt diff --git a/test/data/ua.txt b/test/data/ua.txt deleted file mode 100644 index 95e522e258..0000000000 --- a/test/data/ua.txt +++ /dev/null @@ -1,272 +0,0 @@ -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; Win 9x 4.90; http://www.Abolimba.de) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; JyxoToolbar1.0; http://www.Abolimba.de; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 1.1.4322) - 0 Mozilla/5.0 (compatible; ABrowse 0.4; Syllable) -webkit 420 Mozilla/5.0 (compatible; U; ABrowse 0.6; Syllable) AppleWebKit/420+ (KHTML, like Gecko) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Acoo Browser; InfoPath.2; .NET CLR 2.0.50727; Alexa Toolbar) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Acoo Browser; .NET CLR 1.1.4322; .NET CLR 2.0.50727) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506) - 0 amaya/9.52 libwww/5.4.0 - 0 amaya/11.1 libwww/5.4.0 - 0 Amiga-AWeb/3.5.07 beta - 0 AmigaVoyager/3.4.4 (MorphOS/PPC native) - 0 AmigaVoyager/2.95 (compatible; MC680x0; AmigaOS) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; AOL 7.0; Windows NT 5.1; FunWebProducts) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; AOL 8.0; Windows NT 5.1; SV1) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.0; Windows NT 5.1; .NET CLR 1.1.4322; Zango 10.1.181.0) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; AOL 6.0; Windows NT 5.1) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727) -webkit 523.15 Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30) -webkit 527 Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6 -webkit 523.15 Mozilla/5.0 (X11; U; Linux; C -) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.5 -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Avant Browser; Avant Browser; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1) - 0 Avant Browser (http://www.avantbrowser.com) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; JyxoToolbar1.0; Embedded Web Browser from: http://bsalsa.com/; Avant Browser; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 1.1.4322) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB5; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; Avant Browser) -mozilla 0 Mozilla/5.0 (Windows; U; Win9x; en; Stable) Gecko/20020911 Beonex/0.8.1-stable -mozilla 0 Mozilla/5.0 (Windows; U; WinNT; en; Preview) Gecko/20020603 Beonex/0.8-stable -mozilla 1.0.2 Mozilla/5.0 (Windows; U; WinNT; en; rv:1.0.2) Gecko/20030311 Beonex/0.8.2-stable -mozilla 1.9 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9) Gecko/2008120120 Blackbird/0.9991 -webkit 527 Mozilla/5.0 (X11; 78; CentOS; US-en) AppleWebKit/527+ (KHTML, like Gecko) Bolt/0.862 Version/3.0 Safari/523.15 -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Browzar) - 0 Bunjalloo/0.7.4(Nintendo DS;U;en) - 0 Bunjalloo/0.7.6(Nintendo DS;U;en) -mozilla 1.8.1.4pre Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en; rv:1.8.1.4pre) Gecko/20070511 Camino/1.6pre -mozilla 1.7.2 Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7.2) Gecko/20040825 Camino/0.8.1 -mozilla 1.8.1.12 Mozilla/5.0 (Macintosh; U; Intel Mac OS X Mach-O; en; rv:1.8.1.12) Gecko/20080206 Camino/1.5.5 -mozilla 1.0.1 Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.0.1) Gecko/20030111 Chimera/0.6 -mozilla 1.8.0.10 Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.10) Gecko/20070228 Camino/1.0.4 -webkit 418.9 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418.9 (KHTML, like Gecko, Safari) Safari/419.3 Cheshire/1.0.ALPHA -webkit 419 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/419 (KHTML, like Gecko, Safari/419.3) Cheshire/1.0.ALPHA -chrome 1.0.154.36 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.36 Safari/525.19 -chrome 1.0.154.53 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.53 Safari/525.19 -mozilla 1.9.0.10 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.10) Gecko/2009042815 Firefox/3.0.10 CometBird/3.0.10 -mozilla 1.9.0.5 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.5) Gecko/2009011615 Firefox/3.0.5 CometBird/3.0.5 -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; Crazy Browser 3.0.0 Beta2) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; Crazy Browser 2.0.1) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Crazy Browser 1.0.5; .NET CLR 1.1.4322; InfoPath.1) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Deepnet Explorer 1.5.0; .NET CLR 1.0.3705) -webkit 525.27.1 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Demeter/1.0.9 Safari/125 -webkit 312.8 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; pl-pl) AppleWebKit/312.8 (KHTML, like Gecko, Safari) DeskBrowse/1.0 - 0 Dillo/0.8.5 - 0 Dillo/2.0 - 0 DocZilla/1.0 (Windows; U; WinNT4.0; en-US; rv:1.0.0) Gecko/20020804 - 0 DocZilla/2.7 (Windows; U; Windows NT 5.1; en-US; rv:2.7.0) Gecko/20050706 CiTEC Information -webkit 527 Mozilla/5.0 (Windows; U; Windows NT 5.1; cs-CZ) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Dooble - 0 Doris/1.15 [en] (Symbian) - 0 edbrowse/1.5.17-2 - 0 edbrowse/2.2.10 - 0 edbrowse/3.1.2-1 - 0 ELinks/0.13.GIT (textmode; Linux 2.6.22-2-686 i686; 148x68-3) - 0 ELinks/0.9.3 (textmode; Linux 2.6.11 i686; 79x24) - 0 Enigma Browser -mozilla 1.8.1.12 Mozilla/5.0 (X11; U; Linux i686; en; rv:1.8.1.12) Gecko/20080208 (Debian-1.8.1.12-2) Epiphany/2.20 -mozilla 1.9.0.12 Mozilla/5.0 (X11; U; Linux x86_64; en; rv:1.9.0.12) Gecko/20080528 Fedora/2.24.3-8.fc10 Epiphany/2.22 Firefox/3.0 -mozilla 1.7.3 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.3) Gecko/20041007 Epiphany/1.4.7 -mozilla 1.5 Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031007 Firebird/0.7 -mozilla 1.5 Mozilla/5.0 (Windows; U; Win95; en-US; rv:1.5) Gecko/20031007 Firebird/0.7 -mozilla 1.8.0.3 Mozilla/5.0 (Windows; U; Windows NT 5.0; es-ES; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -mozilla 1.9.1b2 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ko; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -mozilla 1.9.0.8 Mozilla/5.0 (Windows; U; Windows NT 5.1; cs; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 -mozilla 1.7.9 Mozilla/5.0 (Windows; U; WinNT4.0; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 -mozilla 1.9b5 Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5 -mozilla 1.8.0.5 Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.0.5) Gecko/20060819 Firefox/1.5.0.5 -mozilla 1.9.1b3 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 GTB5 -mozilla 1.8.1.12 Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.12) Gecko/20080214 Firefox/2.0.0.12 -mozilla 1.8.1.9 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.9) Gecko/20071113 BonEcho/2.0.0.9 -mozilla 1.8.1 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061026 BonEcho/2.0 -mozilla 1.8.1.21pre Mozilla/5.0 (BeOS; U; Haiku BePC; en-US; rv:1.8.1.21pre) Gecko/20090227 BonEcho/2.0.0.21pre -mozilla 1.9.0.8 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko/2009033017 GranParadiso/3.0.8 -mozilla 1.9.2a2pre Mozilla/5.0 (Windows; U; Windows NT 6.1; cs; rv:1.9.2a2pre) Gecko/20090912 Namoroka/3.6a2pre (.NET CLR 3.5.30729) -mozilla 1.9.2a2pre Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2a2pre) Gecko/20090901 Ubuntu/9.10 (karmic) Namoroka/3.6a2pre -mozilla 1.9.2a1 Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2a1) Gecko/20090806 Namoroka/3.6a1 -mozilla 1.9.1b3pre Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1b3pre) Gecko/20090109 Shiretoko/3.1b3pre -mozilla 1.9.1b4pre Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1b4pre) Gecko/20090311 Shiretoko/3.1b4pre -mozilla 1.8.0.1 Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.1) Gecko/20060314 Flock/0.5.13.2 -mozilla 1.9.0.2 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.2) Gecko/2008092122 Firefox/3.0.2 Flock/2.0b3 -webkit 525.13 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Fluid/0.9.4 Safari/525.13 -mozilla 1.7.12 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20050929 Galeon/1.3.21 -mozilla 1.9.0.8 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko/20090327 Galeon/2.0.7 -mozilla 1.9.1.5 Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091105 Firefox/3.5.5 compat GlobalMojo/1.5.5 GlobalMojoExt/1.5 -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; GreenBrowser) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; GreenBrowser) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; GreenBrowser) - 0 HotJava/1.1.2 FCS -mozilla 0 Mozilla/3.0 (x86 [cs] Windows NT 5.1; Sun) -mozilla 1.8.0.3 Mozilla/5.1 (X11; U; Linux i686; en-US; rv:1.8.0.3) Gecko/20060425 SUSE/1.5.0.3-7 Hv3/alpha -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SIMBAR={CFBFDAEA-F21E-4D6E-A9B0-E100A69B860F}; Hydra Browser; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Hydra Browser; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) - 0 IBrowse/2.3 (AmigaOS 3.9) - 0 Mozilla/5.0 (compatible; IBrowse 3.0; AmigaOS4.0) - 0 iCab/4.0 (Macintosh; U; Intel Mac OS X) - 0 Mozilla/4.5 (compatible; iCab 2.9.1; Macintosh; U; PPC) - 0 iCab/3.0.2 (Macintosh; U; PPC Mac OS X) - 0 ICE Browser/v5_4_3 (Java 1.4.2; Windows XP 5.1 x86) -mozilla 0 Mozilla/5.0 (Java 1.6.0_01; Windows XP 5.1 x86; en) ICEbrowser/v6_1_2 - 0 ICE Browser/5.05 (Java 1.4.0; Windows 2000 5.0 x86) -mozilla 1.8.1.9 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.9) Gecko/20071030 Iceape/1.1.6 (Debian-1.1.6-3) -mozilla 1.8.1.8 Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.8) Gecko/20071008 Iceape/1.1.5 (Ubuntu-1.1.5-1ubuntu0.7.10) -mozilla 1.9.0.3 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092921 IceCat/3.0.3-g1 -mozilla 1.8.1.11 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071203 IceCat/2.0.0.11-g1 -mozilla 1.9.0.5 Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.5) Gecko/2008122011 Iceweasel/3.0.5 (Debian-3.0.5-1) -mozilla 1.8.1.1 Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.1) Gecko/20061205 Iceweasel/2.0.0.1 (Debian-2.0.0.1+dfsg-4) -mozilla 1.9.0.5 Mozilla/5.0 (X11; U; Linux i686; it; rv:1.9.0.5) Gecko/2008122011 Iceweasel/3.0.5 (Debian-3.0.5-1) -msie 4.0 Mozilla/2.0 (compatible; MSIE 4.0; Windows 98) -msie 6.0 Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727) -msie 7.0 Mozilla/4.0 (Mozilla/4.0; MSIE 7.0; Windows NT 5.1; FDM; SV1; .NET CLR 3.0.04506.30) -msie 5.01 Mozilla/4.0 (compatible; MSIE 5.01; Windows NT) -msie 8.0 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 1.1.4322; InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; MS-RTC LM 8; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET CLR 3.0.30729) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727) -msie 5.0b1 Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC) -msie 5.0 Mozilla/4.0 (compatible; MSIE 5.0; Windows NT;) -msie 5.23 Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; GTB6; Ant.com Toolbar 1.6; MSIECrawler) -msie 8.0 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; GTB5; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 1.1.4322; InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; MS-RTC LM 8; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET CLR 3.0.30729) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.2) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; iRider 2.21.1108; FDM) -webkit 528.5 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.5 (KHTML, like Gecko) Iron/0.4.155.0 Safari/528.5 -webkit 528.7 Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/528.7 (KHTML, like Gecko) Iron/1.0.155.0 Safari/528.7 -webkit 525.19 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Iron/0.2.152.0 Safari/12081672.525 -webkit 531.0 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/531.0 (KHTML, like Gecko) Iron/3.0.189.0 Safari/531.0 -mozilla 1.8.1.19 Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.8.1.19) Gecko/20081217 K-Meleon/1.5.2 -mozilla 1.8.1.21 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.21) Gecko/20090331 K-Meleon/1.5.3 -mozilla 1.8.0.5 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.5) Gecko/20060706 K-Meleon/1.0 -mozilla 1.8.1.21 Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.8.1.21) Gecko/20090331 K-Meleon/1.5.3 -mozilla 1.8.0.6 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060731 K-Ninja/2.0.2 -mozilla 1.8.1.4pre Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4pre) Gecko/20070404 K-Ninja/2.1.3 -mozilla 1.8.1.2pre Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1 -mozilla 1.9 Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0 -mozilla 0 Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5 -mozilla 1.9.0.8 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6 -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; KKman2.0) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; KKMAN3.2) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; KKman3.0) - 0 Mozilla/5.0 (compatible; Konqueror/3.1-rc5; i686 Linux; 20020712) - 0 Mozilla/5.0 (compatible; Konqueror/4.3; Windows) KHTML/4.3.0 (like Gecko) - 0 Mozilla/5.0 (compatible; Konqueror/2.2.1; Linux) - 0 Mozilla/5.0 (compatible; Konqueror/3.5; SunOS) - 0 Mozilla/5.0 (compatible; Konqueror/4.1; OpenBSD) KHTML/4.1.4 (like Gecko) - 0 Links (0.96; Linux 2.4.20-18.7 i586) - 0 Links (0.98; Win32; 80x25) - 0 Links (2.1pre18; Linux 2.4.31 i686; 100x37) - 0 Links (2.1; Linux 2.6.18-gentoo-r6 x86_64; 80x24) - 0 Links (2.2; Linux 2.6.25-gentoo-r9 sparc64; 166x52) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Linux 2.6.26-1-amd64) Lobo/0.98.3 -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows XP 5.1) Lobo/0.98.4 - 0 Mozilla/4.0 (compatible; Lotus-Notes/5.0; Windows-NT) - 0 Mozilla/4.0 (compatible; Lotus-Notes/6.0; Windows-NT) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322; Lunascape 2.1.3) -mozilla 1.9.1b3pre Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b3pre) Gecko/2008 Lunascape/4.9.9.98 -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; JyxoToolbar1.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 1.1.4322; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; Lunascape 5.1.4.5) -webkit 528 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528+ (KHTML, like Gecko, Safari/528.0) Lunascape/5.0.2.0 -mozilla 1.9.1.2 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.2) Gecko/20090804 Firefox/3.5.2 Lunascape/5.1.4.5 - 0 Lynx/2.8.6rel.4 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/1.6.3 - 0 Lynx/2.8.3dev.6 libwww-FM/2.14 - 0 Lynx/2.8.5dev.16 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/0.9.7a -mozilla 1.7.12 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20051001 Firefox/1.0.7 Madfox/0.3.2u3 -webkit 530.6 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Maxthon/3.0 Safari/530.6 -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 1.1.4322) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MyIE2) -msie 8.0 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; MAXTHON 2.0) - 0 Midori/0.1.7 -webkit 532 Midori/0.1.5 (X11; Linux; U; en-gb) WebKit/532+ -mozilla 1.0.1 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.1) Gecko/20020919 -mozilla 1.7.12 Mozilla/5.0 (Windows; U; Windows NT 5.0; it-IT; rv:1.7.12) Gecko/20050915 -mozilla 1.4 Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4; MultiZilla v1.5.0.0f) Gecko/20030624 -mozilla 1.2.1 Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.2.1; MultiZilla v1.1.32 final) Gecko/20021130 - 0 NCSA_Mosaic/2.0 (Windows 3.1) - 0 NCSA_Mosaic/3.0 (Windows 95) - 0 NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m) - 0 NCSA_Mosaic/2.6 (X11; SunOS 4.1.3 sun4m) - 0 Mozilla/3.01 (compatible; Netbox/3.5 R92; Linux 2.2) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; NetCaptor 7.5.4; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 1.1.4322; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) -msie 5.01 Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; NetCaptor 6.5.0RC1) -mozilla 1.7.5 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20060127 Netscape/8.1 -mozilla 0 Mozilla/4.04 [en] (X11; I; IRIX 5.3 IP22) -mozilla 0.9.2 Mozilla/5.0 (Windows; U; Win 9x 4.90; de-DE; rv:0.9.2) Gecko/20010726 Netscape6/6.1 -mozilla 1.8.1.12 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.12) Gecko/20080219 Firefox/2.0.0.12 Navigator/9.0.0.6 -mozilla 0 Mozilla/4.08 [en] (WinNT; U ;Nav) -mozilla 1.0.2 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.0.2) Gecko/20030208 Netscape/7.02 -mozilla 0 Mozilla/3.0 (Win95; I) -mozilla 0 Mozilla/4.51 [en] (Win98; U) - 0 NetSurf/2.0 (RISC OS; armv3l) - 0 NetSurf/1.2 (Linux; i686) - 0 Mozilla/4.7 (compatible; OffByOne; Windows 2000) - 0 Mozilla/4.7 (compatible; OffByOne; Windows 98) - 0 Mozilla/4.5 (compatible; OmniWeb/4.1.1-v424.6; Mac_PowerPC) - 0 OmniWeb/2.7-beta-3 OWF/1.0 -webkit 420 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US) AppleWebKit/420+ (KHTML, like Gecko, Safari) OmniWeb/v595 -opera 6.0 Opera/6.0 (Windows 2000; U) [fr] -opera 7.10 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) Opera 7.10 [en] -opera 10.00 Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.2.15 Version/10.00 -opera 5.11 Opera/5.11 (Windows 98; U) [en] -opera 9.51 Opera/9.51 (Macintosh; Intel Mac OS X; U; en) -opera 6.01 Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 4.0) Opera 6.01 [en] -opera 9.02 Opera/9.02 (Windows XP; U; ru) -opera 5.12 Mozilla/4.0 (compatible; MSIE 5.0; Windows 98) Opera 5.12 [en] -opera 9.70 Opera/9.70 (Linux i686 ; U; en) Presto/2.2.1 -opera 7.03 Opera/7.03 (Windows NT 5.0; U) [en] -opera 9.24 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.24 -mozilla 1.9.0.7 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009030821 Firefox/3.0.7 Orca/1.1 build 2 -mozilla 1.9.0.6 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.6) Gecko/2009022300 Firefox/3.0.6 Orca/1.1 build 1 - 0 Mozilla/1.10 [en] (Compatible; RISC OS 3.70; Oregano 1.10) -webkit 530.0 Mozilla/5.0 (compatible; Origyn Web Browser; AmigaOS 4.1; ppc; U; en) AppleWebKit/530.0+ (KHTML, like Gecko, Safari/530.0+) -webkit 531.0 Mozilla/5.0 (compatible; Origyn Web Browser; AmigaOS 4.0; U; en) AppleWebKit/531.0+ (KHTML, like Gecko, Safari/531.0+) -webkit 528.5 Mozilla/5.0 (compatible; Origyn Web Browser; MorphOS; PPC; U) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; PhaseOut-www.phaseout.net) -mozilla 1.4a Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.4a) Gecko/20030411 Phoenix/0.5 -mozilla 1.2b Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.2b) Gecko/20021029 Phoenix/0.4 -webkit 527 Mozilla/5.0 (Windows; U; Windows NT 5.1; cs-CZ) AppleWebKit/527+ (KHTML, like Gecko) QtWeb Internet Browser/2.5 http://www.QtWeb.net -webkit 527 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/527+ (KHTML, like Gecko) QtWeb Internet Browser/1.2 http://www.QtWeb.net -webkit 527 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/527+ (KHTML, like Gecko) QtWeb Internet Browser/1.7 http://www.QtWeb.net -webkit 527 Mozilla/5.0 (X11; U; Linux; cs-CZ) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) rekonq - 0 retawq/0.2.6c [en] (text) -webkit 312.8 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.6 -webkit 528.16 Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_6; it-it) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -webkit 523.15 Mozilla/5.0 (Windows; U; Windows NT 5.1; cs-CZ) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15 -webkit 125.2 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.7 -webkit 528.16 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -webkit 420 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fi-fi) AppleWebKit/420+ (KHTML, like Gecko) Safari/419.3 -mozilla 1.8.1.13 Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.13) Gecko/20080313 SeaMonkey/1.1.9 -mozilla 1.9.1a2pre Mozilla/5.0 (X11; U; Linux i686; rv:1.9.1a2pre) Gecko/20080824052448 SeaMonkey/2.0a1pre -mozilla 1.8.1.6 Mozilla/5.0 (Windows; U; Win 9x 4.90; en-GB; rv:1.8.1.6) Gecko/20070802 SeaMonkey/1.1.4 -mozilla 1.9.1b3pre Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1b3pre) Gecko/20081208 SeaMonkey/2.0a3pre -mozilla 1.9a1 Mozilla/5.0 (BeOS; U; BeOS BePC; en-US; rv:1.9a1) Gecko/20060702 SeaMonkey/1.5a -mozilla 1.9.1b3pre Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081202 SeaMonkey/2.0a2 -webkit 419 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/419 (KHTML, like Gecko) Shiira/1.2.3 Safari/125 -webkit 417.9 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/417.9 (KHTML, like Gecko, Safari) Shiira/1.1 -webkit 418.9.1 Mozilla/5.0 (Macintosh; U; Intel Mac OS X; fr) AppleWebKit/418.9.1 (KHTML, like Gecko) Shiira Safari/125 -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) Sleipnir/2.8.1 -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727) Sleipnir/2.8.4 -webkit 525.27.1 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Stainless/0.4 Safari/525.20.1 -webkit 528.16 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/528.16 (KHTML, like Gecko) Stainless/0.5.3 Safari/525.20.1 -webkit 525.18 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_4_11; en) AppleWebKit/525.18 (KHTML, like Gecko) Sunrise/1.7.4 like Safari/4525.22 -webkit 125.5.7 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/125.5.7 (KHTML, like Gecko) SunriseBrowser/0.853 -mozilla 1.9.0.10pre Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10pre) Gecko/2009041814 Firefox/3.0.10pre (Swiftfox) -msie 6.0 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 1.1.4322; TheWorld) -webkit 1.1.8 Webkit/1.1.8 (Linux; en-us) Uzbl -webkit 1.1.10 Uzbl (X11; U; Linux x86_64; en-GB) AppleWebkit/1.1.10 -webkit 1.1.9 Uzbl (Webkit 1.1.9) (Linux) -webkit 1.1.10 Uzbl (U; Linux x86_64; en-GB) Webkit 1.1.10 - 0 w3m/0.5.1 - 0 w3m/0.5.2 -webkit 103u Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/103u (KHTML, like Gecko) wKiosk/100 -mozilla 1.9.0.9 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.9) Gecko/2009042410 Firefox/3.0.9 Wyzo/3.0.3 - 0 X-Smiles/1.2-20081113 -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30; MEGAUPLOAD 2.0) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30) -mozilla 1.9.0.4 Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.4) Gecko/2008111217 Fedora/3.0.4-1.fc9 Firefox/3.0.4 -mozilla 1.9.0.4 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4 -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; .NET CLR 1.1.4322; FDM) -msie 6.0 mozilla/4.0 (compatible; msie 6.0; windows nt 5.1; mra 4.6 (build 01425); .net clr 2.0.50727) -msie 7.0 mozilla/4.0 (compatible; msie 7.0; windows nt 5.1; mra 4.9 (build 01863)) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; InfoPath?.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; MS-RTC LM 8; .NET CLR 1.1.4322) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; InfoPath?.2; .NET CLR 3.5.21022) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Sky Broadband; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.1.4322) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; MS-RTC LM 8; .NET CLR 1.1.4322) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; InfoPath.2; .NET CLR 3.5.21022) -msie 7.0 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6; User-agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; http://bsalsa.com); .NET CLR 1.1.4322; .NET CLR 2.0.50727 -opera 10.00 Opera/9.80 (Macintosh; Intel Mac OS X; U; en) Presto/2.2.15 Version/10.00 -msie 8.0 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.1.4322; InfoPath?.2; .NET CLR 2.0.50727; CIBA; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) -opera 10.00 Opera/9.80 (X11; Linux x86_64; U; de) Presto/2.2.15 Version/10.00 -opera 10.50 Opera/9.80 (Macintosh; Intel Mac OS X; U; en) Presto/2.5.18 Version/10.50 From 578dcee96a8d4d759b3a7e623177fa36a5133ba7 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Mon, 15 Jun 2015 10:56:29 -0400 Subject: [PATCH 011/925] Revert "Offset: allow offset setter to throw for disconnected elements" This reverts commit 0d11c1182f2012cd6eb06ce1e3fa5a495af9bee3. --- src/offset.js | 2 +- test/unit/core.js | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/offset.js b/src/offset.js index 5ae7153f1c..a6aa2f1d5d 100644 --- a/src/offset.js +++ b/src/offset.js @@ -32,7 +32,7 @@ jQuery.offset = { elem.style.position = "relative"; } - curOffset = curElem.offset(); + curOffset = curElem.offset() || { top: 0, left: 0 }; curCSSTop = jQuery.css( elem, "top" ); curCSSLeft = jQuery.css( elem, "left" ); calculatePosition = ( position === "absolute" || position === "fixed" ) && diff --git a/test/unit/core.js b/test/unit/core.js index d8370637bb..5a87e2f0cf 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -36,6 +36,10 @@ test("jQuery()", function() { expected++; attrObj["width"] = 10; } + if ( jQuery.fn.offset ) { + expected++; + attrObj["offset"] = { "top": 1, "left": 1 }; + } if ( jQuery.fn.css ) { expected += 2; attrObj["css"] = { "paddingLeft": 1, "paddingRight": 1 }; @@ -101,12 +105,16 @@ test("jQuery()", function() { elem = jQuery("\n\nworld")[0]; equal( elem.nodeName.toLowerCase(), "em", "leading newlines" ); - elem = jQuery( "
", attrObj ); + elem = jQuery("
", attrObj ); if ( jQuery.fn.width ) { equal( elem[0].style.width, "10px", "jQuery() quick setter width"); } + if ( jQuery.fn.offset ) { + equal( elem[0].style.top, "1px", "jQuery() quick setter offset"); + } + if ( jQuery.fn.css ) { equal( elem[0].style.paddingLeft, "1px", "jQuery quick setter css"); equal( elem[0].style.paddingRight, "1px", "jQuery quick setter css"); From 40dcc767640c41a4387a343f1ef53ac57ed631c5 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Mon, 15 Jun 2015 11:02:08 -0400 Subject: [PATCH 012/925] Offset: return zeros for disconnected/hidden elements Fixes gh-2310 Close gh-2396 --- src/offset.js | 5 ++++- test/unit/offset.js | 22 ++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/offset.js b/src/offset.js index a6aa2f1d5d..fcb7b14de1 100644 --- a/src/offset.js +++ b/src/offset.js @@ -32,7 +32,7 @@ jQuery.offset = { elem.style.position = "relative"; } - curOffset = curElem.offset() || { top: 0, left: 0 }; + curOffset = curElem.offset(); curCSSTop = jQuery.css( elem, "top" ); curCSSLeft = jQuery.css( elem, "left" ); calculatePosition = ( position === "absolute" || position === "fixed" ) && @@ -103,6 +103,9 @@ jQuery.fn.extend({ left: rect.left + win.pageXOffset - docElem.clientLeft }; } + + // Return zeros for disconnected and hidden elements (gh-2310) + return rect; }, position: function() { diff --git a/test/unit/offset.js b/test/unit/offset.js index 7b2ecc905d..bffe87dd10 100644 --- a/test/unit/offset.js +++ b/test/unit/offset.js @@ -48,30 +48,24 @@ test("empty set", function() { }); test("disconnected element", function() { - expect(1); - - var result; + expect( 2 ); - try { - result = jQuery( document.createElement("div") ).offset(); - } catch ( e ) {} + var result = jQuery( document.createElement( "div" ) ).offset(); - ok( !result, "no position for disconnected element" ); + equal( result.top, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" ); + equal( result.left, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" ); }); test("hidden (display: none) element", function() { - expect(1); - - var result, - node = jQuery("
").appendTo("#qunit-fixture"); + expect( 2 ); - try { + var node = jQuery("
").appendTo("#qunit-fixture"), result = node.offset(); - } catch ( e ) {} node.remove(); - ok( !result, "no position for hidden (display: none) element" ); + equal( result.top, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" ); + equal( result.left, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" ); }); testIframe("offset/absolute", "absolute", function($, iframe) { From 0e4477c676db0427bb9b0bf39df8631501e62f24 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Tue, 16 Jun 2015 11:21:58 -0400 Subject: [PATCH 013/925] Offset: return before getBoundingClientRect to avoid error in IE8-11 --- src/offset.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/offset.js b/src/offset.js index fcb7b14de1..f0139d5eff 100644 --- a/src/offset.js +++ b/src/offset.js @@ -90,10 +90,17 @@ jQuery.fn.extend({ return; } + // Support: IE<=11+ + // Running getBoundingClientRect on a + // disconnected node in IE throws an error + if ( !elem.getClientRects().length ) { + return { top: 0, left: 0 }; + } + rect = elem.getBoundingClientRect(); - // Make sure element is not hidden (display: none) or disconnected - if ( rect.width || rect.height || elem.getClientRects().length ) { + // Make sure element is not hidden (display: none) + if ( rect.width || rect.height ) { doc = elem.ownerDocument; win = getWindow( doc ); docElem = doc.documentElement; From b04124222395a05c80d4f1c3a70333fdb07bfe3d Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Tue, 16 Jun 2015 13:24:12 -0400 Subject: [PATCH 014/925] Offset: add tests for hidden elements + scroll - Also add comments to hidden/disconnected tests noting this is to ensure consistency between branches --- test/data/offset/scroll.html | 2 ++ test/unit/offset.js | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/test/data/offset/scroll.html b/test/data/offset/scroll.html index 113400ce40..ad35ab84a3 100644 --- a/test/data/offset/scroll.html +++ b/test/data/offset/scroll.html @@ -11,6 +11,7 @@ #scroll-1-1 { top: 1px; left: 1px; } #scroll-1-1-1 { top: 1px; left: 1px; } #forceScroll { width: 5000px; height: 5000px; } + #hidden { display: none; } #marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; } @@ -32,6 +33,7 @@
+

Click the white box to move the marker to it.

diff --git a/test/unit/offset.js b/test/unit/offset.js index bffe87dd10..65243387f4 100644 --- a/test/unit/offset.js +++ b/test/unit/offset.js @@ -52,6 +52,9 @@ test("disconnected element", function() { var result = jQuery( document.createElement( "div" ) ).offset(); + // These tests are solely for master/compat consistency + // Retrieving offset on disconnected/hidden elements is not officially + // valid input, but will return zeros for back-compat equal( result.top, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" ); equal( result.left, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" ); }); @@ -64,6 +67,9 @@ test("hidden (display: none) element", function() { node.remove(); + // These tests are solely for master/compat consistency + // Retrieving offset on disconnected/hidden elements is not officially + // valid input, but will return zeros for back-compat equal( result.top, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" ); equal( result.left, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" ); }); @@ -401,7 +407,7 @@ testIframe("offset/table", "table", function( $ ) { }); testIframe("offset/scroll", "scroll", function( $, win ) { - expect(28); + expect( 30 ); equal( $("#scroll-1").offset().top, 7, "jQuery('#scroll-1').offset().top" ); equal( $("#scroll-1").offset().left, 7, "jQuery('#scroll-1').offset().left" ); @@ -409,6 +415,12 @@ testIframe("offset/scroll", "scroll", function( $, win ) { equal( $("#scroll-1-1").offset().top, 11, "jQuery('#scroll-1-1').offset().top" ); equal( $("#scroll-1-1").offset().left, 11, "jQuery('#scroll-1-1').offset().left" ); + // These tests are solely for master/compat consistency + // Retrieving offset on disconnected/hidden elements is not officially + // valid input, but will return zeros for back-compat + equal( $("#hidden").offset().top, 0, "Hidden elements do not subtract scroll" ); + equal( $("#hidden").offset().left, 0, "Hidden elements do not subtract scroll" ); + // scroll offset tests .scrollTop/Left equal( $("#scroll-1").scrollTop(), 5, "jQuery('#scroll-1').scrollTop()" ); equal( $("#scroll-1").scrollLeft(), 5, "jQuery('#scroll-1').scrollLeft()" ); From 219c7494938a10b985b7827990bc419e41585b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Sun, 14 Jun 2015 19:21:57 +0200 Subject: [PATCH 015/925] Core: Use window.setTimeout & friends instead of global equivalents Fixes gh-2177 --- src/.jshintrc | 4 ---- src/ajax.js | 5 ++++- src/core/ready.js | 3 ++- src/deferred.js | 3 ++- src/effects.js | 6 +++++- src/queue/delay.js | 4 +++- src/var/clearInterval.js | 3 +++ src/var/clearTimeout.js | 3 +++ src/var/setInterval.js | 3 +++ src/var/setTimeout.js | 3 +++ 10 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 src/var/clearInterval.js create mode 100644 src/var/clearTimeout.js create mode 100644 src/var/setInterval.js create mode 100644 src/var/setTimeout.js diff --git a/src/.jshintrc b/src/.jshintrc index a48233b29e..6144bf420e 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -14,10 +14,6 @@ "globals": { "window": true, - "setTimeout": true, - "clearTimeout": true, - "setInterval": true, - "clearInterval": true, "jQuery": true, "define": true, diff --git a/src/ajax.js b/src/ajax.js index 70bb49691a..21668165e6 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -2,6 +2,8 @@ define([ "./core", "./var/document", "./var/rnotwhite", + "./var/setTimeout", + "./var/clearTimeout", "./ajax/var/location", "./ajax/var/nonce", "./ajax/var/rquery", @@ -9,7 +11,8 @@ define([ "./ajax/parseJSON", "./ajax/parseXML", "./deferred" -], function( jQuery, document, rnotwhite, location, nonce, rquery ) { +], function( jQuery, document, rnotwhite, setTimeout, clearTimeout, + location, nonce, rquery ) { var rhash = /#.*$/, diff --git a/src/core/ready.js b/src/core/ready.js index bea0496da1..b78b474cdb 100644 --- a/src/core/ready.js +++ b/src/core/ready.js @@ -1,8 +1,9 @@ define([ "../core", "../var/document", + "../var/setTimeout", "../deferred" -], function( jQuery, document ) { +], function( jQuery, document, setTimeout ) { // The deferred used on DOM ready var readyList; diff --git a/src/deferred.js b/src/deferred.js index cdb7f1cc4e..043bf0c04d 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -1,8 +1,9 @@ define([ "./core", "./var/slice", + "./var/setTimeout", "./callbacks" -], function( jQuery, slice ) { +], function( jQuery, slice, setTimeout ) { function Identity( v ) { return v; diff --git a/src/effects.js b/src/effects.js index 3268fcda9a..63874fe78e 100644 --- a/src/effects.js +++ b/src/effects.js @@ -2,6 +2,9 @@ define([ "./core", "./var/document", "./var/rcssNum", + "./var/setInterval", + "./var/clearInterval", + "./var/setTimeout", "./css/var/cssExpand", "./css/var/isHidden", "./css/var/swap", @@ -16,7 +19,8 @@ define([ "./manipulation", "./css", "./effects/Tween" -], function( jQuery, document, rcssNum, cssExpand, isHidden, swap, adjustCSS, dataPriv, showHide ) { +], function( jQuery, document, rcssNum, setInterval, clearInterval, setTimeout, + cssExpand, isHidden, swap, adjustCSS, dataPriv, showHide ) { var fxNow, timerId, diff --git a/src/queue/delay.js b/src/queue/delay.js index 037ef0998e..d2aa7e4092 100644 --- a/src/queue/delay.js +++ b/src/queue/delay.js @@ -1,8 +1,10 @@ define([ "../core", + "../var/setTimeout", + "../var/clearTimeout", "../queue", "../effects" // Delay is optional because of this dependency -], function( jQuery ) { +], function( jQuery, setTimeout, clearTimeout ) { // Based off of the plugin by Clint Helfers, with permission. // http://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ diff --git a/src/var/clearInterval.js b/src/var/clearInterval.js new file mode 100644 index 0000000000..b8fcf31555 --- /dev/null +++ b/src/var/clearInterval.js @@ -0,0 +1,3 @@ +define(function() { + return window.clearInterval; +}); diff --git a/src/var/clearTimeout.js b/src/var/clearTimeout.js new file mode 100644 index 0000000000..ab77b074af --- /dev/null +++ b/src/var/clearTimeout.js @@ -0,0 +1,3 @@ +define(function() { + return window.clearTimeout; +}); diff --git a/src/var/setInterval.js b/src/var/setInterval.js new file mode 100644 index 0000000000..7d9906b1d1 --- /dev/null +++ b/src/var/setInterval.js @@ -0,0 +1,3 @@ +define(function() { + return window.setInterval; +}); diff --git a/src/var/setTimeout.js b/src/var/setTimeout.js new file mode 100644 index 0000000000..cda6b55ad8 --- /dev/null +++ b/src/var/setTimeout.js @@ -0,0 +1,3 @@ +define(function() { + return window.setTimeout; +}); From 842958e7aecd0d75a7ee9e2aaec83457701aa2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Wed, 17 Jun 2015 12:55:59 +0200 Subject: [PATCH 016/925] Core: Switch from modules to just window.setTimeout etc. Using modules for window.setTimeout etc. made those functions cached and disabled Sinon mocking, making effects tests fail. Just writing window.setTimeout directly is smaller anyway. --- src/ajax.js | 9 +++------ src/core/ready.js | 5 ++--- src/deferred.js | 5 ++--- src/effects.js | 12 ++++-------- src/queue/delay.js | 8 +++----- src/var/clearInterval.js | 3 --- src/var/clearTimeout.js | 3 --- src/var/setInterval.js | 3 --- src/var/setTimeout.js | 3 --- 9 files changed, 14 insertions(+), 37 deletions(-) delete mode 100644 src/var/clearInterval.js delete mode 100644 src/var/clearTimeout.js delete mode 100644 src/var/setInterval.js delete mode 100644 src/var/setTimeout.js diff --git a/src/ajax.js b/src/ajax.js index 21668165e6..9c1d6ca853 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -2,8 +2,6 @@ define([ "./core", "./var/document", "./var/rnotwhite", - "./var/setTimeout", - "./var/clearTimeout", "./ajax/var/location", "./ajax/var/nonce", "./ajax/var/rquery", @@ -11,8 +9,7 @@ define([ "./ajax/parseJSON", "./ajax/parseXML", "./deferred" -], function( jQuery, document, rnotwhite, setTimeout, clearTimeout, - location, nonce, rquery ) { +], function( jQuery, document, rnotwhite, location, nonce, rquery ) { var rhash = /#.*$/, @@ -645,7 +642,7 @@ jQuery.extend({ // Timeout if ( s.async && s.timeout > 0 ) { - timeoutTimer = setTimeout(function() { + timeoutTimer = window.setTimeout(function() { jqXHR.abort("timeout"); }, s.timeout ); } @@ -679,7 +676,7 @@ jQuery.extend({ // Clear timeout if it exists if ( timeoutTimer ) { - clearTimeout( timeoutTimer ); + window.clearTimeout( timeoutTimer ); } // Dereference transport for early garbage collection diff --git a/src/core/ready.js b/src/core/ready.js index b78b474cdb..085d19a931 100644 --- a/src/core/ready.js +++ b/src/core/ready.js @@ -1,9 +1,8 @@ define([ "../core", "../var/document", - "../var/setTimeout", "../deferred" -], function( jQuery, document, setTimeout ) { +], function( jQuery, document ) { // The deferred used on DOM ready var readyList; @@ -74,7 +73,7 @@ jQuery.ready.promise = function( obj ) { // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); + window.setTimeout( jQuery.ready ); } else { diff --git a/src/deferred.js b/src/deferred.js index 043bf0c04d..188303c975 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -1,9 +1,8 @@ define([ "./core", "./var/slice", - "./var/setTimeout", "./callbacks" -], function( jQuery, slice, setTimeout ) { +], function( jQuery, slice ) { function Identity( v ) { return v; @@ -174,7 +173,7 @@ jQuery.extend({ if ( depth ) { process(); } else { - setTimeout( process ); + window.setTimeout( process ); } }; } diff --git a/src/effects.js b/src/effects.js index 63874fe78e..53b73fd848 100644 --- a/src/effects.js +++ b/src/effects.js @@ -2,9 +2,6 @@ define([ "./core", "./var/document", "./var/rcssNum", - "./var/setInterval", - "./var/clearInterval", - "./var/setTimeout", "./css/var/cssExpand", "./css/var/isHidden", "./css/var/swap", @@ -19,8 +16,7 @@ define([ "./manipulation", "./css", "./effects/Tween" -], function( jQuery, document, rcssNum, setInterval, clearInterval, setTimeout, - cssExpand, isHidden, swap, adjustCSS, dataPriv, showHide ) { +], function( jQuery, document, rcssNum, cssExpand, isHidden, swap, adjustCSS, dataPriv, showHide ) { var fxNow, timerId, @@ -44,7 +40,7 @@ function raf() { // Animations created synchronously will run synchronously function createFxNow() { - setTimeout(function() { + window.setTimeout(function() { fxNow = undefined; }); return ( fxNow = jQuery.now() ); @@ -638,7 +634,7 @@ jQuery.fx.start = function() { if ( !timerId ) { timerId = window.requestAnimationFrame ? window.requestAnimationFrame( raf ) : - setInterval( jQuery.fx.tick, jQuery.fx.interval ); + window.setInterval( jQuery.fx.tick, jQuery.fx.interval ); } }; @@ -646,7 +642,7 @@ jQuery.fx.stop = function() { if ( window.cancelAnimationFrame ) { window.cancelAnimationFrame( timerId ); } else { - clearInterval( timerId ); + window.clearInterval( timerId ); } timerId = null; diff --git a/src/queue/delay.js b/src/queue/delay.js index d2aa7e4092..93abd0bf26 100644 --- a/src/queue/delay.js +++ b/src/queue/delay.js @@ -1,10 +1,8 @@ define([ "../core", - "../var/setTimeout", - "../var/clearTimeout", "../queue", "../effects" // Delay is optional because of this dependency -], function( jQuery, setTimeout, clearTimeout ) { +], function( jQuery ) { // Based off of the plugin by Clint Helfers, with permission. // http://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ @@ -13,9 +11,9 @@ jQuery.fn.delay = function( time, type ) { type = type || "fx"; return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); + var timeout = window.setTimeout( next, time ); hooks.stop = function() { - clearTimeout( timeout ); + window.clearTimeout( timeout ); }; }); }; diff --git a/src/var/clearInterval.js b/src/var/clearInterval.js deleted file mode 100644 index b8fcf31555..0000000000 --- a/src/var/clearInterval.js +++ /dev/null @@ -1,3 +0,0 @@ -define(function() { - return window.clearInterval; -}); diff --git a/src/var/clearTimeout.js b/src/var/clearTimeout.js deleted file mode 100644 index ab77b074af..0000000000 --- a/src/var/clearTimeout.js +++ /dev/null @@ -1,3 +0,0 @@ -define(function() { - return window.clearTimeout; -}); diff --git a/src/var/setInterval.js b/src/var/setInterval.js deleted file mode 100644 index 7d9906b1d1..0000000000 --- a/src/var/setInterval.js +++ /dev/null @@ -1,3 +0,0 @@ -define(function() { - return window.setInterval; -}); diff --git a/src/var/setTimeout.js b/src/var/setTimeout.js deleted file mode 100644 index cda6b55ad8..0000000000 --- a/src/var/setTimeout.js +++ /dev/null @@ -1,3 +0,0 @@ -define(function() { - return window.setTimeout; -}); From 63a577aa0bcb439c1730c3825407d86c128b17be Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Tue, 16 Jun 2015 11:24:21 -0400 Subject: [PATCH 017/925] Build: space between curly and paren is optional Fixes gh-2399 Close gh-2400 --- build/tasks/build.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/tasks/build.js b/build/tasks/build.js index f59a3340b8..ead3deb19d 100644 --- a/build/tasks/build.js +++ b/build/tasks/build.js @@ -11,7 +11,7 @@ module.exports = function( grunt ) { var fs = require( "fs" ), requirejs = require( "requirejs" ), srcFolder = __dirname + "/../../src/", - rdefineEnd = /\}\);[^}\w]*$/, + rdefineEnd = /\}\s*?\);[^}\w]*$/, config = { baseUrl: "src", name: "jquery", @@ -62,7 +62,7 @@ module.exports = function( grunt ) { } else { contents = contents - .replace( /\s*return\s+[^\}]+(\}\);[^\w\}]*)$/, "$1" ) + .replace( /\s*return\s+[^\}]+(\}\s*?\);[^\w\}]*)$/, "$1" ) // Multiple exports .replace( /\s*exports\.\w+\s*=\s*\w+;/g, "" ); From 90d828bad0d6d318d73d6cf6209d9dc7ac13878c Mon Sep 17 00:00:00 2001 From: Martin Naumann Date: Tue, 16 Jun 2015 23:55:21 +0200 Subject: [PATCH 018/925] CSS: Work around an IE11 fullscreen dimensions bug Fixes gh-1764 Closes gh-2401 --- src/css.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/css.js b/src/css.js index 566f5a0fd6..922f44162c 100644 --- a/src/css.js +++ b/src/css.js @@ -113,6 +113,17 @@ function getWidthOrHeight( elem, name, extra ) { styles = getStyles( elem ), isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + // Support: IE11 only + // Fix for edge case in IE 11. See gh-1764 + if ( document.msFullscreenElement && window.top !== window ) { + // Support: IE11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + if ( elem.getClientRects().length ) { + val = Math.round( elem.getBoundingClientRect()[ name ] * 100 ); + } + } + // Some non-html elements return undefined for offsetWidth, so check for null/undefined // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 From 5153b5334eb2c8317372b46209bd9d092a91afdc Mon Sep 17 00:00:00 2001 From: Gilad Peleg Date: Wed, 10 Jun 2015 17:56:17 +0300 Subject: [PATCH 019/925] Core: organize prop & attr code to be similar Closes gh-2384 --- src/attributes/attr.js | 70 ++++++++++++++++++++---------------------- src/attributes/prop.js | 40 +++++++++++++----------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/attributes/attr.js b/src/attributes/attr.js index bec86c1f5a..07576738a1 100644 --- a/src/attributes/attr.js +++ b/src/attributes/attr.js @@ -1,10 +1,10 @@ define([ "../core", - "../var/rnotwhite", "../core/access", "./support", + "../var/rnotwhite", "../selector" -], function( jQuery, rnotwhite, access, support ) { +], function( jQuery, access, support, rnotwhite ) { var boolHook, attrHandle = jQuery.expr.attrHandle; @@ -23,10 +23,10 @@ jQuery.fn.extend({ jQuery.extend({ attr: function( elem, name, value ) { - var hooks, ret, + var ret, hooks, nType = elem.nodeType; - // don't get/set attributes on text, comment and attribute nodes + // Don't get/set attributes on text, comment and attribute nodes if ( nType === 3 || nType === 8 || nType === 2 ) { return; } @@ -45,30 +45,43 @@ jQuery.extend({ } if ( value !== undefined ) { - if ( value === null ) { jQuery.removeAttr( elem, name ); + return; + } - } else if ( hooks && "set" in hooks && - (ret = hooks.set( elem, value, name )) !== undefined ) { - + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { return ret; - - } else { - elem.setAttribute( name, value + "" ); - return value; } - } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { return ret; + } - } else { - ret = jQuery.find.attr( elem, name ); + ret = jQuery.find.attr( elem, name ); - // Non-existent attributes return null, we normalize to undefined - return ret == null ? - undefined : - ret; + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + jQuery.nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } } }, @@ -78,11 +91,12 @@ jQuery.extend({ attrNames = value && value.match( rnotwhite ); if ( attrNames && elem.nodeType === 1 ) { - while ( (name = attrNames[i++]) ) { + while ( ( name = attrNames[i++] ) ) { propName = jQuery.propFix[ name ] || name; // Boolean attributes get special treatment (#10870) if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false elem[ propName ] = false; } @@ -90,22 +104,6 @@ jQuery.extend({ elem.removeAttribute( name ); } } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - jQuery.nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } } }); diff --git a/src/attributes/prop.js b/src/attributes/prop.js index 58ed765113..4c7b51e8c8 100644 --- a/src/attributes/prop.js +++ b/src/attributes/prop.js @@ -1,7 +1,8 @@ define([ "../core", "../core/access", - "./support" + "./support", + "../selector" ], function( jQuery, access, support ) { var rfocusable = /^(?:input|select|textarea|button)$/i; @@ -19,38 +20,36 @@ jQuery.fn.extend({ }); jQuery.extend({ - propFix: { - "for": "htmlFor", - "class": "className" - }, - prop: function( elem, name, value ) { - var ret, hooks, notxml, + var ret, hooks, nType = elem.nodeType; // Don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + if ( nType === 3 || nType === 8 || nType === 2 ) { return; } - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - if ( notxml ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; } if ( value !== undefined ) { - return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? - ret : - ( elem[ name ] = value ); - - } else { - return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? - ret : - elem[ name ]; + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; }, propHooks: { @@ -62,6 +61,11 @@ jQuery.extend({ -1; } } + }, + + propFix: { + "for": "htmlFor", + "class": "className" } }); From 3a0d582cf63b6e8f77150d9c38d2bf34d0c7790e Mon Sep 17 00:00:00 2001 From: Thomas Tortorini Date: Sun, 14 Jun 2015 23:58:44 +0200 Subject: [PATCH 020/925] CSS: make the getStyles function more readable The new version is not only simpler to read but also smaller by 6 bytes gzipped. Closes gh-2393 --- src/css/var/getStyles.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/css/var/getStyles.js b/src/css/var/getStyles.js index 0d1d51af62..31dfc298ea 100644 --- a/src/css/var/getStyles.js +++ b/src/css/var/getStyles.js @@ -1,12 +1,15 @@ define(function() { return function( elem ) { + // Support: IE<=11+, Firefox<=30+ (#15098, #14150) // IE throws on elements created in popups // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - if ( elem.ownerDocument.defaultView.opener ) { - return elem.ownerDocument.defaultView.getComputedStyle( elem ); + var view = elem.ownerDocument.defaultView; + + if ( !view.opener ) { + view = window; } - return window.getComputedStyle( elem ); + return view.getComputedStyle( elem ); }; }); From cdaed15c7ea1bbfdde5a5bea691c583ce7961526 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Mon, 18 May 2015 17:11:21 -0400 Subject: [PATCH 021/925] Effects: Add tests for jQuery.Tween --- Gruntfile.js | 5 +- external/sinon/LICENSE.txt | 27 - external/sinon/fake_timers.js | 409 --- external/sinon/sinon-1.14.1.js | 5931 ++++++++++++++++++++++++++++++++ src/effects/Tween.js | 8 +- src/selector-native.js | 3 +- test/data/testinit.js | 3 +- test/index.html | 2 +- test/unit/effects.js | 6 +- test/unit/tween.js | 298 ++ 10 files changed, 6246 insertions(+), 446 deletions(-) delete mode 100644 external/sinon/LICENSE.txt delete mode 100644 external/sinon/fake_timers.js create mode 100644 external/sinon/sinon-1.14.1.js create mode 100644 test/unit/tween.js diff --git a/Gruntfile.js b/Gruntfile.js index 0bf20d4542..8c88370e49 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -104,7 +104,7 @@ module.exports = function( grunt ) { gruntfile: "Gruntfile.js", // Right now, check only test helpers - test: [ "test/data/testrunner.js" ], + test: [ "test/data/testrunner.js", "test/unit/tween.js" ], release: [ "build/*.js", "!build/release-notes.js" ], tasks: "build/tasks/*.js" }, @@ -126,7 +126,8 @@ module.exports = function( grunt ) { "selector", "serialize", "support", - "traversing" + "traversing", + "tween" ] }, watch: { diff --git a/external/sinon/LICENSE.txt b/external/sinon/LICENSE.txt deleted file mode 100644 index e7f50d123b..0000000000 --- a/external/sinon/LICENSE.txt +++ /dev/null @@ -1,27 +0,0 @@ -(The BSD License) - -Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of Christian Johansen nor the names of his contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/sinon/fake_timers.js b/external/sinon/fake_timers.js deleted file mode 100644 index 0d51368276..0000000000 --- a/external/sinon/fake_timers.js +++ /dev/null @@ -1,409 +0,0 @@ -/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/ -/*global module, require, window*/ -/** - * Fake timer API - * setTimeout - * setInterval - * clearTimeout - * clearInterval - * tick - * reset - * Date - * - * Inspired by jsUnitMockTimeOut from JsUnit - * - * @author Christian Johansen (christian@cjohansen.no) - * @license BSD - * - * Copyright (c) 2010-2013 Christian Johansen - */ -"use strict"; - -if (typeof sinon == "undefined") { - var sinon = {}; -} - -(function (global) { - // node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref() - // browsers, a number. - // see https://github.com/cjohansen/Sinon.JS/pull/436 - var timeoutResult = setTimeout(function() {}, 0); - var addTimerReturnsObject = typeof timeoutResult === 'object'; - clearTimeout(timeoutResult); - - var id = 1; - - function addTimer(args, recurring) { - if (args.length === 0) { - throw new Error("Function requires at least 1 parameter"); - } - - if (typeof args[0] === "undefined") { - throw new Error("Callback must be provided to timer calls"); - } - - var toId = id++; - var delay = args[1] || 0; - - if (!this.timeouts) { - this.timeouts = {}; - } - - this.timeouts[toId] = { - id: toId, - func: args[0], - callAt: this.now + delay, - invokeArgs: Array.prototype.slice.call(args, 2) - }; - - if (recurring === true) { - this.timeouts[toId].interval = delay; - } - - if (addTimerReturnsObject) { - return { - id: toId, - ref: function() {}, - unref: function() {} - }; - } - else { - return toId; - } - } - - function parseTime(str) { - if (!str) { - return 0; - } - - var strings = str.split(":"); - var l = strings.length, i = l; - var ms = 0, parsed; - - if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { - throw new Error("tick only understands numbers and 'h:m:s'"); - } - - while (i--) { - parsed = parseInt(strings[i], 10); - - if (parsed >= 60) { - throw new Error("Invalid time " + str); - } - - ms += parsed * Math.pow(60, (l - i - 1)); - } - - return ms * 1000; - } - - function createObject(object) { - var newObject; - - if (Object.create) { - newObject = Object.create(object); - } else { - var F = function () {}; - F.prototype = object; - newObject = new F(); - } - - newObject.Date.clock = newObject; - return newObject; - } - - sinon.clock = { - now: 0, - - create: function create(now) { - var clock = createObject(this); - - if (typeof now == "number") { - clock.now = now; - } - - if (!!now && typeof now == "object") { - throw new TypeError("now should be milliseconds since UNIX epoch"); - } - - return clock; - }, - - setTimeout: function setTimeout(callback, timeout) { - return addTimer.call(this, arguments, false); - }, - - clearTimeout: function clearTimeout(timerId) { - if (!timerId) { - // null appears to be allowed in most browsers, and appears to be relied upon by some libraries, like Bootstrap carousel - return; - } - if (!this.timeouts) { - this.timeouts = []; - } - // in Node, timerId is an object with .ref()/.unref(), and - // its .id field is the actual timer id. - if (typeof timerId === 'object') { - timerId = timerId.id - } - if (timerId in this.timeouts) { - delete this.timeouts[timerId]; - } - }, - - setInterval: function setInterval(callback, timeout) { - return addTimer.call(this, arguments, true); - }, - - clearInterval: function clearInterval(timerId) { - this.clearTimeout(timerId); - }, - - setImmediate: function setImmediate(callback) { - var passThruArgs = Array.prototype.slice.call(arguments, 1); - - return addTimer.call(this, [callback, 0].concat(passThruArgs), false); - }, - - clearImmediate: function clearImmediate(timerId) { - this.clearTimeout(timerId); - }, - - tick: function tick(ms) { - ms = typeof ms == "number" ? ms : parseTime(ms); - var tickFrom = this.now, tickTo = this.now + ms, previous = this.now; - var timer = this.firstTimerInRange(tickFrom, tickTo); - - var firstException; - while (timer && tickFrom <= tickTo) { - if (this.timeouts[timer.id]) { - tickFrom = this.now = timer.callAt; - try { - this.callTimer(timer); - } catch (e) { - firstException = firstException || e; - } - } - - timer = this.firstTimerInRange(previous, tickTo); - previous = tickFrom; - } - - this.now = tickTo; - - if (firstException) { - throw firstException; - } - - return this.now; - }, - - firstTimerInRange: function (from, to) { - var timer, smallest = null, originalTimer; - - for (var id in this.timeouts) { - if (this.timeouts.hasOwnProperty(id)) { - if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) { - continue; - } - - if (smallest === null || this.timeouts[id].callAt < smallest) { - originalTimer = this.timeouts[id]; - smallest = this.timeouts[id].callAt; - - timer = { - func: this.timeouts[id].func, - callAt: this.timeouts[id].callAt, - interval: this.timeouts[id].interval, - id: this.timeouts[id].id, - invokeArgs: this.timeouts[id].invokeArgs - }; - } - } - } - - return timer || null; - }, - - callTimer: function (timer) { - if (typeof timer.interval == "number") { - this.timeouts[timer.id].callAt += timer.interval; - } else { - delete this.timeouts[timer.id]; - } - - try { - if (typeof timer.func == "function") { - timer.func.apply(null, timer.invokeArgs); - } else { - eval(timer.func); - } - } catch (e) { - var exception = e; - } - - if (!this.timeouts[timer.id]) { - if (exception) { - throw exception; - } - return; - } - - if (exception) { - throw exception; - } - }, - - reset: function reset() { - this.timeouts = {}; - }, - - Date: (function () { - var NativeDate = Date; - - function ClockDate(year, month, date, hour, minute, second, ms) { - // Defensive and verbose to avoid potential harm in passing - // explicit undefined when user does not pass argument - switch (arguments.length) { - case 0: - return new NativeDate(ClockDate.clock.now); - case 1: - return new NativeDate(year); - case 2: - return new NativeDate(year, month); - case 3: - return new NativeDate(year, month, date); - case 4: - return new NativeDate(year, month, date, hour); - case 5: - return new NativeDate(year, month, date, hour, minute); - case 6: - return new NativeDate(year, month, date, hour, minute, second); - default: - return new NativeDate(year, month, date, hour, minute, second, ms); - } - } - - return mirrorDateProperties(ClockDate, NativeDate); - }()) - }; - - function mirrorDateProperties(target, source) { - if (source.now) { - target.now = function now() { - return target.clock.now; - }; - } else { - delete target.now; - } - - if (source.toSource) { - target.toSource = function toSource() { - return source.toSource(); - }; - } else { - delete target.toSource; - } - - target.toString = function toString() { - return source.toString(); - }; - - target.prototype = source.prototype; - target.parse = source.parse; - target.UTC = source.UTC; - target.prototype.toUTCString = source.prototype.toUTCString; - - for (var prop in source) { - if (source.hasOwnProperty(prop)) { - target[prop] = source[prop]; - } - } - - return target; - } - - var methods = ["Date", "setTimeout", "setInterval", - "clearTimeout", "clearInterval"]; - - if (typeof global.setImmediate !== "undefined") { - methods.push("setImmediate"); - } - - if (typeof global.clearImmediate !== "undefined") { - methods.push("clearImmediate"); - } - - function restore() { - var method; - - for (var i = 0, l = this.methods.length; i < l; i++) { - method = this.methods[i]; - - if (global[method].hadOwnProperty) { - global[method] = this["_" + method]; - } else { - try { - delete global[method]; - } catch (e) {} - } - } - - // Prevent multiple executions which will completely remove these props - this.methods = []; - } - - function stubGlobal(method, clock) { - clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method); - clock["_" + method] = global[method]; - - if (method == "Date") { - var date = mirrorDateProperties(clock[method], global[method]); - global[method] = date; - } else { - global[method] = function () { - return clock[method].apply(clock, arguments); - }; - - for (var prop in clock[method]) { - if (clock[method].hasOwnProperty(prop)) { - global[method][prop] = clock[method][prop]; - } - } - } - - global[method].clock = clock; - } - - sinon.useFakeTimers = function useFakeTimers(now) { - var clock = sinon.clock.create(now); - clock.restore = restore; - clock.methods = Array.prototype.slice.call(arguments, - typeof now == "number" ? 1 : 0); - - if (clock.methods.length === 0) { - clock.methods = methods; - } - - for (var i = 0, l = clock.methods.length; i < l; i++) { - stubGlobal(clock.methods[i], clock); - } - - return clock; - }; -}(typeof global != "undefined" && typeof global !== "function" ? global : this)); - -sinon.timers = { - setTimeout: setTimeout, - clearTimeout: clearTimeout, - setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), - clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined), - setInterval: setInterval, - clearInterval: clearInterval, - Date: Date -}; - -if (typeof module !== 'undefined' && module.exports) { - module.exports = sinon; -} diff --git a/external/sinon/sinon-1.14.1.js b/external/sinon/sinon-1.14.1.js new file mode 100644 index 0000000000..dfbeae46a5 --- /dev/null +++ b/external/sinon/sinon-1.14.1.js @@ -0,0 +1,5931 @@ +/** + * Sinon.JS 1.14.1, 2015/03/16 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define('sinon', [], function () { + return (root.sinon = factory()); + }); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.sinon = factory(); + } +}(this, function () { + var samsam, formatio; + (function () { + function define(mod, deps, fn) { + if (mod == "samsam") { + samsam = deps(); + } else if (typeof deps === "function" && mod.length === 0) { + lolex = deps(); + } else if (typeof fn === "function") { + formatio = fn(samsam); + } + } + define.amd = {}; +((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || + (typeof module === "object" && + function (m) { module.exports = m(); }) || // Node + function (m) { this.samsam = m(); } // Browser globals +)(function () { + var o = Object.prototype; + var div = typeof document !== "undefined" && document.createElement("div"); + + function isNaN(value) { + // Unlike global isNaN, this avoids type coercion + // typeof check avoids IE host object issues, hat tip to + // lodash + var val = value; // JsLint thinks value !== value is "weird" + return typeof value === "number" && value !== val; + } + + function getClass(value) { + // Returns the internal [[Class]] by calling Object.prototype.toString + // with the provided value as this. Return value is a string, naming the + // internal class, e.g. "Array" + return o.toString.call(value).split(/[ \]]/)[1]; + } + + /** + * @name samsam.isArguments + * @param Object object + * + * Returns ``true`` if ``object`` is an ``arguments`` object, + * ``false`` otherwise. + */ + function isArguments(object) { + if (getClass(object) === 'Arguments') { return true; } + if (typeof object !== "object" || typeof object.length !== "number" || + getClass(object) === "Array") { + return false; + } + if (typeof object.callee == "function") { return true; } + try { + object[object.length] = 6; + delete object[object.length]; + } catch (e) { + return true; + } + return false; + } + + /** + * @name samsam.isElement + * @param Object object + * + * Returns ``true`` if ``object`` is a DOM element node. Unlike + * Underscore.js/lodash, this function will return ``false`` if ``object`` + * is an *element-like* object, i.e. a regular object with a ``nodeType`` + * property that holds the value ``1``. + */ + function isElement(object) { + if (!object || object.nodeType !== 1 || !div) { return false; } + try { + object.appendChild(div); + object.removeChild(div); + } catch (e) { + return false; + } + return true; + } + + /** + * @name samsam.keys + * @param Object object + * + * Return an array of own property names. + */ + function keys(object) { + var ks = [], prop; + for (prop in object) { + if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } + } + return ks; + } + + /** + * @name samsam.isDate + * @param Object value + * + * Returns true if the object is a ``Date``, or *date-like*. Duck typing + * of date objects work by checking that the object has a ``getTime`` + * function whose return value equals the return value from the object's + * ``valueOf``. + */ + function isDate(value) { + return typeof value.getTime == "function" && + value.getTime() == value.valueOf(); + } + + /** + * @name samsam.isNegZero + * @param Object value + * + * Returns ``true`` if ``value`` is ``-0``. + */ + function isNegZero(value) { + return value === 0 && 1 / value === -Infinity; + } + + /** + * @name samsam.equal + * @param Object obj1 + * @param Object obj2 + * + * Returns ``true`` if two objects are strictly equal. Compared to + * ``===`` there are two exceptions: + * + * - NaN is considered equal to NaN + * - -0 and +0 are not considered equal + */ + function identical(obj1, obj2) { + if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { + return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); + } + } + + + /** + * @name samsam.deepEqual + * @param Object obj1 + * @param Object obj2 + * + * Deep equal comparison. Two values are "deep equal" if: + * + * - They are equal, according to samsam.identical + * - They are both date objects representing the same time + * - They are both arrays containing elements that are all deepEqual + * - They are objects with the same set of properties, and each property + * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` + * + * Supports cyclic objects. + */ + function deepEqualCyclic(obj1, obj2) { + + // used for cyclic comparison + // contain already visited objects + var objects1 = [], + objects2 = [], + // contain pathes (position in the object structure) + // of the already visited objects + // indexes same as in objects arrays + paths1 = [], + paths2 = [], + // contains combinations of already compared objects + // in the manner: { "$1['ref']$2['ref']": true } + compared = {}; + + /** + * used to check, if the value of a property is an object + * (cyclic logic is only needed for objects) + * only needed for cyclic logic + */ + function isObject(value) { + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Date) && + !(value instanceof Number) && + !(value instanceof RegExp) && + !(value instanceof String)) { + + return true; + } + + return false; + } + + /** + * returns the index of the given object in the + * given objects array, -1 if not contained + * only needed for cyclic logic + */ + function getIndex(objects, obj) { + + var i; + for (i = 0; i < objects.length; i++) { + if (objects[i] === obj) { + return i; + } + } + + return -1; + } + + // does the recursion for the deep equal check + return (function deepEqual(obj1, obj2, path1, path2) { + var type1 = typeof obj1; + var type2 = typeof obj2; + + // == null also matches undefined + if (obj1 === obj2 || + isNaN(obj1) || isNaN(obj2) || + obj1 == null || obj2 == null || + type1 !== "object" || type2 !== "object") { + + return identical(obj1, obj2); + } + + // Elements are only equal if identical(expected, actual) + if (isElement(obj1) || isElement(obj2)) { return false; } + + var isDate1 = isDate(obj1), isDate2 = isDate(obj2); + if (isDate1 || isDate2) { + if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { + return false; + } + } + + if (obj1 instanceof RegExp && obj2 instanceof RegExp) { + if (obj1.toString() !== obj2.toString()) { return false; } + } + + var class1 = getClass(obj1); + var class2 = getClass(obj2); + var keys1 = keys(obj1); + var keys2 = keys(obj2); + + if (isArguments(obj1) || isArguments(obj2)) { + if (obj1.length !== obj2.length) { return false; } + } else { + if (type1 !== type2 || class1 !== class2 || + keys1.length !== keys2.length) { + return false; + } + } + + var key, i, l, + // following vars are used for the cyclic logic + value1, value2, + isObject1, isObject2, + index1, index2, + newPath1, newPath2; + + for (i = 0, l = keys1.length; i < l; i++) { + key = keys1[i]; + if (!o.hasOwnProperty.call(obj2, key)) { + return false; + } + + // Start of the cyclic logic + + value1 = obj1[key]; + value2 = obj2[key]; + + isObject1 = isObject(value1); + isObject2 = isObject(value2); + + // determine, if the objects were already visited + // (it's faster to check for isObject first, than to + // get -1 from getIndex for non objects) + index1 = isObject1 ? getIndex(objects1, value1) : -1; + index2 = isObject2 ? getIndex(objects2, value2) : -1; + + // determine the new pathes of the objects + // - for non cyclic objects the current path will be extended + // by current property name + // - for cyclic objects the stored path is taken + newPath1 = index1 !== -1 + ? paths1[index1] + : path1 + '[' + JSON.stringify(key) + ']'; + newPath2 = index2 !== -1 + ? paths2[index2] + : path2 + '[' + JSON.stringify(key) + ']'; + + // stop recursion if current objects are already compared + if (compared[newPath1 + newPath2]) { + return true; + } + + // remember the current objects and their pathes + if (index1 === -1 && isObject1) { + objects1.push(value1); + paths1.push(newPath1); + } + if (index2 === -1 && isObject2) { + objects2.push(value2); + paths2.push(newPath2); + } + + // remember that the current objects are already compared + if (isObject1 && isObject2) { + compared[newPath1 + newPath2] = true; + } + + // End of cyclic logic + + // neither value1 nor value2 is a cycle + // continue with next level + if (!deepEqual(value1, value2, newPath1, newPath2)) { + return false; + } + } + + return true; + + }(obj1, obj2, '$1', '$2')); + } + + var match; + + function arrayContains(array, subset) { + if (subset.length === 0) { return true; } + var i, l, j, k; + for (i = 0, l = array.length; i < l; ++i) { + if (match(array[i], subset[0])) { + for (j = 0, k = subset.length; j < k; ++j) { + if (!match(array[i + j], subset[j])) { return false; } + } + return true; + } + } + return false; + } + + /** + * @name samsam.match + * @param Object object + * @param Object matcher + * + * Compare arbitrary value ``object`` with matcher. + */ + match = function match(object, matcher) { + if (matcher && typeof matcher.test === "function") { + return matcher.test(object); + } + + if (typeof matcher === "function") { + return matcher(object) === true; + } + + if (typeof matcher === "string") { + matcher = matcher.toLowerCase(); + var notNull = typeof object === "string" || !!object; + return notNull && + (String(object)).toLowerCase().indexOf(matcher) >= 0; + } + + if (typeof matcher === "number") { + return matcher === object; + } + + if (typeof matcher === "boolean") { + return matcher === object; + } + + if (typeof(matcher) === "undefined") { + return typeof(object) === "undefined"; + } + + if (matcher === null) { + return object === null; + } + + if (getClass(object) === "Array" && getClass(matcher) === "Array") { + return arrayContains(object, matcher); + } + + if (matcher && typeof matcher === "object") { + if (matcher === object) { + return true; + } + var prop; + for (prop in matcher) { + var value = object[prop]; + if (typeof value === "undefined" && + typeof object.getAttribute === "function") { + value = object.getAttribute(prop); + } + if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { + if (value !== matcher[prop]) { + return false; + } + } else if (typeof value === "undefined" || !match(value, matcher[prop])) { + return false; + } + } + return true; + } + + throw new Error("Matcher was not a string, a number, a " + + "function, a boolean or an object"); + }; + + return { + isArguments: isArguments, + isElement: isElement, + isDate: isDate, + isNegZero: isNegZero, + identical: identical, + deepEqual: deepEqualCyclic, + match: match, + keys: keys + }; +}); +((typeof define === "function" && define.amd && function (m) { + define("formatio", ["samsam"], m); +}) || (typeof module === "object" && function (m) { + module.exports = m(require("samsam")); +}) || function (m) { this.formatio = m(this.samsam); } +)(function (samsam) { + + var formatio = { + excludeConstructors: ["Object", /^.$/], + quoteStrings: true, + limitChildrenCount: 0 + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global !== "undefined") { + specialObjects.push({ object: global, value: "[object global]" }); + } + if (typeof document !== "undefined") { + specialObjects.push({ + object: document, + value: "[object HTMLDocument]" + }); + } + if (typeof window !== "undefined") { + specialObjects.push({ object: window, value: "[object Window]" }); + } + + function functionName(func) { + if (!func) { return ""; } + if (func.displayName) { return func.displayName; } + if (func.name) { return func.name; } + var matches = func.toString().match(/function\s+([^\(]+)/m); + return (matches && matches[1]) || ""; + } + + function constructorName(f, object) { + var name = functionName(object && object.constructor); + var excludes = f.excludeConstructors || + formatio.excludeConstructors || []; + + var i, l; + for (i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] === "string" && excludes[i] === name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + } + + function isCircular(object, objects) { + if (typeof object !== "object") { return false; } + var i, l; + for (i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { return true; } + } + return false; + } + + function ascii(f, object, processed, indent) { + if (typeof object === "string") { + var qs = f.quoteStrings; + var quote = typeof qs !== "boolean" || qs; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object === "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { return "[Circular]"; } + + if (Object.prototype.toString.call(object) === "[object Array]") { + return ascii.array.call(f, object, processed); + } + + if (!object) { return String((1/object) === -Infinity ? "-0" : object); } + if (samsam.isElement(object)) { return ascii.element(object); } + + if (typeof object.toString === "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + var i, l; + for (i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].object) { + return specialObjects[i].value; + } + } + + return ascii.object.call(f, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + var i, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, array.length) : array.length; + + for (i = 0; i < l; ++i) { + pieces.push(ascii(this, array[i], processed)); + } + + if(l < array.length) + pieces.push("[... " + (array.length - l) + " more elements]"); + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = samsam.keys(object).sort(); + var length = 3; + var prop, str, obj, i, k, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, properties.length) : properties.length; + + for (i = 0; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = constructorName(this, object); + var prefix = cons ? "[" + cons + "] " : ""; + var is = ""; + for (i = 0, k = indent; i < k; ++i) { is += " "; } + + if(l < properties.length) + pieces.push("[... " + (properties.length - l) + " more elements]"); + + if (length + indent > 80) { + return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + + is + "}"; + } + return prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attr, pairs = [], attrName, i, l, val; + + for (i = 0, l = attrs.length; i < l; ++i) { + attr = attrs.item(i); + attrName = attr.nodeName.toLowerCase().replace("html:", ""); + val = attr.nodeValue; + if (attrName !== "contenteditable" || val !== "inherit") { + if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + function Formatio(options) { + for (var opt in options) { + this[opt] = options[opt]; + } + } + + Formatio.prototype = { + functionName: functionName, + + configure: function (options) { + return new Formatio(options); + }, + + constructorName: function (object) { + return constructorName(this, object); + }, + + ascii: function (object, processed, indent) { + return ascii(this, object, processed, indent); + } + }; + + return Formatio.prototype; +}); +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; +} + +/** + * Used to grok the `now` parameter to createClock. + */ +function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); +} + +function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; +} + +function mirrorDateProperties(target, source) { + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + return target; +} + +function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); +} + +function addTimer(clock, timer) { + if (typeof timer.func === "undefined") { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = id++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || 0); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: function() {}, + unref: function() {} + }; + } + else { + return timer.id; + } +} + +function firstTimerInRange(clock, from, to) { + var timers = clock.timers, timer = null; + + for (var id in timers) { + if (!inRange(from, to, timers[id])) { + continue; + } + + if (!timer || ~compareTimers(timer, timers[id])) { + timer = timers[id]; + } + } + + return timer; +} + +function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary +} + +function callTimer(clock, timer) { + if (typeof timer.interval == "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func == "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + var exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } +} + +function uninstall(clock, target) { + var method; + + for (var i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (e) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; +} + +function hijackMethod(target, method, clock) { + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method == "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (var prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; +} + +var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date +}; + +var keys = Object.keys || function (obj) { + var ks = []; + for (var key in obj) { + ks.push(key); + } + return ks; +}; + +exports.timers = timers; + +var createClock = exports.createClock = function (now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + if (!clock.timers) { + clock.timers = []; + } + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id + } + if (timerId in clock.timers) { + delete clock.timers[timerId]; + } + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + clock.clearTimeout(timerId); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + clock.clearTimeout(timerId); + }; + + clock.tick = function tick(ms) { + ms = typeof ms == "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + callTimer(clock, timer); + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + return clock; +}; + +exports.install = function install(target, now, toFake) { + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (var i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); + })(); + var define; +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +var sinon = (function () { +"use strict"; + + var sinon; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinon = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinon = module.exports; + } else { + sinon = {}; + } + + return sinon; +}()); + +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var div = typeof document != "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode == obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method != "function" && typeof method != "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = !!wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method == "function") ? {value: method} : method, + wrappedMethodDesc = sinon.getPropertyDescriptor(object, property), + i; + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + try { + delete object[property]; + } catch (e) {} + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + if (!hasES5Support && object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a != "object" || typeof b != "object") { + if (isReallyNaN(a) && isReallyNaN(b)) { + return true; + } else { + return a === b; + } + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString != Object.prototype.toString.call(b)) { + return false; + } + + if (aString == "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop, aLength = 0, bLength = 0; + + if (aString == "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + + for (prop in b) { + bLength += 1; + } + + return aLength == bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, prop, i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object, descriptor; + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + } + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count == 1 && "once" || + count == 2 && "twice" || + count == 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend util/core.js + */ + +(function (sinon) { + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9" + } + }; + + var result = []; + for (var prop in obj) { + result.push(obj[prop]()); + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1), + source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + }; + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend util/core.js + */ + +(function (sinon) { + function makeApi(sinon) { + + function timesInWords(count) { + switch (count) { + case 1: + return "once"; + case 2: + return "twice"; + case 3: + return "thrice"; + default: + return (count || 0) + " times"; + } + } + + sinon.timesInWords = timesInWords; + return sinon.timesInWords; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ + +(function (sinon, formatio) { + function makeApi(sinon) { + function typeOf(value) { + if (value === null) { + return "null"; + } else if (value === undefined) { + return "undefined"; + } + var string = Object.prototype.toString.call(value); + return string.substring(8, string.length - 1).toLowerCase(); + }; + + sinon.typeOf = typeOf; + return sinon.typeOf; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}( + (typeof sinon == "object" && sinon || null), + (typeof formatio == "object" && formatio) +)); + +/** + * @depend util/core.js + * @depend typeOf.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Match functions + * + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2012 Maximilian Antoni + */ + +(function (sinon) { + function makeApi(sinon) { + function assertType(value, type, name) { + var actual = sinon.typeOf(value); + if (actual !== type) { + throw new TypeError("Expected type of " + name + " to be " + + type + ", but was " + actual); + } + } + + var matcher = { + toString: function () { + return this.message; + } + }; + + function isMatcher(object) { + return matcher.isPrototypeOf(object); + } + + function matchObject(expectation, actual) { + if (actual === null || actual === undefined) { + return false; + } + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + var exp = expectation[key]; + var act = actual[key]; + if (match.isMatcher(exp)) { + if (!exp.test(act)) { + return false; + } + } else if (sinon.typeOf(exp) === "object") { + if (!matchObject(exp, act)) { + return false; + } + } else if (!sinon.deepEqual(exp, act)) { + return false; + } + } + } + return true; + } + + matcher.or = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var or = sinon.create(matcher); + or.test = function (actual) { + return m1.test(actual) || m2.test(actual); + }; + or.message = m1.message + ".or(" + m2.message + ")"; + return or; + }; + + matcher.and = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var and = sinon.create(matcher); + and.test = function (actual) { + return m1.test(actual) && m2.test(actual); + }; + and.message = m1.message + ".and(" + m2.message + ")"; + return and; + }; + + var match = function (expectation, message) { + var m = sinon.create(matcher); + var type = sinon.typeOf(expectation); + switch (type) { + case "object": + if (typeof expectation.test === "function") { + m.test = function (actual) { + return expectation.test(actual) === true; + }; + m.message = "match(" + sinon.functionName(expectation.test) + ")"; + return m; + } + var str = []; + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + str.push(key + ": " + expectation[key]); + } + } + m.test = function (actual) { + return matchObject(expectation, actual); + }; + m.message = "match(" + str.join(", ") + ")"; + break; + case "number": + m.test = function (actual) { + return expectation == actual; + }; + break; + case "string": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return actual.indexOf(expectation) !== -1; + }; + m.message = "match(\"" + expectation + "\")"; + break; + case "regexp": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return expectation.test(actual); + }; + break; + case "function": + m.test = expectation; + if (message) { + m.message = message; + } else { + m.message = "match(" + sinon.functionName(expectation) + ")"; + } + break; + default: + m.test = function (actual) { + return sinon.deepEqual(expectation, actual); + }; + } + if (!m.message) { + m.message = "match(" + expectation + ")"; + } + return m; + }; + + match.isMatcher = isMatcher; + + match.any = match(function () { + return true; + }, "any"); + + match.defined = match(function (actual) { + return actual !== null && actual !== undefined; + }, "defined"); + + match.truthy = match(function (actual) { + return !!actual; + }, "truthy"); + + match.falsy = match(function (actual) { + return !actual; + }, "falsy"); + + match.same = function (expectation) { + return match(function (actual) { + return expectation === actual; + }, "same(" + expectation + ")"); + }; + + match.typeOf = function (type) { + assertType(type, "string", "type"); + return match(function (actual) { + return sinon.typeOf(actual) === type; + }, "typeOf(\"" + type + "\")"); + }; + + match.instanceOf = function (type) { + assertType(type, "function", "type"); + return match(function (actual) { + return actual instanceof type; + }, "instanceOf(" + sinon.functionName(type) + ")"); + }; + + function createPropertyMatcher(propertyTest, messagePrefix) { + return function (property, value) { + assertType(property, "string", "property"); + var onlyProperty = arguments.length === 1; + var message = messagePrefix + "(\"" + property + "\""; + if (!onlyProperty) { + message += ", " + value; + } + message += ")"; + return match(function (actual) { + if (actual === undefined || actual === null || + !propertyTest(actual, property)) { + return false; + } + return onlyProperty || sinon.deepEqual(value, actual[property]); + }, message); + }; + } + + match.has = createPropertyMatcher(function (actual, property) { + if (typeof actual === "object") { + return property in actual; + } + return actual[property] !== undefined; + }, "has"); + + match.hasOwn = createPropertyMatcher(function (actual, property) { + return actual.hasOwnProperty(property); + }, "hasOwn"); + + match.bool = match.typeOf("boolean"); + match.number = match.typeOf("number"); + match.string = match.typeOf("string"); + match.object = match.typeOf("object"); + match.func = match.typeOf("function"); + match.array = match.typeOf("array"); + match.regexp = match.typeOf("regexp"); + match.date = match.typeOf("date"); + + sinon.match = match; + return match; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./typeOf"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ + +(function (sinon, formatio) { + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + }; + + return format; + } + + function getNodeFormatter(value) { + function format(value) { + return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value; + }; + + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function", + formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } catch (e) {} + } + + if (formatio) { + formatter = getFormatioFormatter() + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}( + (typeof sinon == "object" && sinon || null), + (typeof formatio == "object" && formatio) +)); + +/** + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Spy calls + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + * Copyright (c) 2013 Maximilian Antoni + */ + +(function (sinon) { + function makeApi(sinon) { + function throwYieldError(proxy, text, args) { + var msg = sinon.functionName(proxy) + text; + if (args.length) { + msg += " Received [" + slice.call(args).join(", ") + "]"; + } + throw new Error(msg); + } + + var slice = Array.prototype.slice; + + var callProto = { + calledOn: function calledOn(thisValue) { + if (sinon.match && sinon.match.isMatcher(thisValue)) { + return thisValue.test(this.thisValue); + } + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithMatch: function calledWithMatch() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + var actual = this.args[i]; + var expectation = arguments[i]; + if (!sinon.match || !sinon.match(expectation).test(actual)) { + return false; + } + } + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length == this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + return !this.calledWith.apply(this, arguments); + }, + + notCalledWithMatch: function notCalledWithMatch() { + return !this.calledWithMatch.apply(this, arguments); + }, + + returned: function returned(value) { + return sinon.deepEqual(value, this.returnValue); + }, + + threw: function threw(error) { + if (typeof error === "undefined" || !this.exception) { + return !!this.exception; + } + + return this.exception === error || this.exception.name === error; + }, + + calledWithNew: function calledWithNew() { + return this.proxy.prototype && this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + callArg: function (pos) { + this.args[pos](); + }, + + callArgOn: function (pos, thisValue) { + this.args[pos].apply(thisValue); + }, + + callArgWith: function (pos) { + this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + }, + + callArgOnWith: function (pos, thisValue) { + var args = slice.call(arguments, 2); + this.args[pos].apply(thisValue, args); + }, + + yield: function () { + this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + }, + + yieldOn: function (thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (typeof args[i] === "function") { + args[i].apply(thisValue, slice.call(arguments, 1)); + return; + } + } + throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); + }, + + yieldTo: function (prop) { + this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + }, + + yieldToOn: function (prop, thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (args[i] && typeof args[i][prop] === "function") { + args[i][prop].apply(thisValue, slice.call(arguments, 2)); + return; + } + } + throwYieldError(this.proxy, " cannot yield to '" + prop + + "' since no callback was passed.", args); + }, + + toString: function () { + var callStr = this.proxy.toString() + "("; + var args = []; + + for (var i = 0, l = this.args.length; i < l; ++i) { + args.push(sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue != "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + + return callStr; + } + }; + + callProto.invokeCallback = callProto.yield; + + function createSpyCall(spy, thisValue, args, returnValue, exception, id) { + if (typeof id !== "number") { + throw new TypeError("Call id is not a number"); + } + var proxyCall = sinon.create(callProto); + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = id; + + return proxyCall; + } + createSpyCall.toString = callProto.toString; // used by mocks + + sinon.spyCall = createSpyCall; + return createSpyCall; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend extend.js + * @depend call.js + * @depend format.js + */ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + + function makeApi(sinon) { + var push = Array.prototype.push; + var slice = Array.prototype.slice; + var callId = 0; + + function spy(object, property, types) { + if (!property && typeof object == "function") { + return spy.create(object); + } + + if (!object && !property) { + return spy.create(function () { }); + } + + if (types) { + var methodDesc = sinon.getPropertyDescriptor(object, property); + for (var i = 0; i < types.length; i++) { + methodDesc[types[i]] = spy.create(methodDesc[types[i]]); + } + return sinon.wrapMethod(object, property, methodDesc); + } else { + var method = object[property]; + return sinon.wrapMethod(object, property, spy.create(method)); + } + } + + function matchingFake(fakes, args, strict) { + if (!fakes) { + return; + } + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } + } + + function incrementCallCount() { + this.called = true; + this.callCount += 1; + this.notCalled = false; + this.calledOnce = this.callCount == 1; + this.calledTwice = this.callCount == 2; + this.calledThrice = this.callCount == 3; + } + + function createCallProperties() { + this.firstCall = this.getCall(0); + this.secondCall = this.getCall(1); + this.thirdCall = this.getCall(2); + this.lastCall = this.getCall(this.callCount - 1); + } + + var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; + function createProxy(func, proxyLength) { + // Retain the function length: + var p; + if (proxyLength) { + eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) + + ") { return p.invoke(func, this, slice.call(arguments)); });"); + } else { + p = function proxy() { + return p.invoke(func, this, slice.call(arguments)); + }; + } + return p; + } + + var uuid = 0; + + // Public API + var spyApi = { + reset: function () { + if (this.invoking) { + var err = new Error("Cannot reset Sinon function while invoking it. " + + "Move the call to .reset outside of the callback."); + err.name = "InvalidResetException"; + throw err; + } + + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + if (this.fakes) { + for (var i = 0; i < this.fakes.length; i++) { + this.fakes[i].reset(); + } + } + + return this; + }, + + create: function create(func, spyLength) { + var name; + + if (typeof func != "function") { + func = function () { }; + } else { + name = sinon.functionName(func); + } + + if (!spyLength) { + spyLength = func.length; + } + + var proxy = createProxy(func, spyLength); + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy.instantiateFake = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + + incrementCallCount.call(this); + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + // Make call properties available from within the spied function: + createCallProperties.call(this); + + try { + this.invoking = true; + + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + + var thisCall = this.getCall(this.callCount - 1); + if (thisCall.calledWithNew() && typeof returnValue !== "object") { + returnValue = thisValue; + } + } catch (e) { + exception = e; + } finally { + delete this.invoking; + } + + push.call(this.exceptions, exception); + push.call(this.returnValues, returnValue); + + // Make return value and exception available in the calls: + createCallProperties.call(this); + + if (exception !== undefined) { + throw exception; + } + + return returnValue; + }, + + named: function named(name) { + this.displayName = name; + return this; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return sinon.spyCall(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i]); + }, + + getCalls: function () { + var calls = []; + var i; + + for (i = 0; i < this.callCount; i++) { + calls.push(this.getCall(i)); + } + + return calls; + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this.instantiateFake(); + fake.matchingAguments = args; + fake.parent = this; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + for (var i = 0; i < this.args.length; i++) { + if (fake.matches(this.args[i])) { + incrementCallCount.call(fake); + push.call(fake.thisValues, this.thisValues[i]); + push.call(fake.args, this.args[i]); + push.call(fake.returnValues, this.returnValues[i]); + push.call(fake.exceptions, this.exceptions[i]); + push.call(fake.callIds, this.callIds[i]); + } + } + createCallProperties.call(fake); + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length == args.length; + } + }, + + printf: function (format) { + var spy = this; + var args = slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter == "function") { + return formatter.call(null, spy, args); + } else if (!isNaN(parseInt(specifyer, 10))) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } + }; + + function delegateToCalls(method, matchAny, actual, notCalled) { + spyApi[method] = function () { + if (!this.called) { + if (notCalled) { + return notCalled.apply(this, arguments); + } + return false; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; + } + + delegateToCalls("calledOn", true); + delegateToCalls("alwaysCalledOn", false, "calledOn"); + delegateToCalls("calledWith", true); + delegateToCalls("calledWithMatch", true); + delegateToCalls("alwaysCalledWith", false, "calledWith"); + delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); + delegateToCalls("calledWithExactly", true); + delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); + delegateToCalls("neverCalledWith", false, "notCalledWith", + function () { return true; }); + delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", + function () { return true; }); + delegateToCalls("threw", true); + delegateToCalls("alwaysThrew", false, "threw"); + delegateToCalls("returned", true); + delegateToCalls("alwaysReturned", false, "returned"); + delegateToCalls("calledWithNew", true); + delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); + delegateToCalls("callArg", false, "callArgWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgWith = spyApi.callArg; + delegateToCalls("callArgOn", false, "callArgOnWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgOnWith = spyApi.callArgOn; + delegateToCalls("yield", false, "yield", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. + spyApi.invokeCallback = spyApi.yield; + delegateToCalls("yieldOn", false, "yieldOn", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + delegateToCalls("yieldTo", false, "yieldTo", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + + spyApi.formatters = { + c: function (spy) { + return sinon.timesInWords(spy.callCount); + }, + + n: function (spy) { + return spy.toString(); + }, + + C: function (spy) { + var calls = []; + + for (var i = 0, l = spy.callCount; i < l; ++i) { + var stringifiedCall = " " + spy.getCall(i).toString(); + if (/\n/.test(calls[i - 1])) { + stringifiedCall = "\n" + stringifiedCall; + } + push.call(calls, stringifiedCall); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + t: function (spy) { + var objects = []; + + for (var i = 0, l = spy.callCount; i < l; ++i) { + push.call(objects, sinon.format(spy.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spy, args) { + var formatted = []; + + for (var i = 0, l = args.length; i < l; ++i) { + push.call(formatted, sinon.format(args[i])); + } + + return formatted.join(", "); + } + }; + + sinon.extend(spy, spyApi); + + spy.spyCall = sinon.spyCall; + sinon.spy = spy; + + return spy; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./call"); + require("./extend"); + require("./times_in_words"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend util/core.js + * @depend extend.js + */ +/** + * Stub behavior + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Tim Fischbach (mail@timfischbach.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var slice = Array.prototype.slice; + var join = Array.prototype.join; + var useLeftMostCallback = -1; + var useRightMostCallback = -2; + + var nextTick = (function () { + if (typeof process === "object" && typeof process.nextTick === "function") { + return process.nextTick; + } else if (typeof setImmediate === "function") { + return setImmediate; + } else { + return function (callback) { + setTimeout(callback, 0); + }; + } + })(); + + function throwsException(error, message) { + if (typeof error == "string") { + this.exception = new Error(message || ""); + this.exception.name = error; + } else if (!error) { + this.exception = new Error("Error"); + } else { + this.exception = error; + } + + return this; + } + + function getCallback(behavior, args) { + var callArgAt = behavior.callArgAt; + + if (callArgAt >= 0) { + return args[callArgAt]; + } + + var argumentList; + + if (callArgAt === useLeftMostCallback) { + argumentList = args; + } + + if (callArgAt === useRightMostCallback) { + argumentList = slice.call(args).reverse(); + } + + var callArgProp = behavior.callArgProp; + + for (var i = 0, l = argumentList.length; i < l; ++i) { + if (!callArgProp && typeof argumentList[i] == "function") { + return argumentList[i]; + } + + if (callArgProp && argumentList[i] && + typeof argumentList[i][callArgProp] == "function") { + return argumentList[i][callArgProp]; + } + } + + return null; + } + + function makeApi(sinon) { + function getCallbackError(behavior, func, args) { + if (behavior.callArgAt < 0) { + var msg; + + if (behavior.callArgProp) { + msg = sinon.functionName(behavior.stub) + + " expected to yield to '" + behavior.callArgProp + + "', but no object with such a property was passed."; + } else { + msg = sinon.functionName(behavior.stub) + + " expected to yield, but no callback was passed."; + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + behavior.callArgAt + " is not a function: " + func; + } + + function callCallback(behavior, args) { + if (typeof behavior.callArgAt == "number") { + var func = getCallback(behavior, args); + + if (typeof func != "function") { + throw new TypeError(getCallbackError(behavior, func, args)); + } + + if (behavior.callbackAsync) { + nextTick(function () { + func.apply(behavior.callbackContext, behavior.callbackArguments); + }); + } else { + func.apply(behavior.callbackContext, behavior.callbackArguments); + } + } + } + + var proto = { + create: function create(stub) { + var behavior = sinon.extend({}, sinon.behavior); + delete behavior.create; + behavior.stub = stub; + + return behavior; + }, + + isPresent: function isPresent() { + return (typeof this.callArgAt == "number" || + this.exception || + typeof this.returnArgAt == "number" || + this.returnThis || + this.returnValueDefined); + }, + + invoke: function invoke(context, args) { + callCallback(this, args); + + if (this.exception) { + throw this.exception; + } else if (typeof this.returnArgAt == "number") { + return args[this.returnArgAt]; + } else if (this.returnThis) { + return context; + } + + return this.returnValue; + }, + + onCall: function onCall(index) { + return this.stub.onCall(index); + }, + + onFirstCall: function onFirstCall() { + return this.stub.onFirstCall(); + }, + + onSecondCall: function onSecondCall() { + return this.stub.onSecondCall(); + }, + + onThirdCall: function onThirdCall() { + return this.stub.onThirdCall(); + }, + + withArgs: function withArgs(/* arguments */) { + throw new Error("Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" is not supported. " + + "Use \"stub.withArgs(...).onCall(...)\" to define sequential behavior for calls with certain arguments."); + }, + + callsArg: function callsArg(pos) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOn: function callsArgOn(pos, context) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context != "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOnWith: function callsArgWith(pos, context) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context != "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yields: function () { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsRight: function () { + this.callArgAt = useRightMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsOn: function (context) { + if (typeof context != "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + yieldsToOn: function (prop, context) { + if (typeof context != "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + throws: throwsException, + throwsException: throwsException, + + returns: function returns(value) { + this.returnValue = value; + this.returnValueDefined = true; + + return this; + }, + + returnsArg: function returnsArg(pos) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + + this.returnArgAt = pos; + + return this; + }, + + returnsThis: function returnsThis() { + this.returnThis = true; + + return this; + } + }; + + // create asynchronous versions of callsArg* and yields* methods + for (var method in proto) { + // need to avoid creating anotherasync versions of the newly added async methods + if (proto.hasOwnProperty(method) && + method.match(/^(callsArg|yields)/) && + !method.match(/Async/)) { + proto[method + "Async"] = (function (syncFnName) { + return function () { + var result = this[syncFnName].apply(this, arguments); + this.callbackAsync = true; + return result; + }; + })(method); + } + } + + sinon.behavior = proto; + return proto; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend util/core.js + * @depend extend.js + * @depend spy.js + * @depend behavior.js + */ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + function makeApi(sinon) { + function stub(object, property, func) { + if (!!func && typeof func != "function" && typeof func != "object") { + throw new TypeError("Custom stub should be a function or a property descriptor"); + } + + var wrapper; + + if (func) { + if (typeof func == "function") { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = func; + if (sinon.spy && sinon.spy.create) { + var types = sinon.objectKeys(wrapper); + for (var i = 0; i < types.length; i++) { + wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]); + } + } + } + } else { + var stubLength = 0; + if (typeof object == "object" && typeof object[property] == "function") { + stubLength = object[property].length; + } + wrapper = stub.create(stubLength); + } + + if (!object && typeof property === "undefined") { + return sinon.stub.create(); + } + + if (typeof property === "undefined" && typeof object == "object") { + for (var prop in object) { + if (typeof object[prop] === "function") { + stub(object, prop); + } + } + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); + } + + function getDefaultBehavior(stub) { + return stub.defaultBehavior || getParentBehaviour(stub) || sinon.behavior.create(stub); + } + + function getParentBehaviour(stub) { + return (stub.parent && getCurrentBehavior(stub.parent)); + } + + function getCurrentBehavior(stub) { + var behavior = stub.behaviors[stub.callCount - 1]; + return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stub); + } + + var uuid = 0; + + var proto = { + create: function create(stubLength) { + var functionStub = function () { + return getCurrentBehavior(functionStub).invoke(this, arguments); + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub, stubLength); + functionStub.func = orig; + + sinon.extend(functionStub, stub); + functionStub.instantiateFake = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + functionStub.defaultBehavior = null; + functionStub.behaviors = []; + + return functionStub; + }, + + resetBehavior: function () { + var i; + + this.defaultBehavior = null; + this.behaviors = []; + + delete this.returnValue; + delete this.returnArgAt; + this.returnThis = false; + + if (this.fakes) { + for (i = 0; i < this.fakes.length; i++) { + this.fakes[i].resetBehavior(); + } + } + }, + + onCall: function onCall(index) { + if (!this.behaviors[index]) { + this.behaviors[index] = sinon.behavior.create(this); + } + + return this.behaviors[index]; + }, + + onFirstCall: function onFirstCall() { + return this.onCall(0); + }, + + onSecondCall: function onSecondCall() { + return this.onCall(1); + }, + + onThirdCall: function onThirdCall() { + return this.onCall(2); + } + }; + + for (var method in sinon.behavior) { + if (sinon.behavior.hasOwnProperty(method) && + !proto.hasOwnProperty(method) && + method != "create" && + method != "withArgs" && + method != "invoke") { + proto[method] = (function (behaviorMethod) { + return function () { + this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this); + this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments); + return this; + }; + }(method)); + } + } + + sinon.extend(stub, proto); + sinon.stub = stub; + + return stub; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./behavior"); + require("./spy"); + require("./extend"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend call.js + * @depend extend.js + * @depend match.js + * @depend spy.js + * @depend stub.js + * @depend format.js + */ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + function makeApi(sinon) { + var push = [].push; + var match = sinon.match; + + function mock(object) { + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); + } + + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + + sinon.extend(mock, { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore == "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = [], met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } else if (met.length > 0) { + sinon.expectation.pass(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method]; + var length = expectations && expectations.length || 0, i; + + for (i = 0; i < length; i += 1) { + if (!expectations[i].met() && + expectations[i].allowsCall(thisValue, args)) { + return expectations[i].apply(thisValue, args); + } + } + + var messages = [], available, exhausted = 0; + + for (i = 0; i < length; i += 1) { + if (expectations[i].allowsCall(thisValue, args)) { + available = available || expectations[i]; + } else { + exhausted += 1; + } + push.call(messages, " " + expectations[i].toString()); + } + + if (exhausted === 0) { + return available.apply(thisValue, args); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } + }); + + var times = sinon.timesInWords; + var slice = Array.prototype.slice; + + function callCountInWords(callCount) { + if (callCount == 0) { + return "never called"; + } else { + return "called " + times(callCount); + } + } + + function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min == "number" && typeof max == "number") { + var str = times(min); + + if (min != max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min == "number") { + return "at least " + times(min); + } + + return "at most " + times(max); + } + + function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls == "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; + } + + function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls != "number") { + return false; + } + + return expectation.callCount == expectation.maxCalls; + } + + function verifyMatcher(possibleMatcher, arg) { + if (match && match.isMatcher(possibleMatcher)) { + return possibleMatcher.test(arg); + } else { + return true; + } + } + + sinon.expectation = { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return sinon.spy.invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + sinon.format(this.expectedArguments)); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + if (this.expectsExactArgCount && + args.length != this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", didn't match " + this.expectedArguments.toString()); + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", expected " + sinon.format(this.expectedArguments)); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met() && receivedMaxCalls(this)) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length != this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + return false; + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method || "anonymous mock expectation", + args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } else { + sinon.expectation.pass(this.toString()); + } + + return true; + }, + + pass: function pass(message) { + sinon.assert.pass(message); + }, + + fail: function fail(message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } + }; + + sinon.mock = mock; + return mock; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./times_in_words"); + require("./call"); + require("./extend"); + require("./match"); + require("./spy"); + require("./stub"); + require("./format"); + + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend util/core.js + * @depend spy.js + * @depend stub.js + * @depend mock.js + */ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var push = [].push; + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; + } + + function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] == "function") { + fakes[i][method](); + } + } + } + + function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } + } + + function makeApi(sinon) { + var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + reset: function restore() { + each(this, "reset"); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + var original = object[property]; + + if (typeof original != "function") { + if (!hasOwnProperty.call(object, property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + if (!property && !!object && typeof object == "object") { + var stubbedObj = sinon.stub.apply(sinon, arguments); + + for (var prop in stubbedObj) { + if (typeof stubbedObj[prop] === "function") { + this.add(stubbedObj[prop]); + } + } + + return stubbedObj; + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } + }; + + sinon.collection = collection; + return collection; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./mock"); + require("./spy"); + require("./stub"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/*global lolex */ + +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +if (typeof sinon == "undefined") { + var sinon = {}; +} + +(function (global) { + function makeApi(sinon, lol) { + var llx = typeof lolex !== "undefined" ? lolex : lol; + + sinon.useFakeTimers = function () { + var now, methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + sinon.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + sinon.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var sinon = require("./core"); + makeApi(sinon, lolex); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); + } +}(typeof global != "undefined" && typeof global !== "function" ? global : this)); + +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ + +if (typeof sinon == "undefined") { + this.sinon = {}; +} + +(function () { + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = progressEventRaw.loaded || null; + this.total = progressEventRaw.total || null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] == listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] == "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); + } +}()); + +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ + +(function (sinon) { + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + logError.setTimeout(function () { + err.message = msg + err.message; + throw err; + }, 0); + }; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + } + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ + +if (typeof sinon == "undefined") { + this.sinon = {}; +} + +// wrapper for global +(function (global) { + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest != "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate == "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(xdr) { + if (xdr.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xdr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(xdr) { + if (xdr.readyState == FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (xdr.readyState == FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body != "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout" + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload" + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] == "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend == "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status == "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); + } +})(this); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (global) { + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject != "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest != "undefined"; + sinonXhr.workingXHR = sinonXhr.supportsXHR ? sinonXhr.GlobalXMLHttpRequest : sinonXhr.supportsActiveX + ? function () { return new sinonXhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0") } : false; + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + /*jsl:ignore*/ + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + /*jsl:end*/ + + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener == "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate == "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + progress: [], + load: [], + abort: [], + error: [] + } + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] == listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() == header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn) + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr] + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState != FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState == FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body != "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + var xmlDoc; + + if (typeof DOMParser != "undefined") { + var parser = new DOMParser(); + xmlDoc = parser.parseFromString(text, "text/xml"); + } else { + xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + } + + return xmlDoc; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async == "boolean" ? async : true; + this.username = username; + this.password = password; + this.responseText = null; + this.responseXML = null; + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs) + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + if (typeof this.onreadystatechange == "function") { + try { + this.onreadystatechange(); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + this.dispatchEvent(new sinon.Event("readystatechange")); + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + } + break; + } + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (!(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend == "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + this.requestHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + + this.upload.dispatchEvent(new sinon.Event("abort", false, false, this)); + + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + } + + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + var type = this.getResponseHeader("Content-Type"); + + if (this.responseText && + (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) { + try { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } catch (e) { + // Unable to parse XML - no biggie + } + } + + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status == "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (typeof sinon === "undefined") { + return; + } else { + makeApi(sinon); + } + +})(typeof global !== "undefined" ? global : this); + +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +if (typeof sinon == "undefined") { + var sinon = {}; +} + +(function () { + var push = [].push; + function F() {} + + function create(proto) { + F.prototype = proto; + return new F(); + } + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) != "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] != "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response == "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec == "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function () { + var server = create(this); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return !!matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length == 1 && typeof method != "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { this.responses = []; } + + if (arguments.length == 1) { + body = method; + url = method = null; + } + + if (arguments.length == 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body == "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + var request; + + while (request = requests.shift()) { + this.processRequest(request); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState != 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); + } +}()); + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function () { + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock == "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); + } +}()); + +/** + * @depend util/core.js + * @depend extend.js + * @depend collection.js + * @depend util/fake_timers.js + * @depend util/fake_server_with_clock.js + */ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function () { + function makeApi(sinon) { + var push = [].push; + + function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto && !(key in config.injectInto)) { + config.injectInto[key] = value; + sandbox.injectedKeys.push(key); + } else { + push.call(sandbox.args, value); + } + } + + function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer == "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers == "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; + } + + sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + obj.match = sinon.match; + + return obj; + }, + + restore: function () { + sinon.collection.restore.apply(this, arguments); + this.restoreContext(); + }, + + restoreContext: function () { + if (this.injectedKeys) { + for (var i = 0, j = this.injectedKeys.length; i < j; i++) { + delete this.injectInto[this.injectedKeys[i]]; + } + this.injectedKeys = []; + } + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + sandbox.injectedKeys = []; + sandbox.injectInto = config.injectInto; + var prop, value, exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop == "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + }, + + match: sinon.match + }); + + sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + + return sinon.sandbox; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + require("./util/fake_server_with_clock"); + require("./util/fake_timers"); + require("./collection"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}()); + +/** + * @depend util/core.js + * @depend sandbox.js + */ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + function makeApi(sinon) { + var slice = Array.prototype.slice; + + function test(callback) { + var type = typeof callback; + + if (type != "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + function sinonSandboxedTest() { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var args = slice.call(arguments); + var oldDone = args.length && args[args.length - 1]; + var exception, result; + + if (typeof oldDone == "function") { + args[args.length - 1] = function sinonDone(result) { + if (result) { + sandbox.restore(); + throw exception; + } else { + sandbox.verifyAndRestore(); + } + oldDone(result); + }; + } + + try { + result = callback.apply(this, args.concat(sandbox.args)); + } catch (e) { + exception = e; + } + + if (typeof oldDone != "function") { + if (typeof exception !== "undefined") { + sandbox.restore(); + throw exception; + } else { + sandbox.verifyAndRestore(); + } + } + + return result; + } + + if (callback.length) { + return function sinonAsyncSandboxedTest(callback) { + return sinonSandboxedTest.apply(this, arguments); + }; + } + + return sinonSandboxedTest; + } + + test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.test = test; + return test; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./sandbox"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (sinon) { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend util/core.js + * @depend test.js + */ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; + } + + function makeApi(sinon) { + function testCase(tests, prefix) { + /*jsl:ignore*/ + if (!tests || typeof tests != "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + /*jsl:end*/ + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}, testName, property, method; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + + for (testName in tests) { + if (tests.hasOwnProperty(testName)) { + property = tests[testName]; + + if (/^(setUp|tearDown)$/.test(testName)) { + continue; + } + + if (typeof property == "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; + } + + sinon.testCase = testCase; + return testCase; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./test"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Assertions matching the test spy retrieval interface. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon, global) { + var slice = Array.prototype.slice; + + function makeApi(sinon) { + var assert; + + function verifyIsStub() { + var method; + + for (var i = 0, l = arguments.length; i < l; ++i) { + method = arguments[i]; + + if (!method) { + assert.fail("fake is not a spy"); + } + + if (method.proxy) { + verifyIsStub(method.proxy); + } else { + if (typeof method != "function") { + assert.fail(method + " is not a function"); + } + + if (typeof method.getCall != "function") { + assert.fail(method + " is not stubbed"); + } + } + + } + } + + function failAssertion(object, msg) { + object = object || global; + var failMethod = object.fail || assert.fail; + failMethod.call(object, msg); + } + + function mirrorPropAsAssertion(name, method, message) { + if (arguments.length == 2) { + message = method; + method = name; + } + + assert[name] = function (fake) { + verifyIsStub(fake); + + var args = slice.call(arguments, 1); + var failed = false; + + if (typeof method == "function") { + failed = !method(fake); + } else { + failed = typeof fake[method] == "function" ? + !fake[method].apply(fake, args) : !fake[method]; + } + + if (failed) { + failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args))); + } else { + assert.pass(name); + } + }; + } + + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) ? prop : + prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); + } + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass(assertion) {}, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = "", actual = ""; + + if (!sinon.calledInOrder(arguments)) { + try { + expected = [].join.call(arguments, ", "); + var calls = slice.call(arguments); + var i = calls.length; + while (i) { + if (!calls[--i].called) { + calls.splice(i, 1); + } + } + actual = sinon.orderByFirstCall(calls).join(", "); + } catch (e) { + // If this fails, we'll just fall back to the blank string + } + + failAssertion(this, "expected " + expected + " to be " + + "called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, + + callCount: function assertCallCount(method, count) { + verifyIsStub(method); + + if (method.callCount != count) { + var msg = "expected %n to be called " + sinon.timesInWords(count) + + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, + + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } + + var o = options || {}; + var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix; + var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail; + + for (var method in this) { + if (method != "expose" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = this[method]; + } + } + + return target; + }, + + match: function match(actual, expectation) { + var matcher = sinon.match(expectation); + if (matcher.test(actual)) { + assert.pass("match"); + } else { + var formatted = [ + "expected value to match", + " expected = " + sinon.format(expectation), + " actual = " + sinon.format(actual) + ] + failAssertion(this, formatted.join("\n")); + } + } + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; }, + "expected %n to not have been called but was called %c%C"); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t"); + mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); + mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); + mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + sinon.assert = assert; + return assert; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require == "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (!sinon) { + return; + } else { + makeApi(sinon); + } + +}(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global)); + + return sinon; +})); diff --git a/src/effects/Tween.js b/src/effects/Tween.js index 478fed007b..d7cd8e606e 100644 --- a/src/effects/Tween.js +++ b/src/effects/Tween.js @@ -59,8 +59,10 @@ Tween.propHooks = { get: function( tween ) { var result; - if ( tween.elem[ tween.prop ] != null && - (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { return tween.elem[ tween.prop ]; } @@ -78,7 +80,7 @@ Tween.propHooks = { // Use .style if available and use plain properties where available. if ( jQuery.fx.step[ tween.prop ] ) { jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.style && + } else if ( tween.elem.nodeType === 1 && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); diff --git a/src/selector-native.js b/src/selector-native.js index 1b2dd185ba..5717918bcd 100644 --- a/src/selector-native.js +++ b/src/selector-native.js @@ -154,7 +154,8 @@ jQuery.extend({ expr: { attrHandle: {}, match: { - bool: /^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$/i, + bool: new RegExp( "^(?:checked|selected|async|autofocus|autoplay|controls|defer" + + "|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$", "i" ), needsContext: /^[\x20\t\r\n\f]*[>+~]/ } } diff --git a/test/data/testinit.js b/test/data/testinit.js index 468956d4df..e59d9db325 100644 --- a/test/data/testinit.js +++ b/test/data/testinit.js @@ -285,7 +285,8 @@ this.loadTests = function() { "unit/ajax.js", "unit/effects.js", "unit/offset.js", - "unit/dimensions.js" + "unit/dimensions.js", + "unit/tween.js" ]; // Ensure load order (to preserve test numbers) diff --git a/test/index.html b/test/index.html index d8ccdaa454..0285176d3d 100644 --- a/test/index.html +++ b/test/index.html @@ -13,9 +13,9 @@ + - diff --git a/test/unit/effects.js b/test/unit/effects.js index 571fda0316..7aff2242ab 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -10,13 +10,15 @@ var oldRaf = window.requestAnimationFrame; module("effects", { setup: function() { window.requestAnimationFrame = null; - this.clock = sinon.useFakeTimers( 505877050 ); + this.sandbox = sinon.sandbox.create(); + this.clock = this.sandbox.useFakeTimers( 505877050 ); this._oldInterval = jQuery.fx.interval; + jQuery.fx.step = {}; jQuery.fx.interval = 10; jQuery.now = Date.now; }, teardown: function() { - this.clock.restore(); + this.sandbox.restore(); jQuery.now = Date.now; jQuery.fx.stop(); jQuery.fx.interval = this._oldInterval; diff --git a/test/unit/tween.js b/test/unit/tween.js new file mode 100644 index 0000000000..8b40d974e4 --- /dev/null +++ b/test/unit/tween.js @@ -0,0 +1,298 @@ +( function() { + +// Can't test what ain't there +if ( !jQuery.fx ) { + return; +} + +var oldRaf = window.requestAnimationFrame; + +module( "tween", { + setup: function() { + window.requestAnimationFrame = null; + this.sandbox = sinon.sandbox.create(); + this.clock = this.sandbox.useFakeTimers( 505877050 ); + this._oldInterval = jQuery.fx.interval; + jQuery.fx.step = {}; + jQuery.fx.interval = 10; + jQuery.now = Date.now; + }, + teardown: function() { + this.sandbox.restore(); + jQuery.now = Date.now; + jQuery.fx.stop(); + jQuery.fx.interval = this._oldInterval; + window.requestAnimationFrame = oldRaf; + return moduleTeardown.apply( this, arguments ); + } +} ); + +test( "jQuery.Tween - Default propHooks on plain objects", function() { + expect( 8 ); + var propHooks, defaultHook, testObject, fakeTween, stepSpy; + + propHooks = jQuery.Tween.propHooks; + equal( typeof propHooks, "object", "jQuery.Tween.propHooks exists" ); + + defaultHook = propHooks._default; + ok( defaultHook, "_default propHook exists" ); + + testObject = { test: 0 }; + fakeTween = { elem: testObject, prop: "test", now: 10, unit: "px" }; + + equal( defaultHook.get( fakeTween ), 0, "Can get property of object" ); + + fakeTween.prop = "testMissing"; + equal( defaultHook.get( fakeTween ), undefined, "Can get missing property on object" ); + + defaultHook.set( fakeTween ); + equal( testObject.testMissing, 10, "Sets missing value properly on plain object" ); + + fakeTween.prop = "opacity"; + defaultHook.set( fakeTween ); + equal( testObject.opacity, 10, "Correctly set opacity on plain object" ); + + fakeTween.prop = "test"; + stepSpy = jQuery.fx.step.test = this.sandbox.spy(); + + defaultHook.set( fakeTween ); + ok( stepSpy.calledWith( fakeTween ), "Step function called with Tween" ); + equal( testObject.test, 0, "Because step didn't set, value is unchanged" ); +} ); + +test( "jQuery.Tween - Default propHooks on elements", function() { + expect( 19 ); + var propHooks, defaultHook, testElement, fakeTween, cssStub, styleStub, stepSpy; + + propHooks = jQuery.Tween.propHooks; + equal( typeof propHooks, "object", "jQuery.Tween.propHooks exists" ); + + defaultHook = propHooks._default; + ok( defaultHook, "_default propHook exists" ); + + testElement = jQuery( "
" )[ 0 ]; + fakeTween = { elem: testElement, prop: "height", now: 10, unit: "px" }; + + cssStub = this.sandbox.stub( jQuery, "css" ).returns( 10 ); + + equal( defaultHook.get( fakeTween ), 10, "Gets expected style value" ); + ok( cssStub.calledWith( testElement, "height", "" ), "Calls jQuery.css correctly" ); + + fakeTween.prop = "testOpti"; + testElement.testOpti = 15; + cssStub.reset(); + + equal( defaultHook.get( fakeTween ), 15, "Gets expected value not defined on style" ); + equal( cssStub.callCount, 0, "Did not call jQuery.css" ); + + fakeTween.prop = "testMissing"; + equal( defaultHook.get( fakeTween ), 10, "Can get missing property on element" ); + ok( cssStub.calledWith( testElement, "testMissing", "" ), "...using jQuery.css" ); + + cssStub.returns( "" ); + equal( defaultHook.get( fakeTween ), 0, "Uses 0 for empty string" ); + + cssStub.returns( "auto" ); + equal( defaultHook.get( fakeTween ), 0, "Uses 0 for 'auto'" ); + + cssStub.returns( null ); + equal( defaultHook.get( fakeTween ), 0, "Uses 0 for null" ); + + cssStub.returns( undefined ); + equal( defaultHook.get( fakeTween ), 0, "Uses 0 for undefined" ); + + cssStub.reset(); + + // Setters + styleStub = this.sandbox.stub( jQuery, "style" ); + fakeTween.prop = "height"; + + defaultHook.set( fakeTween ); + ok( styleStub.calledWith( testElement, "height", "10px" ), + "Calls jQuery.style with elem, prop, now+unit" ); + + styleStub.reset(); + fakeTween.prop = "testMissing"; + + defaultHook.set( fakeTween ); + equal( styleStub.callCount, 0, "Did not call jQuery.style for non css property" ); + equal( testElement.testMissing, 10, "Instead, set value on element directly" ); + + jQuery.cssHooks.testMissing = jQuery.noop; + fakeTween.now = 11; + + defaultHook.set( fakeTween ); + delete jQuery.cssHooks.testMissing; + + ok( styleStub.calledWith( testElement, "testMissing", "11px" ), + "Presence of cssHooks causes jQuery.style with elem, prop, now+unit" ); + equal( testElement.testMissing, 10, "And value was unchanged" ); + + stepSpy = jQuery.fx.step.test = this.sandbox.spy(); + styleStub.reset(); + + fakeTween.prop = "test"; + defaultHook.set( fakeTween ); + ok( stepSpy.calledWith( fakeTween ), "Step function called with Tween" ); + equal( styleStub.callCount, 0, "Did not call jQuery.style" ); +} ); + +test( "jQuery.Tween - Plain Object", function() { + expect( 13 ); + var testObject = { test: 100 }, + testOptions = { duration: 100 }, + tween, easingSpy; + + tween = jQuery.Tween( testObject, testOptions, "test", 0, "linear" ); + equal( tween.elem, testObject, "Sets .element" ); + equal( tween.options, testOptions, "sets .options" ); + equal( tween.prop, "test", "sets .prop" ); + equal( tween.end, 0, "sets .end" ); + + equal( tween.easing, "linear", "sets .easing when provided" ); + + equal( tween.start, 100, "Reads .start value during construction" ); + equal( tween.now, 100, "Reads .now value during construction" ); + + easingSpy = this.sandbox.spy( jQuery.easing, "linear" ); + + equal( tween.run( 0.1 ), tween, ".run() returns this" ); + + equal( tween.now, 90, "Calculated tween" ); + + ok( easingSpy.calledWith( 0.1, 0.1 * testOptions.duration, 0, 1, testOptions.duration ), + "...using jQuery.easing.linear with back-compat arguments" ); + equal( testObject.test, 90, "Set value" ); + + tween.run( 1 ); + equal( testObject.test, 0, "Checking another value" ); + + tween.run( 0 ); + equal( testObject.test, 100, "Can even go back in time" ); +} ); + +test( "jQuery.Tween - Element", function() { + expect( 15 ); + var testElement = jQuery( "
" ).css( "height", 100 )[ 0 ], + testOptions = { duration: 100 }, + tween, easingSpy, eased; + + tween = jQuery.Tween( testElement, testOptions, "height", 0 ); + equal( tween.elem, testElement, "Sets .element" ); + equal( tween.options, testOptions, "sets .options" ); + equal( tween.prop, "height", "sets .prop" ); + equal( tween.end, 0, "sets .end" ); + + equal( tween.easing, jQuery.easing._default, "sets .easing to default when not provided" ); + equal( tween.unit, "px", "sets .unit to px when not provided" ); + + equal( tween.start, 100, "Reads .start value during construction" ); + equal( tween.now, 100, "Reads .now value during construction" ); + + easingSpy = this.sandbox.spy( jQuery.easing, "swing" ); + + equal( tween.run( 0.1 ), tween, ".run() returns this" ); + equal( tween.pos, jQuery.easing.swing( 0.1 ), "set .pos" ); + eased = 100 - ( jQuery.easing.swing( 0.1 ) * 100 ); + equal( tween.now, eased, "Calculated tween" ); + + ok( easingSpy.calledWith( 0.1, 0.1 * testOptions.duration, 0, 1, testOptions.duration ), + "...using jQuery.easing.linear with back-compat arguments" ); + equal( parseFloat( testElement.style.height ).toFixed( 5 ), eased.toFixed( 5 ), "Set value" ); + + tween.run( 1 ); + equal( testElement.style.height, "0px", "Checking another value" ); + + tween.run( 0 ); + equal( testElement.style.height, "100px", "Can even go back in time" ); +} ); + +test( "jQuery.Tween - No duration", function() { + expect( 3 ); + + var testObject = { test: 100 }, + testOptions = { duration: 0 }, + tween, easingSpy; + + tween = jQuery.Tween( testObject, testOptions, "test", 0 ); + easingSpy = this.sandbox.spy( jQuery.easing, "swing" ); + tween.run( 0.5 ); + + equal( tween.pos, 0.5, "set .pos correctly" ); + equal( testObject.test, 50, "set value on object correctly" ); + equal( easingSpy.callCount, 0, "didn't ease the value" ); +} ); + +test( "jQuery.Tween - step function option", function() { + expect( 4 ); + + var testObject = { test: 100 }, + testOptions = { duration: 100, step: this.sandbox.spy() }, + tween, propHookSpy; + + propHookSpy = this.sandbox.spy( jQuery.Tween.propHooks._default, "set" ); + + tween = jQuery.Tween( testObject, testOptions, "test", 0, "linear" ); + equal( testOptions.step.callCount, 0, "didn't call step on create" ); + + tween.run( 0.5 ); + ok( testOptions.step.calledOn( testObject ), + "Called step function in context of animated object" ); + ok( testOptions.step.calledWith( 50, tween ), "Called step function with correct parameters" ); + + ok( testOptions.step.calledBefore( propHookSpy ), + "Called step function before calling propHook.set" ); + +} ); + +test( "jQuery.Tween - custom propHooks", function() { + expect( 3 ); + + var testObject = {}, + testOptions = { duration: 100, step: this.sandbox.spy() }, + propHook = { + get: sinon.stub().returns( 100 ), + set: sinon.stub() + }, + tween; + + jQuery.Tween.propHooks.testHooked = propHook; + + tween = jQuery.Tween( testObject, testOptions, "testHooked", 0, "linear" ); + ok( propHook.get.calledWith( tween ), "called propHook.get on create" ); + equal( tween.now, 100, "Used return value from propHook.get" ); + + tween.run( 0.5 ); + ok( propHook.set.calledWith( tween ), "Called propHook.set function with correct parameters" ); + + delete jQuery.Tween.propHooks.testHooked; +} ); + +test( "jQuery.Tween - custom propHooks - advanced values", function() { + expect( 5 ); + + var testObject = {}, + testOptions = { duration: 100, step: this.sandbox.spy() }, + propHook = { + get: sinon.stub().returns( [ 0, 0 ] ), + set: sinon.spy() + }, + tween; + + jQuery.Tween.propHooks.testHooked = propHook; + + tween = jQuery.Tween( testObject, testOptions, "testHooked", [ 1, 1 ], "linear" ); + ok( propHook.get.calledWith( tween ), "called propHook.get on create" ); + deepEqual( tween.start, [ 0, 0 ], "Used return value from get" ); + + tween.run( 0.5 ); + + // Some day this NaN assumption might change - perhaps add a "calc" helper to the hooks? + ok( isNaN( tween.now ), "Used return value from propHook.get" ); + equal( tween.pos, 0.5, "But the eased percent is still avaliable" ); + ok( propHook.set.calledWith( tween ), "Called propHook.set function with correct parameters" ); + + delete jQuery.Tween.propHooks.testHooked; +} ); + +} )(); From b3b2d6c3dd51fbdc69e1942e9af75cc99a1834c2 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Tue, 19 May 2015 17:48:42 -0400 Subject: [PATCH 022/925] Effects: Adding unit tests for jQuery.Animation Closes gh-2326 --- Gruntfile.js | 5 +- src/effects.js | 44 ++++---- test/data/testinit.js | 1 + test/unit/animation.js | 230 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+), 20 deletions(-) create mode 100644 test/unit/animation.js diff --git a/Gruntfile.js b/Gruntfile.js index 8c88370e49..1a6bdac1ac 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -103,14 +103,15 @@ module.exports = function( grunt ) { src: "src/**/*.js", gruntfile: "Gruntfile.js", - // Right now, check only test helpers - test: [ "test/data/testrunner.js", "test/unit/tween.js" ], + // Check parts of tests that pass + test: [ "test/data/testrunner.js", "test/unit/animation.js", "test/unit/tween.js" ], release: [ "build/*.js", "!build/release-notes.js" ], tasks: "build/tasks/*.js" }, testswarm: { tests: [ "ajax", + "animation", "attributes", "callbacks", "core", diff --git a/src/effects.js b/src/effects.js index 53b73fd848..40da887053 100644 --- a/src/effects.js +++ b/src/effects.js @@ -2,6 +2,7 @@ define([ "./core", "./var/document", "./var/rcssNum", + "./var/rnotwhite", "./css/var/cssExpand", "./css/var/isHidden", "./css/var/swap", @@ -16,20 +17,13 @@ define([ "./manipulation", "./css", "./effects/Tween" -], function( jQuery, document, rcssNum, cssExpand, isHidden, swap, adjustCSS, dataPriv, showHide ) { +], function( jQuery, document, rcssNum, rnotwhite, cssExpand, isHidden, swap, + adjustCSS, dataPriv, showHide ) { var fxNow, timerId, rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/, - animationPrefilters = [ defaultPrefilter ], - tweeners = { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }; + rrun = /queueHooks$/; function raf() { if ( timerId ) { @@ -69,7 +63,7 @@ function genFx( type, includeWidth ) { function createTween( value, prop, animation ) { var tween, - collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), index = 0, length = collection.length; for ( ; index < length; index++ ) { @@ -281,7 +275,7 @@ function Animation( elem, properties, options ) { var result, stopped, index = 0, - length = animationPrefilters.length, + length = Animation.prefilters.length, deferred = jQuery.Deferred().always( function() { // Don't match elem in the :animated selector delete tick.elem; @@ -357,8 +351,12 @@ function Animation( elem, properties, options ) { propFilter( props, animation.opts.specialEasing ); for ( ; index < length ; index++ ) { - result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); if ( result ) { + if ( jQuery.isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + jQuery.proxy( result.stop, result ); + } return result; } } @@ -386,12 +384,20 @@ function Animation( elem, properties, options ) { jQuery.Animation = jQuery.extend( Animation, { + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + tweener: function( props, callback ) { if ( jQuery.isFunction( props ) ) { callback = props; props = [ "*" ]; } else { - props = props.split(" "); + props = props.match( rnotwhite ); } var prop, @@ -400,16 +406,18 @@ jQuery.Animation = jQuery.extend( Animation, { for ( ; index < length ; index++ ) { prop = props[ index ]; - tweeners[ prop ] = tweeners[ prop ] || []; - tweeners[ prop ].unshift( callback ); + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); } }, + prefilters: [ defaultPrefilter ], + prefilter: function( callback, prepend ) { if ( prepend ) { - animationPrefilters.unshift( callback ); + Animation.prefilters.unshift( callback ); } else { - animationPrefilters.push( callback ); + Animation.prefilters.push( callback ); } } }); diff --git a/test/data/testinit.js b/test/data/testinit.js index e59d9db325..ef90fe45c9 100644 --- a/test/data/testinit.js +++ b/test/data/testinit.js @@ -286,6 +286,7 @@ this.loadTests = function() { "unit/effects.js", "unit/offset.js", "unit/dimensions.js", + "unit/animation.js", "unit/tween.js" ]; diff --git a/test/unit/animation.js b/test/unit/animation.js new file mode 100644 index 0000000000..6bb070145e --- /dev/null +++ b/test/unit/animation.js @@ -0,0 +1,230 @@ +( function() { + +// Can't test what ain't there +if ( !jQuery.fx ) { + return; +} + +var oldRaf = window.requestAnimationFrame, + defaultPrefilter = jQuery.Animation.prefilters[ 0 ], + defaultTweener = jQuery.Animation.tweeners[ "*" ][ 0 ], + startTime = 505877050; + +// This module tests jQuery.Animation and the corresponding 1.8+ effects APIs +module( "animation", { + setup: function() { + window.requestAnimationFrame = null; + this.sandbox = sinon.sandbox.create(); + this.clock = this.sandbox.useFakeTimers( startTime ); + this._oldInterval = jQuery.fx.interval; + jQuery.fx.step = {}; + jQuery.fx.interval = 10; + jQuery.now = Date.now; + jQuery.Animation.prefilters = [ defaultPrefilter ]; + jQuery.Animation.tweeners = { "*": [ defaultTweener ] }; + }, + teardown: function() { + this.sandbox.restore(); + jQuery.now = Date.now; + jQuery.fx.stop(); + jQuery.fx.interval = this._oldInterval; + window.requestAnimationFrame = oldRaf; + return moduleTeardown.apply( this, arguments ); + } +} ); + +test( "Animation( subject, props, opts ) - shape", function() { + expect( 20 ); + + var subject = { test: 0 }, + props = { test: 1 }, + opts = { queue: "fx", duration: 100 }, + animation = jQuery.Animation( subject, props, opts ); + + equal( animation.elem, subject, ".elem is set to the exact object passed" ); + equal( animation.originalOptions, opts, ".originalOptions is set to options passed" ); + equal( animation.originalProperties, props, ".originalProperties is set to props passed" ); + + notEqual( animation.props, props, ".props is not the original however" ); + deepEqual( animation.props, props, ".props is a copy of the original" ); + + deepEqual( animation.opts, { + duration: 100, + queue: "fx", + specialEasing: { test: undefined }, + easing: jQuery.easing._default + }, ".options is filled with default easing and specialEasing" ); + + equal( animation.startTime, startTime, "startTime was set" ); + equal( animation.duration, 100, ".duration is set" ); + + equal( animation.tweens.length, 1, ".tweens has one Tween" ); + equal( typeof animation.tweens[ 0 ].run, "function", "which has a .run function" ); + + equal( typeof animation.createTween, "function", ".createTween is a function" ); + equal( typeof animation.stop, "function", ".stop is a function" ); + + equal( typeof animation.done, "function", ".done is a function" ); + equal( typeof animation.fail, "function", ".fail is a function" ); + equal( typeof animation.always, "function", ".always is a function" ); + equal( typeof animation.progress, "function", ".progress is a function" ); + + equal( jQuery.timers.length, 1, "Added a timers function" ); + equal( jQuery.timers[ 0 ].elem, subject, "...with .elem as the subject" ); + equal( jQuery.timers[ 0 ].anim, animation, "...with .anim as the animation" ); + equal( jQuery.timers[ 0 ].queue, opts.queue, "...with .queue" ); + + // Cleanup after ourselves by ticking to the end + this.clock.tick( 100 ); +} ); + +test( "Animation.prefilter( fn ) - calls prefilter after defaultPrefilter", function() { + expect( 1 ); + + var prefilter = this.sandbox.stub(), + defaultSpy = this.sandbox.spy( jQuery.Animation.prefilters, 0 ); + + jQuery.Animation.prefilter( prefilter ); + + jQuery.Animation( {}, {}, {} ); + ok( prefilter.calledAfter( defaultSpy ), "our prefilter called after" ); +} ); + +test( "Animation.prefilter( fn, true ) - calls prefilter before defaultPrefilter", function() { + expect( 1 ); + + var prefilter = this.sandbox.stub(), + defaultSpy = this.sandbox.spy( jQuery.Animation.prefilters, 0 ); + + jQuery.Animation.prefilter( prefilter, true ); + + jQuery.Animation( {}, {}, {} ); + ok( prefilter.calledBefore( defaultSpy ), "our prefilter called before" ); +} ); + +test( "Animation.prefilter - prefilter return hooks", function() { + expect( 34 ); + + var animation, realAnimation, element, + sandbox = this.sandbox, + ourAnimation = { stop: this.sandbox.spy() }, + target = { height: 50 }, + props = { height: 100 }, + opts = { duration: 100 }, + prefilter = this.sandbox.spy( function() { + realAnimation = this; + sandbox.spy( realAnimation, "createTween" ); + + deepEqual( realAnimation.originalProperties, props, "originalProperties" ); + equal( arguments[ 0 ], this.elem, "first param elem" ); + equal( arguments[ 1 ], this.props, "second param props" ); + equal( arguments[ 2 ], this.opts, "third param opts" ); + return ourAnimation; + } ), + defaultSpy = sandbox.spy( jQuery.Animation.prefilters, 0 ), + queueSpy = sandbox.spy( function( next ) { + next(); + } ), + TweenSpy = sandbox.spy( jQuery, "Tween" ); + + jQuery.Animation.prefilter( prefilter, true ); + + sandbox.stub( jQuery.fx, "timer" ); + + animation = jQuery.Animation( target, props, opts ); + + equal( prefilter.callCount, 1, "Called prefilter" ); + + equal( defaultSpy.callCount, 0, + "Returning something from a prefilter caused remaining prefilters to not run" ); + equal( jQuery.fx.timer.callCount, 0, "Returning something never queues a timer" ); + equal( animation, ourAnimation, "Returning something returned it from jQuery.Animation" ); + equal( realAnimation.createTween.callCount, 0, "Returning something never creates tweens" ); + equal( TweenSpy.callCount, 0, "Returning something never creates tweens" ); + + // Test overriden usage on queues: + prefilter.reset(); + element = jQuery( "
" ) + .css( "height", 50 ) + .animate( props, 100 ) + .queue( queueSpy ) + .animate( props, 100 ) + .queue( queueSpy ) + .animate( props, 100 ) + .queue( queueSpy ); + + equal( prefilter.callCount, 1, "Called prefilter" ); + equal( queueSpy.callCount, 0, "Next function in queue not called" ); + + realAnimation.opts.complete.call( realAnimation.elem ); + equal( queueSpy.callCount, 1, "Next function in queue called after complete" ); + + equal( prefilter.callCount, 2, "Called prefilter again - animation #2" ); + equal( ourAnimation.stop.callCount, 0, ".stop() on our animation hasn't been called" ); + + element.stop(); + equal( ourAnimation.stop.callCount, 1, ".stop() called ourAnimation.stop()" ); + ok( !ourAnimation.stop.args[ 0 ][ 0 ], ".stop( falsy ) (undefined or false are both valid)" ); + + equal( queueSpy.callCount, 2, "Next queue function called" ); + ok( queueSpy.calledAfter( ourAnimation.stop ), "After our animation was told to stop" ); + + // ourAnimation.stop.reset(); + equal( prefilter.callCount, 3, "Got the next animation" ); + + ourAnimation.stop.reset(); + + // do not clear queue, gotoEnd + element.stop( false, true ); + ok( ourAnimation.stop.calledWith( true ), ".stop(true) calls .stop(true)" ); + ok( queueSpy.calledAfter( ourAnimation.stop ), + "and the next queue function ran after we were told" ); +} ); + +test( "Animation.tweener( fn ) - unshifts a * tweener", function() { + expect( 2 ); + var starTweeners = jQuery.Animation.tweeners[ "*" ]; + + jQuery.Animation.tweener( jQuery.noop ); + equal( starTweeners.length, 2 ); + deepEqual( starTweeners, [ jQuery.noop, defaultTweener ] ); +} ); + +test( "Animation.tweener( 'prop', fn ) - unshifts a 'prop' tweener", function() { + expect( 4 ); + var tweeners = jQuery.Animation.tweeners, + fn = function() {}; + + jQuery.Animation.tweener( "prop", jQuery.noop ); + equal( tweeners.prop.length, 1 ); + deepEqual( tweeners.prop, [ jQuery.noop ] ); + + jQuery.Animation.tweener( "prop", fn ); + equal( tweeners.prop.length, 2 ); + deepEqual( tweeners.prop, [ fn, jQuery.noop ] ); +} ); + +test( "Animation.tweener( 'list of props', fn ) - unshifts a tweener to each prop", function() { + expect( 2 ); + var tweeners = jQuery.Animation.tweeners, + fn = function() {}; + + jQuery.Animation.tweener( "list of props", jQuery.noop ); + deepEqual( tweeners, { + list: [ jQuery.noop ], + of: [ jQuery.noop ], + props: [ jQuery.noop ], + "*": [ defaultTweener ] + } ); + + // Test with extra whitespaces + jQuery.Animation.tweener( " list\t of \tprops\n*", fn ); + deepEqual( tweeners, { + list: [ fn, jQuery.noop ], + of: [ fn, jQuery.noop ], + props: [ fn, jQuery.noop ], + "*": [ fn, defaultTweener ] + } ); +} ); + +} )(); From a44cfa00665d21c3197e25c2d63741dc15f6ffb9 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Fri, 26 Jun 2015 20:20:48 -0400 Subject: [PATCH 023/925] Tests: Lower the checks rounding error The CSS value rounding error was causig failures on FF and IE. --- test/unit/tween.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/tween.js b/test/unit/tween.js index 8b40d974e4..39894c5757 100644 --- a/test/unit/tween.js +++ b/test/unit/tween.js @@ -198,7 +198,7 @@ test( "jQuery.Tween - Element", function() { ok( easingSpy.calledWith( 0.1, 0.1 * testOptions.duration, 0, 1, testOptions.duration ), "...using jQuery.easing.linear with back-compat arguments" ); - equal( parseFloat( testElement.style.height ).toFixed( 5 ), eased.toFixed( 5 ), "Set value" ); + equal( parseFloat( testElement.style.height ).toFixed( 2 ), eased.toFixed( 2 ), "Set value" ); tween.run( 1 ); equal( testElement.style.height, "0px", "Checking another value" ); From 8e4aac8cb03ffb88373ea99629165d82ff5eccdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Wed, 1 Jul 2015 23:20:32 +0200 Subject: [PATCH 024/925] CSS: Improve a comment explaining IE11 fullscreen bug --- src/css.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/css.js b/src/css.js index 922f44162c..373fdcb7e4 100644 --- a/src/css.js +++ b/src/css.js @@ -114,7 +114,8 @@ function getWidthOrHeight( elem, name, extra ) { isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; // Support: IE11 only - // Fix for edge case in IE 11. See gh-1764 + // In IE 11 fullscreen elements inside of an iframe have + // 100x too small dimensions (gh-1764). if ( document.msFullscreenElement && window.top !== window ) { // Support: IE11 only // Running getBoundingClientRect on a disconnected node From 84ccf2606c6b97d5875774bf774f9f2aae950ae7 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Mon, 6 Jul 2015 10:13:01 -0400 Subject: [PATCH 025/925] Deferred: add .catch handler Fixes gh-2102 --- src/deferred.js | 3 +++ test/unit/deferred.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/deferred.js b/src/deferred.js index 188303c975..216baabb07 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -33,6 +33,9 @@ jQuery.extend({ deferred.done( arguments ).fail( arguments ); return this; }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, // Keep pipe for back-compat pipe: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 97f9111dd3..af91b36169 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -167,6 +167,42 @@ test( "jQuery.Deferred.then - filtering (fail)", function( assert ) { }); }); +test( "jQuery.Deferred.catch", function( assert ) { + assert.expect( 4 ); + + var value1, value2, value3, + defer = jQuery.Deferred(), + piped = defer.catch(function( a, b ) { + return a * b; + }), + done = jQuery.map( new Array( 3 ), function() { return assert.async(); } ); + + piped.done(function( result ) { + value3 = result; + }); + + defer.fail(function( a, b ) { + value1 = a; + value2 = b; + }); + + defer.reject( 2, 3 ).catch(function() { + assert.strictEqual( value1, 2, "first reject value ok" ); + assert.strictEqual( value2, 3, "second reject value ok" ); + assert.strictEqual( value3, 6, "result of filter ok" ); + done.pop().call(); + }); + + jQuery.Deferred().resolve().catch(function() { + assert.ok( false, "then should not be called on resolve" ); + }).then( done.pop() ); + + jQuery.Deferred().reject().catch( jQuery.noop ).done(function( value ) { + assert.strictEqual( value, undefined, "then fail callback can return undefined/null" ); + done.pop().call(); + }); +}); + test( "[PIPE ONLY] jQuery.Deferred.pipe - filtering (fail)", function( assert ) { assert.expect( 4 ); From b60b26e18477d21fa5ec9c6572e114fc5d441730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Thu, 2 Jul 2015 01:20:18 +0200 Subject: [PATCH 026/925] CSS: Make .css("width") & .css("height") return fractional values Fixes gh-1724 Closes gh-2439 --- src/css.js | 26 +++++++++++++++--------- test/unit/css.js | 45 +++++++++++++++++++++++++++++++++++++++++ test/unit/dimensions.js | 40 +++++++++++++++++++++--------------- 3 files changed, 86 insertions(+), 25 deletions(-) diff --git a/src/css.js b/src/css.js index 373fdcb7e4..859985f9d1 100644 --- a/src/css.js +++ b/src/css.js @@ -108,21 +108,23 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { function getWidthOrHeight( elem, name, extra ) { // Start with offset property, which is equivalent to the border-box value - var valueIsBorderBox = true, - val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + var val, + valueIsBorderBox = true, styles = getStyles( elem ), isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + // Support: IE <= 11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + if ( elem.getClientRects().length ) { + val = elem.getBoundingClientRect()[ name ]; + } + // Support: IE11 only // In IE 11 fullscreen elements inside of an iframe have // 100x too small dimensions (gh-1764). if ( document.msFullscreenElement && window.top !== window ) { - // Support: IE11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - if ( elem.getClientRects().length ) { - val = Math.round( elem.getBoundingClientRect()[ name ] * 100 ); - } + val *= 100; } // Some non-html elements return undefined for offsetWidth, so check for null/undefined @@ -309,7 +311,13 @@ jQuery.each([ "height", "width" ], function( i, name ) { // Certain elements can have dimension info if we invisibly show them // but it must have a current display style that would benefit return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - elem.offsetWidth === 0 ? + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <= 11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? swap( elem, cssShow, function() { return getWidthOrHeight( elem, name, extra ); }) : diff --git a/test/unit/css.js b/test/unit/css.js index 22a3c76365..06de4acd9f 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -846,6 +846,51 @@ testIframeWithCallback( "css('width') should work correctly before document read } ); +( function() { + var supportsFractionalGBCR, + qunitFixture = document.getElementById( "qunit-fixture" ), + div = document.createElement( "div" ); + div.style.width = "3.3px"; + qunitFixture.appendChild( div ); + supportsFractionalGBCR = div.getBoundingClientRect().width.toFixed(1) === "3.3"; + qunitFixture.removeChild( div ); + + test( "css('width') and css('height') should return fractional values for nodes in the document", function() { + if ( !supportsFractionalGBCR ) { + expect( 1 ); + ok( true, "This browser doesn't support fractional values in getBoundingClientRect()" ); + return; + } + + expect( 2 ); + + var el = jQuery( "
" ).appendTo( "#qunit-fixture" ); + jQuery( "" ).appendTo( "#qunit-fixture" ); + + equal( Number( el.css( "width" ).replace( /px$/, "" ) ).toFixed( 1 ), "33.3", + "css('width') should return fractional values" ); + equal( Number( el.css( "height" ).replace( /px$/, "" ) ).toFixed( 1 ), "88.8", + "css('height') should return fractional values" ); + } ); + + test( "css('width') and css('height') should return fractional values for disconnected nodes", function() { + if ( !supportsFractionalGBCR ) { + expect( 1 ); + ok( true, "This browser doesn't support fractional values in getBoundingClientRect()" ); + return; + } + + expect( 2 ); + + var el = jQuery( "
" ); + + equal( Number( el.css( "width" ).replace( /px$/, "" ) ).toFixed( 1 ), "33.3", + "css('width') should return fractional values" ); + equal( Number( el.css( "height" ).replace( /px$/, "" ) ).toFixed( 1 ), "88.8", + "css('height') should return fractional values" ); + } ); +} )(); + test("certain css values of 'normal' should be convertable to a number, see #8627", function() { expect ( 3 ); diff --git a/test/unit/dimensions.js b/test/unit/dimensions.js index 7e31771617..04810494a7 100644 --- a/test/unit/dimensions.js +++ b/test/unit/dimensions.js @@ -258,10 +258,12 @@ test("child of a hidden elem (or unconnected node) has accurate inner/outer/Widt equal( $divChild.outerWidth(), $divNormal.outerWidth(), "child of a hidden element outerWidth() is wrong see #9441" ); equal( $divChild.outerWidth(true), $divNormal.outerWidth( true ), "child of a hidden element outerWidth( true ) is wrong see #9300" ); - equal( $divChild.height(), $divNormal.height(), "child of a hidden element height() is wrong see #9441" ); - equal( $divChild.innerHeight(), $divNormal.innerHeight(), "child of a hidden element innerHeight() is wrong see #9441" ); - equal( $divChild.outerHeight(), $divNormal.outerHeight(), "child of a hidden element outerHeight() is wrong see #9441" ); - equal( $divChild.outerHeight(true), $divNormal.outerHeight( true ), "child of a hidden element outerHeight( true ) is wrong see #9300" ); + // Support: IE 10-11, Edge + // Child height is not always decimal + equal( $divChild.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "child of a hidden element height() is wrong see #9441" ); + equal( $divChild.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "child of a hidden element innerHeight() is wrong see #9441" ); + equal( $divChild.outerHeight().toFixed( 3 ), $divNormal.outerHeight().toFixed( 3 ), "child of a hidden element outerHeight() is wrong see #9441" ); + equal( $divChild.outerHeight( true ).toFixed( 3 ), $divNormal.outerHeight( true ).toFixed( 3 ), "child of a hidden element outerHeight( true ) is wrong see #9300" ); // tests that child div of an unconnected div works the same as a normal div equal( $divUnconnected.width(), $divNormal.width(), "unconnected element width() is wrong see #9441" ); @@ -269,10 +271,12 @@ test("child of a hidden elem (or unconnected node) has accurate inner/outer/Widt equal( $divUnconnected.outerWidth(), $divNormal.outerWidth(), "unconnected element outerWidth() is wrong see #9441" ); equal( $divUnconnected.outerWidth(true), $divNormal.outerWidth( true ), "unconnected element outerWidth( true ) is wrong see #9300" ); - equal( $divUnconnected.height(), $divNormal.height(), "unconnected element height() is wrong see #9441" ); - equal( $divUnconnected.innerHeight(), $divNormal.innerHeight(), "unconnected element innerHeight() is wrong see #9441" ); - equal( $divUnconnected.outerHeight(), $divNormal.outerHeight(), "unconnected element outerHeight() is wrong see #9441" ); - equal( $divUnconnected.outerHeight(true), $divNormal.outerHeight( true ), "unconnected element outerHeight( true ) is wrong see #9300" ); + // Support: IE 10-11, Edge + // Child height is not always decimal + equal( $divUnconnected.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "unconnected element height() is wrong see #9441" ); + equal( $divUnconnected.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "unconnected element innerHeight() is wrong see #9441" ); + equal( $divUnconnected.outerHeight().toFixed( 3 ), $divNormal.outerHeight().toFixed( 3 ), "unconnected element outerHeight() is wrong see #9441" ); + equal( $divUnconnected.outerHeight( true ).toFixed( 3 ), $divNormal.outerHeight( true ).toFixed( 3 ), "unconnected element outerHeight( true ) is wrong see #9300" ); // teardown html $divHiddenParent.remove(); @@ -329,10 +333,12 @@ test("box-sizing:border-box child of a hidden elem (or unconnected node) has acc equal( $divChild.outerWidth(), $divNormal.outerWidth(), "child of a hidden element outerWidth() is wrong see #10413" ); equal( $divChild.outerWidth(true), $divNormal.outerWidth( true ), "child of a hidden element outerWidth( true ) is wrong see #10413" ); - equal( $divChild.height(), $divNormal.height(), "child of a hidden element height() is wrong see #10413" ); - equal( $divChild.innerHeight(), $divNormal.innerHeight(), "child of a hidden element innerHeight() is wrong see #10413" ); - equal( $divChild.outerHeight(), $divNormal.outerHeight(), "child of a hidden element outerHeight() is wrong see #10413" ); - equal( $divChild.outerHeight(true), $divNormal.outerHeight( true ), "child of a hidden element outerHeight( true ) is wrong see #10413" ); + // Support: IE 10-11, Edge + // Child height is not always decimal + equal( $divChild.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "child of a hidden element height() is wrong see #10413" ); + equal( $divChild.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "child of a hidden element innerHeight() is wrong see #10413" ); + equal( $divChild.outerHeight().toFixed( 3 ), $divNormal.outerHeight().toFixed( 3 ), "child of a hidden element outerHeight() is wrong see #10413" ); + equal( $divChild.outerHeight( true ).toFixed( 3 ), $divNormal.outerHeight( true ).toFixed( 3 ), "child of a hidden element outerHeight( true ) is wrong see #10413" ); // tests that child div of an unconnected div works the same as a normal div equal( $divUnconnected.width(), $divNormal.width(), "unconnected element width() is wrong see #10413" ); @@ -340,10 +346,12 @@ test("box-sizing:border-box child of a hidden elem (or unconnected node) has acc equal( $divUnconnected.outerWidth(), $divNormal.outerWidth(), "unconnected element outerWidth() is wrong see #10413" ); equal( $divUnconnected.outerWidth(true), $divNormal.outerWidth( true ), "unconnected element outerWidth( true ) is wrong see #10413" ); - equal( $divUnconnected.height(), $divNormal.height(), "unconnected element height() is wrong see #10413" ); - equal( $divUnconnected.innerHeight(), $divNormal.innerHeight(), "unconnected element innerHeight() is wrong see #10413" ); - equal( $divUnconnected.outerHeight(), $divNormal.outerHeight(), "unconnected element outerHeight() is wrong see #10413" ); - equal( $divUnconnected.outerHeight(true), $divNormal.outerHeight( true ), "unconnected element outerHeight( true ) is wrong see #10413" ); + // Support: IE 10-11, Edge + // Child height is not always decimal + equal( $divUnconnected.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "unconnected element height() is wrong see #10413" ); + equal( $divUnconnected.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "unconnected element innerHeight() is wrong see #10413" ); + equal( $divUnconnected.outerHeight().toFixed( 3 ), $divNormal.outerHeight().toFixed( 3 ), "unconnected element outerHeight() is wrong see #10413" ); + equal( $divUnconnected.outerHeight( true ).toFixed( 3 ), $divNormal.outerHeight( true ).toFixed( 3 ), "unconnected element outerHeight( true ) is wrong see #10413" ); // teardown html $divHiddenParent.remove(); From 8887106702baa69ed80baa65c5a249786bffc77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Tue, 23 Jun 2015 01:44:18 +0200 Subject: [PATCH 027/925] CSS: Add an integration test for issue gh-1764 Refs gh-1764 Refs gh-2401 Closes gh-2425 --- .../data/gh-1764-fullscreen-iframe.css | 18 ++++ .../data/gh-1764-fullscreen-iframe.html | 21 ++++ test/integration/data/gh-1764-fullscreen.js | 99 +++++++++++++++++++ test/integration/gh-1764-fullscreen.html | 34 +++++++ 4 files changed, 172 insertions(+) create mode 100644 test/integration/data/gh-1764-fullscreen-iframe.css create mode 100644 test/integration/data/gh-1764-fullscreen-iframe.html create mode 100644 test/integration/data/gh-1764-fullscreen.js create mode 100644 test/integration/gh-1764-fullscreen.html diff --git a/test/integration/data/gh-1764-fullscreen-iframe.css b/test/integration/data/gh-1764-fullscreen-iframe.css new file mode 100644 index 0000000000..ba4b4fa7d3 --- /dev/null +++ b/test/integration/data/gh-1764-fullscreen-iframe.css @@ -0,0 +1,18 @@ +.result { + font-size: 24px; + margin: 0.5em 0; + width: 700px; + height: 56px; +} + +.error { + background-color: red; +} + +.warn { + background-color: yellow; +} + +.success { + background-color: lightgreen; +} diff --git a/test/integration/data/gh-1764-fullscreen-iframe.html b/test/integration/data/gh-1764-fullscreen-iframe.html new file mode 100644 index 0000000000..bed56b8ecf --- /dev/null +++ b/test/integration/data/gh-1764-fullscreen-iframe.html @@ -0,0 +1,21 @@ + + + + + + Test for gh-1764 - test iframe + + + + +
+
+ +
+ + + + + diff --git a/test/integration/data/gh-1764-fullscreen.js b/test/integration/data/gh-1764-fullscreen.js new file mode 100644 index 0000000000..e093c0dc78 --- /dev/null +++ b/test/integration/data/gh-1764-fullscreen.js @@ -0,0 +1,99 @@ +/* exported bootstrapFrom */ + +// `mode` may be "iframe" or not specified. +function bootstrapFrom( mainSelector, mode ) { + if ( mode === "iframe" && window.parent === window ) { + jQuery( mainSelector + " .result" ) + .attr( "class", "result warn" ) + .text( "This test should be run in an iframe. Open ../gh-1764-fullscreen.html." ); + jQuery( mainSelector + " .toggle-fullscreen" ).remove(); + return; + } + + var fullscreenSupported = document.exitFullscreen || + document.exitFullscreen || + document.msExitFullscreen || + document.mozCancelFullScreen || + document.webkitExitFullscreen; + + function isFullscreen() { + return !!(document.fullscreenElement || + document.mozFullScreenElement || + document.webkitFullscreenElement || + document.msFullscreenElement); + } + + function requestFullscreen( element ) { + if ( !isFullscreen() ) { + if ( element.requestFullscreen ) { + element.requestFullscreen(); + } else if ( element.msRequestFullscreen ) { + element.msRequestFullscreen(); + } else if ( element.mozRequestFullScreen ) { + element.mozRequestFullScreen(); + } else if ( element.webkitRequestFullscreen ) { + element.webkitRequestFullscreen(); + } + } + } + + function exitFullscreen() { + if ( document.exitFullscreen ) { + document.exitFullscreen(); + } else if ( document.msExitFullscreen ) { + document.msExitFullscreen(); + } else if ( document.mozCancelFullScreen ) { + document.mozCancelFullScreen(); + } else if ( document.webkitExitFullscreen ) { + document.webkitExitFullscreen(); + } + } + + function runTest() { + var dimensions; + if ( !fullscreenSupported ) { + jQuery( mainSelector + " .result" ) + .attr( "class", "result success" ) + .text( "Fullscreen mode is not supported in this browser. Test not run." ); + } else if ( !isFullscreen() ) { + jQuery( mainSelector + " .result" ) + .attr( "class", "result warn" ) + .text( "Enable fullscreen mode to fire the test." ); + } else { + dimensions = jQuery( mainSelector + " .result" ).css( [ "width", "height" ] ); + dimensions.width = parseFloat( dimensions.width ).toFixed( 3 ); + dimensions.height = parseFloat( dimensions.height ).toFixed( 3 ); + if ( dimensions.width === "700.000" && dimensions.height === "56.000" ) { + jQuery( mainSelector + " .result" ) + .attr( "class", "result success" ) + .text( "Dimensions in fullscreen mode are computed correctly." ); + } else { + jQuery( mainSelector + " .result" ) + .attr( "class", "result error" ) + .html( "Incorrect dimensions; " + + "expected: { width: '700.000', height: '56.000' };
" + + "got: { width: '" + dimensions.width + "', height: '" + + dimensions.height + "' }." ); + } + } + } + + function toggleFullscreen() { + if ( isFullscreen() ) { + exitFullscreen(); + } else { + requestFullscreen( jQuery( mainSelector + " .container" )[0] ); + } + } + + $( mainSelector + " .toggle-fullscreen" ).on( "click", toggleFullscreen ); + + $( document ).on( [ + "webkitfullscreenchange", + "mozfullscreenchange", + "fullscreenchange", + "MSFullscreenChange", + ].join( " " ), runTest ); + + runTest(); +} diff --git a/test/integration/gh-1764-fullscreen.html b/test/integration/gh-1764-fullscreen.html new file mode 100644 index 0000000000..1cba659438 --- /dev/null +++ b/test/integration/gh-1764-fullscreen.html @@ -0,0 +1,34 @@ + + + + + + Test for gh-1764 + + + + + +
+
+ +
+ + + + + + From dc8ba6af921f4c9650fb1e4cfc698b2a276fa53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Wed, 8 Jul 2015 13:00:38 +0200 Subject: [PATCH 028/925] Tests: Remove a trailing comma for compatibility with the compat branch --- test/integration/data/gh-1764-fullscreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/data/gh-1764-fullscreen.js b/test/integration/data/gh-1764-fullscreen.js index e093c0dc78..c13c609f3b 100644 --- a/test/integration/data/gh-1764-fullscreen.js +++ b/test/integration/data/gh-1764-fullscreen.js @@ -92,7 +92,7 @@ function bootstrapFrom( mainSelector, mode ) { "webkitfullscreenchange", "mozfullscreenchange", "fullscreenchange", - "MSFullscreenChange", + "MSFullscreenChange" ].join( " " ), runTest ); runTest(); From 8f13997e89ca325a49f7581fad7e85fe37bad166 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Wed, 8 Jul 2015 13:41:48 -0400 Subject: [PATCH 029/925] Build: update AUTHORS.txt --- AUTHORS.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 48d68dcd4f..d8403cb731 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -245,3 +245,18 @@ Nicolas HENRY Anne-Gaelle Colom George Mauer Leonardo Braga +Stephen Edgar +Mr21 +Winston Howes +Jon Hester +Alexander O'Mara +Bastian Buchholz +Arthur Stolyar +Calvin Metcalf +Mu Haibao +Richard McDaniel +Chris Rebert +Gabriel Schulhof +Gilad Peleg +Martin Naumann +Thomas Tortorini From 3ec73efb26317239a4f22f0b023b0b99a4300a20 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Sat, 11 Jul 2015 11:41:06 -0400 Subject: [PATCH 030/925] Build: add mailmap entry --- .mailmap | 1 + AUTHORS.txt | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 2ccdcb9ceb..2b3a980c08 100644 --- a/.mailmap +++ b/.mailmap @@ -85,6 +85,7 @@ Scott GonzÃĄlez Scott Jehl Sebastian Burkhard Senya Pugach +Thomas Tortorini Mr21 Timmy Willison Timmy Willison Timo Tijhof diff --git a/AUTHORS.txt b/AUTHORS.txt index d8403cb731..01e8686888 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -246,7 +246,7 @@ Anne-Gaelle Colom George Mauer Leonardo Braga Stephen Edgar -Mr21 +Thomas Tortorini Winston Howes Jon Hester Alexander O'Mara @@ -259,4 +259,3 @@ Chris Rebert Gabriel Schulhof Gilad Peleg Martin Naumann -Thomas Tortorini From a2ae215d999637e8d9d0906abcbf6b1ca35c8e6e Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Fri, 10 Jul 2015 20:58:43 +0300 Subject: [PATCH 031/925] Ajax: Remove jsonp callbacks through "jQuery#removeProp" method Fixes gh-2323 Closes gh-2464 --- src/ajax/jsonp.js | 10 ++++++++-- test/unit/ajax.js | 32 +++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/ajax/jsonp.js b/src/ajax/jsonp.js index a04898f67d..f469344e0b 100644 --- a/src/ajax/jsonp.js +++ b/src/ajax/jsonp.js @@ -64,8 +64,14 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { // Clean-up function (fires after converters) jqXHR.always(function() { - // Restore preexisting value - window[ callbackName ] = overwritten; + // If previous value didn't exist - remove it + if ( overwritten === undefined ) { + jQuery( window ).removeProp( callbackName ); + + // Otherwise restore preexisting value + } else { + window[ callbackName ] = overwritten; + } // Save back as free if ( s[ callbackName ] ) { diff --git a/test/unit/ajax.js b/test/unit/ajax.js index 8a4ca7a657..359fa9d819 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -1,12 +1,4 @@ module( "ajax", { - setup: function() { - var jsonpCallback = this.jsonpCallback = jQuery.ajaxSettings.jsonpCallback; - jQuery.ajaxSettings.jsonpCallback = function() { - var callback = jsonpCallback.apply( this, arguments ); - Globals.register( callback ); - return callback; - }; - }, teardown: function() { jQuery( document ).off( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError ajaxSuccess" ); moduleTeardown.apply( this, arguments ); @@ -742,7 +734,7 @@ module( "ajax", { } ]); - ajaxTest( "jQuery.ajax() - JSONP - Explicit callback param" + label, 9, { + ajaxTest( "jQuery.ajax() - JSONP - Explicit callback param" + label, 10, { setup: function() { Globals.register("functionToCleanUp"); Globals.register("XXX"); @@ -765,6 +757,11 @@ module( "ajax", { crossDomain: crossDomain, jsonpCallback: "jsonpResults", success: function( data ) { + strictEqual( + typeof window[ "jsonpResults" ], + "function", + "should not rewrite original function" + ); ok( data.data, "JSON results returned (GET, custom callback name)" ); } }, { @@ -1356,16 +1353,29 @@ module( "ajax", { ]); jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) { - ajaxTest( "#8205 - jQuery.ajax() - JSONP - re-use callbacks name" + label, 2, { + ajaxTest( "#8205 - jQuery.ajax() - JSONP - re-use callbacks name" + label, 4, { url: "data/jsonp.php", dataType: "jsonp", crossDomain: crossDomain, beforeSend: function( jqXHR, s ) { s.callback = s.jsonpCallback; + + ok( this.callback in window, "JSONP callback name is in the window" ); }, success: function() { var previous = this; - strictEqual( previous.jsonpCallback, undefined, "jsonpCallback option is set back to default in callbacks" ); + + strictEqual( + previous.jsonpCallback, + undefined, + "jsonpCallback option is set back to default in callbacks" + ); + + ok( + !( this.callback in window ), + "JSONP callback name was removed from the window" + ); + jQuery.ajax({ url: "data/jsonp.php", dataType: "jsonp", From c44dd7775b387094d8c921c7e839e3c266e4f2c8 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Mon, 13 Jul 2015 15:01:33 -0400 Subject: [PATCH 032/925] Release: properly set the dist remote when it's a real release --- build/release/dist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/release/dist.js b/build/release/dist.js index 7d134b429d..f17ea4ba70 100644 --- a/build/release/dist.js +++ b/build/release/dist.js @@ -4,7 +4,7 @@ module.exports = function( Release, complete ) { fs = require( "fs" ), shell = require( "shelljs" ), pkg = require( Release.dir.repo + "/package.json" ), - distRemote = Release.remote.replace( "jquery", "jquery-dist" ), + distRemote = Release.remote.replace( "jquery.git", "jquery-dist.git" ), // These files are included with the distrubtion files = [ "src", From bf591fb597a056bf2fc9bc474010374695b18d1a Mon Sep 17 00:00:00 2001 From: Marek Lewandowski Date: Mon, 13 Jul 2015 14:39:52 +0200 Subject: [PATCH 033/925] Selector: Define jQuery.uniqueSort in selector-native too Fixes gh-2466 Closes gh-2467 --- src/selector-native.js | 46 ++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/selector-native.js b/src/selector-native.js index 5717918bcd..2d5cb30263 100644 --- a/src/selector-native.js +++ b/src/selector-native.js @@ -67,6 +67,28 @@ var hasDuplicate, // Not directly comparable, sort on existence of method return a.compareDocumentPosition ? -1 : 1; + }, + uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 0, + j = 0; + + hasDuplicate = false; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; }; jQuery.extend({ @@ -99,28 +121,8 @@ jQuery.extend({ return results; }, - unique: function( results ) { - var elem, - duplicates = [], - i = 0, - j = 0; - - hasDuplicate = false; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - return results; - }, + uniqueSort: uniqueSort, + unique: uniqueSort, text: function( elem ) { var node, ret = "", From 1c59b308d201d6dd0f0aed2ad0256d01b9f68047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 20 Jul 2015 00:55:48 +0200 Subject: [PATCH 034/925] Build: Acknowledge Android 2.3 is not ES5-compatible Android 2.3 chokes on unquoted reserved words being used as property names which was making Deferred tests not run. Acknowledge the sad fact that Android 2.3 is not ES5-compliant browser and enable the "es3" option in JSHint config. Fixes gh-2478 Closes gh-2481 --- src/.jshintrc | 3 +++ test/.jshintrc | 3 +++ test/unit/core.js | 2 +- test/unit/css.js | 2 +- test/unit/deferred.js | 8 ++++---- test/unit/effects.js | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/.jshintrc b/src/.jshintrc index 6144bf420e..281b683b90 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -12,8 +12,11 @@ "sub": true, + "es3": true, + "globals": { "window": true, + "JSON": false, "jQuery": true, "define": true, diff --git a/test/.jshintrc b/test/.jshintrc index 7aef665c68..6b380d6baf 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -10,6 +10,8 @@ "undef": true, "unused": true, + "es3": true, + "evil": true, "sub": true, @@ -21,6 +23,7 @@ "require": false, "define": false, "DOMParser": false, + "JSON": false, "Promise": false, "Symbol": false, "QUnit": false, diff --git a/test/unit/core.js b/test/unit/core.js index 5a87e2f0cf..8cfe98f838 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -1400,7 +1400,7 @@ test("jQuery.parseJSON", function() { deepEqual( jQuery.parseJSON( "{ \"string\": \"\", \"number\": 4.2e+1, \"object\": {}," + "\"array\": [[]], \"boolean\": [ true, false ], \"null\": null }"), - { string: "", number: 42, object: {}, array: [[]], boolean: [ true, false ], "null": null }, + { string: "", number: 42, object: {}, array: [[]], "boolean": [ true, false ], "null": null }, "Dictionary of all data types" ); diff --git a/test/unit/css.js b/test/unit/css.js index 06de4acd9f..47db2c8f31 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -1225,7 +1225,7 @@ test( "Do not throw on frame elements from css method (#15098)", 1, function() { if ( transformName ) { equal( elemStyle[ transformName ], transformVal, "setting properly-prefixed transform" ); } - equal( elemStyle.undefined, undefined, "Nothing writes to node.style.undefined" ); + equal( elemStyle[ "undefined" ], undefined, "Nothing writes to node.style.undefined" ); } ); test( "Don't detect fake set properties on a node when caching the prefixed version", function() { diff --git a/test/unit/deferred.js b/test/unit/deferred.js index af91b36169..1aa15435fe 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -172,7 +172,7 @@ test( "jQuery.Deferred.catch", function( assert ) { var value1, value2, value3, defer = jQuery.Deferred(), - piped = defer.catch(function( a, b ) { + piped = defer[ "catch" ](function( a, b ) { return a * b; }), done = jQuery.map( new Array( 3 ), function() { return assert.async(); } ); @@ -186,18 +186,18 @@ test( "jQuery.Deferred.catch", function( assert ) { value2 = b; }); - defer.reject( 2, 3 ).catch(function() { + defer.reject( 2, 3 )[ "catch" ](function() { assert.strictEqual( value1, 2, "first reject value ok" ); assert.strictEqual( value2, 3, "second reject value ok" ); assert.strictEqual( value3, 6, "result of filter ok" ); done.pop().call(); }); - jQuery.Deferred().resolve().catch(function() { + jQuery.Deferred().resolve()[ "catch" ](function() { assert.ok( false, "then should not be called on resolve" ); }).then( done.pop() ); - jQuery.Deferred().reject().catch( jQuery.noop ).done(function( value ) { + jQuery.Deferred().reject()[ "catch" ]( jQuery.noop ).done(function( value ) { assert.strictEqual( value, undefined, "then fail callback can return undefined/null" ); done.pop().call(); }); diff --git a/test/unit/effects.js b/test/unit/effects.js index 7aff2242ab..47f8bdf0af 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -1579,7 +1579,7 @@ test("Initial step callback should show element as :animated (#14623)", 1, funct var foo = jQuery( "#foo" ); foo.animate({ - opacity: 0, + opacity: 0 }, { duration: 100, step: function() { From d24275372624bac897c4131fd1507a58c09a1483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 20 Jul 2015 20:22:34 +0200 Subject: [PATCH 035/925] Core: Add a support comment for Safari 8 Related issue: https://bugs.webkit.org/show_bug.cgi?id=137337 Thanks @phistuck! Refs cfe468f29c4cbe1a457d0feb17dec90dcfd7c280 --- src/core/support.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/support.js b/src/core/support.js index 4ef1dacab9..68d20e3538 100644 --- a/src/core/support.js +++ b/src/core/support.js @@ -3,6 +3,11 @@ define([ "../var/support" ], function( document, support ) { +// Support: Safari 8+ +// In Safari 8 documents created via document.implementation.createHTMLDocument +// collapse sibling forms: the second one becomes a child of the first one. +// Because of that, this security measure has to be disabled in Safari 8. +// https://bugs.webkit.org/show_bug.cgi?id=137337 support.createHTMLDocument = (function() { var body = document.implementation.createHTMLDocument( "" ).body; body.innerHTML = "
"; From bf48c21d225c31f0f9b5441d95f73615ca3dcfdb Mon Sep 17 00:00:00 2001 From: Thomas Tortorini Date: Thu, 25 Jun 2015 06:18:04 +0200 Subject: [PATCH 036/925] Core: .each/.map should accept an undefined/null value Fixes gh-2267 Closes gh-2363 --- src/core.js | 20 +++++++++----------- test/unit/core.js | 12 ++++++++++++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/core.js b/src/core.js index ba6eeceb8f..e8024aaf10 100644 --- a/src/core.js +++ b/src/core.js @@ -268,11 +268,10 @@ jQuery.extend({ }, each: function( obj, callback ) { - var i = 0, - length = obj.length, - isArray = isArraylike( obj ); + var length, i = 0; - if ( isArray ) { + if ( isArrayLike( obj ) ) { + length = obj.length; for ( ; i < length; i++ ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; @@ -301,7 +300,7 @@ jQuery.extend({ var ret = results || []; if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { + if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr @@ -355,14 +354,13 @@ jQuery.extend({ // arg is for internal usage only map: function( elems, callback, arg ) { - var value, + var length, value, i = 0, - length = elems.length, - isArray = isArraylike( elems ), ret = []; // Go through the array, translating each of the items to their new values - if ( isArray ) { + if ( isArrayLike( elems ) ) { + length = elems.length; for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); @@ -441,13 +439,13 @@ function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); -function isArraylike( obj ) { +function isArrayLike( obj ) { // Support: iOS 8.2 (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE - var length = "length" in obj && obj.length, + var length = !!obj && "length" in obj && obj.length, type = jQuery.type( obj ); if ( type === "function" || jQuery.isWindow( obj ) ) { diff --git a/test/unit/core.js b/test/unit/core.js index 8cfe98f838..1ddf695a65 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -1198,6 +1198,18 @@ test("jQuery.each(Object,Function)", function() { equal( i, document.styleSheets.length, "Iteration over document.styleSheets" ); }); +test("jQuery.each/map(undefined/null,Function)", 1, function() { + try { + jQuery.each( undefined, jQuery.noop ); + jQuery.each( null, jQuery.noop ); + jQuery.map( undefined, jQuery.noop ); + jQuery.map( null, jQuery.noop ); + ok( true, "jQuery.each/map( undefined/null, function() {} );" ); + } catch ( e ) { + ok( false, "each/map must accept null and undefined values" ); + } +}); + test( "JIT compilation does not interfere with length retrieval (gh-2145)", function() { expect( 4 ); From 93bee4701d14202045a88aab156da0daf9418430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 27 Jul 2015 22:14:48 +0200 Subject: [PATCH 037/925] Core: Adjust comments & tests after dropping Safari 6 support Support comments that mentioned only Safari < 7 were checked & updated to account for bugs existing in newer versions as well; Safari 6 support test results were removed. Refs gh-2482 --- src/event.js | 2 +- src/offset.js | 2 +- test/unit/css.js | 2 +- test/unit/support.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/event.js b/src/event.js index 04ee403d5d..53cfcd5ee0 100644 --- a/src/event.js +++ b/src/event.js @@ -593,7 +593,7 @@ jQuery.event = { event[ prop ] = originalEvent[ prop ]; } - // Support: Safari 6.0+ + // Support: Safari 6-8+ // Target should not be a text node (#504, #13143) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; diff --git a/src/offset.js b/src/offset.js index f0139d5eff..23a4b2a181 100644 --- a/src/offset.js +++ b/src/offset.js @@ -203,7 +203,7 @@ jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( }; }); -// Support: Safari<7+, Chrome<37+ +// Support: Safari<7-8+, Chrome<37-44+ // Add the top/left cssHooks using jQuery.fn.position // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 // Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280 diff --git a/test/unit/css.js b/test/unit/css.js index 47db2c8f31..181695b706 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -1121,7 +1121,7 @@ test( "show() after hide() should always set display to initial value (#14750)", equal( div.css( "display" ), "list-item", "should get last set display value" ); }); -// Support: IE < 11, Safari < 7 +// Support: IE < 11 // We have to jump through the hoops here in order to test work with "order" CSS property, // that some browsers do not support. This test is not, strictly speaking, correct, // but it's the best that we can do. diff --git a/test/unit/support.js b/test/unit/support.js index 46f904efad..d62c8b988b 100644 --- a/test/unit/support.js +++ b/test/unit/support.js @@ -143,7 +143,7 @@ testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Sec "radioValue": true, "reliableMarginRight": true }; - } else if ( /(6|7)\.0(\.\d+|) safari/i.test( userAgent ) ) { + } else if ( /7\.0(\.\d+|) safari/i.test( userAgent ) ) { expected = { "ajax": true, "boxSizingReliable": true, From 2792845534e36c39dbb9c8369ed96aaefa560081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 27 Jul 2015 22:29:38 +0200 Subject: [PATCH 038/925] Event: Update support comments for mouseenter/mouseleave implementation Custom mouseenter/mouseleave implementation was needed because of: 1. Safari 6 not implementing mouseenter/mouseleave at all. 2. Chrome sending mouseenter too often. The second issue has been fixed in Chrome but exists now in Safari 7 (it's fixed in Safari 8) so we have to keep it for now, unfortunately. --- src/event.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/event.js b/src/event.js index 53cfcd5ee0..f0604503c2 100644 --- a/src/event.js +++ b/src/event.js @@ -772,13 +772,10 @@ jQuery.Event.prototype = { // so that event delegation works in jQuery. // Do the same for pointerenter/pointerleave and pointerover/pointerout // -// Support: Safari<7.0 -// Safari doesn't support mouseenter/mouseleave at all. -// -// Support: Chrome 34+ -// Mouseenter doesn't perform while left mouse button is pressed -// (and initiated outside the observed element) -// https://code.google.com/p/chromium/issues/detail?id=333868 +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://code.google.com/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout", From 6044fb6a7384aec85906949835ef9a58114896ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Mon, 5 May 2014 20:04:03 +0200 Subject: [PATCH 039/925] Ajax: Account for Android 2.3 not firing window.onerror on script errors Android 2.3 doesn't fire the window.onerror handler, just accept the reality there and skip the test. Refs gh-1573 Refs gh-1786 Refs jquery/jquery.com#108 Closes gh-2458 --- test/unit/ajax.js | 9 +++++++++ test/unit/manipulation.js | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/test/unit/ajax.js b/test/unit/ajax.js index 359fa9d819..e216f34a23 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -1450,6 +1450,15 @@ module( "ajax", { }); asyncTest( "#11743 - jQuery.ajax() - script, throws exception", 1, function() { + // Support: Android 2.3 only + // Android 2.3 doesn't fire the window.onerror handler, just accept the reality there. + if ( /android 2\.3/i.test( navigator.userAgent ) ) { + ok( true, "Test skipped, Android 2.3 doesn't fire window.onerror for " + + "errors in dynamically included scripts" ); + start(); + return; + } + var onerror = window.onerror; window.onerror = function() { ok( true, "Exception thrown" ); diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index 5a84520118..c1dac6993f 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -2241,6 +2241,17 @@ test( "Ensure oldIE creates a new set on appendTo (#8894)", function() { }); asyncTest( "html() - script exceptions bubble (#11743)", 2, function() { + // Support: Android 2.3 only + // Android 2.3 doesn't fire the window.onerror handler, just accept the reality there. + if ( /android 2\.3/i.test( navigator.userAgent ) ) { + ok( true, "Test skipped, Android 2.3 doesn't fire window.onerror for " + + "errors in dynamically included scripts" ); + ok( true, "Test skipped, Android 2.3 doesn't fire window.onerror for " + + "errors in dynamically included scripts" ); + start(); + return; + } + var onerror = window.onerror; setTimeout(function() { From 5fe76c663f8a4986af62edb434a1708c006d0b21 Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Sat, 18 Jul 2015 17:47:31 -0700 Subject: [PATCH 040/925] Data: remove user data in cleanData Fixes gh-2503 Closes gh-2480 --- src/manipulation.js | 25 +++++++++++++++---------- test/unit/manipulation.js | 24 +++++++++++++++++++++++- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/manipulation.js b/src/manipulation.js index 4bac1417fa..00d65de5a3 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -270,19 +270,24 @@ jQuery.extend({ i = 0; for ( ; (elem = elems[ i ]) !== undefined; i++ ) { - if ( jQuery.acceptData( elem ) && (data = elem[ dataPriv.expando ])) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); + if ( jQuery.acceptData( elem ) ) { + if ( (data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } } } + delete elem[ dataPriv.expando ]; + } + if ( elem[ dataUser.expando ] ) { + delete elem[ dataUser.expando ]; } - delete elem[ dataPriv.expando ]; } } } diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index c1dac6993f..fffbf38cea 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -2064,7 +2064,7 @@ test( "jQuery.cleanData", function() { }); test( "jQuery.cleanData eliminates all private data (gh-2127)", function() { - expect( 2 ); + expect( 3 ); var div = jQuery( "
" ).appendTo( "#qunit-fixture" ); @@ -2074,12 +2074,34 @@ test( "jQuery.cleanData eliminates all private data (gh-2127)", function() { div.remove(); + ok( !jQuery.hasData( div[ 0 ] ), "Removed element hasData should return false" ); + ok( jQuery.isEmptyObject( jQuery._data( div[ 0 ] ) ), "Private data is empty after node is removed" ); div.remove(); }); +test( "jQuery.cleanData eliminates all public data", function() { + expect( 2 ); + + var key, + div = jQuery( "
" ); + div.data( "some", "data" ); + ok( !jQuery.isEmptyObject( jQuery.data( div[ 0 ] ) ), "Ensure some public data exists" ); + + div.remove(); + + ok( !jQuery.hasData( div[ 0 ] ), "Removed element hasData should return false" ); + + // Make sure the expando is gone + for ( key in div[ 0 ] ) { + if ( /^jQuery/.test( key ) ) { + ok( false, "Expando was not removed when there was no more data" ); + } + } +}); + test( "domManip plain-text caching (trac-6779)", function() { expect( 1 ); From 360a4780339b7f412b75ad8a06dca7f39616f654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pe=CC=81rel?= Date: Thu, 30 Jul 2015 15:34:34 +0200 Subject: [PATCH 041/925] Docs: Fix various spelling mistakes Closes gh-2487 --- build/release/dist.js | 2 +- test/data/support/csp.php | 2 +- test/unit/css.js | 2 +- test/unit/event.js | 2 +- test/unit/manipulation.js | 2 +- test/unit/tween.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/release/dist.js b/build/release/dist.js index f17ea4ba70..8ef6faf860 100644 --- a/build/release/dist.js +++ b/build/release/dist.js @@ -5,7 +5,7 @@ module.exports = function( Release, complete ) { shell = require( "shelljs" ), pkg = require( Release.dir.repo + "/package.json" ), distRemote = Release.remote.replace( "jquery.git", "jquery-dist.git" ), - // These files are included with the distrubtion + // These files are included with the distribution files = [ "src", "LICENSE.txt", diff --git a/test/data/support/csp.php b/test/data/support/csp.php index d01def783f..0783e1774b 100644 --- a/test/data/support/csp.php +++ b/test/data/support/csp.php @@ -1,5 +1,5 @@ diff --git a/test/unit/css.js b/test/unit/css.js index 181695b706..b4337a1eac 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -29,7 +29,7 @@ test("css(String|Hash)", function() { equal( div2.find("input").css("height"), "20px", "Height on hidden input." ); equal( div2.find("textarea").css("height"), "20px", "Height on hidden textarea." ); - equal( div2.find("div").css("height"), "20px", "Height on hidden textarea." ); + equal( div2.find("div").css("height"), "20px", "Height on hidden div." ); div2.remove(); diff --git a/test/unit/event.js b/test/unit/event.js index ba3d9481a7..46a9d72699 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -2725,7 +2725,7 @@ test( "Donor event interference", function( assert ) { jQuery( "#donor-input" )[ 0 ].click(); } ); -test( "originalEvent property for Chrome, Safari and FF of simualted event", function( assert ) { +test( "originalEvent property for Chrome, Safari and FF of simulated event", function( assert ) { var userAgent = window.navigator.userAgent; if ( !(/chrome/i.test( userAgent ) || diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index fffbf38cea..7befa29c4d 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -498,7 +498,7 @@ test( "html(String) tag-hyphenated elements (Bug #1987)", function() { var j = jQuery("text"); ok( jQuery.nodeName(j[0], "TR-MULTIPLE-HYPHENS"), "Tags with multiple hypens" ); ok( jQuery.nodeName(j.children()[0], "TD-WITH-HYPHEN"), "Tags with multiple hypens" ); - equal( j.children().text(), "text", "Tags with multple hypens behave normally" ); + equal( j.children().text(), "text", "Tags with multiple hypens behave normally" ); }); test( "IE8 serialization bug", function() { diff --git a/test/unit/tween.js b/test/unit/tween.js index 39894c5757..1386c2878b 100644 --- a/test/unit/tween.js +++ b/test/unit/tween.js @@ -289,7 +289,7 @@ test( "jQuery.Tween - custom propHooks - advanced values", function() { // Some day this NaN assumption might change - perhaps add a "calc" helper to the hooks? ok( isNaN( tween.now ), "Used return value from propHook.get" ); - equal( tween.pos, 0.5, "But the eased percent is still avaliable" ); + equal( tween.pos, 0.5, "But the eased percent is still available" ); ok( propHook.set.calledWith( tween ), "Called propHook.set function with correct parameters" ); delete jQuery.Tween.propHooks.testHooked; From aabe94edb4880c75eeebc5b5b5d66a9ad17008fe Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Wed, 29 Jul 2015 18:10:04 +0300 Subject: [PATCH 042/925] Tests: don't use deprecated argument in test declaration Closes gh-2507 --- test/unit/ajax.js | 20 ++++++-- test/unit/attributes.js | 7 ++- test/unit/core.js | 12 +++-- test/unit/css.js | 24 +++++++--- test/unit/data.js | 32 +++++++++---- test/unit/dimensions.js | 8 +++- test/unit/effects.js | 99 +++++++++++++++++++++++++++++---------- test/unit/event.js | 12 +++-- test/unit/manipulation.js | 28 ++++++++--- test/unit/queue.js | 8 +++- test/unit/wrap.js | 8 +++- 11 files changed, 193 insertions(+), 65 deletions(-) diff --git a/test/unit/ajax.js b/test/unit/ajax.js index e216f34a23..6eedc57842 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -6,7 +6,9 @@ module( "ajax", { }); (function() { - test("Unit Testing Environment", 2, function () { + test("Unit Testing Environment", function () { + expect( 2 ); + ok( hasPHP, "Running in an environment with PHP support. The AJAX tests only run if the environment supports PHP!" ); ok( !isLocal, "Unit tests are not ran from file:// (especially in Chrome. If you must test from file:// with Chrome, run it with the --allow-file-access-from-files flag!)" ); }); @@ -1288,7 +1290,9 @@ module( "ajax", { } }); - test( "#7531 - jQuery.ajax() - Location object as url", 1, function () { + test( "#7531 - jQuery.ajax() - Location object as url", function () { + expect( 1 ); + var xhr, success = false; try { @@ -1389,7 +1393,9 @@ module( "ajax", { }); }); - test( "#9887 - jQuery.ajax() - Context with circular references (#9887)", 2, function () { + test( "#9887 - jQuery.ajax() - Context with circular references (#9887)", function () { + expect( 2 ); + var success = false, context = {}; context.field = context; @@ -1699,7 +1705,9 @@ module( "ajax", { //----------- jQuery.domManip() - test( "#11264 - jQuery.domManip() - no side effect because of ajaxSetup or global events", 1, function() { + test( "#11264 - jQuery.domManip() - no side effect because of ajaxSetup or global events", function() { + expect( 1 ); + jQuery.ajaxSetup({ type: "POST" }); @@ -2086,7 +2094,9 @@ module( "ajax", { //----------- jQuery.active - test( "jQuery.active", 1, function() { + test( "jQuery.active", function() { + expect( 1 ); + ok( jQuery.active === 0, "ajax active counter should be zero: " + jQuery.active ); }); diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 526cfc99db..cec7b60558 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -734,7 +734,8 @@ test( "prop('tabindex')", function() { equal( jQuery("#linkWithNoHrefWithNegativeTabIndex").prop("tabindex"), -1, "anchor without href, no tabindex set" ); }); -test( "prop('tabindex', value)", 10, function() { +test( "prop('tabindex', value)", function() { + expect( 10 ); var clone, element = jQuery("#divWithNoTabIndex"); @@ -1452,7 +1453,9 @@ test( "coords returns correct values in IE6/IE7, see #10828", function() { equal( area.attr("coords"), "0,0,0,0", "did not retrieve coords correctly" ); }); -test( "should not throw at $(option).val() (#14686)", 1, function() { +test( "should not throw at $(option).val() (#14686)", function() { + expect( 1 ); + try { jQuery( "