diff --git a/karma.conf.js b/karma.conf.js index a99351a..4d42e54 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -9,7 +9,8 @@ module.exports = function( config ) { proxies : { '/test/' : '/base/test/', - '/js/' : '/base/js/' + '/js/' : '/base/js/', + '/node_modules/syn/dist/' : '/base/node_modules/syn/dist/' }, // List of files / patterns to load in the browser @@ -25,7 +26,7 @@ module.exports = function( config ) { {pattern: "test/plugins/*/*.html", watched: true, served: true, included: false}, // JS files for iframe {pattern: "js/impress.js", watched: true, served: true, included: false}, - {pattern: "test/syn/syn.js", watched: true, served: true, included: false} + {pattern: "node_modules/syn/dist/global/syn.js", watched: false, served: true, included: false} ], // List of files to exclude diff --git a/package.json b/package.json index 982a5c8..89860be 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "karma-firefox-launcher": "~0.1", "karma-qunit": "1.0.0", "karma-sauce-launcher": "1.0.0", - "qunitjs": "2.0.0-rc1" + "qunitjs": "2.0.0-rc1", + "syn": "*" } } diff --git a/qunit_test_runner.html b/qunit_test_runner.html index cc316bf..92f24d6 100644 --- a/qunit_test_runner.html +++ b/qunit_test_runner.html @@ -7,14 +7,14 @@ QUnit tests for impress.js - +
- + diff --git a/test/core_tests_presentation.html b/test/core_tests_presentation.html index 52152ef..34ebb9f 100644 --- a/test/core_tests_presentation.html +++ b/test/core_tests_presentation.html @@ -19,6 +19,6 @@ - + diff --git a/test/qunit/qunit.css b/test/qunit/qunit.css deleted file mode 100644 index a59e282..0000000 --- a/test/qunit/qunit.css +++ /dev/null @@ -1,301 +0,0 @@ -/*! - * QUnit 1.20.0 - * http://qunitjs.com/ - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2015-10-27T17:53Z - */ - -/** Font Family and Sizes */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; -} - -#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } -#qunit-tests { font-size: smaller; } - - -/** Resets */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { - margin: 0; - padding: 0; -} - - -/** Header */ - -#qunit-header { - padding: 0.5em 0 0.5em 1em; - - color: #8699A4; - background-color: #0D3349; - - font-size: 1.5em; - line-height: 1em; - font-weight: 400; - - border-radius: 5px 5px 0 0; -} - -#qunit-header a { - text-decoration: none; - color: #C2CCD1; -} - -#qunit-header a:hover, -#qunit-header a:focus { - color: #FFF; -} - -#qunit-testrunner-toolbar label { - display: inline-block; - padding: 0 0.5em 0 0.1em; -} - -#qunit-banner { - height: 5px; -} - -#qunit-testrunner-toolbar { - padding: 0.5em 1em 0.5em 1em; - color: #5E740B; - background-color: #EEE; - overflow: hidden; -} - -#qunit-filteredTest { - padding: 0.5em 1em 0.5em 1em; - background-color: #F4FF77; - color: #366097; -} - -#qunit-userAgent { - padding: 0.5em 1em 0.5em 1em; - background-color: #2B81AF; - color: #FFF; - text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; -} - -#qunit-modulefilter-container { - float: right; - padding: 0.2em; -} - -.qunit-url-config { - display: inline-block; - padding: 0.1em; -} - -.qunit-filter { - display: block; - float: right; - margin-left: 1em; -} - -/** Tests: Pass/Fail */ - -#qunit-tests { - list-style-position: inside; -} - -#qunit-tests li { - padding: 0.4em 1em 0.4em 1em; - border-bottom: 1px solid #FFF; - list-style-position: inside; -} - -#qunit-tests > li { - display: none; -} - -#qunit-tests li.running, -#qunit-tests li.pass, -#qunit-tests li.fail, -#qunit-tests li.skipped { - display: list-item; -} - -#qunit-tests.hidepass li.running, -#qunit-tests.hidepass li.pass { - visibility: hidden; - position: absolute; - width: 0; - height: 0; - padding: 0; - border: 0; - margin: 0; -} - -#qunit-tests li strong { - cursor: pointer; -} - -#qunit-tests li.skipped strong { - cursor: default; -} - -#qunit-tests li a { - padding: 0.5em; - color: #C2CCD1; - text-decoration: none; -} - -#qunit-tests li p a { - padding: 0.25em; - color: #6B6464; -} -#qunit-tests li a:hover, -#qunit-tests li a:focus { - color: #000; -} - -#qunit-tests li .runtime { - float: right; - font-size: smaller; -} - -.qunit-assert-list { - margin-top: 0.5em; - padding: 0.5em; - - background-color: #FFF; - - border-radius: 5px; -} - -.qunit-source { - margin: 0.6em 0 0.3em; -} - -.qunit-collapsed { - display: none; -} - -#qunit-tests table { - border-collapse: collapse; - margin-top: 0.2em; -} - -#qunit-tests th { - text-align: right; - vertical-align: top; - padding: 0 0.5em 0 0; -} - -#qunit-tests td { - vertical-align: top; -} - -#qunit-tests pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} - -#qunit-tests del { - background-color: #E0F2BE; - color: #374E0C; - text-decoration: none; -} - -#qunit-tests ins { - background-color: #FFCACA; - color: #500; - text-decoration: none; -} - -/*** Test Counts */ - -#qunit-tests b.counts { color: #000; } -#qunit-tests b.passed { color: #5E740B; } -#qunit-tests b.failed { color: #710909; } - -#qunit-tests li li { - padding: 5px; - background-color: #FFF; - border-bottom: none; - list-style-position: inside; -} - -/*** Passing Styles */ - -#qunit-tests li li.pass { - color: #3C510C; - background-color: #FFF; - border-left: 10px solid #C6E746; -} - -#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } -#qunit-tests .pass .test-name { color: #366097; } - -#qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999; } - -#qunit-banner.qunit-pass { background-color: #C6E746; } - -/*** Failing Styles */ - -#qunit-tests li li.fail { - color: #710909; - background-color: #FFF; - border-left: 10px solid #EE5757; - white-space: pre; -} - -#qunit-tests > li:last-child { - border-radius: 0 0 5px 5px; -} - -#qunit-tests .fail { color: #000; background-color: #EE5757; } -#qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000; } - -#qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: #008000; } - -#qunit-banner.qunit-fail { background-color: #EE5757; } - -/*** Skipped tests */ - -#qunit-tests .skipped { - background-color: #EBECE9; -} - -#qunit-tests .qunit-skipped-label { - background-color: #F4FF77; - display: inline-block; - font-style: normal; - color: #366097; - line-height: 1.8em; - padding: 0 0.5em; - margin: -0.4em 0.4em -0.4em 0; -} - -/** Result */ - -#qunit-testresult { - padding: 0.5em 1em 0.5em 1em; - - color: #2B81AF; - background-color: #D2E0E6; - - border-bottom: 1px solid #FFF; -} -#qunit-testresult .module-name { - font-weight: 700; -} - -/** Fixture */ - -#qunit-fixture { - position: absolute; - top: -10000px; - left: -10000px; - width: 1000px; - height: 1000px; -} diff --git a/test/qunit/qunit.js b/test/qunit/qunit.js deleted file mode 100644 index 904943f..0000000 --- a/test/qunit/qunit.js +++ /dev/null @@ -1,4158 +0,0 @@ -/*! - * QUnit 1.20.0 - * http://qunitjs.com/ - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2015-10-27T17:53Z - */ - -(function( global ) { - -var QUnit = {}; - -var Date = global.Date; -var now = Date.now || function() { - return new Date().getTime(); -}; - -var setTimeout = global.setTimeout; -var clearTimeout = global.clearTimeout; - -// Store a local window from the global to allow direct references. -var window = global.window; - -var defined = { - document: window && window.document !== undefined, - setTimeout: setTimeout !== undefined, - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch ( e ) { - return false; - } - }() ) -}; - -var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ); -var globalStartCalled = false; -var runStarted = false; - -var toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty; - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var i, j, - result = a.slice(); - - for ( i = 0; i < result.length; i++ ) { - for ( j = 0; j < b.length; j++ ) { - if ( result[ i ] === b[ j ] ) { - result.splice( i, 1 ); - i--; - break; - } - } - } - return result; -} - -// from jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; -} - -/** - * Makes a clone of an object using only Array or Object as base, - * and copies over the own enumerable properties. - * - * @param {Object} obj - * @return {Object} New object with only the own properties (recursively). - */ -function objectValues ( obj ) { - var key, val, - vals = QUnit.is( "array", obj ) ? [] : {}; - for ( key in obj ) { - if ( hasOwn.call( obj, key ) ) { - val = obj[ key ]; - vals[ key ] = val === Object( val ) ? objectValues( val ) : val; - } - } - return vals; -} - -function extend( a, b, undefOnly ) { - for ( var prop in b ) { - if ( hasOwn.call( b, prop ) ) { - - // Avoid "Member not found" error in IE8 caused by messing with window.constructor - // This block runs on every environment, so `global` is being used instead of `window` - // to avoid errors on node. - if ( prop !== "constructor" || a !== global ) { - if ( b[ prop ] === undefined ) { - delete a[ prop ]; - } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { - a[ prop ] = b[ prop ]; - } - } - } - } - - return a; -} - -function objectType( obj ) { - if ( typeof obj === "undefined" ) { - return "undefined"; - } - - // Consider: typeof null === object - if ( obj === null ) { - return "null"; - } - - var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), - type = match && match[ 1 ]; - - switch ( type ) { - case "Number": - if ( isNaN( obj ) ) { - return "nan"; - } - return "number"; - case "String": - case "Boolean": - case "Array": - case "Set": - case "Map": - case "Date": - case "RegExp": - case "Function": - case "Symbol": - return type.toLowerCase(); - } - if ( typeof obj === "object" ) { - return "object"; - } -} - -// Safe object type checking -function is( type, obj ) { - return QUnit.objectType( obj ) === type; -} - -var getUrlParams = function() { - var i, current; - var urlParams = {}; - var location = window.location; - var params = location.search.slice( 1 ).split( "&" ); - var length = params.length; - - if ( params[ 0 ] ) { - for ( i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - if ( urlParams[ current[ 0 ] ] ) { - urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] ); - } else { - urlParams[ current[ 0 ] ] = current[ 1 ]; - } - } - } - - return urlParams; -}; - -// Doesn't support IE6 to IE9, it will return undefined on these browsers -// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack -function extractStacktrace( e, offset ) { - offset = offset === undefined ? 4 : offset; - - var stack, include, i; - - if ( e.stack ) { - stack = e.stack.split( "\n" ); - if ( /^error$/i.test( stack[ 0 ] ) ) { - stack.shift(); - } - if ( fileName ) { - include = []; - for ( i = offset; i < stack.length; i++ ) { - if ( stack[ i ].indexOf( fileName ) !== -1 ) { - break; - } - include.push( stack[ i ] ); - } - if ( include.length ) { - return include.join( "\n" ); - } - } - return stack[ offset ]; - - // Support: Safari <=6 only - } else if ( e.sourceURL ) { - - // exclude useless self-reference for generated Error objects - if ( /qunit.js$/.test( e.sourceURL ) ) { - return; - } - - // for actual exceptions, this is useful - return e.sourceURL + ":" + e.line; - } -} - -function sourceFromStacktrace( offset ) { - var error = new Error(); - - // Support: Safari <=7 only, IE <=10 - 11 only - // Not all browsers generate the `stack` property for `new Error()`, see also #636 - if ( !error.stack ) { - try { - throw error; - } catch ( err ) { - error = err; - } - } - - return extractStacktrace( error, offset ); -} - -/** - * Config object: Maintain internal state - * Later exposed as QUnit.config - * `config` initialized at top of scope - */ -var config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - // by default, modify document.title when suite is done - altertitle: true, - - // HTML Reporter: collapse every test except the first failing test - // If false, all failing tests will be expanded - collapse: true, - - // by default, scroll to top of the page when suite is done - scrolltop: true, - - // depth up-to which object will be dumped - maxDepth: 5, - - // when enabled, all tests must call expect() - requireExpects: false, - - // add checkboxes that are persisted in the query-string - // when enabled, the id is set to `true` as a `QUnit.config` property - urlConfig: [ - { - id: "hidepassed", - label: "Hide passed tests", - tooltip: "Only show tests and assertions that fail. Stored as query-strings." - }, - { - id: "noglobals", - label: "Check for Globals", - tooltip: "Enabling this will test if any test introduces new properties on the " + - "global object (`window` in Browsers). Stored as query-strings." - }, - { - id: "notrycatch", - label: "No try-catch", - tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + - "exceptions in IE reasonable. Stored as query-strings." - } - ], - - // Set of all modules. - modules: [], - - // Stack of nested modules - moduleStack: [], - - // The first unnamed module - currentModule: { - name: "", - tests: [] - }, - - callbacks: {} -}; - -var urlParams = defined.document ? getUrlParams() : {}; - -// Push a loose unnamed module to the modules collection -config.modules.push( config.currentModule ); - -if ( urlParams.filter === true ) { - delete urlParams.filter; -} - -// String search anywhere in moduleName+testName -config.filter = urlParams.filter; - -config.testId = []; -if ( urlParams.testId ) { - // Ensure that urlParams.testId is an array - urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," ); - for (var i = 0; i < urlParams.testId.length; i++ ) { - config.testId.push( urlParams.testId[ i ] ); - } -} - -var loggingCallbacks = {}; - -// Register logging callbacks -function registerLoggingCallbacks( obj ) { - var i, l, key, - callbackNames = [ "begin", "done", "log", "testStart", "testDone", - "moduleStart", "moduleDone" ]; - - function registerLoggingCallback( key ) { - var loggingCallback = function( callback ) { - if ( objectType( callback ) !== "function" ) { - throw new Error( - "QUnit logging methods require a callback function as their first parameters." - ); - } - - config.callbacks[ key ].push( callback ); - }; - - // DEPRECATED: This will be removed on QUnit 2.0.0+ - // Stores the registered functions allowing restoring - // at verifyLoggingCallbacks() if modified - loggingCallbacks[ key ] = loggingCallback; - - return loggingCallback; - } - - for ( i = 0, l = callbackNames.length; i < l; i++ ) { - key = callbackNames[ i ]; - - // Initialize key collection of logging callback - if ( objectType( config.callbacks[ key ] ) === "undefined" ) { - config.callbacks[ key ] = []; - } - - obj[ key ] = registerLoggingCallback( key ); - } -} - -function runLoggingCallbacks( key, args ) { - var i, l, callbacks; - - callbacks = config.callbacks[ key ]; - for ( i = 0, l = callbacks.length; i < l; i++ ) { - callbacks[ i ]( args ); - } -} - -// DEPRECATED: This will be removed on 2.0.0+ -// This function verifies if the loggingCallbacks were modified by the user -// If so, it will restore it, assign the given callback and print a console warning -function verifyLoggingCallbacks() { - var loggingCallback, userCallback; - - for ( loggingCallback in loggingCallbacks ) { - if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { - - userCallback = QUnit[ loggingCallback ]; - - // Restore the callback function - QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; - - // Assign the deprecated given callback - QUnit[ loggingCallback ]( userCallback ); - - if ( global.console && global.console.warn ) { - global.console.warn( - "QUnit." + loggingCallback + " was replaced with a new value.\n" + - "Please, check out the documentation on how to apply logging callbacks.\n" + - "Reference: http://api.qunitjs.com/category/callbacks/" - ); - } - } - } -} - -( function() { - if ( !defined.document ) { - return; - } - - // `onErrorFnPrev` initialized at top of scope - // Preserve other handlers - var onErrorFnPrev = window.onerror; - - // Cover uncaught exceptions - // Returning true will suppress the default browser handler, - // returning false will let it run. - window.onerror = function( error, filePath, linerNr ) { - var ret = false; - if ( onErrorFnPrev ) { - ret = onErrorFnPrev( error, filePath, linerNr ); - } - - // Treat return value as window.onerror itself does, - // Only do our handling if not suppressed. - if ( ret !== true ) { - if ( QUnit.config.current ) { - if ( QUnit.config.current.ignoreGlobalErrors ) { - return true; - } - QUnit.pushFailure( error, filePath + ":" + linerNr ); - } else { - QUnit.test( "global failure", extend(function() { - QUnit.pushFailure( error, filePath + ":" + linerNr ); - }, { validTest: true } ) ); - } - return false; - } - - return ret; - }; -} )(); - -QUnit.urlParams = urlParams; - -// Figure out if we're running the tests from a server or not -QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" ); - -// Expose the current QUnit version -QUnit.version = "1.20.0"; - -extend( QUnit, { - - // call on start of module test to prepend name to all tests - module: function( name, testEnvironment, executeNow ) { - var module, moduleFns; - var currentModule = config.currentModule; - - if ( arguments.length === 2 ) { - if ( testEnvironment instanceof Function ) { - executeNow = testEnvironment; - testEnvironment = undefined; - } - } - - // DEPRECATED: handles setup/teardown functions, - // beforeEach and afterEach should be used instead - if ( testEnvironment && testEnvironment.setup ) { - testEnvironment.beforeEach = testEnvironment.setup; - delete testEnvironment.setup; - } - if ( testEnvironment && testEnvironment.teardown ) { - testEnvironment.afterEach = testEnvironment.teardown; - delete testEnvironment.teardown; - } - - module = createModule(); - - moduleFns = { - beforeEach: setHook( module, "beforeEach" ), - afterEach: setHook( module, "afterEach" ) - }; - - if ( executeNow instanceof Function ) { - config.moduleStack.push( module ); - setCurrentModule( module ); - executeNow.call( module.testEnvironment, moduleFns ); - config.moduleStack.pop(); - module = module.parentModule || currentModule; - } - - setCurrentModule( module ); - - function createModule() { - var parentModule = config.moduleStack.length ? - config.moduleStack.slice( -1 )[ 0 ] : null; - var moduleName = parentModule !== null ? - [ parentModule.name, name ].join( " > " ) : name; - var module = { - name: moduleName, - parentModule: parentModule, - tests: [] - }; - - var env = {}; - if ( parentModule ) { - extend( env, parentModule.testEnvironment ); - delete env.beforeEach; - delete env.afterEach; - } - extend( env, testEnvironment ); - module.testEnvironment = env; - - config.modules.push( module ); - return module; - } - - function setCurrentModule( module ) { - config.currentModule = module; - } - - }, - - // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. - asyncTest: asyncTest, - - test: test, - - skip: skip, - - only: only, - - // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. - // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. - start: function( count ) { - var globalStartAlreadyCalled = globalStartCalled; - - if ( !config.current ) { - globalStartCalled = true; - - if ( runStarted ) { - throw new Error( "Called start() outside of a test context while already started" ); - } else if ( globalStartAlreadyCalled || count > 1 ) { - throw new Error( "Called start() outside of a test context too many times" ); - } else if ( config.autostart ) { - throw new Error( "Called start() outside of a test context when " + - "QUnit.config.autostart was true" ); - } else if ( !config.pageLoaded ) { - - // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it - config.autostart = true; - return; - } - } else { - - // If a test is running, adjust its semaphore - config.current.semaphore -= count || 1; - - // If semaphore is non-numeric, throw error - if ( isNaN( config.current.semaphore ) ) { - config.current.semaphore = 0; - - QUnit.pushFailure( - "Called start() with a non-numeric decrement.", - sourceFromStacktrace( 2 ) - ); - return; - } - - // Don't start until equal number of stop-calls - if ( config.current.semaphore > 0 ) { - return; - } - - // throw an Error if start is called more often than stop - if ( config.current.semaphore < 0 ) { - config.current.semaphore = 0; - - QUnit.pushFailure( - "Called start() while already started (test's semaphore was 0 already)", - sourceFromStacktrace( 2 ) - ); - return; - } - } - - resumeProcessing(); - }, - - // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. - stop: function( count ) { - - // If there isn't a test running, don't allow QUnit.stop() to be called - if ( !config.current ) { - throw new Error( "Called stop() outside of a test context" ); - } - - // If a test is running, adjust its semaphore - config.current.semaphore += count || 1; - - pauseProcessing(); - }, - - config: config, - - is: is, - - objectType: objectType, - - extend: extend, - - load: function() { - config.pageLoaded = true; - - // Initialize the configuration options - extend( config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: 0, - updateRate: 1000, - autostart: true, - filter: "" - }, true ); - - config.blocking = false; - - if ( config.autostart ) { - resumeProcessing(); - } - }, - - stack: function( offset ) { - offset = ( offset || 0 ) + 2; - return sourceFromStacktrace( offset ); - } -}); - -registerLoggingCallbacks( QUnit ); - -function begin() { - var i, l, - modulesLog = []; - - // If the test run hasn't officially begun yet - if ( !config.started ) { - - // Record the time of the test run's beginning - config.started = now(); - - verifyLoggingCallbacks(); - - // Delete the loose unnamed module if unused. - if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { - config.modules.shift(); - } - - // Avoid unnecessary information by not logging modules' test environments - for ( i = 0, l = config.modules.length; i < l; i++ ) { - modulesLog.push({ - name: config.modules[ i ].name, - tests: config.modules[ i ].tests - }); - } - - // The test run is officially beginning now - runLoggingCallbacks( "begin", { - totalTests: Test.count, - modules: modulesLog - }); - } - - config.blocking = false; - process( true ); -} - -function process( last ) { - function next() { - process( last ); - } - var start = now(); - config.depth = ( config.depth || 0 ) + 1; - - while ( config.queue.length && !config.blocking ) { - if ( !defined.setTimeout || config.updateRate <= 0 || - ( ( now() - start ) < config.updateRate ) ) { - if ( config.current ) { - - // Reset async tracking for each phase of the Test lifecycle - config.current.usedAsync = false; - } - config.queue.shift()(); - } else { - setTimeout( next, 13 ); - break; - } - } - config.depth--; - if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { - done(); - } -} - -function pauseProcessing() { - config.blocking = true; - - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout( config.timeout ); - config.timeout = setTimeout(function() { - if ( config.current ) { - config.current.semaphore = 0; - QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); - } else { - throw new Error( "Test timed out" ); - } - resumeProcessing(); - }, config.testTimeout ); - } -} - -function resumeProcessing() { - runStarted = true; - - // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) - if ( defined.setTimeout ) { - setTimeout(function() { - if ( config.current && config.current.semaphore > 0 ) { - return; - } - if ( config.timeout ) { - clearTimeout( config.timeout ); - } - - begin(); - }, 13 ); - } else { - begin(); - } -} - -function done() { - var runtime, passed; - - config.autorun = true; - - // Log the last module results - if ( config.previousModule ) { - runLoggingCallbacks( "moduleDone", { - name: config.previousModule.name, - tests: config.previousModule.tests, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all, - runtime: now() - config.moduleStats.started - }); - } - delete config.previousModule; - - runtime = now() - config.started; - passed = config.stats.all - config.stats.bad; - - runLoggingCallbacks( "done", { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - }); -} - -function setHook( module, hookName ) { - if ( module.testEnvironment === undefined ) { - module.testEnvironment = {}; - } - - return function( callback ) { - module.testEnvironment[ hookName ] = callback; - }; -} - -var focused = false; - -function Test( settings ) { - var i, l; - - ++Test.count; - - extend( this, settings ); - this.assertions = []; - this.semaphore = 0; - this.usedAsync = false; - this.module = config.currentModule; - this.stack = sourceFromStacktrace( 3 ); - - // Register unique strings - for ( i = 0, l = this.module.tests; i < l.length; i++ ) { - if ( this.module.tests[ i ].name === this.testName ) { - this.testName += " "; - } - } - - this.testId = generateHash( this.module.name, this.testName ); - - this.module.tests.push({ - name: this.testName, - testId: this.testId - }); - - if ( settings.skip ) { - - // Skipped tests will fully ignore any sent callback - this.callback = function() {}; - this.async = false; - this.expected = 0; - } else { - this.assert = new Assert( this ); - } -} - -Test.count = 0; - -Test.prototype = { - before: function() { - if ( - - // Emit moduleStart when we're switching from one module to another - this.module !== config.previousModule || - - // They could be equal (both undefined) but if the previousModule property doesn't - // yet exist it means this is the first test in a suite that isn't wrapped in a - // module, in which case we'll just emit a moduleStart event for 'undefined'. - // Without this, reporters can get testStart before moduleStart which is a problem. - !hasOwn.call( config, "previousModule" ) - ) { - if ( hasOwn.call( config, "previousModule" ) ) { - runLoggingCallbacks( "moduleDone", { - name: config.previousModule.name, - tests: config.previousModule.tests, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all, - runtime: now() - config.moduleStats.started - }); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0, started: now() }; - runLoggingCallbacks( "moduleStart", { - name: this.module.name, - tests: this.module.tests - }); - } - - config.current = this; - - if ( this.module.testEnvironment ) { - delete this.module.testEnvironment.beforeEach; - delete this.module.testEnvironment.afterEach; - } - this.testEnvironment = extend( {}, this.module.testEnvironment ); - - this.started = now(); - runLoggingCallbacks( "testStart", { - name: this.testName, - module: this.module.name, - testId: this.testId - }); - - if ( !config.pollution ) { - saveGlobal(); - } - }, - - run: function() { - var promise; - - config.current = this; - - if ( this.async ) { - QUnit.stop(); - } - - this.callbackStarted = now(); - - if ( config.notrycatch ) { - runTest( this ); - return; - } - - try { - runTest( this ); - } catch ( e ) { - this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + - this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); - - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - QUnit.start(); - } - } - - function runTest( test ) { - promise = test.callback.call( test.testEnvironment, test.assert ); - test.resolvePromise( promise ); - } - }, - - after: function() { - checkPollution(); - }, - - queueHook: function( hook, hookName ) { - var promise, - test = this; - return function runHook() { - config.current = test; - if ( config.notrycatch ) { - callHook(); - return; - } - try { - callHook(); - } catch ( error ) { - test.pushFailure( hookName + " failed on " + test.testName + ": " + - ( error.message || error ), extractStacktrace( error, 0 ) ); - } - - function callHook() { - promise = hook.call( test.testEnvironment, test.assert ); - test.resolvePromise( promise, hookName ); - } - }; - }, - - // Currently only used for module level hooks, can be used to add global level ones - hooks: function( handler ) { - var hooks = []; - - function processHooks( test, module ) { - if ( module.parentModule ) { - processHooks( test, module.parentModule ); - } - if ( module.testEnvironment && - QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) { - hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) ); - } - } - - // Hooks are ignored on skipped tests - if ( !this.skip ) { - processHooks( this, this.module ); - } - return hooks; - }, - - finish: function() { - config.current = this; - if ( config.requireExpects && this.expected === null ) { - this.pushFailure( "Expected number of assertions to be defined, but expect() was " + - "not called.", this.stack ); - } else if ( this.expected !== null && this.expected !== this.assertions.length ) { - this.pushFailure( "Expected " + this.expected + " assertions, but " + - this.assertions.length + " were run", this.stack ); - } else if ( this.expected === null && !this.assertions.length ) { - this.pushFailure( "Expected at least one assertion, but none were run - call " + - "expect(0) to accept zero assertions.", this.stack ); - } - - var i, - bad = 0; - - this.runtime = now() - this.started; - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - for ( i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[ i ].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - runLoggingCallbacks( "testDone", { - name: this.testName, - module: this.module.name, - skipped: !!this.skip, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length, - runtime: this.runtime, - - // HTML Reporter use - assertions: this.assertions, - testId: this.testId, - - // Source of Test - source: this.stack, - - // DEPRECATED: this property will be removed in 2.0.0, use runtime instead - duration: this.runtime - }); - - // QUnit.reset() is deprecated and will be replaced for a new - // fixture reset function on QUnit 2.0/2.1. - // It's still called here for backwards compatibility handling - QUnit.reset(); - - config.current = undefined; - }, - - queue: function() { - var priority, - test = this; - - if ( !this.valid() ) { - return; - } - - function run() { - - // each of these can by async - synchronize([ - function() { - test.before(); - }, - - test.hooks( "beforeEach" ), - function() { - test.run(); - }, - - test.hooks( "afterEach" ).reverse(), - - function() { - test.after(); - }, - function() { - test.finish(); - } - ]); - } - - // Prioritize previously failed tests, detected from sessionStorage - priority = QUnit.config.reorder && defined.sessionStorage && - +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); - - return synchronize( run, priority ); - }, - - push: function( result, actual, expected, message, negative ) { - var source, - details = { - module: this.module.name, - name: this.testName, - result: result, - message: message, - actual: actual, - expected: expected, - testId: this.testId, - negative: negative || false, - runtime: now() - this.started - }; - - if ( !result ) { - source = sourceFromStacktrace(); - - if ( source ) { - details.source = source; - } - } - - runLoggingCallbacks( "log", details ); - - this.assertions.push({ - result: !!result, - message: message - }); - }, - - pushFailure: function( message, source, actual ) { - if ( !( this instanceof Test ) ) { - throw new Error( "pushFailure() assertion outside test context, was " + - sourceFromStacktrace( 2 ) ); - } - - var details = { - module: this.module.name, - name: this.testName, - result: false, - message: message || "error", - actual: actual || null, - testId: this.testId, - runtime: now() - this.started - }; - - if ( source ) { - details.source = source; - } - - runLoggingCallbacks( "log", details ); - - this.assertions.push({ - result: false, - message: message - }); - }, - - resolvePromise: function( promise, phase ) { - var then, message, - test = this; - if ( promise != null ) { - then = promise.then; - if ( QUnit.objectType( then ) === "function" ) { - QUnit.stop(); - then.call( - promise, - function() { QUnit.start(); }, - function( error ) { - message = "Promise rejected " + - ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + - " " + test.testName + ": " + ( error.message || error ); - test.pushFailure( message, extractStacktrace( error, 0 ) ); - - // else next test will carry the responsibility - saveGlobal(); - - // Unblock - QUnit.start(); - } - ); - } - } - }, - - valid: function() { - var include, - filter = config.filter && config.filter.toLowerCase(), - module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(), - fullName = ( this.module.name + ": " + this.testName ).toLowerCase(); - - function testInModuleChain( testModule ) { - var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; - if ( testModuleName === module ) { - return true; - } else if ( testModule.parentModule ) { - return testInModuleChain( testModule.parentModule ); - } else { - return false; - } - } - - // Internally-generated tests are always valid - if ( this.callback && this.callback.validTest ) { - return true; - } - - if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) { - return false; - } - - if ( module && !testInModuleChain( this.module ) ) { - return false; - } - - if ( !filter ) { - return true; - } - - include = filter.charAt( 0 ) !== "!"; - if ( !include ) { - filter = filter.slice( 1 ); - } - - // If the filter matches, we need to honour include - if ( fullName.indexOf( filter ) !== -1 ) { - return include; - } - - // Otherwise, do the opposite - return !include; - } -}; - -// Resets the test setup. Useful for tests that modify the DOM. -/* -DEPRECATED: Use multiple tests instead of resetting inside a test. -Use testStart or testDone for custom cleanup. -This method will throw an error in 2.0, and will be removed in 2.1 -*/ -QUnit.reset = function() { - - // Return on non-browser environments - // This is necessary to not break on node tests - if ( !defined.document ) { - return; - } - - var fixture = defined.document && document.getElementById && - document.getElementById( "qunit-fixture" ); - - if ( fixture ) { - fixture.innerHTML = config.fixture; - } -}; - -QUnit.pushFailure = function() { - if ( !QUnit.config.current ) { - throw new Error( "pushFailure() assertion outside test context, in " + - sourceFromStacktrace( 2 ) ); - } - - // Gets current test obj - var currentTest = QUnit.config.current; - - return currentTest.pushFailure.apply( currentTest, arguments ); -}; - -// Based on Java's String.hashCode, a simple but not -// rigorously collision resistant hashing function -function generateHash( module, testName ) { - var hex, - i = 0, - hash = 0, - str = module + "\x1C" + testName, - len = str.length; - - for ( ; i < len; i++ ) { - hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); - hash |= 0; - } - - // Convert the possibly negative integer hash code into an 8 character hex string, which isn't - // strictly necessary but increases user understanding that the id is a SHA-like hash - hex = ( 0x100000000 + hash ).toString( 16 ); - if ( hex.length < 8 ) { - hex = "0000000" + hex; - } - - return hex.slice( -8 ); -} - -function synchronize( callback, priority ) { - var last = !priority; - - if ( QUnit.objectType( callback ) === "array" ) { - while ( callback.length ) { - synchronize( callback.shift() ); - } - return; - } - - if ( priority ) { - priorityFill( callback ); - } else { - config.queue.push( callback ); - } - - if ( config.autorun && !config.blocking ) { - process( last ); - } -} - -// Place previously failed tests on a queue priority line, respecting the order they get assigned. -function priorityFill( callback ) { - var queue, prioritizedQueue; - - queue = config.queue.slice( priorityFill.pos ); - prioritizedQueue = config.queue.slice( 0, -config.queue.length + priorityFill.pos ); - - queue.unshift( callback ); - queue.unshift.apply( queue, prioritizedQueue ); - - config.queue = queue; - - priorityFill.pos += 1; -} -priorityFill.pos = 0; - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in global ) { - if ( hasOwn.call( global, key ) ) { - - // in Opera sometimes DOM element ids show up here, ignore them - if ( /^qunit-test-output/.test( key ) ) { - continue; - } - config.pollution.push( key ); - } - } - } -} - -function checkPollution() { - var newGlobals, - deletedGlobals, - old = config.pollution; - - saveGlobal(); - - newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); - } - - deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); - } -} - -// Will be exposed as QUnit.asyncTest -function asyncTest( testName, expected, callback ) { - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - QUnit.test( testName, expected, callback, true ); -} - -// Will be exposed as QUnit.test -function test( testName, expected, callback, async ) { - if ( focused ) { return; } - - var newTest; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - newTest = new Test({ - testName: testName, - expected: expected, - async: async, - callback: callback - }); - - newTest.queue(); -} - -// Will be exposed as QUnit.skip -function skip( testName ) { - if ( focused ) { return; } - - var test = new Test({ - testName: testName, - skip: true - }); - - test.queue(); -} - -// Will be exposed as QUnit.only -function only( testName, expected, callback, async ) { - var newTest; - - if ( focused ) { return; } - - QUnit.config.queue.length = 0; - focused = true; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - newTest = new Test({ - testName: testName, - expected: expected, - async: async, - callback: callback - }); - - newTest.queue(); -} - -function Assert( testContext ) { - this.test = testContext; -} - -// Assert helpers -QUnit.assert = Assert.prototype = { - - // Specify the number of expected assertions to guarantee that failed test - // (no assertions are run at all) don't slip through. - expect: function( asserts ) { - if ( arguments.length === 1 ) { - this.test.expected = asserts; - } else { - return this.test.expected; - } - }, - - // Increment this Test's semaphore counter, then return a function that - // decrements that counter a maximum of once. - async: function( count ) { - var test = this.test, - popped = false, - acceptCallCount = count; - - if ( typeof acceptCallCount === "undefined" ) { - acceptCallCount = 1; - } - - test.semaphore += 1; - test.usedAsync = true; - pauseProcessing(); - - return function done() { - - if ( popped ) { - test.pushFailure( "Too many calls to the `assert.async` callback", - sourceFromStacktrace( 2 ) ); - return; - } - acceptCallCount -= 1; - if ( acceptCallCount > 0 ) { - return; - } - - test.semaphore -= 1; - popped = true; - resumeProcessing(); - }; - }, - - // Exports test.push() to the user API - push: function( /* result, actual, expected, message, negative */ ) { - var assert = this, - currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; - - // Backwards compatibility fix. - // Allows the direct use of global exported assertions and QUnit.assert.* - // Although, it's use is not recommended as it can leak assertions - // to other tests from async tests, because we only get a reference to the current test, - // not exactly the test where assertion were intended to be called. - if ( !currentTest ) { - throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) ); - } - - if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { - currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", - sourceFromStacktrace( 2 ) ); - - // Allow this assertion to continue running anyway... - } - - if ( !( assert instanceof Assert ) ) { - assert = currentTest.assert; - } - return assert.test.push.apply( assert.test, arguments ); - }, - - ok: function( result, message ) { - message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + - QUnit.dump.parse( result ) ); - this.push( !!result, result, true, message ); - }, - - notOk: function( result, message ) { - message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + - QUnit.dump.parse( result ) ); - this.push( !result, result, false, message, true ); - }, - - equal: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - this.push( expected == actual, actual, expected, message ); - }, - - notEqual: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - this.push( expected != actual, actual, expected, message, true ); - }, - - propEqual: function( actual, expected, message ) { - actual = objectValues( actual ); - expected = objectValues( expected ); - this.push( QUnit.equiv( actual, expected ), actual, expected, message ); - }, - - notPropEqual: function( actual, expected, message ) { - actual = objectValues( actual ); - expected = objectValues( expected ); - this.push( !QUnit.equiv( actual, expected ), actual, expected, message, true ); - }, - - deepEqual: function( actual, expected, message ) { - this.push( QUnit.equiv( actual, expected ), actual, expected, message ); - }, - - notDeepEqual: function( actual, expected, message ) { - this.push( !QUnit.equiv( actual, expected ), actual, expected, message, true ); - }, - - strictEqual: function( actual, expected, message ) { - this.push( expected === actual, actual, expected, message ); - }, - - notStrictEqual: function( actual, expected, message ) { - this.push( expected !== actual, actual, expected, message, true ); - }, - - "throws": function( block, expected, message ) { - var actual, expectedType, - expectedOutput = expected, - ok = false, - currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; - - // 'expected' is optional unless doing string comparison - if ( message == null && typeof expected === "string" ) { - message = expected; - expected = null; - } - - currentTest.ignoreGlobalErrors = true; - try { - block.call( currentTest.testEnvironment ); - } catch (e) { - actual = e; - } - currentTest.ignoreGlobalErrors = false; - - if ( actual ) { - expectedType = QUnit.objectType( expected ); - - // we don't want to validate thrown error - if ( !expected ) { - ok = true; - expectedOutput = null; - - // expected is a regexp - } else if ( expectedType === "regexp" ) { - ok = expected.test( errorString( actual ) ); - - // expected is a string - } else if ( expectedType === "string" ) { - ok = expected === errorString( actual ); - - // expected is a constructor, maybe an Error constructor - } else if ( expectedType === "function" && actual instanceof expected ) { - ok = true; - - // expected is an Error object - } else if ( expectedType === "object" ) { - ok = actual instanceof expected.constructor && - actual.name === expected.name && - actual.message === expected.message; - - // expected is a validation function which returns true if validation passed - } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { - expectedOutput = null; - ok = true; - } - } - - currentTest.assert.push( ok, actual, expectedOutput, message ); - } -}; - -// Provide an alternative to assert.throws(), for environments that consider throws a reserved word -// Known to us are: Closure Compiler, Narwhal -(function() { - /*jshint sub:true */ - Assert.prototype.raises = Assert.prototype[ "throws" ]; -}()); - -function errorString( error ) { - var name, message, - resultErrorString = error.toString(); - if ( resultErrorString.substring( 0, 7 ) === "[object" ) { - name = error.name ? error.name.toString() : "Error"; - message = error.message ? error.message.toString() : ""; - if ( name && message ) { - return name + ": " + message; - } else if ( name ) { - return name; - } else if ( message ) { - return message; - } else { - return "Error"; - } - } else { - return resultErrorString; - } -} - -// Test for equality any JavaScript type. -// Author: Philippe Rathé -QUnit.equiv = (function() { - - // Stack to decide between skip/abort functions - var callers = []; - - // Stack to avoiding loops from circular referencing - var parents = []; - var parentsB = []; - - function useStrictEquality( b, a ) { - - /*jshint eqeqeq:false */ - if ( b instanceof a.constructor || a instanceof b.constructor ) { - - // To catch short annotation VS 'new' annotation of a declaration. e.g.: - // `var i = 1;` - // `var j = new Number(1);` - return a == b; - } else { - return a === b; - } - } - - function compareConstructors( a, b ) { - var getProto = Object.getPrototypeOf || function( obj ) { - - /*jshint proto: true */ - return obj.__proto__; - }; - var protoA = getProto( a ); - var protoB = getProto( b ); - - // Comparing constructors is more strict than using `instanceof` - if ( a.constructor === b.constructor ) { - return true; - } - - // Ref #851 - // If the obj prototype descends from a null constructor, treat it - // as a null prototype. - if ( protoA && protoA.constructor === null ) { - protoA = null; - } - if ( protoB && protoB.constructor === null ) { - protoB = null; - } - - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if ( ( protoA === null && protoB === Object.prototype ) || - ( protoB === null && protoA === Object.prototype ) ) { - return true; - } - - return false; - } - - var callbacks = { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - "symbol": useStrictEquality, - - "nan": function( b ) { - return isNaN( b ); - }, - - "date": function( b, a ) { - return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function( b, a ) { - return QUnit.objectType( b ) === "regexp" && - - // The regex itself - a.source === b.source && - - // And its modifiers - a.global === b.global && - - // (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline && - a.sticky === b.sticky; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function() { - var caller = callers[ callers.length - 1 ]; - return caller !== Object && typeof caller !== "undefined"; - }, - - "array": function( b, a ) { - var i, j, len, loop, aCircular, bCircular; - - // b could be an object literal here - if ( QUnit.objectType( b ) !== "array" ) { - return false; - } - - len = a.length; - if ( len !== b.length ) { - // safe and faster - return false; - } - - // Track reference to avoid circular references - parents.push( a ); - parentsB.push( b ); - for ( i = 0; i < len; i++ ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[ j ] === a[ i ]; - bCircular = parentsB[ j ] === b[ i ]; - if ( aCircular || bCircular ) { - if ( a[ i ] === b[ i ] || aCircular && bCircular ) { - loop = true; - } else { - parents.pop(); - parentsB.pop(); - return false; - } - } - } - if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { - parents.pop(); - parentsB.pop(); - return false; - } - } - parents.pop(); - parentsB.pop(); - return true; - }, - - "set": function( b, a ) { - var aArray, bArray; - - // `b` could be any object here - if ( QUnit.objectType( b ) !== "set" ) { - return false; - } - - aArray = []; - a.forEach( function( v ) { - aArray.push( v ); - }); - bArray = []; - b.forEach( function( v ) { - bArray.push( v ); - }); - - return innerEquiv( bArray, aArray ); - }, - - "map": function( b, a ) { - var aArray, bArray; - - // `b` could be any object here - if ( QUnit.objectType( b ) !== "map" ) { - return false; - } - - aArray = []; - a.forEach( function( v, k ) { - aArray.push( [ k, v ] ); - }); - bArray = []; - b.forEach( function( v, k ) { - bArray.push( [ k, v ] ); - }); - - return innerEquiv( bArray, aArray ); - }, - - "object": function( b, a ) { - var i, j, loop, aCircular, bCircular; - - // Default to true - var eq = true; - var aProperties = []; - var bProperties = []; - - if ( compareConstructors( a, b ) === false ) { - return false; - } - - // Stack constructor before traversing properties - callers.push( a.constructor ); - - // Track reference to avoid circular references - parents.push( a ); - parentsB.push( b ); - - // Be strict: don't ensure hasOwnProperty and go deep - for ( i in a ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[ j ] === a[ i ]; - bCircular = parentsB[ j ] === b[ i ]; - if ( aCircular || bCircular ) { - if ( a[ i ] === b[ i ] || aCircular && bCircular ) { - loop = true; - } else { - eq = false; - break; - } - } - } - aProperties.push( i ); - if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { - eq = false; - break; - } - } - - parents.pop(); - parentsB.pop(); - - // Unstack, we are done - callers.pop(); - - for ( i in b ) { - - // Collect b's properties - bProperties.push( i ); - } - - // Ensures identical properties name - return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); - } - }; - - function typeEquiv( a, b ) { - var prop = QUnit.objectType( a ); - return callbacks[ prop ]( b, a ); - } - - // The real equiv function - function innerEquiv() { - var args = [].slice.apply( arguments ); - if ( args.length < 2 ) { - - // End transition - return true; - } - - return ( (function( a, b ) { - if ( a === b ) { - - // Catch the most you can - return true; - } else if ( a === null || b === null || typeof a === "undefined" || - typeof b === "undefined" || - QUnit.objectType( a ) !== QUnit.objectType( b ) ) { - - // Don't lose time with error prone cases - return false; - } else { - return typeEquiv( a, b ); - } - - // Apply transition with (1..n) arguments - }( args[ 0 ], args[ 1 ] ) ) && - innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) ); - } - - return innerEquiv; -}()); - -// Based on jsDump by Ariel Flesler -// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html -QUnit.dump = (function() { - function quote( str ) { - return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\""; - } - function literal( o ) { - return o + ""; - } - function join( pre, arr, post ) { - var s = dump.separator(), - base = dump.indent(), - inner = dump.indent( 1 ); - if ( arr.join ) { - arr = arr.join( "," + s + inner ); - } - if ( !arr ) { - return pre + post; - } - return [ pre, inner + arr, base + post ].join( s ); - } - function array( arr, stack ) { - var i = arr.length, - ret = new Array( i ); - - if ( dump.maxDepth && dump.depth > dump.maxDepth ) { - return "[object Array]"; - } - - this.up(); - while ( i-- ) { - ret[ i ] = this.parse( arr[ i ], undefined, stack ); - } - this.down(); - return join( "[", ret, "]" ); - } - - var reName = /^function (\w+)/, - dump = { - - // objType is used mostly internally, you can fix a (custom) type in advance - parse: function( obj, objType, stack ) { - stack = stack || []; - var res, parser, parserType, - inStack = inArray( obj, stack ); - - if ( inStack !== -1 ) { - return "recursion(" + ( inStack - stack.length ) + ")"; - } - - objType = objType || this.typeOf( obj ); - parser = this.parsers[ objType ]; - parserType = typeof parser; - - if ( parserType === "function" ) { - stack.push( obj ); - res = parser.call( this, obj, stack ); - stack.pop(); - return res; - } - return ( parserType === "string" ) ? parser : this.parsers.error; - }, - typeOf: function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if ( typeof obj === "undefined" ) { - type = "undefined"; - } else if ( QUnit.is( "regexp", obj ) ) { - type = "regexp"; - } else if ( QUnit.is( "date", obj ) ) { - type = "date"; - } else if ( QUnit.is( "function", obj ) ) { - type = "function"; - } else if ( obj.setInterval !== undefined && - obj.document !== undefined && - obj.nodeType === undefined ) { - type = "window"; - } else if ( obj.nodeType === 9 ) { - type = "document"; - } else if ( obj.nodeType ) { - type = "node"; - } else if ( - - // native arrays - toString.call( obj ) === "[object Array]" || - - // NodeList objects - ( typeof obj.length === "number" && obj.item !== undefined && - ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && - obj[ 0 ] === undefined ) ) ) - ) { - type = "array"; - } else if ( obj.constructor === Error.prototype.constructor ) { - type = "error"; - } else { - type = typeof obj; - } - return type; - }, - separator: function() { - return this.multiline ? this.HTML ? "
" : "\n" : this.HTML ? " " : " "; - }, - // extra can be a number, shortcut for increasing-calling-decreasing - indent: function( extra ) { - if ( !this.multiline ) { - return ""; - } - var chr = this.indentChar; - if ( this.HTML ) { - chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); - } - return new Array( this.depth + ( extra || 0 ) ).join( chr ); - }, - up: function( a ) { - this.depth += a || 1; - }, - down: function( a ) { - this.depth -= a || 1; - }, - setParser: function( name, parser ) { - this.parsers[ name ] = parser; - }, - // The next 3 are exposed so you can use them - quote: quote, - literal: literal, - join: join, - // - depth: 1, - maxDepth: QUnit.config.maxDepth, - - // This is the list of parsers, to modify them, use dump.setParser - parsers: { - window: "[Window]", - document: "[Document]", - error: function( error ) { - return "Error(\"" + error.message + "\")"; - }, - unknown: "[Unknown]", - "null": "null", - "undefined": "undefined", - "function": function( fn ) { - var ret = "function", - - // functions never have name in IE - name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; - - if ( name ) { - ret += " " + name; - } - ret += "( "; - - ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); - return join( ret, dump.parse( fn, "functionCode" ), "}" ); - }, - array: array, - nodelist: array, - "arguments": array, - object: function( map, stack ) { - var keys, key, val, i, nonEnumerableProperties, - ret = []; - - if ( dump.maxDepth && dump.depth > dump.maxDepth ) { - return "[object Object]"; - } - - dump.up(); - keys = []; - for ( key in map ) { - keys.push( key ); - } - - // Some properties are not always enumerable on Error objects. - nonEnumerableProperties = [ "message", "name" ]; - for ( i in nonEnumerableProperties ) { - key = nonEnumerableProperties[ i ]; - if ( key in map && inArray( key, keys ) < 0 ) { - keys.push( key ); - } - } - keys.sort(); - for ( i = 0; i < keys.length; i++ ) { - key = keys[ i ]; - val = map[ key ]; - ret.push( dump.parse( key, "key" ) + ": " + - dump.parse( val, undefined, stack ) ); - } - dump.down(); - return join( "{", ret, "}" ); - }, - node: function( node ) { - var len, i, val, - open = dump.HTML ? "<" : "<", - close = dump.HTML ? ">" : ">", - tag = node.nodeName.toLowerCase(), - ret = open + tag, - attrs = node.attributes; - - if ( attrs ) { - for ( i = 0, len = attrs.length; i < len; i++ ) { - val = attrs[ i ].nodeValue; - - // IE6 includes all attributes in .attributes, even ones not explicitly - // set. Those have values like undefined, null, 0, false, "" or - // "inherit". - if ( val && val !== "inherit" ) { - ret += " " + attrs[ i ].nodeName + "=" + - dump.parse( val, "attribute" ); - } - } - } - ret += close; - - // Show content of TextNode or CDATASection - if ( node.nodeType === 3 || node.nodeType === 4 ) { - ret += node.nodeValue; - } - - return ret + open + "/" + tag + close; - }, - - // function calls it internally, it's the arguments part of the function - functionArgs: function( fn ) { - var args, - l = fn.length; - - if ( !l ) { - return ""; - } - - args = new Array( l ); - while ( l-- ) { - - // 97 is 'a' - args[ l ] = String.fromCharCode( 97 + l ); - } - return " " + args.join( ", " ) + " "; - }, - // object calls it internally, the key part of an item in a map - key: quote, - // function calls it internally, it's the content of the function - functionCode: "[code]", - // node calls it internally, it's an html attribute value - attribute: quote, - string: quote, - date: quote, - regexp: literal, - number: literal, - "boolean": literal - }, - // if true, entities are escaped ( <, >, \t, space and \n ) - HTML: false, - // indentation unit - indentChar: " ", - // if true, items in a collection, are separated by a \n, else just a space. - multiline: true - }; - - return dump; -}()); - -// back compat -QUnit.jsDump = QUnit.dump; - -// For browser, export only select globals -if ( defined.document ) { - - // Deprecated - // Extend assert methods to QUnit and Global scope through Backwards compatibility - (function() { - var i, - assertions = Assert.prototype; - - function applyCurrent( current ) { - return function() { - var assert = new Assert( QUnit.config.current ); - current.apply( assert, arguments ); - }; - } - - for ( i in assertions ) { - QUnit[ i ] = applyCurrent( assertions[ i ] ); - } - })(); - - (function() { - var i, l, - keys = [ - "test", - "module", - "expect", - "asyncTest", - "start", - "stop", - "ok", - "notOk", - "equal", - "notEqual", - "propEqual", - "notPropEqual", - "deepEqual", - "notDeepEqual", - "strictEqual", - "notStrictEqual", - "throws", - "raises" - ]; - - for ( i = 0, l = keys.length; i < l; i++ ) { - window[ keys[ i ] ] = QUnit[ keys[ i ] ]; - } - })(); - - window.QUnit = QUnit; -} - -// For nodejs -if ( typeof module !== "undefined" && module && module.exports ) { - module.exports = QUnit; - - // For consistency with CommonJS environments' exports - module.exports.QUnit = QUnit; -} - -// For CommonJS with exports, but without module.exports, like Rhino -if ( typeof exports !== "undefined" && exports ) { - exports.QUnit = QUnit; -} - -if ( typeof define === "function" && define.amd ) { - define( function() { - return QUnit; - } ); - QUnit.config.autostart = false; -} - -/* - * This file is a modified version of google-diff-match-patch's JavaScript implementation - * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), - * modifications are licensed as more fully set forth in LICENSE.txt. - * - * The original source of google-diff-match-patch is attributable and licensed as follows: - * - * Copyright 2006 Google Inc. - * http://code.google.com/p/google-diff-match-patch/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * More Info: - * https://code.google.com/p/google-diff-match-patch/ - * - * Usage: QUnit.diff(expected, actual) - * - */ -QUnit.diff = ( function() { - function DiffMatchPatch() { - } - - // DIFF FUNCTIONS - - /** - * The data structure representing a diff is an array of tuples: - * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] - * which means: delete 'Hello', add 'Goodbye' and keep ' world.' - */ - var DIFF_DELETE = -1, - DIFF_INSERT = 1, - DIFF_EQUAL = 0; - - /** - * Find the differences between two texts. Simplifies the problem by stripping - * any common prefix or suffix off the texts before diffing. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {boolean=} optChecklines Optional speedup flag. If present and false, - * then don't run a line-level diff first to identify the changed areas. - * Defaults to true, which does a faster, slightly less optimal diff. - * @return {!Array.} Array of diff tuples. - */ - DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) { - var deadline, checklines, commonlength, - commonprefix, commonsuffix, diffs; - - // The diff must be complete in up to 1 second. - deadline = ( new Date() ).getTime() + 1000; - - // Check for null inputs. - if ( text1 === null || text2 === null ) { - throw new Error( "Null input. (DiffMain)" ); - } - - // Check for equality (speedup). - if ( text1 === text2 ) { - if ( text1 ) { - return [ - [ DIFF_EQUAL, text1 ] - ]; - } - return []; - } - - if ( typeof optChecklines === "undefined" ) { - optChecklines = true; - } - - checklines = optChecklines; - - // Trim off common prefix (speedup). - commonlength = this.diffCommonPrefix( text1, text2 ); - commonprefix = text1.substring( 0, commonlength ); - text1 = text1.substring( commonlength ); - text2 = text2.substring( commonlength ); - - // Trim off common suffix (speedup). - commonlength = this.diffCommonSuffix( text1, text2 ); - commonsuffix = text1.substring( text1.length - commonlength ); - text1 = text1.substring( 0, text1.length - commonlength ); - text2 = text2.substring( 0, text2.length - commonlength ); - - // Compute the diff on the middle block. - diffs = this.diffCompute( text1, text2, checklines, deadline ); - - // Restore the prefix and suffix. - if ( commonprefix ) { - diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); - } - if ( commonsuffix ) { - diffs.push( [ DIFF_EQUAL, commonsuffix ] ); - } - this.diffCleanupMerge( diffs ); - return diffs; - }; - - /** - * Reduce the number of edits by eliminating operationally trivial equalities. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { - var changes, equalities, equalitiesLength, lastequality, - pointer, preIns, preDel, postIns, postDel; - changes = false; - equalities = []; // Stack of indices where equalities are found. - equalitiesLength = 0; // Keeping our own length var is faster in JS. - /** @type {?string} */ - lastequality = null; - // Always equal to diffs[equalities[equalitiesLength - 1]][1] - pointer = 0; // Index of current position. - // Is there an insertion operation before the last equality. - preIns = false; - // Is there a deletion operation before the last equality. - preDel = false; - // Is there an insertion operation after the last equality. - postIns = false; - // Is there a deletion operation after the last equality. - postDel = false; - while ( pointer < diffs.length ) { - - // Equality found. - if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { - if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) { - - // Candidate found. - equalities[ equalitiesLength++ ] = pointer; - preIns = postIns; - preDel = postDel; - lastequality = diffs[ pointer ][ 1 ]; - } else { - - // Not a candidate, and can never become one. - equalitiesLength = 0; - lastequality = null; - } - postIns = postDel = false; - - // An insertion or deletion. - } else { - - if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { - postDel = true; - } else { - postIns = true; - } - - /* - * Five types to be split: - * ABXYCD - * AXCD - * ABXC - * AXCD - * ABXC - */ - if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || - ( ( lastequality.length < 2 ) && - ( preIns + preDel + postIns + postDel ) === 3 ) ) ) { - - // Duplicate record. - diffs.splice( - equalities[ equalitiesLength - 1 ], - 0, - [ DIFF_DELETE, lastequality ] - ); - - // Change second copy to insert. - diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; - equalitiesLength--; // Throw away the equality we just deleted; - lastequality = null; - if ( preIns && preDel ) { - // No changes made which could affect previous entry, keep going. - postIns = postDel = true; - equalitiesLength = 0; - } else { - equalitiesLength--; // Throw away the previous equality. - pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; - postIns = postDel = false; - } - changes = true; - } - } - pointer++; - } - - if ( changes ) { - this.diffCleanupMerge( diffs ); - } - }; - - /** - * Convert a diff array into a pretty HTML report. - * @param {!Array.} diffs Array of diff tuples. - * @param {integer} string to be beautified. - * @return {string} HTML representation. - */ - DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { - var op, data, x, - html = []; - for ( x = 0; x < diffs.length; x++ ) { - op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal) - data = diffs[ x ][ 1 ]; // Text of change. - switch ( op ) { - case DIFF_INSERT: - html[ x ] = "" + data + ""; - break; - case DIFF_DELETE: - html[ x ] = "" + data + ""; - break; - case DIFF_EQUAL: - html[ x ] = "" + data + ""; - break; - } - } - return html.join( "" ); - }; - - /** - * Determine the common prefix of two strings. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the start of each - * string. - */ - DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { - var pointermid, pointermax, pointermin, pointerstart; - // Quick check for common null cases. - if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) { - return 0; - } - // Binary search. - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - pointermin = 0; - pointermax = Math.min( text1.length, text2.length ); - pointermid = pointermax; - pointerstart = 0; - while ( pointermin < pointermid ) { - if ( text1.substring( pointerstart, pointermid ) === - text2.substring( pointerstart, pointermid ) ) { - pointermin = pointermid; - pointerstart = pointermin; - } else { - pointermax = pointermid; - } - pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); - } - return pointermid; - }; - - /** - * Determine the common suffix of two strings. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the end of each string. - */ - DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { - var pointermid, pointermax, pointermin, pointerend; - // Quick check for common null cases. - if ( !text1 || - !text2 || - text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) { - return 0; - } - // Binary search. - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - pointermin = 0; - pointermax = Math.min( text1.length, text2.length ); - pointermid = pointermax; - pointerend = 0; - while ( pointermin < pointermid ) { - if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) === - text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { - pointermin = pointermid; - pointerend = pointermin; - } else { - pointermax = pointermid; - } - pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); - } - return pointermid; - }; - - /** - * Find the differences between two texts. Assumes that the texts do not - * have any common prefix or suffix. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {boolean} checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster, slightly less optimal diff. - * @param {number} deadline Time when the diff should be complete by. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { - var diffs, longtext, shorttext, i, hm, - text1A, text2A, text1B, text2B, - midCommon, diffsA, diffsB; - - if ( !text1 ) { - // Just add some text (speedup). - return [ - [ DIFF_INSERT, text2 ] - ]; - } - - if ( !text2 ) { - // Just delete some text (speedup). - return [ - [ DIFF_DELETE, text1 ] - ]; - } - - longtext = text1.length > text2.length ? text1 : text2; - shorttext = text1.length > text2.length ? text2 : text1; - i = longtext.indexOf( shorttext ); - if ( i !== -1 ) { - // Shorter text is inside the longer text (speedup). - diffs = [ - [ DIFF_INSERT, longtext.substring( 0, i ) ], - [ DIFF_EQUAL, shorttext ], - [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] - ]; - // Swap insertions for deletions if diff is reversed. - if ( text1.length > text2.length ) { - diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE; - } - return diffs; - } - - if ( shorttext.length === 1 ) { - // Single character string. - // After the previous speedup, the character can't be an equality. - return [ - [ DIFF_DELETE, text1 ], - [ DIFF_INSERT, text2 ] - ]; - } - - // Check to see if the problem can be split in two. - hm = this.diffHalfMatch( text1, text2 ); - if ( hm ) { - // A half-match was found, sort out the return data. - text1A = hm[ 0 ]; - text1B = hm[ 1 ]; - text2A = hm[ 2 ]; - text2B = hm[ 3 ]; - midCommon = hm[ 4 ]; - // Send both pairs off for separate processing. - diffsA = this.DiffMain( text1A, text2A, checklines, deadline ); - diffsB = this.DiffMain( text1B, text2B, checklines, deadline ); - // Merge the results. - return diffsA.concat( [ - [ DIFF_EQUAL, midCommon ] - ], diffsB ); - } - - if ( checklines && text1.length > 100 && text2.length > 100 ) { - return this.diffLineMode( text1, text2, deadline ); - } - - return this.diffBisect( text1, text2, deadline ); - }; - - /** - * Do the two texts share a substring which is at least half the length of the - * longer text? - * This speedup can produce non-minimal diffs. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {Array.} Five element Array, containing the prefix of - * text1, the suffix of text1, the prefix of text2, the suffix of - * text2 and the common middle. Or null if there was no match. - * @private - */ - DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) { - var longtext, shorttext, dmp, - text1A, text2B, text2A, text1B, midCommon, - hm1, hm2, hm; - - longtext = text1.length > text2.length ? text1 : text2; - shorttext = text1.length > text2.length ? text2 : text1; - if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) { - return null; // Pointless. - } - dmp = this; // 'this' becomes 'window' in a closure. - - /** - * Does a substring of shorttext exist within longtext such that the substring - * is at least half the length of longtext? - * Closure, but does not reference any external variables. - * @param {string} longtext Longer string. - * @param {string} shorttext Shorter string. - * @param {number} i Start index of quarter length substring within longtext. - * @return {Array.} Five element Array, containing the prefix of - * longtext, the suffix of longtext, the prefix of shorttext, the suffix - * of shorttext and the common middle. Or null if there was no match. - * @private - */ - function diffHalfMatchI( longtext, shorttext, i ) { - var seed, j, bestCommon, prefixLength, suffixLength, - bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; - // Start with a 1/4 length substring at position i as a seed. - seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) ); - j = -1; - bestCommon = ""; - while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) { - prefixLength = dmp.diffCommonPrefix( longtext.substring( i ), - shorttext.substring( j ) ); - suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ), - shorttext.substring( 0, j ) ); - if ( bestCommon.length < suffixLength + prefixLength ) { - bestCommon = shorttext.substring( j - suffixLength, j ) + - shorttext.substring( j, j + prefixLength ); - bestLongtextA = longtext.substring( 0, i - suffixLength ); - bestLongtextB = longtext.substring( i + prefixLength ); - bestShorttextA = shorttext.substring( 0, j - suffixLength ); - bestShorttextB = shorttext.substring( j + prefixLength ); - } - } - if ( bestCommon.length * 2 >= longtext.length ) { - return [ bestLongtextA, bestLongtextB, - bestShorttextA, bestShorttextB, bestCommon - ]; - } else { - return null; - } - } - - // First check if the second quarter is the seed for a half-match. - hm1 = diffHalfMatchI( longtext, shorttext, - Math.ceil( longtext.length / 4 ) ); - // Check again based on the third quarter. - hm2 = diffHalfMatchI( longtext, shorttext, - Math.ceil( longtext.length / 2 ) ); - if ( !hm1 && !hm2 ) { - return null; - } else if ( !hm2 ) { - hm = hm1; - } else if ( !hm1 ) { - hm = hm2; - } else { - // Both matched. Select the longest. - hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2; - } - - // A half-match was found, sort out the return data. - text1A, text1B, text2A, text2B; - if ( text1.length > text2.length ) { - text1A = hm[ 0 ]; - text1B = hm[ 1 ]; - text2A = hm[ 2 ]; - text2B = hm[ 3 ]; - } else { - text2A = hm[ 0 ]; - text2B = hm[ 1 ]; - text1A = hm[ 2 ]; - text1B = hm[ 3 ]; - } - midCommon = hm[ 4 ]; - return [ text1A, text1B, text2A, text2B, midCommon ]; - }; - - /** - * Do a quick line-level diff on both strings, then rediff the parts for - * greater accuracy. - * This speedup can produce non-minimal diffs. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} deadline Time when the diff should be complete by. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) { - var a, diffs, linearray, pointer, countInsert, - countDelete, textInsert, textDelete, j; - // Scan the text on a line-by-line basis first. - a = this.diffLinesToChars( text1, text2 ); - text1 = a.chars1; - text2 = a.chars2; - linearray = a.lineArray; - - diffs = this.DiffMain( text1, text2, false, deadline ); - - // Convert the diff back to original text. - this.diffCharsToLines( diffs, linearray ); - // Eliminate freak matches (e.g. blank lines) - this.diffCleanupSemantic( diffs ); - - // Rediff any replacement blocks, this time character-by-character. - // Add a dummy entry at the end. - diffs.push( [ DIFF_EQUAL, "" ] ); - pointer = 0; - countDelete = 0; - countInsert = 0; - textDelete = ""; - textInsert = ""; - while ( pointer < diffs.length ) { - switch ( diffs[ pointer ][ 0 ] ) { - case DIFF_INSERT: - countInsert++; - textInsert += diffs[ pointer ][ 1 ]; - break; - case DIFF_DELETE: - countDelete++; - textDelete += diffs[ pointer ][ 1 ]; - break; - case DIFF_EQUAL: - // Upon reaching an equality, check for prior redundancies. - if ( countDelete >= 1 && countInsert >= 1 ) { - // Delete the offending records and add the merged ones. - diffs.splice( pointer - countDelete - countInsert, - countDelete + countInsert ); - pointer = pointer - countDelete - countInsert; - a = this.DiffMain( textDelete, textInsert, false, deadline ); - for ( j = a.length - 1; j >= 0; j-- ) { - diffs.splice( pointer, 0, a[ j ] ); - } - pointer = pointer + a.length; - } - countInsert = 0; - countDelete = 0; - textDelete = ""; - textInsert = ""; - break; - } - pointer++; - } - diffs.pop(); // Remove the dummy entry at the end. - - return diffs; - }; - - /** - * Find the 'middle snake' of a diff, split the problem in two - * and return the recursively constructed diff. - * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} deadline Time at which to bail if not yet complete. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) { - var text1Length, text2Length, maxD, vOffset, vLength, - v1, v2, x, delta, front, k1start, k1end, k2start, - k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; - // Cache the text lengths to prevent multiple calls. - text1Length = text1.length; - text2Length = text2.length; - maxD = Math.ceil( ( text1Length + text2Length ) / 2 ); - vOffset = maxD; - vLength = 2 * maxD; - v1 = new Array( vLength ); - v2 = new Array( vLength ); - // Setting all elements to -1 is faster in Chrome & Firefox than mixing - // integers and undefined. - for ( x = 0; x < vLength; x++ ) { - v1[ x ] = -1; - v2[ x ] = -1; - } - v1[ vOffset + 1 ] = 0; - v2[ vOffset + 1 ] = 0; - delta = text1Length - text2Length; - // If the total number of characters is odd, then the front path will collide - // with the reverse path. - front = ( delta % 2 !== 0 ); - // Offsets for start and end of k loop. - // Prevents mapping of space beyond the grid. - k1start = 0; - k1end = 0; - k2start = 0; - k2end = 0; - for ( d = 0; d < maxD; d++ ) { - // Bail out if deadline is reached. - if ( ( new Date() ).getTime() > deadline ) { - break; - } - - // Walk the front path one step. - for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) { - k1Offset = vOffset + k1; - if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { - x1 = v1[ k1Offset + 1 ]; - } else { - x1 = v1[ k1Offset - 1 ] + 1; - } - y1 = x1 - k1; - while ( x1 < text1Length && y1 < text2Length && - text1.charAt( x1 ) === text2.charAt( y1 ) ) { - x1++; - y1++; - } - v1[ k1Offset ] = x1; - if ( x1 > text1Length ) { - // Ran off the right of the graph. - k1end += 2; - } else if ( y1 > text2Length ) { - // Ran off the bottom of the graph. - k1start += 2; - } else if ( front ) { - k2Offset = vOffset + delta - k1; - if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) { - // Mirror x2 onto top-left coordinate system. - x2 = text1Length - v2[ k2Offset ]; - if ( x1 >= x2 ) { - // Overlap detected. - return this.diffBisectSplit( text1, text2, x1, y1, deadline ); - } - } - } - } - - // Walk the reverse path one step. - for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) { - k2Offset = vOffset + k2; - if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { - x2 = v2[ k2Offset + 1 ]; - } else { - x2 = v2[ k2Offset - 1 ] + 1; - } - y2 = x2 - k2; - while ( x2 < text1Length && y2 < text2Length && - text1.charAt( text1Length - x2 - 1 ) === - text2.charAt( text2Length - y2 - 1 ) ) { - x2++; - y2++; - } - v2[ k2Offset ] = x2; - if ( x2 > text1Length ) { - // Ran off the left of the graph. - k2end += 2; - } else if ( y2 > text2Length ) { - // Ran off the top of the graph. - k2start += 2; - } else if ( !front ) { - k1Offset = vOffset + delta - k2; - if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) { - x1 = v1[ k1Offset ]; - y1 = vOffset + x1 - k1Offset; - // Mirror x2 onto top-left coordinate system. - x2 = text1Length - x2; - if ( x1 >= x2 ) { - // Overlap detected. - return this.diffBisectSplit( text1, text2, x1, y1, deadline ); - } - } - } - } - } - // Diff took too long and hit the deadline or - // number of diffs equals number of characters, no commonality at all. - return [ - [ DIFF_DELETE, text1 ], - [ DIFF_INSERT, text2 ] - ]; - }; - - /** - * Given the location of the 'middle snake', split the diff in two parts - * and recurse. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} x Index of split point in text1. - * @param {number} y Index of split point in text2. - * @param {number} deadline Time at which to bail if not yet complete. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { - var text1a, text1b, text2a, text2b, diffs, diffsb; - text1a = text1.substring( 0, x ); - text2a = text2.substring( 0, y ); - text1b = text1.substring( x ); - text2b = text2.substring( y ); - - // Compute both diffs serially. - diffs = this.DiffMain( text1a, text2a, false, deadline ); - diffsb = this.DiffMain( text1b, text2b, false, deadline ); - - return diffs.concat( diffsb ); - }; - - /** - * Reduce the number of edits by eliminating semantically trivial equalities. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) { - var changes, equalities, equalitiesLength, lastequality, - pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, - lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; - changes = false; - equalities = []; // Stack of indices where equalities are found. - equalitiesLength = 0; // Keeping our own length var is faster in JS. - /** @type {?string} */ - lastequality = null; - // Always equal to diffs[equalities[equalitiesLength - 1]][1] - pointer = 0; // Index of current position. - // Number of characters that changed prior to the equality. - lengthInsertions1 = 0; - lengthDeletions1 = 0; - // Number of characters that changed after the equality. - lengthInsertions2 = 0; - lengthDeletions2 = 0; - while ( pointer < diffs.length ) { - if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. - equalities[ equalitiesLength++ ] = pointer; - lengthInsertions1 = lengthInsertions2; - lengthDeletions1 = lengthDeletions2; - lengthInsertions2 = 0; - lengthDeletions2 = 0; - lastequality = diffs[ pointer ][ 1 ]; - } else { // An insertion or deletion. - if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) { - lengthInsertions2 += diffs[ pointer ][ 1 ].length; - } else { - lengthDeletions2 += diffs[ pointer ][ 1 ].length; - } - // Eliminate an equality that is smaller or equal to the edits on both - // sides of it. - if ( lastequality && ( lastequality.length <= - Math.max( lengthInsertions1, lengthDeletions1 ) ) && - ( lastequality.length <= Math.max( lengthInsertions2, - lengthDeletions2 ) ) ) { - - // Duplicate record. - diffs.splice( - equalities[ equalitiesLength - 1 ], - 0, - [ DIFF_DELETE, lastequality ] - ); - - // Change second copy to insert. - diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; - - // Throw away the equality we just deleted. - equalitiesLength--; - - // Throw away the previous equality (it needs to be reevaluated). - equalitiesLength--; - pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; - - // Reset the counters. - lengthInsertions1 = 0; - lengthDeletions1 = 0; - lengthInsertions2 = 0; - lengthDeletions2 = 0; - lastequality = null; - changes = true; - } - } - pointer++; - } - - // Normalize the diff. - if ( changes ) { - this.diffCleanupMerge( diffs ); - } - - // Find any overlaps between deletions and insertions. - // e.g: abcxxxxxxdef - // -> abcxxxdef - // e.g: xxxabcdefxxx - // -> defxxxabc - // Only extract an overlap if it is as big as the edit ahead or behind it. - pointer = 1; - while ( pointer < diffs.length ) { - if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE && - diffs[ pointer ][ 0 ] === DIFF_INSERT ) { - deletion = diffs[ pointer - 1 ][ 1 ]; - insertion = diffs[ pointer ][ 1 ]; - overlapLength1 = this.diffCommonOverlap( deletion, insertion ); - overlapLength2 = this.diffCommonOverlap( insertion, deletion ); - if ( overlapLength1 >= overlapLength2 ) { - if ( overlapLength1 >= deletion.length / 2 || - overlapLength1 >= insertion.length / 2 ) { - // Overlap found. Insert an equality and trim the surrounding edits. - diffs.splice( - pointer, - 0, - [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] - ); - diffs[ pointer - 1 ][ 1 ] = - deletion.substring( 0, deletion.length - overlapLength1 ); - diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 ); - pointer++; - } - } else { - if ( overlapLength2 >= deletion.length / 2 || - overlapLength2 >= insertion.length / 2 ) { - - // Reverse overlap found. - // Insert an equality and swap and trim the surrounding edits. - diffs.splice( - pointer, - 0, - [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] - ); - - diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT; - diffs[ pointer - 1 ][ 1 ] = - insertion.substring( 0, insertion.length - overlapLength2 ); - diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE; - diffs[ pointer + 1 ][ 1 ] = - deletion.substring( overlapLength2 ); - pointer++; - } - } - pointer++; - } - pointer++; - } - }; - - /** - * Determine if the suffix of one string is the prefix of another. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the end of the first - * string and the start of the second string. - * @private - */ - DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) { - var text1Length, text2Length, textLength, - best, length, pattern, found; - // Cache the text lengths to prevent multiple calls. - text1Length = text1.length; - text2Length = text2.length; - // Eliminate the null case. - if ( text1Length === 0 || text2Length === 0 ) { - return 0; - } - // Truncate the longer string. - if ( text1Length > text2Length ) { - text1 = text1.substring( text1Length - text2Length ); - } else if ( text1Length < text2Length ) { - text2 = text2.substring( 0, text1Length ); - } - textLength = Math.min( text1Length, text2Length ); - // Quick check for the worst case. - if ( text1 === text2 ) { - return textLength; - } - - // Start by looking for a single character match - // and increase length until no match is found. - // Performance analysis: http://neil.fraser.name/news/2010/11/04/ - best = 0; - length = 1; - while ( true ) { - pattern = text1.substring( textLength - length ); - found = text2.indexOf( pattern ); - if ( found === -1 ) { - return best; - } - length += found; - if ( found === 0 || text1.substring( textLength - length ) === - text2.substring( 0, length ) ) { - best = length; - length++; - } - } - }; - - /** - * Split two texts into an array of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {{chars1: string, chars2: string, lineArray: !Array.}} - * An object containing the encoded text1, the encoded text2 and - * the array of unique strings. - * The zeroth element of the array of unique strings is intentionally blank. - * @private - */ - DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) { - var lineArray, lineHash, chars1, chars2; - lineArray = []; // e.g. lineArray[4] === 'Hello\n' - lineHash = {}; // e.g. lineHash['Hello\n'] === 4 - - // '\x00' is a valid character, but various debuggers don't like it. - // So we'll insert a junk entry to avoid generating a null character. - lineArray[ 0 ] = ""; - - /** - * Split a text into an array of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * Modifies linearray and linehash through being a closure. - * @param {string} text String to encode. - * @return {string} Encoded string. - * @private - */ - function diffLinesToCharsMunge( text ) { - var chars, lineStart, lineEnd, lineArrayLength, line; - chars = ""; - // Walk the text, pulling out a substring for each line. - // text.split('\n') would would temporarily double our memory footprint. - // Modifying text would create many large strings to garbage collect. - lineStart = 0; - lineEnd = -1; - // Keeping our own length variable is faster than looking it up. - lineArrayLength = lineArray.length; - while ( lineEnd < text.length - 1 ) { - lineEnd = text.indexOf( "\n", lineStart ); - if ( lineEnd === -1 ) { - lineEnd = text.length - 1; - } - line = text.substring( lineStart, lineEnd + 1 ); - lineStart = lineEnd + 1; - - if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) : - ( lineHash[ line ] !== undefined ) ) { - chars += String.fromCharCode( lineHash[ line ] ); - } else { - chars += String.fromCharCode( lineArrayLength ); - lineHash[ line ] = lineArrayLength; - lineArray[ lineArrayLength++ ] = line; - } - } - return chars; - } - - chars1 = diffLinesToCharsMunge( text1 ); - chars2 = diffLinesToCharsMunge( text2 ); - return { - chars1: chars1, - chars2: chars2, - lineArray: lineArray - }; - }; - - /** - * Rehydrate the text in a diff from a string of line hashes to real lines of - * text. - * @param {!Array.} diffs Array of diff tuples. - * @param {!Array.} lineArray Array of unique strings. - * @private - */ - DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { - var x, chars, text, y; - for ( x = 0; x < diffs.length; x++ ) { - chars = diffs[ x ][ 1 ]; - text = []; - for ( y = 0; y < chars.length; y++ ) { - text[ y ] = lineArray[ chars.charCodeAt( y ) ]; - } - diffs[ x ][ 1 ] = text.join( "" ); - } - }; - - /** - * Reorder and merge like edit sections. Merge equalities. - * Any edit section can move as long as it doesn't cross an equality. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) { - var pointer, countDelete, countInsert, textInsert, textDelete, - commonlength, changes, diffPointer, position; - diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. - pointer = 0; - countDelete = 0; - countInsert = 0; - textDelete = ""; - textInsert = ""; - commonlength; - while ( pointer < diffs.length ) { - switch ( diffs[ pointer ][ 0 ] ) { - case DIFF_INSERT: - countInsert++; - textInsert += diffs[ pointer ][ 1 ]; - pointer++; - break; - case DIFF_DELETE: - countDelete++; - textDelete += diffs[ pointer ][ 1 ]; - pointer++; - break; - case DIFF_EQUAL: - // Upon reaching an equality, check for prior redundancies. - if ( countDelete + countInsert > 1 ) { - if ( countDelete !== 0 && countInsert !== 0 ) { - // Factor out any common prefixies. - commonlength = this.diffCommonPrefix( textInsert, textDelete ); - if ( commonlength !== 0 ) { - if ( ( pointer - countDelete - countInsert ) > 0 && - diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] === - DIFF_EQUAL ) { - diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] += - textInsert.substring( 0, commonlength ); - } else { - diffs.splice( 0, 0, [ DIFF_EQUAL, - textInsert.substring( 0, commonlength ) - ] ); - pointer++; - } - textInsert = textInsert.substring( commonlength ); - textDelete = textDelete.substring( commonlength ); - } - // Factor out any common suffixies. - commonlength = this.diffCommonSuffix( textInsert, textDelete ); - if ( commonlength !== 0 ) { - diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length - - commonlength ) + diffs[ pointer ][ 1 ]; - textInsert = textInsert.substring( 0, textInsert.length - - commonlength ); - textDelete = textDelete.substring( 0, textDelete.length - - commonlength ); - } - } - // Delete the offending records and add the merged ones. - if ( countDelete === 0 ) { - diffs.splice( pointer - countInsert, - countDelete + countInsert, [ DIFF_INSERT, textInsert ] ); - } else if ( countInsert === 0 ) { - diffs.splice( pointer - countDelete, - countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); - } else { - diffs.splice( - pointer - countDelete - countInsert, - countDelete + countInsert, - [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] - ); - } - pointer = pointer - countDelete - countInsert + - ( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1; - } else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) { - - // Merge this equality with the previous one. - diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ]; - diffs.splice( pointer, 1 ); - } else { - pointer++; - } - countInsert = 0; - countDelete = 0; - textDelete = ""; - textInsert = ""; - break; - } - } - if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) { - diffs.pop(); // Remove the dummy entry at the end. - } - - // Second pass: look for single edits surrounded on both sides by equalities - // which can be shifted sideways to eliminate an equality. - // e.g: ABAC -> ABAC - changes = false; - pointer = 1; - - // Intentionally ignore the first and last element (don't need checking). - while ( pointer < diffs.length - 1 ) { - if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL && - diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) { - - diffPointer = diffs[ pointer ][ 1 ]; - position = diffPointer.substring( - diffPointer.length - diffs[ pointer - 1 ][ 1 ].length - ); - - // This is a single edit surrounded by equalities. - if ( position === diffs[ pointer - 1 ][ 1 ] ) { - - // Shift the edit over the previous equality. - diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] + - diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length - - diffs[ pointer - 1 ][ 1 ].length ); - diffs[ pointer + 1 ][ 1 ] = - diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ]; - diffs.splice( pointer - 1, 1 ); - changes = true; - } else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) === - diffs[ pointer + 1 ][ 1 ] ) { - - // Shift the edit over the next equality. - diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ]; - diffs[ pointer ][ 1 ] = - diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) + - diffs[ pointer + 1 ][ 1 ]; - diffs.splice( pointer + 1, 1 ); - changes = true; - } - } - pointer++; - } - // If shifts were made, the diff needs reordering and another shift sweep. - if ( changes ) { - this.diffCleanupMerge( diffs ); - } - }; - - return function( o, n ) { - var diff, output, text; - diff = new DiffMatchPatch(); - output = diff.DiffMain( o, n ); - diff.diffCleanupEfficiency( output ); - text = diff.diffPrettyHtml( output ); - - return text; - }; -}() ); - -// Get a reference to the global object, like window in browsers -}( (function() { - return this; -})() )); - -(function() { - -// Don't load the HTML Reporter on non-Browser environments -if ( typeof window === "undefined" || !window.document ) { - return; -} - -// Deprecated QUnit.init - Ref #530 -// Re-initialize the configuration options -QUnit.init = function() { - var tests, banner, result, qunit, - config = QUnit.config; - - config.stats = { all: 0, bad: 0 }; - config.moduleStats = { all: 0, bad: 0 }; - config.started = 0; - config.updateRate = 1000; - config.blocking = false; - config.autostart = true; - config.autorun = false; - config.filter = ""; - config.queue = []; - - // Return on non-browser environments - // This is necessary to not break on node tests - if ( typeof window === "undefined" ) { - return; - } - - qunit = id( "qunit" ); - if ( qunit ) { - qunit.innerHTML = - "

" + escapeText( document.title ) + "

" + - "

" + - "
" + - "

" + - "
    "; - } - - tests = id( "qunit-tests" ); - banner = id( "qunit-banner" ); - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = "Running...
     "; - } -}; - -var config = QUnit.config, - collapseNext = false, - hasOwn = Object.prototype.hasOwnProperty, - defined = { - document: window.document !== undefined, - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch ( e ) { - return false; - } - }()) - }, - modulesList = []; - -/** -* Escape text for attribute or text content. -*/ -function escapeText( s ) { - if ( !s ) { - return ""; - } - s = s + ""; - - // Both single quotes and double quotes (for attributes) - return s.replace( /['"<>&]/g, function( s ) { - switch ( s ) { - case "'": - return "'"; - case "\"": - return """; - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; - } - }); -} - -/** - * @param {HTMLElement} elem - * @param {string} type - * @param {Function} fn - */ -function addEvent( elem, type, fn ) { - if ( elem.addEventListener ) { - - // Standards-based browsers - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - - // support: IE <9 - elem.attachEvent( "on" + type, function() { - var event = window.event; - if ( !event.target ) { - event.target = event.srcElement || document; - } - - fn.call( elem, event ); - }); - } -} - -/** - * @param {Array|NodeList} elems - * @param {string} type - * @param {Function} fn - */ -function addEvents( elems, type, fn ) { - var i = elems.length; - while ( i-- ) { - addEvent( elems[ i ], type, fn ); - } -} - -function hasClass( elem, name ) { - return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0; -} - -function addClass( elem, name ) { - if ( !hasClass( elem, name ) ) { - elem.className += ( elem.className ? " " : "" ) + name; - } -} - -function toggleClass( elem, name ) { - if ( hasClass( elem, name ) ) { - removeClass( elem, name ); - } else { - addClass( elem, name ); - } -} - -function removeClass( elem, name ) { - var set = " " + elem.className + " "; - - // Class name may appear multiple times - while ( set.indexOf( " " + name + " " ) >= 0 ) { - set = set.replace( " " + name + " ", " " ); - } - - // trim for prettiness - elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); -} - -function id( name ) { - return defined.document && document.getElementById && document.getElementById( name ); -} - -function getUrlConfigHtml() { - var i, j, val, - escaped, escapedTooltip, - selection = false, - len = config.urlConfig.length, - urlConfigHtml = ""; - - for ( i = 0; i < len; i++ ) { - val = config.urlConfig[ i ]; - if ( typeof val === "string" ) { - val = { - id: val, - label: val - }; - } - - escaped = escapeText( val.id ); - escapedTooltip = escapeText( val.tooltip ); - - if ( config[ val.id ] === undefined ) { - config[ val.id ] = QUnit.urlParams[ val.id ]; - } - - if ( !val.value || typeof val.value === "string" ) { - urlConfigHtml += ""; - } else { - urlConfigHtml += ""; - } - } - - return urlConfigHtml; -} - -// Handle "click" events on toolbar checkboxes and "change" for select menus. -// Updates the URL with the new state of `config.urlConfig` values. -function toolbarChanged() { - var updatedUrl, value, - field = this, - params = {}; - - // Detect if field is a select menu or a checkbox - if ( "selectedIndex" in field ) { - value = field.options[ field.selectedIndex ].value || undefined; - } else { - value = field.checked ? ( field.defaultValue || true ) : undefined; - } - - params[ field.name ] = value; - updatedUrl = setUrl( params ); - - if ( "hidepassed" === field.name && "replaceState" in window.history ) { - config[ field.name ] = value || false; - if ( value ) { - addClass( id( "qunit-tests" ), "hidepass" ); - } else { - removeClass( id( "qunit-tests" ), "hidepass" ); - } - - // It is not necessary to refresh the whole page - window.history.replaceState( null, "", updatedUrl ); - } else { - window.location = updatedUrl; - } -} - -function setUrl( params ) { - var key, - querystring = "?"; - - params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); - - for ( key in params ) { - if ( hasOwn.call( params, key ) ) { - if ( params[ key ] === undefined ) { - continue; - } - querystring += encodeURIComponent( key ); - if ( params[ key ] !== true ) { - querystring += "=" + encodeURIComponent( params[ key ] ); - } - querystring += "&"; - } - } - return location.protocol + "//" + location.host + - location.pathname + querystring.slice( 0, -1 ); -} - -function applyUrlParams() { - var selectedModule, - modulesList = id( "qunit-modulefilter" ), - filter = id( "qunit-filter-input" ).value; - - selectedModule = modulesList ? - decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) : - undefined; - - window.location = setUrl({ - module: ( selectedModule === "" ) ? undefined : selectedModule, - filter: ( filter === "" ) ? undefined : filter, - - // Remove testId filter - testId: undefined - }); -} - -function toolbarUrlConfigContainer() { - var urlConfigContainer = document.createElement( "span" ); - - urlConfigContainer.innerHTML = getUrlConfigHtml(); - addClass( urlConfigContainer, "qunit-url-config" ); - - // For oldIE support: - // * Add handlers to the individual elements instead of the container - // * Use "click" instead of "change" for checkboxes - addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged ); - addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged ); - - return urlConfigContainer; -} - -function toolbarLooseFilter() { - var filter = document.createElement( "form" ), - label = document.createElement( "label" ), - input = document.createElement( "input" ), - button = document.createElement( "button" ); - - addClass( filter, "qunit-filter" ); - - label.innerHTML = "Filter: "; - - input.type = "text"; - input.value = config.filter || ""; - input.name = "filter"; - input.id = "qunit-filter-input"; - - button.innerHTML = "Go"; - - label.appendChild( input ); - - filter.appendChild( label ); - filter.appendChild( button ); - addEvent( filter, "submit", function( ev ) { - applyUrlParams(); - - if ( ev && ev.preventDefault ) { - ev.preventDefault(); - } - - return false; - }); - - return filter; -} - -function toolbarModuleFilterHtml() { - var i, - moduleFilterHtml = ""; - - if ( !modulesList.length ) { - return false; - } - - modulesList.sort(function( a, b ) { - return a.localeCompare( b ); - }); - - moduleFilterHtml += "" + - ""; - - return moduleFilterHtml; -} - -function toolbarModuleFilter() { - var toolbar = id( "qunit-testrunner-toolbar" ), - moduleFilter = document.createElement( "span" ), - moduleFilterHtml = toolbarModuleFilterHtml(); - - if ( !toolbar || !moduleFilterHtml ) { - return false; - } - - moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); - moduleFilter.innerHTML = moduleFilterHtml; - - addEvent( moduleFilter.lastChild, "change", applyUrlParams ); - - toolbar.appendChild( moduleFilter ); -} - -function appendToolbar() { - var toolbar = id( "qunit-testrunner-toolbar" ); - - if ( toolbar ) { - toolbar.appendChild( toolbarUrlConfigContainer() ); - toolbar.appendChild( toolbarLooseFilter() ); - } -} - -function appendHeader() { - var header = id( "qunit-header" ); - - if ( header ) { - header.innerHTML = "" + header.innerHTML + " "; - } -} - -function appendBanner() { - var banner = id( "qunit-banner" ); - - if ( banner ) { - banner.className = ""; - } -} - -function appendTestResults() { - var tests = id( "qunit-tests" ), - result = id( "qunit-testresult" ); - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - tests.innerHTML = ""; - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = "Running...
     "; - } -} - -function storeFixture() { - var fixture = id( "qunit-fixture" ); - if ( fixture ) { - config.fixture = fixture.innerHTML; - } -} - -function appendFilteredTest() { - var testId = QUnit.config.testId; - if ( !testId || testId.length <= 0 ) { - return ""; - } - return "
    Rerunning selected tests: " + testId.join(", ") + - " " + "Run all tests" + "
    "; -} - -function appendUserAgent() { - var userAgent = id( "qunit-userAgent" ); - - if ( userAgent ) { - userAgent.innerHTML = ""; - userAgent.appendChild( - document.createTextNode( - "QUnit " + QUnit.version + "; " + navigator.userAgent - ) - ); - } -} - -function appendTestsList( modules ) { - var i, l, x, z, test, moduleObj; - - for ( i = 0, l = modules.length; i < l; i++ ) { - moduleObj = modules[ i ]; - - if ( moduleObj.name ) { - modulesList.push( moduleObj.name ); - } - - for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { - test = moduleObj.tests[ x ]; - - appendTest( test.name, test.testId, moduleObj.name ); - } - } -} - -function appendTest( name, testId, moduleName ) { - var title, rerunTrigger, testBlock, assertList, - tests = id( "qunit-tests" ); - - if ( !tests ) { - return; - } - - title = document.createElement( "strong" ); - title.innerHTML = getNameHtml( name, moduleName ); - - rerunTrigger = document.createElement( "a" ); - rerunTrigger.innerHTML = "Rerun"; - rerunTrigger.href = setUrl({ testId: testId }); - - testBlock = document.createElement( "li" ); - testBlock.appendChild( title ); - testBlock.appendChild( rerunTrigger ); - testBlock.id = "qunit-test-output-" + testId; - - assertList = document.createElement( "ol" ); - assertList.className = "qunit-assert-list"; - - testBlock.appendChild( assertList ); - - tests.appendChild( testBlock ); -} - -// HTML Reporter initialization and load -QUnit.begin(function( details ) { - var qunit = id( "qunit" ); - - // Fixture is the only one necessary to run without the #qunit element - storeFixture(); - - if ( qunit ) { - qunit.innerHTML = - "

    " + escapeText( document.title ) + "

    " + - "

    " + - "
    " + - appendFilteredTest() + - "

    " + - "
      "; - } - - appendHeader(); - appendBanner(); - appendTestResults(); - appendUserAgent(); - appendToolbar(); - appendTestsList( details.modules ); - toolbarModuleFilter(); - - if ( qunit && config.hidepassed ) { - addClass( qunit.lastChild, "hidepass" ); - } -}); - -QUnit.done(function( details ) { - var i, key, - banner = id( "qunit-banner" ), - tests = id( "qunit-tests" ), - html = [ - "Tests completed in ", - details.runtime, - " milliseconds.
      ", - "", - details.passed, - " assertions of ", - details.total, - " passed, ", - details.failed, - " failed." - ].join( "" ); - - if ( banner ) { - banner.className = details.failed ? "qunit-fail" : "qunit-pass"; - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && defined.document && document.title ) { - - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - ( details.failed ? "\u2716" : "\u2714" ), - document.title.replace( /^[\u2714\u2716] /i, "" ) - ].join( " " ); - } - - // clear own sessionStorage items if all tests passed - if ( config.reorder && defined.sessionStorage && details.failed === 0 ) { - for ( i = 0; i < sessionStorage.length; i++ ) { - key = sessionStorage.key( i++ ); - if ( key.indexOf( "qunit-test-" ) === 0 ) { - sessionStorage.removeItem( key ); - } - } - } - - // scroll back to top to show results - if ( config.scrolltop && window.scrollTo ) { - window.scrollTo( 0, 0 ); - } -}); - -function getNameHtml( name, module ) { - var nameHtml = ""; - - if ( module ) { - nameHtml = "" + escapeText( module ) + ": "; - } - - nameHtml += "" + escapeText( name ) + ""; - - return nameHtml; -} - -QUnit.testStart(function( details ) { - var running, testBlock, bad; - - testBlock = id( "qunit-test-output-" + details.testId ); - if ( testBlock ) { - testBlock.className = "running"; - } else { - - // Report later registered tests - appendTest( details.name, details.testId, details.module ); - } - - running = id( "qunit-testresult" ); - if ( running ) { - bad = QUnit.config.reorder && defined.sessionStorage && - +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); - - running.innerHTML = ( bad ? - "Rerunning previously failed test:
      " : - "Running:
      " ) + - getNameHtml( details.name, details.module ); - } - -}); - -function stripHtml( string ) { - // strip tags, html entity and whitespaces - return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\"/g, "").replace(/\s+/g, ""); -} - -QUnit.log(function( details ) { - var assertList, assertLi, - message, expected, actual, diff, - showDiff = false, - testItem = id( "qunit-test-output-" + details.testId ); - - if ( !testItem ) { - return; - } - - message = escapeText( details.message ) || ( details.result ? "okay" : "failed" ); - message = "" + message + ""; - message += "@ " + details.runtime + " ms"; - - // pushFailure doesn't provide details.expected - // when it calls, it's implicit to also not show expected and diff stuff - // Also, we need to check details.expected existence, as it can exist and be undefined - if ( !details.result && hasOwn.call( details, "expected" ) ) { - if ( details.negative ) { - expected = escapeText( "NOT " + QUnit.dump.parse( details.expected ) ); - } else { - expected = escapeText( QUnit.dump.parse( details.expected ) ); - } - - actual = escapeText( QUnit.dump.parse( details.actual ) ); - message += ""; - - if ( actual !== expected ) { - - message += ""; - - // Don't show diff if actual or expected are booleans - if ( !( /^(true|false)$/.test( actual ) ) && - !( /^(true|false)$/.test( expected ) ) ) { - diff = QUnit.diff( expected, actual ); - showDiff = stripHtml( diff ).length !== - stripHtml( expected ).length + - stripHtml( actual ).length; - } - - // Don't show diff if expected and actual are totally different - if ( showDiff ) { - message += ""; - } - } else if ( expected.indexOf( "[object Array]" ) !== -1 || - expected.indexOf( "[object Object]" ) !== -1 ) { - message += ""; - } - - if ( details.source ) { - message += ""; - } - - message += "
      Expected:
      " +
      -			expected +
      -			"
      Result:
      " +
      -				actual + "
      Diff:
      " +
      -					diff + "
      Message: " + - "Diff suppressed as the depth of object is more than current max depth (" + - QUnit.config.maxDepth + ").

      Hint: Use QUnit.dump.maxDepth to " + - " run with a higher max depth or " + - "Rerun without max depth.

      Source:
      " +
      -				escapeText( details.source ) + "
      "; - - // this occours when pushFailure is set and we have an extracted stack trace - } else if ( !details.result && details.source ) { - message += "" + - "" + - "
      Source:
      " +
      -			escapeText( details.source ) + "
      "; - } - - assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; - - assertLi = document.createElement( "li" ); - assertLi.className = details.result ? "pass" : "fail"; - assertLi.innerHTML = message; - assertList.appendChild( assertLi ); -}); - -QUnit.testDone(function( details ) { - var testTitle, time, testItem, assertList, - good, bad, testCounts, skipped, sourceName, - tests = id( "qunit-tests" ); - - if ( !tests ) { - return; - } - - testItem = id( "qunit-test-output-" + details.testId ); - - assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; - - good = details.passed; - bad = details.failed; - - // store result when possible - if ( config.reorder && defined.sessionStorage ) { - if ( bad ) { - sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad ); - } else { - sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name ); - } - } - - if ( bad === 0 ) { - - // Collapse the passing tests - addClass( assertList, "qunit-collapsed" ); - } else if ( bad && config.collapse && !collapseNext ) { - - // Skip collapsing the first failing test - collapseNext = true; - } else { - - // Collapse remaining tests - addClass( assertList, "qunit-collapsed" ); - } - - // testItem.firstChild is the test name - testTitle = testItem.firstChild; - - testCounts = bad ? - "" + bad + ", " + "" + good + ", " : - ""; - - testTitle.innerHTML += " (" + testCounts + - details.assertions.length + ")"; - - if ( details.skipped ) { - testItem.className = "skipped"; - skipped = document.createElement( "em" ); - skipped.className = "qunit-skipped-label"; - skipped.innerHTML = "skipped"; - testItem.insertBefore( skipped, testTitle ); - } else { - addEvent( testTitle, "click", function() { - toggleClass( assertList, "qunit-collapsed" ); - }); - - testItem.className = bad ? "fail" : "pass"; - - time = document.createElement( "span" ); - time.className = "runtime"; - time.innerHTML = details.runtime + " ms"; - testItem.insertBefore( time, assertList ); - } - - // Show the source of the test when showing assertions - if ( details.source ) { - sourceName = document.createElement( "p" ); - sourceName.innerHTML = "Source: " + details.source; - addClass( sourceName, "qunit-source" ); - if ( bad === 0 ) { - addClass( sourceName, "qunit-collapsed" ); - } - addEvent( testTitle, "click", function() { - toggleClass( sourceName, "qunit-collapsed" ); - }); - testItem.appendChild( sourceName ); - } -}); - -if ( defined.document ) { - - // Avoid readyState issue with phantomjs - // Ref: #818 - var notPhantom = ( function( p ) { - return !( p && p.version && p.version.major > 0 ); - } )( window.phantom ); - - if ( notPhantom && document.readyState === "complete" ) { - QUnit.load(); - } else { - addEvent( window, "load", QUnit.load ); - } -} else { - config.pageLoaded = true; - config.autorun = true; -} - -})(); diff --git a/test/syn/syn.js b/test/syn/syn.js deleted file mode 100644 index 188e04a..0000000 --- a/test/syn/syn.js +++ /dev/null @@ -1,2422 +0,0 @@ -/** - * syn - 0.0.3 - * - * @copyright 2015 Bitovi - * Fri, 06 Mar 2015 01:47:33 GMT - * @license MIT - */ - -/*[global-shim]*/ -(function (exports, global){ - var origDefine = global.define; - - var get = function(name){ - var parts = name.split("."), - cur = global, - i; - for(i = 0 ; i < parts.length; i++){ - if(!cur) { - break; - } - cur = cur[parts[i]]; - } - return cur; - }; - var modules = global.define && global.define.modules || {}; - var ourDefine = global.define = function(moduleName, deps, callback){ - var module; - if(typeof deps === "function") { - callback = deps; - deps = []; - } - var args = [], - i; - for(i =0; i < deps.length; i++) { - args.push( exports[deps[i]] ? get(exports[deps[i]]) : ( modules[deps[i]] || get(deps[i]) ) ); - } - // CJS has no dependencies but 3 callback arguments - if(!deps.length && callback.length) { - module = { exports: {} }; - var require = function(name) { - return exports[name] ? get(exports[name]) : modules[name]; - }; - args.push(require, module.exports, module); - } - - global.define = origDefine; - var result = callback ? callback.apply(null, args) : undefined; - global.define = ourDefine; - - // Favor CJS module.exports over the return value - modules[moduleName] = module && module.exports ? module.exports : result; - }; - global.define.modules = modules; - global.define.amd = true; - global.System = { - define: function(__name, __code){ - global.define = origDefine; - eval("(function() { " + __code + " \n }).call(global);"); - global.define = ourDefine; - } - }; -})({},window) -/*syn@0.1.1#synthetic*/ -define('syn/synthetic', function(require, exports, module) { -var opts = window.syn ? window.syn : {}; -var extend = function (d, s) { - var p; - for (p in s) { - d[p] = s[p]; - } - return d; - }, browser = { - msie: !!(window.attachEvent && !window.opera), - opera: !!window.opera, - webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1, - safari: navigator.userAgent.indexOf('AppleWebKit/') > -1 && navigator.userAgent.indexOf('Chrome/') === -1, - gecko: navigator.userAgent.indexOf('Gecko') > -1, - mobilesafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/), - rhino: navigator.userAgent.match(/Rhino/) && true - }, createEventObject = function (type, options, element) { - var event = element.ownerDocument.createEventObject(); - return extend(event, options); - }, data = {}, id = 1, expando = '_synthetic' + new Date().getTime(), bind, unbind, schedule, key = /keypress|keyup|keydown/, page = /load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll/, activeElement, syn = function (type, element, options, callback) { - return new syn.init(type, element, options, callback); - }; -syn.config = opts; -syn.__tryFocus = function tryFocus(element) { - try { - element.focus(); - } catch (e) { - } -}; -bind = function (el, ev, f) { - return el.addEventListener ? el.addEventListener(ev, f, false) : el.attachEvent('on' + ev, f); -}; -unbind = function (el, ev, f) { - return el.addEventListener ? el.removeEventListener(ev, f, false) : el.detachEvent('on' + ev, f); -}; -schedule = syn.config.schedule || function (fn, ms) { - setTimeout(fn, ms); -}; -extend(syn, { - init: function (type, element, options, callback) { - var args = syn.args(options, element, callback), self = this; - this.queue = []; - this.element = args.element; - if (typeof this[type] === 'function') { - this[type](args.element, args.options, function (defaults, el) { - if (args.callback) { - args.callback.apply(self, arguments); - } - self.done.apply(self, arguments); - }); - } else { - this.result = syn.trigger(args.element, type, args.options); - if (args.callback) { - args.callback.call(this, args.element, this.result); - } - } - }, - jquery: function (el, fast) { - if (window.FuncUnit && window.FuncUnit.jQuery) { - return window.FuncUnit.jQuery; - } - if (el) { - return syn.helpers.getWindow(el).jQuery || window.jQuery; - } else { - return window.jQuery; - } - }, - args: function () { - var res = {}, i = 0; - for (; i < arguments.length; i++) { - if (typeof arguments[i] === 'function') { - res.callback = arguments[i]; - } else if (arguments[i] && arguments[i].jquery) { - res.element = arguments[i][0]; - } else if (arguments[i] && arguments[i].nodeName) { - res.element = arguments[i]; - } else if (res.options && typeof arguments[i] === 'string') { - res.element = document.getElementById(arguments[i]); - } else if (arguments[i]) { - res.options = arguments[i]; - } - } - return res; - }, - click: function (element, options, callback) { - syn('click!', element, options, callback); - }, - defaults: { - focus: function focus() { - if (!syn.support.focusChanges) { - var element = this, nodeName = element.nodeName.toLowerCase(); - syn.data(element, 'syntheticvalue', element.value); - if (nodeName === 'input' || nodeName === 'textarea') { - bind(element, 'blur', function blur() { - if (syn.data(element, 'syntheticvalue') !== element.value) { - syn.trigger(element, 'change', {}); - } - unbind(element, 'blur', blur); - }); - } - } - }, - submit: function () { - syn.onParents(this, function (el) { - if (el.nodeName.toLowerCase() === 'form') { - el.submit(); - return false; - } - }); - } - }, - changeOnBlur: function (element, prop, value) { - bind(element, 'blur', function onblur() { - if (value !== element[prop]) { - syn.trigger(element, 'change', {}); - } - unbind(element, 'blur', onblur); - }); - }, - closest: function (el, type) { - while (el && el.nodeName.toLowerCase() !== type.toLowerCase()) { - el = el.parentNode; - } - return el; - }, - data: function (el, key, value) { - var d; - if (!el[expando]) { - el[expando] = id++; - } - if (!data[el[expando]]) { - data[el[expando]] = {}; - } - d = data[el[expando]]; - if (value) { - data[el[expando]][key] = value; - } else { - return data[el[expando]][key]; - } - }, - onParents: function (el, func) { - var res; - while (el && res !== false) { - res = func(el); - el = el.parentNode; - } - return el; - }, - focusable: /^(a|area|frame|iframe|label|input|select|textarea|button|html|object)$/i, - isFocusable: function (elem) { - var attributeNode; - if (elem.getAttributeNode) { - attributeNode = elem.getAttributeNode('tabIndex'); - } - return this.focusable.test(elem.nodeName) || attributeNode && attributeNode.specified && syn.isVisible(elem); - }, - isVisible: function (elem) { - return elem.offsetWidth && elem.offsetHeight || elem.clientWidth && elem.clientHeight; - }, - tabIndex: function (elem) { - var attributeNode = elem.getAttributeNode('tabIndex'); - return attributeNode && attributeNode.specified && (parseInt(elem.getAttribute('tabIndex')) || 0); - }, - bind: bind, - unbind: unbind, - schedule: schedule, - browser: browser, - helpers: { - createEventObject: createEventObject, - createBasicStandardEvent: function (type, defaults, doc) { - var event; - try { - event = doc.createEvent('Events'); - } catch (e2) { - event = doc.createEvent('UIEvents'); - } finally { - event.initEvent(type, true, true); - extend(event, defaults); - } - return event; - }, - inArray: function (item, array) { - var i = 0; - for (; i < array.length; i++) { - if (array[i] === item) { - return i; - } - } - return -1; - }, - getWindow: function (element) { - if (element.ownerDocument) { - return element.ownerDocument.defaultView || element.ownerDocument.parentWindow; - } - }, - extend: extend, - scrollOffset: function (win, set) { - var doc = win.document.documentElement, body = win.document.body; - if (set) { - window.scrollTo(set.left, set.top); - } else { - return { - left: (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0), - top: (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0) - }; - } - }, - scrollDimensions: function (win) { - var doc = win.document.documentElement, body = win.document.body, docWidth = doc.clientWidth, docHeight = doc.clientHeight, compat = win.document.compatMode === 'CSS1Compat'; - return { - height: compat && docHeight || body.clientHeight || docHeight, - width: compat && docWidth || body.clientWidth || docWidth - }; - }, - addOffset: function (options, el) { - var jq = syn.jquery(el), off; - if (typeof options === 'object' && options.clientX === undefined && options.clientY === undefined && options.pageX === undefined && options.pageY === undefined && jq) { - el = jq(el); - off = el.offset(); - options.pageX = off.left + el.width() / 2; - options.pageY = off.top + el.height() / 2; - } - } - }, - key: { - ctrlKey: null, - altKey: null, - shiftKey: null, - metaKey: null - }, - dispatch: function (event, element, type, autoPrevent) { - if (element.dispatchEvent && event) { - var preventDefault = event.preventDefault, prevents = autoPrevent ? -1 : 0; - if (autoPrevent) { - bind(element, type, function ontype(ev) { - ev.preventDefault(); - unbind(this, type, ontype); - }); - } - event.preventDefault = function () { - prevents++; - if (++prevents > 0) { - preventDefault.apply(this, []); - } - }; - element.dispatchEvent(event); - return prevents <= 0; - } else { - try { - window.event = event; - } catch (e) { - } - return element.sourceIndex <= 0 || element.fireEvent && element.fireEvent('on' + type, event); - } - }, - create: { - page: { - event: function (type, options, element) { - var doc = syn.helpers.getWindow(element).document || document, event; - if (doc.createEvent) { - event = doc.createEvent('Events'); - event.initEvent(type, true, true); - return event; - } else { - try { - event = createEventObject(type, options, element); - } catch (e) { - } - return event; - } - } - }, - focus: { - event: function (type, options, element) { - syn.onParents(element, function (el) { - if (syn.isFocusable(el)) { - if (el.nodeName.toLowerCase() !== 'html') { - syn.__tryFocus(el); - activeElement = el; - } else if (activeElement) { - var doc = syn.helpers.getWindow(element).document; - if (doc !== window.document) { - return false; - } else if (doc.activeElement) { - doc.activeElement.blur(); - activeElement = null; - } else { - activeElement.blur(); - activeElement = null; - } - } - return false; - } - }); - return true; - } - } - }, - support: { - clickChanges: false, - clickSubmits: false, - keypressSubmits: false, - mouseupSubmits: false, - radioClickChanges: false, - focusChanges: false, - linkHrefJS: false, - keyCharacters: false, - backspaceWorks: false, - mouseDownUpClicks: false, - tabKeyTabs: false, - keypressOnAnchorClicks: false, - optionClickBubbles: false, - ready: 0 - }, - trigger: function (element, type, options) { - if (!options) { - options = {}; - } - var create = syn.create, setup = create[type] && create[type].setup, kind = key.test(type) ? 'key' : page.test(type) ? 'page' : 'mouse', createType = create[type] || {}, createKind = create[kind], event, ret, autoPrevent, dispatchEl = element; - if (syn.support.ready === 2 && setup) { - setup(type, options, element); - } - autoPrevent = options._autoPrevent; - delete options._autoPrevent; - if (createType.event) { - ret = createType.event(type, options, element); - } else { - options = createKind.options ? createKind.options(type, options, element) : options; - if (!syn.support.changeBubbles && /option/i.test(element.nodeName)) { - dispatchEl = element.parentNode; - } - event = createKind.event(type, options, dispatchEl); - ret = syn.dispatch(event, dispatchEl, type, autoPrevent); - } - if (ret && syn.support.ready === 2 && syn.defaults[type]) { - syn.defaults[type].call(element, options, autoPrevent); - } - return ret; - }, - eventSupported: function (eventName) { - var el = document.createElement('div'); - eventName = 'on' + eventName; - var isSupported = eventName in el; - if (!isSupported) { - el.setAttribute(eventName, 'return;'); - isSupported = typeof el[eventName] === 'function'; - } - el = null; - return isSupported; - } -}); -extend(syn.init.prototype, { - then: function (type, element, options, callback) { - if (syn.autoDelay) { - this.delay(); - } - var args = syn.args(options, element, callback), self = this; - this.queue.unshift(function (el, prevented) { - if (typeof this[type] === 'function') { - this.element = args.element || el; - this[type](this.element, args.options, function (defaults, el) { - if (args.callback) { - args.callback.apply(self, arguments); - } - self.done.apply(self, arguments); - }); - } else { - this.result = syn.trigger(args.element, type, args.options); - if (args.callback) { - args.callback.call(this, args.element, this.result); - } - return this; - } - }); - return this; - }, - delay: function (timeout, callback) { - if (typeof timeout === 'function') { - callback = timeout; - timeout = null; - } - timeout = timeout || 600; - var self = this; - this.queue.unshift(function () { - schedule(function () { - if (callback) { - callback.apply(self, []); - } - self.done.apply(self, arguments); - }, timeout); - }); - return this; - }, - done: function (defaults, el) { - if (el) { - this.element = el; - } - if (this.queue.length) { - this.queue.pop().call(this, this.element, defaults); - } - }, - '_click': function (element, options, callback, force) { - syn.helpers.addOffset(options, element); - syn.trigger(element, 'mousedown', options); - schedule(function () { - syn.trigger(element, 'mouseup', options); - if (!syn.support.mouseDownUpClicks || force) { - syn.trigger(element, 'click', options); - callback(true); - } else { - syn.create.click.setup('click', options, element); - syn.defaults.click.call(element); - schedule(function () { - callback(true); - }, 1); - } - }, 1); - }, - '_rightClick': function (element, options, callback) { - syn.helpers.addOffset(options, element); - var mouseopts = extend(extend({}, syn.mouse.browser.right.mouseup), options); - syn.trigger(element, 'mousedown', mouseopts); - schedule(function () { - syn.trigger(element, 'mouseup', mouseopts); - if (syn.mouse.browser.right.contextmenu) { - syn.trigger(element, 'contextmenu', extend(extend({}, syn.mouse.browser.right.contextmenu), options)); - } - callback(true); - }, 1); - }, - '_dblclick': function (element, options, callback) { - syn.helpers.addOffset(options, element); - var self = this; - this._click(element, options, function () { - schedule(function () { - self._click(element, options, function () { - syn.trigger(element, 'dblclick', options); - callback(true); - }, true); - }, 2); - }); - } -}); -var actions = [ - 'click', - 'dblclick', - 'move', - 'drag', - 'key', - 'type', - 'rightClick' - ], makeAction = function (name) { - syn[name] = function (element, options, callback) { - return syn('_' + name, element, options, callback); - }; - syn.init.prototype[name] = function (element, options, callback) { - return this.then('_' + name, element, options, callback); - }; - }, i = 0; -for (; i < actions.length; i++) { - makeAction(actions[i]); -} -module.exports = syn; -}); -/*syn@0.1.1#mouse*/ -define('syn/mouse', function(require, exports, module) { -var syn = require('syn/synthetic'); -var h = syn.helpers, getWin = h.getWindow; -syn.mouse = {}; -h.extend(syn.defaults, { - mousedown: function (options) { - syn.trigger(this, 'focus', {}); - }, - click: function () { - var element = this, href, type, createChange, radioChanged, nodeName, scope; - try { - href = element.href; - type = element.type; - createChange = syn.data(element, 'createChange'); - radioChanged = syn.data(element, 'radioChanged'); - scope = getWin(element); - nodeName = element.nodeName.toLowerCase(); - } catch (e) { - return; - } - if (!syn.support.linkHrefJS && /^\s*javascript:/.test(href)) { - var code = href.replace(/^\s*javascript:/, ''); - if (code !== '//' && code.indexOf('void(0)') === -1) { - if (window.selenium) { - eval('with(selenium.browserbot.getCurrentWindow()){' + code + '}'); - } else { - eval('with(scope){' + code + '}'); - } - } - } - if (!syn.support.clickSubmits && (nodeName === 'input' && type === 'submit') || nodeName === 'button') { - var form = syn.closest(element, 'form'); - if (form) { - syn.trigger(form, 'submit', {}); - } - } - if (nodeName === 'a' && element.href && !/^\s*javascript:/.test(href)) { - scope.location.href = href; - } - if (nodeName === 'input' && type === 'checkbox') { - if (!syn.support.clickChanges) { - syn.trigger(element, 'change', {}); - } - } - if (nodeName === 'input' && type === 'radio') { - if (radioChanged && !syn.support.radioClickChanges) { - syn.trigger(element, 'change', {}); - } - } - if (nodeName === 'option' && createChange) { - syn.trigger(element.parentNode, 'change', {}); - syn.data(element, 'createChange', false); - } - } -}); -h.extend(syn.create, { - mouse: { - options: function (type, options, element) { - var doc = document.documentElement, body = document.body, center = [ - options.pageX || 0, - options.pageY || 0 - ], left = syn.mouse.browser && syn.mouse.browser.left[type], right = syn.mouse.browser && syn.mouse.browser.right[type]; - return h.extend({ - bubbles: true, - cancelable: true, - view: window, - detail: 1, - screenX: 1, - screenY: 1, - clientX: options.clientX || center[0] - (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0), - clientY: options.clientY || center[1] - (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0), - ctrlKey: !!syn.key.ctrlKey, - altKey: !!syn.key.altKey, - shiftKey: !!syn.key.shiftKey, - metaKey: !!syn.key.metaKey, - button: left && left.button !== null ? left.button : right && right.button || (type === 'contextmenu' ? 2 : 0), - relatedTarget: document.documentElement - }, options); - }, - event: function (type, defaults, element) { - var doc = getWin(element).document || document, event; - if (doc.createEvent) { - try { - event = doc.createEvent('MouseEvents'); - event.initMouseEvent(type, defaults.bubbles, defaults.cancelable, defaults.view, defaults.detail, defaults.screenX, defaults.screenY, defaults.clientX, defaults.clientY, defaults.ctrlKey, defaults.altKey, defaults.shiftKey, defaults.metaKey, defaults.button, defaults.relatedTarget); - } catch (e) { - event = h.createBasicStandardEvent(type, defaults, doc); - } - event.synthetic = true; - return event; - } else { - try { - event = h.createEventObject(type, defaults, element); - } catch (e) { - } - return event; - } - } - }, - click: { - setup: function (type, options, element) { - var nodeName = element.nodeName.toLowerCase(); - if (!syn.support.clickChecks && !syn.support.changeChecks && nodeName === 'input') { - type = element.type.toLowerCase(); - if (type === 'checkbox') { - element.checked = !element.checked; - } - if (type === 'radio') { - if (!element.checked) { - try { - syn.data(element, 'radioChanged', true); - } catch (e) { - } - element.checked = true; - } - } - } - if (nodeName === 'a' && element.href && !/^\s*javascript:/.test(element.href)) { - syn.data(element, 'href', element.href); - } - if (/option/i.test(element.nodeName)) { - var child = element.parentNode.firstChild, i = -1; - while (child) { - if (child.nodeType === 1) { - i++; - if (child === element) { - break; - } - } - child = child.nextSibling; - } - if (i !== element.parentNode.selectedIndex) { - element.parentNode.selectedIndex = i; - syn.data(element, 'createChange', true); - } - } - } - }, - mousedown: { - setup: function (type, options, element) { - var nn = element.nodeName.toLowerCase(); - if (syn.browser.safari && (nn === 'select' || nn === 'option')) { - options._autoPrevent = true; - } - } - } -}); -}); -/*syn@0.1.1#mouse.support*/ -define('syn/mouse.support', function(require, exports, module) { -var syn = require('syn/synthetic'); -require('syn/mouse'); -if (!document.body) { - syn.schedule(function () { - checkSupport(syn); - }, 1); -} else { - window.__synthTest = function () { - syn.support.linkHrefJS = true; - }; - var div = document.createElement('div'), checkbox, submit, form, select; - div.innerHTML = '
      ' + '' + '' + '' + '' + '' + '' + '' + '' + '
      '; - document.documentElement.appendChild(div); - form = div.firstChild; - checkbox = form.childNodes[0]; - submit = form.childNodes[2]; - select = form.getElementsByTagName('select')[0]; - syn.trigger(form.childNodes[6], 'click', {}); - checkbox.checked = false; - checkbox.onchange = function () { - syn.support.clickChanges = true; - }; - syn.trigger(checkbox, 'click', {}); - syn.support.clickChecks = checkbox.checked; - checkbox.checked = false; - syn.trigger(checkbox, 'change', {}); - syn.support.changeChecks = checkbox.checked; - form.onsubmit = function (ev) { - if (ev.preventDefault) { - ev.preventDefault(); - } - syn.support.clickSubmits = true; - return false; - }; - syn.trigger(submit, 'click', {}); - form.childNodes[1].onchange = function () { - syn.support.radioClickChanges = true; - }; - syn.trigger(form.childNodes[1], 'click', {}); - syn.bind(div, 'click', function onclick() { - syn.support.optionClickBubbles = true; - syn.unbind(div, 'click', onclick); - }); - syn.trigger(select.firstChild, 'click', {}); - syn.support.changeBubbles = syn.eventSupported('change'); - div.onclick = function () { - syn.support.mouseDownUpClicks = true; - }; - syn.trigger(div, 'mousedown', {}); - syn.trigger(div, 'mouseup', {}); - document.documentElement.removeChild(div); - syn.support.ready++; -} -}); -/*syn@0.1.1#browsers*/ -define('syn/browsers', function(require, exports, module) { -var syn = require('syn/synthetic'); -require('syn/mouse'); -syn.key.browsers = { - webkit: { - 'prevent': { - 'keyup': [], - 'keydown': [ - 'char', - 'keypress' - ], - 'keypress': ['char'] - }, - 'character': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 'char', - 'char' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'specialChars': { - 'keydown': [ - 0, - 'char' - ], - 'keyup': [ - 0, - 'char' - ] - }, - 'navigation': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'special': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'tab': { - 'keydown': [ - 0, - 'char' - ], - 'keyup': [ - 0, - 'char' - ] - }, - 'pause-break': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'caps': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'escape': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'num-lock': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'scroll-lock': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'print': { - 'keyup': [ - 0, - 'key' - ] - }, - 'function': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - '\r': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 'char', - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - } - }, - gecko: { - 'prevent': { - 'keyup': [], - 'keydown': ['char'], - 'keypress': ['char'] - }, - 'character': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 'char', - 0 - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'specialChars': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'navigation': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'special': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - '\t': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'pause-break': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'caps': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'escape': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'num-lock': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'scroll-lock': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - 'print': { - 'keyup': [ - 0, - 'key' - ] - }, - 'function': { - 'keydown': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - }, - '\r': { - 'keydown': [ - 0, - 'key' - ], - 'keypress': [ - 0, - 'key' - ], - 'keyup': [ - 0, - 'key' - ] - } - }, - msie: { - 'prevent': { - 'keyup': [], - 'keydown': [ - 'char', - 'keypress' - ], - 'keypress': ['char'] - }, - 'character': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'char' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'specialChars': { - 'keydown': [ - null, - 'char' - ], - 'keyup': [ - null, - 'char' - ] - }, - 'navigation': { - 'keydown': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'special': { - 'keydown': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'tab': { - 'keydown': [ - null, - 'char' - ], - 'keyup': [ - null, - 'char' - ] - }, - 'pause-break': { - 'keydown': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'caps': { - 'keydown': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'escape': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'num-lock': { - 'keydown': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'scroll-lock': { - 'keydown': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'print': { - 'keyup': [ - null, - 'key' - ] - }, - 'function': { - 'keydown': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - '\r': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - } - }, - opera: { - 'prevent': { - 'keyup': [], - 'keydown': [], - 'keypress': ['char'] - }, - 'character': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'char' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'specialChars': { - 'keydown': [ - null, - 'char' - ], - 'keypress': [ - null, - 'char' - ], - 'keyup': [ - null, - 'char' - ] - }, - 'navigation': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ] - }, - 'special': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'tab': { - 'keydown': [ - null, - 'char' - ], - 'keypress': [ - null, - 'char' - ], - 'keyup': [ - null, - 'char' - ] - }, - 'pause-break': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'caps': { - 'keydown': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'escape': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ] - }, - 'num-lock': { - 'keyup': [ - null, - 'key' - ], - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ] - }, - 'scroll-lock': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - 'print': {}, - 'function': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - }, - '\r': { - 'keydown': [ - null, - 'key' - ], - 'keypress': [ - null, - 'key' - ], - 'keyup': [ - null, - 'key' - ] - } - } -}; -syn.mouse.browsers = { - webkit: { - 'right': { - 'mousedown': { - 'button': 2, - 'which': 3 - }, - 'mouseup': { - 'button': 2, - 'which': 3 - }, - 'contextmenu': { - 'button': 2, - 'which': 3 - } - }, - 'left': { - 'mousedown': { - 'button': 0, - 'which': 1 - }, - 'mouseup': { - 'button': 0, - 'which': 1 - }, - 'click': { - 'button': 0, - 'which': 1 - } - } - }, - opera: { - 'right': { - 'mousedown': { - 'button': 2, - 'which': 3 - }, - 'mouseup': { - 'button': 2, - 'which': 3 - } - }, - 'left': { - 'mousedown': { - 'button': 0, - 'which': 1 - }, - 'mouseup': { - 'button': 0, - 'which': 1 - }, - 'click': { - 'button': 0, - 'which': 1 - } - } - }, - msie: { - 'right': { - 'mousedown': { 'button': 2 }, - 'mouseup': { 'button': 2 }, - 'contextmenu': { 'button': 0 } - }, - 'left': { - 'mousedown': { 'button': 1 }, - 'mouseup': { 'button': 1 }, - 'click': { 'button': 0 } - } - }, - chrome: { - 'right': { - 'mousedown': { - 'button': 2, - 'which': 3 - }, - 'mouseup': { - 'button': 2, - 'which': 3 - }, - 'contextmenu': { - 'button': 2, - 'which': 3 - } - }, - 'left': { - 'mousedown': { - 'button': 0, - 'which': 1 - }, - 'mouseup': { - 'button': 0, - 'which': 1 - }, - 'click': { - 'button': 0, - 'which': 1 - } - } - }, - gecko: { - 'left': { - 'mousedown': { - 'button': 0, - 'which': 1 - }, - 'mouseup': { - 'button': 0, - 'which': 1 - }, - 'click': { - 'button': 0, - 'which': 1 - } - }, - 'right': { - 'mousedown': { - 'button': 2, - 'which': 3 - }, - 'mouseup': { - 'button': 2, - 'which': 3 - }, - 'contextmenu': { - 'button': 2, - 'which': 3 - } - } - } -}; -syn.key.browser = function () { - if (syn.key.browsers[window.navigator.userAgent]) { - return syn.key.browsers[window.navigator.userAgent]; - } - for (var browser in syn.browser) { - if (syn.browser[browser] && syn.key.browsers[browser]) { - return syn.key.browsers[browser]; - } - } - return syn.key.browsers.gecko; -}(); -syn.mouse.browser = function () { - if (syn.mouse.browsers[window.navigator.userAgent]) { - return syn.mouse.browsers[window.navigator.userAgent]; - } - for (var browser in syn.browser) { - if (syn.browser[browser] && syn.mouse.browsers[browser]) { - return syn.mouse.browsers[browser]; - } - } - return syn.mouse.browsers.gecko; -}(); -}); -/*syn@0.1.1#typeable*/ -define('syn/typeable', function(require, exports, module) { -var syn = require('syn/synthetic'); -var typeables = []; -var __indexOf = [].indexOf || function (item) { - for (var i = 0, l = this.length; i < l; i++) { - if (i in this && this[i] === item) { - return i; - } - } - return -1; - }; -syn.typeable = function (fn) { - if (__indexOf.call(typeables, fn) === -1) { - typeables.push(fn); - } -}; -syn.typeable.test = function (el) { - for (var i = 0, len = typeables.length; i < len; i++) { - if (typeables[i](el)) { - return true; - } - } - return false; -}; -var type = syn.typeable; -var typeableExp = /input|textarea/i; -type(function (el) { - return typeableExp.test(el.nodeName); -}); -type(function (el) { - return __indexOf.call([ - '', - 'true' - ], el.getAttribute('contenteditable')) !== -1; -}); -}); -/*syn@0.1.1#key*/ -define('syn/key', function(require, exports, module) { -var syn = require('syn/synthetic'); -require('syn/typeable'); -require('syn/browsers'); -var h = syn.helpers, getSelection = function (el) { - var real, r, start; - if (el.selectionStart !== undefined) { - if (document.activeElement && document.activeElement !== el && el.selectionStart === el.selectionEnd && el.selectionStart === 0) { - return { - start: el.value.length, - end: el.value.length - }; - } - return { - start: el.selectionStart, - end: el.selectionEnd - }; - } else { - try { - if (el.nodeName.toLowerCase() === 'input') { - real = h.getWindow(el).document.selection.createRange(); - r = el.createTextRange(); - r.setEndPoint('EndToStart', real); - start = r.text.length; - return { - start: start, - end: start + real.text.length - }; - } else { - real = h.getWindow(el).document.selection.createRange(); - r = real.duplicate(); - var r2 = real.duplicate(), r3 = real.duplicate(); - r2.collapse(); - r3.collapse(false); - r2.moveStart('character', -1); - r3.moveStart('character', -1); - r.moveToElementText(el); - r.setEndPoint('EndToEnd', real); - start = r.text.length - real.text.length; - var end = r.text.length; - if (start !== 0 && r2.text === '') { - start += 2; - } - if (end !== 0 && r3.text === '') { - end += 2; - } - return { - start: start, - end: end - }; - } - } catch (e) { - var prop = formElExp.test(el.nodeName) ? 'value' : 'textContent'; - return { - start: el[prop].length, - end: el[prop].length - }; - } - } - }, getFocusable = function (el) { - var document = h.getWindow(el).document, res = []; - var els = document.getElementsByTagName('*'), len = els.length; - for (var i = 0; i < len; i++) { - if (syn.isFocusable(els[i]) && els[i] !== document.documentElement) { - res.push(els[i]); - } - } - return res; - }, formElExp = /input|textarea/i, textProperty = function () { - var el = document.createElement('span'); - return el.textContent != null ? 'textContent' : 'innerText'; - }(), getText = function (el) { - if (formElExp.test(el.nodeName)) { - return el.value; - } - return el[textProperty]; - }, setText = function (el, value) { - if (formElExp.test(el.nodeName)) { - el.value = value; - } else { - el[textProperty] = value; - } - }; -h.extend(syn, { - keycodes: { - '\b': 8, - '\t': 9, - '\r': 13, - 'shift': 16, - 'ctrl': 17, - 'alt': 18, - 'pause-break': 19, - 'caps': 20, - 'escape': 27, - 'num-lock': 144, - 'scroll-lock': 145, - 'print': 44, - 'page-up': 33, - 'page-down': 34, - 'end': 35, - 'home': 36, - 'left': 37, - 'up': 38, - 'right': 39, - 'down': 40, - 'insert': 45, - 'delete': 46, - ' ': 32, - '0': 48, - '1': 49, - '2': 50, - '3': 51, - '4': 52, - '5': 53, - '6': 54, - '7': 55, - '8': 56, - '9': 57, - 'a': 65, - 'b': 66, - 'c': 67, - 'd': 68, - 'e': 69, - 'f': 70, - 'g': 71, - 'h': 72, - 'i': 73, - 'j': 74, - 'k': 75, - 'l': 76, - 'm': 77, - 'n': 78, - 'o': 79, - 'p': 80, - 'q': 81, - 'r': 82, - 's': 83, - 't': 84, - 'u': 85, - 'v': 86, - 'w': 87, - 'x': 88, - 'y': 89, - 'z': 90, - 'num0': 96, - 'num1': 97, - 'num2': 98, - 'num3': 99, - 'num4': 100, - 'num5': 101, - 'num6': 102, - 'num7': 103, - 'num8': 104, - 'num9': 105, - '*': 106, - '+': 107, - 'subtract': 109, - 'decimal': 110, - 'divide': 111, - ';': 186, - '=': 187, - ',': 188, - 'dash': 189, - '-': 189, - 'period': 190, - '.': 190, - 'forward-slash': 191, - '/': 191, - '`': 192, - '[': 219, - '\\': 220, - ']': 221, - '\'': 222, - 'left window key': 91, - 'right window key': 92, - 'select key': 93, - 'f1': 112, - 'f2': 113, - 'f3': 114, - 'f4': 115, - 'f5': 116, - 'f6': 117, - 'f7': 118, - 'f8': 119, - 'f9': 120, - 'f10': 121, - 'f11': 122, - 'f12': 123 - }, - selectText: function (el, start, end) { - if (el.setSelectionRange) { - if (!end) { - syn.__tryFocus(el); - el.setSelectionRange(start, start); - } else { - el.selectionStart = start; - el.selectionEnd = end; - } - } else if (el.createTextRange) { - var r = el.createTextRange(); - r.moveStart('character', start); - end = end || start; - r.moveEnd('character', end - el.value.length); - r.select(); - } - }, - getText: function (el) { - if (syn.typeable.test(el)) { - var sel = getSelection(el); - return el.value.substring(sel.start, sel.end); - } - var win = syn.helpers.getWindow(el); - if (win.getSelection) { - return win.getSelection().toString(); - } else if (win.document.getSelection) { - return win.document.getSelection().toString(); - } else { - return win.document.selection.createRange().text; - } - }, - getSelection: getSelection -}); -h.extend(syn.key, { - data: function (key) { - if (syn.key.browser[key]) { - return syn.key.browser[key]; - } - for (var kind in syn.key.kinds) { - if (h.inArray(key, syn.key.kinds[kind]) > -1) { - return syn.key.browser[kind]; - } - } - return syn.key.browser.character; - }, - isSpecial: function (keyCode) { - var specials = syn.key.kinds.special; - for (var i = 0; i < specials.length; i++) { - if (syn.keycodes[specials[i]] === keyCode) { - return specials[i]; - } - } - }, - options: function (key, event) { - var keyData = syn.key.data(key); - if (!keyData[event]) { - return null; - } - var charCode = keyData[event][0], keyCode = keyData[event][1], result = {}; - if (keyCode === 'key') { - result.keyCode = syn.keycodes[key]; - } else if (keyCode === 'char') { - result.keyCode = key.charCodeAt(0); - } else { - result.keyCode = keyCode; - } - if (charCode === 'char') { - result.charCode = key.charCodeAt(0); - } else if (charCode !== null) { - result.charCode = charCode; - } - if (result.keyCode) { - result.which = result.keyCode; - } else { - result.which = result.charCode; - } - return result; - }, - kinds: { - special: [ - 'shift', - 'ctrl', - 'alt', - 'caps' - ], - specialChars: ['\b'], - navigation: [ - 'page-up', - 'page-down', - 'end', - 'home', - 'left', - 'up', - 'right', - 'down', - 'insert', - 'delete' - ], - 'function': [ - 'f1', - 'f2', - 'f3', - 'f4', - 'f5', - 'f6', - 'f7', - 'f8', - 'f9', - 'f10', - 'f11', - 'f12' - ] - }, - getDefault: function (key) { - if (syn.key.defaults[key]) { - return syn.key.defaults[key]; - } - for (var kind in syn.key.kinds) { - if (h.inArray(key, syn.key.kinds[kind]) > -1 && syn.key.defaults[kind]) { - return syn.key.defaults[kind]; - } - } - return syn.key.defaults.character; - }, - defaults: { - 'character': function (options, scope, key, force, sel) { - if (/num\d+/.test(key)) { - key = key.match(/\d+/)[0]; - } - if (force || !syn.support.keyCharacters && syn.typeable.test(this)) { - var current = getText(this), before = current.substr(0, sel.start), after = current.substr(sel.end), character = key; - setText(this, before + character + after); - var charLength = character === '\n' && syn.support.textareaCarriage ? 2 : character.length; - syn.selectText(this, before.length + charLength); - } - }, - 'c': function (options, scope, key, force, sel) { - if (syn.key.ctrlKey) { - syn.key.clipboard = syn.getText(this); - } else { - syn.key.defaults.character.apply(this, arguments); - } - }, - 'v': function (options, scope, key, force, sel) { - if (syn.key.ctrlKey) { - syn.key.defaults.character.call(this, options, scope, syn.key.clipboard, true, sel); - } else { - syn.key.defaults.character.apply(this, arguments); - } - }, - 'a': function (options, scope, key, force, sel) { - if (syn.key.ctrlKey) { - syn.selectText(this, 0, getText(this).length); - } else { - syn.key.defaults.character.apply(this, arguments); - } - }, - 'home': function () { - syn.onParents(this, function (el) { - if (el.scrollHeight !== el.clientHeight) { - el.scrollTop = 0; - return false; - } - }); - }, - 'end': function () { - syn.onParents(this, function (el) { - if (el.scrollHeight !== el.clientHeight) { - el.scrollTop = el.scrollHeight; - return false; - } - }); - }, - 'page-down': function () { - syn.onParents(this, function (el) { - if (el.scrollHeight !== el.clientHeight) { - var ch = el.clientHeight; - el.scrollTop += ch; - return false; - } - }); - }, - 'page-up': function () { - syn.onParents(this, function (el) { - if (el.scrollHeight !== el.clientHeight) { - var ch = el.clientHeight; - el.scrollTop -= ch; - return false; - } - }); - }, - '\b': function (options, scope, key, force, sel) { - if (!syn.support.backspaceWorks && syn.typeable.test(this)) { - var current = getText(this), before = current.substr(0, sel.start), after = current.substr(sel.end); - if (sel.start === sel.end && sel.start > 0) { - setText(this, before.substring(0, before.length - 1) + after); - syn.selectText(this, sel.start - 1); - } else { - setText(this, before + after); - syn.selectText(this, sel.start); - } - } - }, - 'delete': function (options, scope, key, force, sel) { - if (!syn.support.backspaceWorks && syn.typeable.test(this)) { - var current = getText(this), before = current.substr(0, sel.start), after = current.substr(sel.end); - if (sel.start === sel.end && sel.start <= getText(this).length - 1) { - setText(this, before + after.substring(1)); - } else { - setText(this, before + after); - } - syn.selectText(this, sel.start); - } - }, - '\r': function (options, scope, key, force, sel) { - var nodeName = this.nodeName.toLowerCase(); - if (nodeName === 'input') { - syn.trigger(this, 'change', {}); - } - if (!syn.support.keypressSubmits && nodeName === 'input') { - var form = syn.closest(this, 'form'); - if (form) { - syn.trigger(form, 'submit', {}); - } - } - if (!syn.support.keyCharacters && nodeName === 'textarea') { - syn.key.defaults.character.call(this, options, scope, '\n', undefined, sel); - } - if (!syn.support.keypressOnAnchorClicks && nodeName === 'a') { - syn.trigger(this, 'click', {}); - } - }, - '\t': function (options, scope) { - var focusEls = getFocusable(this), current = null, i = 0, el, firstNotIndexed, orders = []; - for (; i < focusEls.length; i++) { - orders.push([ - focusEls[i], - i - ]); - } - var sort = function (order1, order2) { - var el1 = order1[0], el2 = order2[0], tab1 = syn.tabIndex(el1) || 0, tab2 = syn.tabIndex(el2) || 0; - if (tab1 === tab2) { - return order1[1] - order2[1]; - } else { - if (tab1 === 0) { - return 1; - } else if (tab2 === 0) { - return -1; - } else { - return tab1 - tab2; - } - } - }; - orders.sort(sort); - for (i = 0; i < orders.length; i++) { - el = orders[i][0]; - if (this === el) { - if (!syn.key.shiftKey) { - current = orders[i + 1][0]; - if (!current) { - current = orders[0][0]; - } - } else { - current = orders[i - 1][0]; - if (!current) { - current = orders[focusEls.length - 1][0]; - } - } - } - } - if (!current) { - current = firstNotIndexed; - } else { - syn.__tryFocus(current); - } - return current; - }, - 'left': function (options, scope, key, force, sel) { - if (syn.typeable.test(this)) { - if (syn.key.shiftKey) { - syn.selectText(this, sel.start === 0 ? 0 : sel.start - 1, sel.end); - } else { - syn.selectText(this, sel.start === 0 ? 0 : sel.start - 1); - } - } - }, - 'right': function (options, scope, key, force, sel) { - if (syn.typeable.test(this)) { - if (syn.key.shiftKey) { - syn.selectText(this, sel.start, sel.end + 1 > getText(this).length ? getText(this).length : sel.end + 1); - } else { - syn.selectText(this, sel.end + 1 > getText(this).length ? getText(this).length : sel.end + 1); - } - } - }, - 'up': function () { - if (/select/i.test(this.nodeName)) { - this.selectedIndex = this.selectedIndex ? this.selectedIndex - 1 : 0; - } - }, - 'down': function () { - if (/select/i.test(this.nodeName)) { - syn.changeOnBlur(this, 'selectedIndex', this.selectedIndex); - this.selectedIndex = this.selectedIndex + 1; - } - }, - 'shift': function () { - return null; - }, - 'ctrl': function () { - return null; - } - } -}); -h.extend(syn.create, { - keydown: { - setup: function (type, options, element) { - if (h.inArray(options, syn.key.kinds.special) !== -1) { - syn.key[options + 'Key'] = element; - } - } - }, - keypress: { - setup: function (type, options, element) { - if (syn.support.keyCharacters && !syn.support.keysOnNotFocused) { - syn.__tryFocus(element); - } - } - }, - keyup: { - setup: function (type, options, element) { - if (h.inArray(options, syn.key.kinds.special) !== -1) { - syn.key[options + 'Key'] = null; - } - } - }, - key: { - options: function (type, options, element) { - options = typeof options !== 'object' ? { character: options } : options; - options = h.extend({}, options); - if (options.character) { - h.extend(options, syn.key.options(options.character, type)); - delete options.character; - } - options = h.extend({ - ctrlKey: !!syn.key.ctrlKey, - altKey: !!syn.key.altKey, - shiftKey: !!syn.key.shiftKey, - metaKey: !!syn.key.metaKey - }, options); - return options; - }, - event: function (type, options, element) { - var doc = h.getWindow(element).document || document, event; - if (doc.createEvent) { - try { - event = doc.createEvent('KeyEvents'); - event.initKeyEvent(type, true, true, window, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode, options.charCode); - } catch (e) { - event = h.createBasicStandardEvent(type, options, doc); - } - event.synthetic = true; - return event; - } else { - try { - event = h.createEventObject.apply(this, arguments); - h.extend(event, options); - } catch (e) { - } - return event; - } - } - } -}); -var convert = { - 'enter': '\r', - 'backspace': '\b', - 'tab': '\t', - 'space': ' ' - }; -h.extend(syn.init.prototype, { - _key: function (element, options, callback) { - if (/-up$/.test(options) && h.inArray(options.replace('-up', ''), syn.key.kinds.special) !== -1) { - syn.trigger(element, 'keyup', options.replace('-up', '')); - return callback(true, element); - } - var activeElement = h.getWindow(element).document.activeElement, caret = syn.typeable.test(element) && getSelection(element), key = convert[options] || options, runDefaults = syn.trigger(element, 'keydown', key), getDefault = syn.key.getDefault, prevent = syn.key.browser.prevent, defaultResult, keypressOptions = syn.key.options(key, 'keypress'); - if (runDefaults) { - if (!keypressOptions) { - defaultResult = getDefault(key).call(element, keypressOptions, h.getWindow(element), key, undefined, caret); - } else { - if (activeElement !== h.getWindow(element).document.activeElement) { - element = h.getWindow(element).document.activeElement; - } - runDefaults = syn.trigger(element, 'keypress', keypressOptions); - if (runDefaults) { - defaultResult = getDefault(key).call(element, keypressOptions, h.getWindow(element), key, undefined, caret); - } - } - } else { - if (keypressOptions && h.inArray('keypress', prevent.keydown) === -1) { - if (activeElement !== h.getWindow(element).document.activeElement) { - element = h.getWindow(element).document.activeElement; - } - syn.trigger(element, 'keypress', keypressOptions); - } - } - if (defaultResult && defaultResult.nodeName) { - element = defaultResult; - } - if (defaultResult !== null) { - syn.schedule(function () { - if (syn.support.oninput) { - syn.trigger(element, 'input', syn.key.options(key, 'input')); - } - syn.trigger(element, 'keyup', syn.key.options(key, 'keyup')); - callback(runDefaults, element); - }, 1); - } else { - callback(runDefaults, element); - } - return element; - }, - _type: function (element, options, callback) { - var parts = (options + '').match(/(\[[^\]]+\])|([^\[])/g), self = this, runNextPart = function (runDefaults, el) { - var part = parts.shift(); - if (!part) { - callback(runDefaults, el); - return; - } - el = el || element; - if (part.length > 1) { - part = part.substr(1, part.length - 2); - } - self._key(el, part, runNextPart); - }; - runNextPart(); - } -}); -}); -/*syn@0.1.1#key.support*/ -define('syn/key.support', function(require, exports, module) { -var syn = require('syn/synthetic'); -require('syn/key'); -if (!syn.config.support) { - (function checkForSupport() { - if (!document.body) { - return syn.schedule(checkForSupport, 1); - } - var div = document.createElement('div'), checkbox, submit, form, anchor, textarea, inputter, one, doc; - doc = document.documentElement; - div.innerHTML = '
      ' + '' + '' + '' + '' + '' + '' + '' + '' + '
      '; - doc.insertBefore(div, doc.firstElementChild || doc.children[0]); - form = div.firstChild; - checkbox = form.childNodes[0]; - submit = form.childNodes[2]; - anchor = form.getElementsByTagName('a')[0]; - textarea = form.getElementsByTagName('textarea')[0]; - inputter = form.childNodes[3]; - one = form.childNodes[4]; - form.onsubmit = function (ev) { - if (ev.preventDefault) { - ev.preventDefault(); - } - syn.support.keypressSubmits = true; - ev.returnValue = false; - return false; - }; - syn.__tryFocus(inputter); - syn.trigger(inputter, 'keypress', '\r'); - syn.trigger(inputter, 'keypress', 'a'); - syn.support.keyCharacters = inputter.value === 'a'; - inputter.value = 'a'; - syn.trigger(inputter, 'keypress', '\b'); - syn.support.backspaceWorks = inputter.value === ''; - inputter.onchange = function () { - syn.support.focusChanges = true; - }; - syn.__tryFocus(inputter); - syn.trigger(inputter, 'keypress', 'a'); - syn.__tryFocus(form.childNodes[5]); - syn.trigger(inputter, 'keypress', 'b'); - syn.support.keysOnNotFocused = inputter.value === 'ab'; - syn.bind(anchor, 'click', function (ev) { - if (ev.preventDefault) { - ev.preventDefault(); - } - syn.support.keypressOnAnchorClicks = true; - ev.returnValue = false; - return false; - }); - syn.trigger(anchor, 'keypress', '\r'); - syn.support.textareaCarriage = textarea.value.length === 4; - syn.support.oninput = 'oninput' in one; - doc.removeChild(div); - syn.support.ready++; - }()); -} else { - syn.helpers.extend(syn.support, syn.config.support); -} -}); -/*syn@0.1.1#drag*/ -define('syn/drag', function(require, exports, module) { -var syn = require('syn/synthetic'); -(function dragSupport() { - if (!document.body) { - syn.schedule(dragSupport, 1); - return; - } - var div = document.createElement('div'); - document.body.appendChild(div); - syn.helpers.extend(div.style, { - width: '100px', - height: '10000px', - backgroundColor: 'blue', - position: 'absolute', - top: '10px', - left: '0px', - zIndex: 19999 - }); - document.body.scrollTop = 11; - if (!document.elementFromPoint) { - return; - } - var el = document.elementFromPoint(3, 1); - if (el === div) { - syn.support.elementFromClient = true; - } else { - syn.support.elementFromPage = true; - } - document.body.removeChild(div); - document.body.scrollTop = 0; -}()); -var elementFromPoint = function (point, element) { - var clientX = point.clientX, clientY = point.clientY, win = syn.helpers.getWindow(element), el; - if (syn.support.elementFromPage) { - var off = syn.helpers.scrollOffset(win); - clientX = clientX + off.left; - clientY = clientY + off.top; - } - el = win.document.elementFromPoint ? win.document.elementFromPoint(clientX, clientY) : element; - if (el === win.document.documentElement && (point.clientY < 0 || point.clientX < 0)) { - return element; - } else { - return el; - } - }, createEventAtPoint = function (event, point, element) { - var el = elementFromPoint(point, element); - syn.trigger(el || element, event, point); - return el; - }, mouseMove = function (point, element, last) { - var el = elementFromPoint(point, element); - if (last !== el && el && last) { - var options = syn.helpers.extend({}, point); - options.relatedTarget = el; - syn.trigger(last, 'mouseout', options); - options.relatedTarget = last; - syn.trigger(el, 'mouseover', options); - } - syn.trigger(el || element, 'mousemove', point); - return el; - }, startMove = function (start, end, duration, element, callback) { - var startTime = new Date(), distX = end.clientX - start.clientX, distY = end.clientY - start.clientY, win = syn.helpers.getWindow(element), current = elementFromPoint(start, element), cursor = win.document.createElement('div'), calls = 0, move; - move = function onmove() { - var now = new Date(), scrollOffset = syn.helpers.scrollOffset(win), fraction = (calls === 0 ? 0 : now - startTime) / duration, options = { - clientX: distX * fraction + start.clientX, - clientY: distY * fraction + start.clientY - }; - calls++; - if (fraction < 1) { - syn.helpers.extend(cursor.style, { - left: options.clientX + scrollOffset.left + 2 + 'px', - top: options.clientY + scrollOffset.top + 2 + 'px' - }); - current = mouseMove(options, element, current); - syn.schedule(onmove, 15); - } else { - current = mouseMove(end, element, current); - win.document.body.removeChild(cursor); - callback(); - } - }; - syn.helpers.extend(cursor.style, { - height: '5px', - width: '5px', - backgroundColor: 'red', - position: 'absolute', - zIndex: 19999, - fontSize: '1px' - }); - win.document.body.appendChild(cursor); - move(); - }, startDrag = function (start, end, duration, element, callback) { - createEventAtPoint('mousedown', start, element); - startMove(start, end, duration, element, function () { - createEventAtPoint('mouseup', end, element); - callback(); - }); - }, center = function (el) { - var j = syn.jquery()(el), o = j.offset(); - return { - pageX: o.left + j.outerWidth() / 2, - pageY: o.top + j.outerHeight() / 2 - }; - }, convertOption = function (option, win, from) { - var page = /(\d+)[x ](\d+)/, client = /(\d+)X(\d+)/, relative = /([+-]\d+)[xX ]([+-]\d+)/, parts; - if (typeof option === 'string' && relative.test(option) && from) { - var cent = center(from); - parts = option.match(relative); - option = { - pageX: cent.pageX + parseInt(parts[1]), - pageY: cent.pageY + parseInt(parts[2]) - }; - } - if (typeof option === 'string' && page.test(option)) { - parts = option.match(page); - option = { - pageX: parseInt(parts[1]), - pageY: parseInt(parts[2]) - }; - } - if (typeof option === 'string' && client.test(option)) { - parts = option.match(client); - option = { - clientX: parseInt(parts[1]), - clientY: parseInt(parts[2]) - }; - } - if (typeof option === 'string') { - option = syn.jquery()(option, win.document)[0]; - } - if (option.nodeName) { - option = center(option); - } - if (option.pageX) { - var off = syn.helpers.scrollOffset(win); - option = { - clientX: option.pageX - off.left, - clientY: option.pageY - off.top - }; - } - return option; - }, adjust = function (from, to, win) { - if (from.clientY < 0) { - var off = syn.helpers.scrollOffset(win); - var top = off.top + from.clientY - 100, diff = top - off.top; - if (top > 0) { - } else { - top = 0; - diff = -off.top; - } - from.clientY = from.clientY - diff; - to.clientY = to.clientY - diff; - syn.helpers.scrollOffset(win, { - top: top, - left: off.left - }); - } - }; -syn.helpers.extend(syn.init.prototype, { - _move: function (from, options, callback) { - var win = syn.helpers.getWindow(from), fro = convertOption(options.from || from, win, from), to = convertOption(options.to || options, win, from); - if (options.adjust !== false) { - adjust(fro, to, win); - } - startMove(fro, to, options.duration || 500, from, callback); - }, - _drag: function (from, options, callback) { - var win = syn.helpers.getWindow(from), fro = convertOption(options.from || from, win, from), to = convertOption(options.to || options, win, from); - if (options.adjust !== false) { - adjust(fro, to, win); - } - startDrag(fro, to, options.duration || 500, from, callback); - } -}); -}); -/*syn@0.1.1#syn*/ -define('syn', function(require, exports, module) { -var syn = require('syn/synthetic'); -require('syn/mouse.support'); -require('syn/browsers'); -require('syn/key.support'); -require('syn/drag'); -window.syn = syn; -module.exports = syn; -});