diff --git a/.gitignore b/.gitignore index fa152b4..51a9a51 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ logs/* build/ .DS_Store /node_modules/ +*.log +.grunt diff --git a/.travis.yml b/.travis.yml index 758db5b..481a518 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,10 @@ node_js: notifications: email: true -before_script: +install: + - npm install - npm install -g grunt-cli - + - npm install bower + - bower install script: - grunt tests diff --git a/dist/README.md b/CHANGELOG.md similarity index 65% rename from dist/README.md rename to CHANGELOG.md index 16cc225..4fbf9af 100644 --- a/dist/README.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ ##Change Log +###Version 1.3.1 +* Fix broken bower distribution + +###Version 1.3.0 +* MomentJS integration - https://github.com/siddii/angular-timer/pull/159 + +###Version 1.2.1 +* Add adaptive interpolation symbol - https://github.com/siddii/angular-timer/pull/153 + +###Version 1.2.0 +* Reset method https://github.com/siddii/angular-timer/pull/46 + +###Version 1.1.9 +* Fix for issue https://github.com/siddii/angular-timer/issues/128 (Remove class scoping on `timer` directive) + +###Version 1.1.8 +* Fix for issue https://github.com/siddii/angular-timer/issues/117 (0 minute display 0 minutes) + +###Version 1.1.7 +* Fix for https://github.com/siddii/angular-timer/issues/101 (start-time doesn't work in combination with autostart) + +###Version 1.1.6 +* Added countdown finished callback - https://github.com/siddii/angular-timer/pull/64 + ###Version 1.1.5 * Fix for - https://github.com/siddii/angular-timer/issues/84 @@ -48,4 +72,4 @@ * 'auto-start' attribute name change to 'autostart' in support of Angular 1.2. See #14 ###Version 1.0.3 -* Successful Bower integration! +* Successful Bower integration! \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index 3c3b5db..7215355 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,6 +8,7 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-karma'); grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadNpmTasks('grunt-gh-pages'); var userConfig = { dist_dir: 'dist', @@ -30,15 +31,66 @@ module.exports = function (grunt) { ' */\n' }, + /** + * The directories to delete when `grunt clean` is executed. + */ + clean: [ + '<%= dist_dir %>' + ], + + /* Copy all example into dist/examples */ + copy: { + examples: { + src: 'examples/*', + dest: 'dist/' + }, + nav: { + src: 'navbar.html', + dest: 'dist/' + }, + example: { + src: 'examples.html', + dest: 'dist/' + } + }, + concat: { compile_js: { options: { banner: '<%= meta.banner %>' }, src: [ - 'app/**/*.js' + 'app/**/*.js' ], dest: '<%= dist_dir %>/<%= pkg.name %>.js' + }, + compile_all_js: { + src: [ + '<%= dist_dir %>/<%= pkg.name %>.min.js', + 'bower_components/moment/min/moment-with-locales.min.js', + 'bower_components/humanize-duration/humanize-duration.js' + ], + dest: '<%= dist_dir %>/assets/js/<%= pkg.name %>-all.min.js' + }, + compile_bower_js: { + src: [ + 'bower_components/angular/angular.min.js', + 'bower_components/jquery/jquery.min.js', + 'bower_components/bootstrap/docs/assets/js/bootstrap.min.js', + 'docs/docs.js', + 'docs/prettify.js', + 'docs/application.js' + ], + dest: '<%= dist_dir %>/assets/js/<%= pkg.name %>-bower.js' + }, + compile_bower_css: { + src: [ + 'bower_components/bootstrap/docs/assets/css/bootstrap.css', + 'bower_components/bootstrap/docs/assets/css/bootstrap-responsive.css', + 'docs/css/docs.css', + 'docs/css/prettify.css' + ], + dest: '<%= dist_dir %>/assets/css/<%= pkg.name %>-bower.css' } }, @@ -67,7 +119,15 @@ module.exports = function (grunt) { sub: true, boss: true, eqnull: true + } + }, + + 'gh-pages': { + options: { + base: 'dist', + message: 'Update gh-pages' }, + src: ['**'] }, connect: { @@ -119,7 +179,52 @@ module.exports = function (grunt) { grunt.registerTask('tests', [ 'connect:testserver', 'build', 'karma:unit', 'karma:e2e']); grunt.registerTask('build', [ - 'jshint', 'concat', 'uglify' + 'clean', 'jshint', 'concat:compile_js', 'uglify', 'concat:compile_all_js', 'concat:compile_bower_js', 'concat:compile_bower_css','copy:examples','copy:nav','copy:example' ]); + /** + * A utility function to get all app JavaScript sources. + */ + function filterForJS ( files ) { + return files.filter( function ( file ) { + return file.match( /\.js$/ ); + }); + } + + /** + * A utility function to get all app CSS sources. + */ + function filterForCSS ( files ) { + return files.filter( function ( file ) { + return file.match( /\.css$/ ); + }); + } + + /** + * The index.html template includes the stylesheet and javascript sources + * based on dynamic names calculated in this Gruntfile. This task assembles + * the list into variables for the template to use and then runs the + * compilation. + */ + grunt.registerMultiTask( 'index', 'Process index.html template', function () { + var dirRE = new RegExp( '^('+grunt.config('build_dir')+'|'+grunt.config('dist_dir')+')\/', 'g' ); + var jsFiles = filterForJS( this.filesSrc ).map( function ( file ) { + return file.replace( dirRE, '' ); + }); + var cssFiles = filterForCSS( this.filesSrc ).map( function ( file ) { + return file.replace( dirRE, '' ); + }); + + grunt.file.copy('index.tpl.html', this.data.dir + 'index.html', { + process: function ( contents, path ) { + return grunt.template.process( contents, { + data: { + scripts: jsFiles, + styles: cssFiles, + version: grunt.config( 'pkg.version' ) + } + }); + } + }); + }); }; diff --git a/README.md b/README.md index 90d2ac8..83f19f6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,28 @@ -## angular-timer — A simple, re-usable, inter-operable timer directive [![Build Status](https://travis-ci.org/siddii/angular-timer.png)](https://travis-ci.org/siddii/angular-timer) +## angular-timer — A simple, re-usable, inter-operable timer directive + +[![Build Status](https://travis-ci.org/siddii/angular-timer.png)](https://travis-ci.org/siddii/angular-timer) +[![](https://www.codeshelter.co/static/badges/badge-flat.svg)](https://www.codeshelter.co/) ### Getting started With any of the following options... * Download the latest script file - https://raw.github.com/siddii/angular-timer/master/dist/angular-timer.min.js * Clone the repo - `git@github.com:siddii/angular-timer.git` * Install angular-timer using [Bower](http://bower.io) - `bower install angular-timer` +* Add ``timer`` to your list of modules + +### Requirements +With Bower install : +* Install humanize-duration using [Bower](http://bower.io) - `bower install humanize-duration` +* Install momentjs using [Bower](http://bower.io) - `bower install momentjs` + +And include these scripts in your webpage : +* bower_components/momentjs/min/moment.min.js +* bower_components/momentjs/min/locales.min.js +* bower_components/humanize-duration/humanize-duration.js + ### Running locally +Install all bower components - `bower install` Using [Grunt](http://gruntjs.com/) type `grunt` from command line, the default task will open index.html page in your default browser @@ -16,4 +32,5 @@ Following command will run both unit & End-to-End (e2e) tests grunt tests ``` - +### Examples +There are some examples on the index page http://siddii.github.io/angular-timer/index.html. Please go over them to get an understanding on how this module works. diff --git a/app/js/_timer.js b/app/js/_timer.js new file mode 100644 index 0000000..70ca899 --- /dev/null +++ b/app/js/_timer.js @@ -0,0 +1,425 @@ +var timerModule = angular.module('timer', []) + .directive('timer', ['$compile', function ($compile) { + return { + restrict: 'EA', + replace: false, + scope: { + interval: '=interval', + startTimeAttr: '=startTime', + endTimeAttr: '=endTime', + countdownattr: '=countdown', + finishCallback: '&finishCallback', + autoStart: '&autoStart', + language: '@?', + fallback: '@?', + maxTimeUnit: '=', + seconds: '=?', + minutes: '=?', + hours: '=?', + days: '=?', + months: '=?', + years: '=?', + secondsS: '=?', + minutesS: '=?', + hoursS: '=?', + daysS: '=?', + monthsS: '=?', + yearsS: '=?' + }, + controller: ['$scope', '$element', '$attrs', '$timeout', 'I18nService', '$interpolate', 'progressBarService', function ($scope, $element, $attrs, $timeout, I18nService, $interpolate, progressBarService) { + + // Checking for trim function since IE8 doesn't have it + // If not a function, create tirm with RegEx to mimic native trim + if (typeof String.prototype.trim !== 'function') { + String.prototype.trim = function () { + return this.replace(/^\s+|\s+$/g, ''); + }; + } + + //angular 1.2 doesn't support attributes ending in "-start", so we're + //supporting both "autostart" and "auto-start" as a solution for + //backward and forward compatibility. + $scope.autoStart = $attrs.autoStart || $attrs.autostart; + + + $scope.language = $scope.language || 'en'; + $scope.fallback = $scope.fallback || 'en'; + + //allow to change the language of the directive while already launched + $scope.$watch('language', function(newVal, oldVal) { + if(newVal !== undefined) { + i18nService.init(newVal, $scope.fallback); + } + }); + + //init momentJS i18n, default english + var i18nService = new I18nService(); + i18nService.init($scope.language, $scope.fallback); + + //progress bar + $scope.displayProgressBar = 0; + $scope.displayProgressActive = 'active'; //Bootstrap active effect for progress bar + + if ($element.html().trim().length === 0) { + $element.append($compile('' + $interpolate.startSymbol() + 'millis' + $interpolate.endSymbol() + '')($scope)); + } else { + $element.append($compile($element.contents())($scope)); + } + + $scope.startTime = null; + $scope.endTime = null; + $scope.timeoutId = null; + $scope.countdown = angular.isNumber($scope.countdownattr) && parseInt($scope.countdownattr, 10) >= 0 ? parseInt($scope.countdownattr, 10) : undefined; + $scope.isRunning = false; + + $scope.$on('timer-start', function () { + $scope.start(); + }); + + $scope.$on('timer-resume', function () { + $scope.resume(); + }); + + $scope.$on('timer-stop', function () { + $scope.stop(); + }); + + $scope.$on('timer-clear', function () { + $scope.clear(); + }); + + $scope.$on('timer-reset', function () { + $scope.reset(); + }); + + $scope.$on('timer-set-countdown', function (e, countdown) { + $scope.countdown = countdown; + }); + + function resetTimeout() { + if ($scope.timeoutId) { + clearTimeout($scope.timeoutId); + } + } + + $scope.$watch('startTimeAttr', function(newValue, oldValue) { + if (newValue !== oldValue && $scope.isRunning) { + $scope.start(); + } + }); + + $scope.$watch('endTimeAttr', function(newValue, oldValue) { + if (newValue !== oldValue && $scope.isRunning) { + $scope.start(); + } + }); + + $scope.start = function () { + $scope.startTime = $scope.startTimeAttr ? moment($scope.startTimeAttr) : moment(); + $scope.endTime = $scope.endTimeAttr ? moment($scope.endTimeAttr) : null; + if (!angular.isNumber($scope.countdown)) { + $scope.countdown = angular.isNumber($scope.countdownattr) && parseInt($scope.countdownattr, 10) > 0 ? parseInt($scope.countdownattr, 10) : undefined; + } + resetTimeout(); + tick(); + $scope.isRunning = true; + $scope.$emit('timer-started', { + timeoutId: $scope.timeoutId, + millis: $scope.millis, + seconds: $scope.seconds, + minutes: $scope.minutes, + hours: $scope.hours, + days: $scope.days + }); + }; + + $scope.resume = function () { + resetTimeout(); + if ($scope.countdownattr) { + $scope.countdown += 1; + } + $scope.startTime = moment().diff((moment($scope.stoppedTime).diff(moment($scope.startTime)))); + tick(); + $scope.isRunning = true; + $scope.$emit('timer-started', { + timeoutId: $scope.timeoutId, + millis: $scope.millis, + seconds: $scope.seconds, + minutes: $scope.minutes, + hours: $scope.hours, + days: $scope.days + }); + }; + + $scope.stop = $scope.pause = function () { + var timeoutId = $scope.timeoutId; + $scope.clear(); + $scope.$emit('timer-stopped', { + timeoutId: timeoutId, + millis: $scope.millis, + seconds: $scope.seconds, + minutes: $scope.minutes, + hours: $scope.hours, + days: $scope.days + }); + }; + + $scope.clear = function () { + // same as stop but without the event being triggered + $scope.stoppedTime = moment(); + resetTimeout(); + $scope.timeoutId = null; + $scope.isRunning = false; + }; + + $scope.reset = function () { + $scope.startTime = $scope.startTimeAttr ? moment($scope.startTimeAttr) : moment(); + $scope.endTime = $scope.endTimeAttr ? moment($scope.endTimeAttr) : null; + $scope.countdown = angular.isNumber($scope.countdownattr) && parseInt($scope.countdownattr, 10) > 0 ? parseInt($scope.countdownattr, 10) : undefined; + resetTimeout(); + tick(); + $scope.isRunning = false; + $scope.clear(); + $scope.$emit('timer-reseted', { + timeoutId: $scope.timeoutId, + millis: $scope.millis, + seconds: $scope.seconds, + minutes: $scope.minutes, + hours: $scope.hours, + days: $scope.days + }); + }; + + $element.bind('$destroy', function () { + resetTimeout(); + $scope.isRunning = false; + }); + + + function calculateTimeUnits() { + var timeUnits = {}; //will contains time with units + + if ($attrs.startTime !== undefined){ + $scope.millis = moment().diff(moment($scope.startTimeAttr)); + } + + timeUnits = i18nService.getTimeUnits($scope.millis); + + // compute time values based on maxTimeUnit specification + if (!$scope.maxTimeUnit || $scope.maxTimeUnit === 'day') { + $scope.seconds = Math.floor(($scope.millis / 1000) % 60); + $scope.minutes = Math.floor((($scope.millis / (60000)) % 60)); + $scope.hours = Math.floor((($scope.millis / (3600000)) % 24)); + $scope.days = Math.floor((($scope.millis / (3600000)) / 24)); + $scope.months = 0; + $scope.years = 0; + } else if ($scope.maxTimeUnit === 'second') { + $scope.seconds = Math.floor($scope.millis / 1000); + $scope.minutes = 0; + $scope.hours = 0; + $scope.days = 0; + $scope.months = 0; + $scope.years = 0; + } else if ($scope.maxTimeUnit === 'minute') { + $scope.seconds = Math.floor(($scope.millis / 1000) % 60); + $scope.minutes = Math.floor($scope.millis / 60000); + $scope.hours = 0; + $scope.days = 0; + $scope.months = 0; + $scope.years = 0; + } else if ($scope.maxTimeUnit === 'hour') { + $scope.seconds = Math.floor(($scope.millis / 1000) % 60); + $scope.minutes = Math.floor((($scope.millis / (60000)) % 60)); + $scope.hours = Math.floor($scope.millis / 3600000); + $scope.days = 0; + $scope.months = 0; + $scope.years = 0; + } else if ($scope.maxTimeUnit === 'month') { + $scope.seconds = Math.floor(($scope.millis / 1000) % 60); + $scope.minutes = Math.floor((($scope.millis / (60000)) % 60)); + $scope.hours = Math.floor((($scope.millis / (3600000)) % 24)); + $scope.days = Math.floor((($scope.millis / (3600000)) / 24) % 30); + $scope.months = Math.floor((($scope.millis / (3600000)) / 24) / 30); + $scope.years = 0; + } else if ($scope.maxTimeUnit === 'year') { + $scope.seconds = Math.floor(($scope.millis / 1000) % 60); + $scope.minutes = Math.floor((($scope.millis / (60000)) % 60)); + $scope.hours = Math.floor((($scope.millis / (3600000)) % 24)); + $scope.days = Math.floor((($scope.millis / (3600000)) / 24) % 30); + $scope.months = Math.floor((($scope.millis / (3600000)) / 24 / 30) % 12); + $scope.years = Math.floor(($scope.millis / (3600000)) / 24 / 365); + } + // plural - singular unit decision (old syntax, for backwards compatibility and English only, could be deprecated!) + $scope.secondsS = ($scope.seconds === 1) ? '' : 's'; + $scope.minutesS = ($scope.minutes === 1) ? '' : 's'; + $scope.hoursS = ($scope.hours === 1) ? '' : 's'; + $scope.daysS = ($scope.days === 1)? '' : 's'; + $scope.monthsS = ($scope.months === 1)? '' : 's'; + $scope.yearsS = ($scope.years === 1)? '' : 's'; + + + // new plural-singular unit decision functions (for custom units and multilingual support) + $scope.secondUnit = timeUnits.seconds; + $scope.minuteUnit = timeUnits.minutes; + $scope.hourUnit = timeUnits.hours; + $scope.dayUnit = timeUnits.days; + $scope.monthUnit = timeUnits.months; + $scope.yearUnit = timeUnits.years; + + //add leading zero if number is smaller than 10 + $scope.sseconds = $scope.seconds < 10 ? '0' + $scope.seconds : $scope.seconds; + $scope.mminutes = $scope.minutes < 10 ? '0' + $scope.minutes : $scope.minutes; + $scope.hhours = $scope.hours < 10 ? '0' + $scope.hours : $scope.hours; + $scope.ddays = $scope.days < 10 ? '0' + $scope.days : $scope.days; + $scope.mmonths = $scope.months < 10 ? '0' + $scope.months : $scope.months; + $scope.yyears = $scope.years < 10 ? '0' + $scope.years : $scope.years; + + } + + //determine initial values of time units and add AddSeconds functionality + if ($scope.countdownattr) { + $scope.millis = $scope.countdownattr * 1000; + + $scope.addCDSeconds = function (extraSeconds) { + $scope.countdown += extraSeconds; + if (!$scope.isRunning) { + $scope.start(); + } + }; + + $scope.$on('timer-add-cd-seconds', function (e, extraSeconds) { + $scope.addCDSeconds(extraSeconds); + }); + + $scope.$on('timer-set-countdown-seconds', function (e, countdownSeconds) { + if (!$scope.isRunning) { + $scope.clear(); + } + + $scope.countdown = countdownSeconds; + $scope.millis = countdownSeconds * 1000; + calculateTimeUnits(); + }); + } else { + $scope.millis = 0; + } + calculateTimeUnits(); + + var tick = function tick() { + var typeTimer = null; // countdown or endTimeAttr + $scope.millis = moment().diff($scope.startTime); + var adjustment = $scope.millis % 1000; + + if ($scope.endTimeAttr) { + typeTimer = $scope.endTimeAttr; + $scope.millis = moment($scope.endTime).diff(moment()); + adjustment = $scope.interval - $scope.millis % 1000; + } + + if ($scope.countdownattr) { + typeTimer = $scope.countdownattr; + $scope.millis = $scope.countdown * 1000; + } + + if ($scope.millis < 0) { + $scope.stop(); + $scope.millis = 0; + calculateTimeUnits(); + if($scope.finishCallback) { + $scope.$eval($scope.finishCallback); + } + return; + } + calculateTimeUnits(); + + //We are not using $timeout for a reason. Please read here - https://github.com/siddii/angular-timer/pull/5 + $scope.timeoutId = setTimeout(function () { + tick(); + // since you choose not to use $timeout, at least preserve angular cycle two way data binding + // by calling $scope.$apply() instead of $scope.$digest() + $scope.$apply(); + }, $scope.interval - adjustment); + + $scope.$emit('timer-tick', { + timeoutId: $scope.timeoutId, + millis: $scope.millis, + seconds: $scope.seconds, + minutes: $scope.minutes, + hours: $scope.hours, + days: $scope.days + }); + + if ($scope.countdown > 0) { + $scope.countdown--; + } + else if ($scope.countdown <= 0) { + $scope.stop(); + if($scope.finishCallback) { + $scope.$eval($scope.finishCallback); + } + } + + if(typeTimer !== null){ + //calculate progress bar + $scope.progressBar = progressBarService.calculateProgressBar($scope.startTime, $scope.millis, $scope.endTime, $scope.countdownattr); + + if($scope.progressBar === 100){ + $scope.displayProgressActive = ''; //No more Bootstrap active effect + } + } + }; + + if ($scope.autoStart === undefined || $scope.autoStart === true) { + $scope.start(); + } + }] + }; + }]) + .directive('timerControls', function() { + return { + restrict: 'EA', + scope: true, + controller: ['$scope', function($scope) { + $scope.timerStatus = "reset"; + $scope.$on('timer-started', function() { + $scope.timerStatus = "started"; + }); + $scope.$on('timer-stopped', function() { + $scope.timerStatus = "stopped"; + }); + $scope.$on('timer-reset', function() { + $scope.timerStatus = "reset"; + }); + $scope.timerStart = function() { + $scope.$broadcast('timer-start'); + }; + $scope.timerStop = function() { + $scope.$broadcast('timer-stop'); + }; + $scope.timerResume = function() { + $scope.$broadcast('timer-resume'); + }; + $scope.timerToggle = function() { + switch ($scope.timerStatus) { + case "started": + $scope.timerStop(); + break; + case "stopped": + $scope.timerResume(); + break; + case "reset": + $scope.timerStart(); + break; + } + }; + $scope.timerAddCDSeconds = function(extraSeconds) { + $scope.$broadcast('timer-add-cd-seconds', extraSeconds); + }; + }] + }; + }); + +/* commonjs package manager support (eg componentjs) */ +if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){ + module.exports = timerModule; +} diff --git a/app/js/i18nService.js b/app/js/i18nService.js new file mode 100644 index 0000000..9f7a596 --- /dev/null +++ b/app/js/i18nService.js @@ -0,0 +1,65 @@ +var app = angular.module('timer'); + +app.factory('I18nService', function() { + + var I18nService = function() {}; + + I18nService.prototype.language = 'en'; + I18nService.prototype.fallback = 'en'; + I18nService.prototype.timeHumanizer = {}; + + I18nService.prototype.init = function init(lang, fallback) { + var supported_languages = humanizeDuration.getSupportedLanguages(); + + this.fallback = (fallback !== undefined) ? fallback : 'en'; + if (supported_languages.indexOf(fallback) === -1) { + this.fallback = 'en'; + } + + this.language = lang; + if (supported_languages.indexOf(lang) === -1) { + this.language = this.fallback; + } + + // It should be handle by the user's application itself, and not inside the directive + // moment init + // moment.locale(this.language); + + //human duration init, using it because momentjs does not allow accurate time ( + // momentJS: a few moment ago, human duration : 4 seconds ago + this.timeHumanizer = humanizeDuration.humanizer({ + language: this.language, + halfUnit:false + }); + }; + + /** + * get time with units from momentJS i18n + * @param {int} millis + * @returns {{millis: string, seconds: string, minutes: string, hours: string, days: string, months: string, years: string}} + */ + I18nService.prototype.getTimeUnits = function getTimeUnits(millis) { + var diffFromAlarm = Math.round(millis/1000) * 1000; //time in milliseconds, get rid of the last 3 ms value to avoid 2.12 seconds display + + var time = {}; + + if (typeof this.timeHumanizer != 'undefined'){ + time = { + 'millis' : this.timeHumanizer(diffFromAlarm, { units: ["ms"] }), + 'seconds' : this.timeHumanizer(diffFromAlarm, { units: ["s"] }), + 'minutes' : this.timeHumanizer(diffFromAlarm, { units: ["m", "s"] }) , + 'hours' : this.timeHumanizer(diffFromAlarm, { units: ["h", "m", "s"] }) , + 'days' : this.timeHumanizer(diffFromAlarm, { units: ["d", "h", "m", "s"] }) , + 'months' : this.timeHumanizer(diffFromAlarm, { units: ["mo", "d", "h", "m", "s"] }) , + 'years' : this.timeHumanizer(diffFromAlarm, { units: ["y", "mo", "d", "h", "m", "s"] }) + }; + } + else { + console.error('i18nService has not been initialized. You must call i18nService.init("en") for example'); + } + + return time; + }; + + return I18nService; +}); diff --git a/app/js/progressBarService.js b/app/js/progressBarService.js new file mode 100644 index 0000000..b6eb87c --- /dev/null +++ b/app/js/progressBarService.js @@ -0,0 +1,46 @@ +var app = angular.module('timer'); + +app.factory('progressBarService', function() { + + var ProgressBarService = function() {}; + + /** + * calculate the remaining time in a progress bar in percentage + * @param {momentjs} startValue in seconds + * @param {integer} currentCountdown, where are we in the countdown + * @param {integer} remainingTime, remaining milliseconds + * @param {integer} endTime, end time, can be undefined + * @param {integer} coutdown, original coutdown value, can be undefined + * + * joke : https://www.youtube.com/watch?v=gENVB6tjq_M + * @return {float} 0 --> 100 + */ + ProgressBarService.prototype.calculateProgressBar = function calculateProgressBar(startValue, remainingTime, endTimeAttr, coutdown) { + var displayProgressBar = 0, + endTimeValue, + initialCountdown; + + remainingTime = remainingTime / 1000; //seconds + + + if(endTimeAttr !== null){ + endTimeValue = moment(endTimeAttr); + initialCountdown = endTimeValue.diff(startValue, 'seconds'); + displayProgressBar = remainingTime * 100 / initialCountdown; + } + else { + displayProgressBar = remainingTime * 100 / coutdown; + } + + displayProgressBar = 100 - displayProgressBar; //To have 0 to 100 and not 100 to 0 + displayProgressBar = Math.round(displayProgressBar * 10) / 10; //learn more why : http://stackoverflow.com/questions/588004/is-floating-point-math-broken + + if(displayProgressBar > 100){ //security + displayProgressBar = 100; + } + + return displayProgressBar; + }; + + return new ProgressBarService(); +}); diff --git a/app/js/timer.js b/app/js/timer.js deleted file mode 100644 index ced1501..0000000 --- a/app/js/timer.js +++ /dev/null @@ -1,252 +0,0 @@ -var timerModule = angular.module('timer', []) - .directive('timer', ['$compile', function ($compile) { - return { - restrict: 'EAC', - replace: false, - scope: { - interval: '=interval', - startTimeAttr: '=startTime', - endTimeAttr: '=endTime', - countdownattr: '=countdown', - autoStart: '&autoStart', - maxTimeUnit: '=' - }, - controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) { - - // Checking for trim function since IE8 doesn't have it - // If not a function, create tirm with RegEx to mimic native trim - if (typeof String.prototype.trim !== 'function') { - String.prototype.trim = function () { - return this.replace(/^\s+|\s+$/g, ''); - }; - } - - //angular 1.2 doesn't support attributes ending in "-start", so we're - //supporting both "autostart" and "auto-start" as a solution for - //backward and forward compatibility. - $scope.autoStart = $attrs.autoStart || $attrs.autostart; - - if ($element.html().trim().length === 0) { - $element.append($compile('{{millis}}')($scope)); - } else { - $element.append($compile($element.contents())($scope)); - } - - $scope.startTime = null; - $scope.endTime = null; - $scope.timeoutId = null; - $scope.countdown = $scope.countdownattr && parseInt($scope.countdownattr, 10) >= 0 ? parseInt($scope.countdownattr, 10) : undefined; - $scope.isRunning = false; - - $scope.$on('timer-start', function () { - $scope.start(); - }); - - $scope.$on('timer-resume', function () { - $scope.resume(); - }); - - $scope.$on('timer-stop', function () { - $scope.stop(); - }); - - $scope.$on('timer-clear', function () { - $scope.clear(); - }); - - $scope.$on('timer-set-countdown', function (e, countdown) { - $scope.countdown = countdown; - }); - - function resetTimeout() { - if ($scope.timeoutId) { - clearTimeout($scope.timeoutId); - } - } - - $scope.start = $element[0].start = function () { - $scope.startTime = $scope.startTimeAttr ? new Date($scope.startTimeAttr) : new Date(); - $scope.endTime = $scope.endTimeAttr ? new Date($scope.endTimeAttr) : null; - if (!$scope.countdown) { - $scope.countdown = $scope.countdownattr && parseInt($scope.countdownattr, 10) > 0 ? parseInt($scope.countdownattr, 10) : undefined; - } - resetTimeout(); - tick(); - $scope.isRunning = true; - }; - - $scope.resume = $element[0].resume = function () { - resetTimeout(); - if ($scope.countdownattr) { - $scope.countdown += 1; - } - $scope.startTime = new Date() - ($scope.stoppedTime - $scope.startTime); - tick(); - $scope.isRunning = true; - }; - - $scope.stop = $scope.pause = $element[0].stop = $element[0].pause = function () { - var timeoutId = $scope.timeoutId; - $scope.clear(); - $scope.$emit('timer-stopped', {timeoutId: timeoutId, millis: $scope.millis, seconds: $scope.seconds, minutes: $scope.minutes, hours: $scope.hours, days: $scope.days}); - }; - - $scope.clear = $element[0].clear = function () { - // same as stop but without the event being triggered - $scope.stoppedTime = new Date(); - resetTimeout(); - $scope.timeoutId = null; - $scope.isRunning = false; - }; - - $element.bind('$destroy', function () { - resetTimeout(); - $scope.isRunning = false; - }); - - function calculateTimeUnits() { - - // compute time values based on maxTimeUnit specification - if (!$scope.maxTimeUnit || $scope.maxTimeUnit === 'day') { - $scope.seconds = Math.floor(($scope.millis / 1000) % 60); - $scope.minutes = Math.floor((($scope.millis / (60000)) % 60)); - $scope.hours = Math.floor((($scope.millis / (3600000)) % 24)); - $scope.days = Math.floor((($scope.millis / (3600000)) / 24)); - $scope.months = 0; - $scope.years = 0; - } else if ($scope.maxTimeUnit === 'second') { - $scope.seconds = Math.floor($scope.millis / 1000); - $scope.minutes = 0; - $scope.hours = 0; - $scope.days = 0; - $scope.months = 0; - $scope.years = 0; - } else if ($scope.maxTimeUnit === 'minute') { - $scope.seconds = Math.floor(($scope.millis / 1000) % 60); - $scope.minutes = Math.floor($scope.millis / 60000); - $scope.hours = 0; - $scope.days = 0; - $scope.months = 0; - $scope.years = 0; - } else if ($scope.maxTimeUnit === 'hour') { - $scope.seconds = Math.floor(($scope.millis / 1000) % 60); - $scope.minutes = Math.floor((($scope.millis / (60000)) % 60)); - $scope.hours = Math.floor($scope.millis / 3600000); - $scope.days = 0; - $scope.months = 0; - $scope.years = 0; - } else if ($scope.maxTimeUnit === 'month') { - $scope.seconds = Math.floor(($scope.millis / 1000) % 60); - $scope.minutes = Math.floor((($scope.millis / (60000)) % 60)); - $scope.hours = Math.floor((($scope.millis / (3600000)) % 24)); - $scope.days = Math.floor((($scope.millis / (3600000)) / 24) % 30); - $scope.months = Math.floor((($scope.millis / (3600000)) / 24) / 30); - $scope.years = 0; - } else if ($scope.maxTimeUnit === 'year') { - $scope.seconds = Math.floor(($scope.millis / 1000) % 60); - $scope.minutes = Math.floor((($scope.millis / (60000)) % 60)); - $scope.hours = Math.floor((($scope.millis / (3600000)) % 24)); - $scope.days = Math.floor((($scope.millis / (3600000)) / 24) % 30); - $scope.months = Math.floor((($scope.millis / (3600000)) / 24 / 30) % 12); - $scope.years = Math.floor(($scope.millis / (3600000)) / 24 / 365); - } - - // plural - singular unit decision - $scope.secondsS = $scope.seconds == 1 ? '' : 's'; - $scope.minutesS = $scope.minutes == 1 ? '' : 's'; - $scope.hoursS = $scope.hours == 1 ? '' : 's'; - $scope.daysS = $scope.days == 1 ? '' : 's'; - $scope.monthsS = $scope.months == 1 ? '' : 's'; - $scope.yearsS = $scope.years == 1 ? '' : 's'; - //add leading zero if number is smaller than 10 - $scope.sseconds = $scope.seconds < 10 ? '0' + $scope.seconds : $scope.seconds; - $scope.mminutes = $scope.minutes < 10 ? '0' + $scope.minutes : $scope.minutes; - $scope.hhours = $scope.hours < 10 ? '0' + $scope.hours : $scope.hours; - $scope.ddays = $scope.days < 10 ? '0' + $scope.days : $scope.days; - $scope.mmonths = $scope.months < 10 ? '0' + $scope.months : $scope.months; - $scope.yyears = $scope.years < 10 ? '0' + $scope.years : $scope.years; - - } - - //determine initial values of time units and add AddSeconds functionality - if ($scope.countdownattr) { - $scope.millis = $scope.countdownattr * 1000; - - $scope.addCDSeconds = $element[0].addCDSeconds = function (extraSeconds) { - $scope.countdown += extraSeconds; - $scope.$digest(); - if (!$scope.isRunning) { - $scope.start(); - } - }; - - $scope.$on('timer-add-cd-seconds', function (e, extraSeconds) { - $timeout(function () { - $scope.addCDSeconds(extraSeconds); - }); - }); - - $scope.$on('timer-set-countdown-seconds', function (e, countdownSeconds) { - if (!$scope.isRunning) { - $scope.clear(); - } - - $scope.countdown = countdownSeconds; - $scope.millis = countdownSeconds * 1000; - calculateTimeUnits(); - }); - } else { - $scope.millis = 0; - } - calculateTimeUnits(); - - var tick = function () { - - $scope.millis = new Date() - $scope.startTime; - var adjustment = $scope.millis % 1000; - - if ($scope.endTimeAttr) { - $scope.millis = $scope.endTime - new Date(); - adjustment = $scope.interval - $scope.millis % 1000; - } - - - if ($scope.countdownattr) { - $scope.millis = $scope.countdown * 1000; - } - - if ($scope.millis < 0) { - $scope.stop(); - $scope.millis = 0; - calculateTimeUnits(); - return; - } - calculateTimeUnits(); - - //We are not using $timeout for a reason. Please read here - https://github.com/siddii/angular-timer/pull/5 - $scope.timeoutId = setTimeout(function () { - tick(); - $scope.$digest(); - }, $scope.interval - adjustment); - - $scope.$emit('timer-tick', {timeoutId: $scope.timeoutId, millis: $scope.millis}); - - if ($scope.countdown > 0) { - $scope.countdown--; - } - else if ($scope.countdown <= 0) { - $scope.stop(); - } - }; - - if ($scope.autoStart === undefined || $scope.autoStart === true) { - $scope.start(); - } - }] - }; - }]); - -/* commonjs package manager support (eg componentjs) */ -if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){ - module.exports = timerModule; -} diff --git a/bower.json b/bower.json index e876804..829ba31 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "author": "Siddique Hameed", "name": "angular-timer", - "version": "1.1.5", + "version": "1.3.5", "homepage": "https://github.com/siddii/angular-timer", "description": "Angular-Timer : A simple AngularJS directive demonstrating re-usability & interoperability", "repository": { @@ -9,13 +9,21 @@ "url": "git://github.com/siddii/angular-timer.git" }, "dependencies": { - "angular": ">= 1.0.7" + "angular": ">= 1.0.7", + "moment": "~2.11.2", + "humanize-duration": "~3.10.0" }, "devDependencies": { - "bootstrap": "2.3.2", - "angular-scenario": ">= 1.0.7", - "angular-mocks": ">= 1.0.7" + "bootstrap": "2.3.2", + "angular-scenario": ">= 1.0.7", + "angular-mocks": ">= 1.0.7" }, "main": "./dist/angular-timer.js", - "ignore": ["./node_modules/", "./bower_components/"] + "ignore": [ + "node_modules", + "bower_components" + ], + "resolutions": { + "moment": "~2.9.0" + } } diff --git a/bower_components/angular-mocks/.bower.json b/bower_components/angular-mocks/.bower.json index 0cfe175..8dbeca1 100644 --- a/bower_components/angular-mocks/.bower.json +++ b/bower_components/angular-mocks/.bower.json @@ -1,16 +1,17 @@ { "name": "angular-mocks", - "version": "1.2.0-rc.2", + "version": "1.3.14", "main": "./angular-mocks.js", + "ignore": [], "dependencies": { - "angular": "1.2.0-rc.2" + "angular": "1.3.14" }, "homepage": "https://github.com/angular/bower-angular-mocks", - "_release": "1.2.0-rc.2", + "_release": "1.3.14", "_resolution": { "type": "version", - "tag": "v1.2.0-rc.2", - "commit": "9bdf39463a7e59c35f4f6163853c8da4fbf81ea3" + "tag": "v1.3.14", + "commit": "b33962810730adca9a0f7165ecd4835b6bf40abb" }, "_source": "git://github.com/angular/bower-angular-mocks.git", "_target": ">= 1.0.7", diff --git a/bower_components/angular-mocks/README.md b/bower_components/angular-mocks/README.md index 69bc520..440cce9 100644 --- a/bower_components/angular-mocks/README.md +++ b/bower_components/angular-mocks/README.md @@ -1,4 +1,63 @@ -bower-angular-mocks -=================== +# packaged angular-mocks -angular-mocks.js bower repo \ No newline at end of file +This repo is for distribution on `npm` and `bower`. The source for this module is in the +[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngMock). +Please file issues and pull requests against that repo. + +## Install + +You can install this package either with `npm` or with `bower`. + +### npm + +```shell +npm install angular-mocks +``` + +You can `require` ngMock modules: + +```js +var angular = require('angular'); +angular.module('myMod', [ + require('angular-animate'), + require('angular-mocks/ngMock') + require('angular-mocks/ngAnimateMock') +]); +``` + +### bower + +```shell +bower install angular-mocks +``` + +The mocks are then available at `bower_components/angular-mocks/angular-mocks.js`. + +## Documentation + +Documentation is available on the +[AngularJS docs site](https://docs.angularjs.org/guide/unit-testing). + +## License + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/bower_components/angular-mocks/angular-mocks.js b/bower_components/angular-mocks/angular-mocks.js old mode 100755 new mode 100644 index 993912e..b6bd983 --- a/bower_components/angular-mocks/angular-mocks.js +++ b/bower_components/angular-mocks/angular-mocks.js @@ -1,13 +1,14 @@ /** - * @license AngularJS v1.2.0-rc.2 - * (c) 2010-2012 Google, Inc. http://angularjs.org + * @license AngularJS v1.3.14 + * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT - * - * TODO(vojta): wrap whole file into closure during build */ +(function(window, angular, undefined) { + +'use strict'; /** - * @ngdoc overview + * @ngdoc object * @name angular.mock * @description * @@ -18,7 +19,7 @@ angular.mock = {}; /** * ! This is a private undocumented service ! * - * @name ngMock.$browser + * @name $browser * * @description * This service is a mock implementation of {@link ng.$browser}. It provides fake @@ -52,9 +53,10 @@ angular.mock.$Browser = function() { self.onUrlChange = function(listener) { self.pollFns.push( function() { - if (self.$$lastUrl != self.$$url) { + if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) { self.$$lastUrl = self.$$url; - listener(self.$$url); + self.$$lastState = self.$$state; + listener(self.$$url, self.$$state); } } ); @@ -62,6 +64,8 @@ angular.mock.$Browser = function() { return listener; }; + self.$$checkUrlChange = angular.noop; + self.cookieHash = {}; self.lastCookieHash = {}; self.deferredFns = []; @@ -70,11 +74,17 @@ angular.mock.$Browser = function() { self.defer = function(fn, delay) { delay = delay || 0; self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId}); - self.deferredFns.sort(function(a,b){ return a.time - b.time;}); + self.deferredFns.sort(function(a, b) { return a.time - b.time;}); return self.deferredNextId++; }; + /** + * @name $browser#defer.now + * + * @description + * Current milliseconds mock time. + */ self.defer.now = 0; @@ -95,8 +105,7 @@ angular.mock.$Browser = function() { /** - * @name ngMock.$browser#defer.flush - * @methodOf ngMock.$browser + * @name $browser#defer.flush * * @description * Flushes all pending requests and executes the defer callbacks. @@ -108,9 +117,9 @@ angular.mock.$Browser = function() { self.defer.now += delay; } else { if (self.deferredFns.length) { - self.defer.now = self.deferredFns[self.deferredFns.length-1].time; + self.defer.now = self.deferredFns[self.deferredFns.length - 1].time; } else { - throw Error('No deferred tasks to be flushed'); + throw new Error('No deferred tasks to be flushed'); } } @@ -119,30 +128,7 @@ angular.mock.$Browser = function() { } }; - /** - * @name ngMock.$browser#defer.flushNext - * @methodOf ngMock.$browser - * - * @description - * Flushes next pending request and compares it to the provided delay - * - * @param {number=} expectedDelay the delay value that will be asserted against the delay of the next timeout function - */ - self.defer.flushNext = function(expectedDelay) { - var tick = self.deferredFns.shift(); - expect(tick.time).toEqual(expectedDelay); - tick.fn(); - }; - - /** - * @name ngMock.$browser#defer.now - * @propertyOf ngMock.$browser - * - * @description - * Current milliseconds mock time. - */ - - self.$$baseHref = ''; + self.$$baseHref = '/'; self.baseHref = function() { return this.$$baseHref; }; @@ -150,14 +136,13 @@ angular.mock.$Browser = function() { angular.mock.$Browser.prototype = { /** - * @name ngMock.$browser#poll - * @methodOf ngMock.$browser + * @name $browser#poll * * @description * run all fns in pollFns */ poll: function poll() { - angular.forEach(this.pollFns, function(pollFn){ + angular.forEach(this.pollFns, function(pollFn) { pollFn(); }); }, @@ -167,18 +152,27 @@ angular.mock.$Browser.prototype = { return pollFn; }, - url: function(url, replace) { + url: function(url, replace, state) { + if (angular.isUndefined(state)) { + state = null; + } if (url) { this.$$url = url; + // Native pushState serializes & copies the object; simulate it. + this.$$state = angular.copy(state); return this; } return this.$$url; }, + state: function() { + return this.$$state; + }, + cookies: function(name, value) { if (name) { - if (value == undefined) { + if (angular.isUndefined(value)) { delete this.cookieHash[name]; } else { if (angular.isString(value) && //strings only @@ -202,25 +196,25 @@ angular.mock.$Browser.prototype = { /** - * @ngdoc object - * @name ngMock.$exceptionHandlerProvider + * @ngdoc provider + * @name $exceptionHandlerProvider * * @description - * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors passed - * into the `$exceptionHandler`. + * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors + * passed to the `$exceptionHandler`. */ /** - * @ngdoc object - * @name ngMock.$exceptionHandler + * @ngdoc service + * @name $exceptionHandler * * @description * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed - * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration + * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration * information. * * - *
+ * ```js
  *   describe('$exceptionHandlerProvider', function() {
  *
  *     it('should capture log messages and exceptions', function() {
@@ -241,7 +235,7 @@ angular.mock.$Browser.prototype = {
  *       });
  *     });
  *   });
- * 
+ * ``` */ angular.mock.$ExceptionHandlerProvider = function() { @@ -249,44 +243,42 @@ angular.mock.$ExceptionHandlerProvider = function() { /** * @ngdoc method - * @name ngMock.$exceptionHandlerProvider#mode - * @methodOf ngMock.$exceptionHandlerProvider + * @name $exceptionHandlerProvider#mode * * @description * Sets the logging mode. * * @param {string} mode Mode of operation, defaults to `rethrow`. * - * - `rethrow`: If any errors are passed into the handler in tests, it typically - * means that there is a bug in the application or test, so this mock will - * make these tests fail. - * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` mode stores an - * array of errors in `$exceptionHandler.errors`, to allow later assertion of them. - * See {@link ngMock.$log#assertEmpty assertEmpty()} and - * {@link ngMock.$log#reset reset()} + * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` + * mode stores an array of errors in `$exceptionHandler.errors`, to allow later + * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and + * {@link ngMock.$log#reset reset()} + * - `rethrow`: If any errors are passed to the handler in tests, it typically means that there + * is a bug in the application or test, so this mock will make these tests fail. + * For any implementations that expect exceptions to be thrown, the `rethrow` mode + * will also maintain a log of thrown errors. */ this.mode = function(mode) { - switch(mode) { - case 'rethrow': - handler = function(e) { - throw e; - }; - break; + + switch (mode) { case 'log': + case 'rethrow': var errors = []; - handler = function(e) { if (arguments.length == 1) { errors.push(e); } else { errors.push([].slice.call(arguments, 0)); } + if (mode === "rethrow") { + throw e; + } }; - handler.errors = errors; break; default: - throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); + throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); } }; @@ -300,7 +292,7 @@ angular.mock.$ExceptionHandlerProvider = function() { /** * @ngdoc service - * @name ngMock.$log + * @name $log * * @description * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays @@ -316,15 +308,15 @@ angular.mock.$LogProvider = function() { } this.debugEnabled = function(flag) { - if (angular.isDefined(flag)) { - debug = flag; - return this; - } else { - return debug; - } + if (angular.isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } }; - this.$get = function () { + this.$get = function() { var $log = { log: function() { $log.log.logs.push(concat([], arguments, 0)); }, warn: function() { $log.warn.logs.push(concat([], arguments, 0)); }, @@ -339,110 +331,105 @@ angular.mock.$LogProvider = function() { /** * @ngdoc method - * @name ngMock.$log#reset - * @methodOf ngMock.$log + * @name $log#reset * * @description * Reset all of the logging arrays to empty. */ - $log.reset = function () { + $log.reset = function() { /** * @ngdoc property - * @name ngMock.$log#log.logs - * @propertyOf ngMock.$log + * @name $log#log.logs * * @description - * Array of messages logged using {@link ngMock.$log#log}. + * Array of messages logged using {@link ng.$log#log `log()`}. * * @example - *
+       * ```js
        * $log.log('Some Log');
        * var first = $log.log.logs.unshift();
-       * 
+ * ``` */ $log.log.logs = []; /** * @ngdoc property - * @name ngMock.$log#info.logs - * @propertyOf ngMock.$log + * @name $log#info.logs * * @description - * Array of messages logged using {@link ngMock.$log#info}. + * Array of messages logged using {@link ng.$log#info `info()`}. * * @example - *
+       * ```js
        * $log.info('Some Info');
        * var first = $log.info.logs.unshift();
-       * 
+ * ``` */ $log.info.logs = []; /** * @ngdoc property - * @name ngMock.$log#warn.logs - * @propertyOf ngMock.$log + * @name $log#warn.logs * * @description - * Array of messages logged using {@link ngMock.$log#warn}. + * Array of messages logged using {@link ng.$log#warn `warn()`}. * * @example - *
+       * ```js
        * $log.warn('Some Warning');
        * var first = $log.warn.logs.unshift();
-       * 
+ * ``` */ $log.warn.logs = []; /** * @ngdoc property - * @name ngMock.$log#error.logs - * @propertyOf ngMock.$log + * @name $log#error.logs * * @description - * Array of messages logged using {@link ngMock.$log#error}. + * Array of messages logged using {@link ng.$log#error `error()`}. * * @example - *
-       * $log.log('Some Error');
+       * ```js
+       * $log.error('Some Error');
        * var first = $log.error.logs.unshift();
-       * 
+ * ``` */ $log.error.logs = []; /** * @ngdoc property - * @name ngMock.$log#debug.logs - * @propertyOf ngMock.$log + * @name $log#debug.logs * * @description - * Array of messages logged using {@link ngMock.$log#debug}. + * Array of messages logged using {@link ng.$log#debug `debug()`}. * * @example - *
+       * ```js
        * $log.debug('Some Error');
        * var first = $log.debug.logs.unshift();
-       * 
+ * ``` */ - $log.debug.logs = [] + $log.debug.logs = []; }; /** * @ngdoc method - * @name ngMock.$log#assertEmpty - * @methodOf ngMock.$log + * @name $log#assertEmpty * * @description - * Assert that the all of the logging methods have no logged messages. If messages present, an exception is thrown. + * Assert that all of the logging methods have no logged messages. If any messages are present, + * an exception is thrown. */ $log.assertEmpty = function() { var errors = []; angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) { angular.forEach($log[logLevel].logs, function(log) { - angular.forEach(log, function (logItem) { - errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || '')); + angular.forEach(log, function(logItem) { + errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + + (logItem.stack || '')); }); }); }); if (errors.length) { - errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or an expected " + - "log message was not checked and removed:"); + errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " + + "an expected log message was not checked and removed:"); errors.push(''); throw new Error(errors.join('\n---------\n')); } @@ -454,242 +441,393 @@ angular.mock.$LogProvider = function() { }; -(function() { - var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; +/** + * @ngdoc service + * @name $interval + * + * @description + * Mock implementation of the $interval service. + * + * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to + * move forward by `millis` milliseconds and trigger any functions scheduled to run in that + * time. + * + * @param {function()} fn A function that should be called repeatedly. + * @param {number} delay Number of milliseconds between each function call. + * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat + * indefinitely. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @returns {promise} A promise which will be notified on each iteration. + */ +angular.mock.$IntervalProvider = function() { + this.$get = ['$browser', '$rootScope', '$q', '$$q', + function($browser, $rootScope, $q, $$q) { + var repeatFns = [], + nextRepeatId = 0, + now = 0; + + var $interval = function(fn, delay, count, invokeApply) { + var iteration = 0, + skipApply = (angular.isDefined(invokeApply) && !invokeApply), + deferred = (skipApply ? $$q : $q).defer(), + promise = deferred.promise; + + count = (angular.isDefined(count)) ? count : 0; + promise.then(null, null, fn); + + promise.$$intervalId = nextRepeatId; + + function tick() { + deferred.notify(iteration++); + + if (count > 0 && iteration >= count) { + var fnIndex; + deferred.resolve(iteration); + + angular.forEach(repeatFns, function(fn, index) { + if (fn.id === promise.$$intervalId) fnIndex = index; + }); + + if (fnIndex !== undefined) { + repeatFns.splice(fnIndex, 1); + } + } + + if (skipApply) { + $browser.defer.flush(); + } else { + $rootScope.$apply(); + } + } + + repeatFns.push({ + nextTime:(now + delay), + delay: delay, + fn: tick, + id: nextRepeatId, + deferred: deferred + }); + repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;}); + + nextRepeatId++; + return promise; + }; + /** + * @ngdoc method + * @name $interval#cancel + * + * @description + * Cancels a task associated with the `promise`. + * + * @param {promise} promise A promise from calling the `$interval` function. + * @returns {boolean} Returns `true` if the task was successfully cancelled. + */ + $interval.cancel = function(promise) { + if (!promise) return false; + var fnIndex; + + angular.forEach(repeatFns, function(fn, index) { + if (fn.id === promise.$$intervalId) fnIndex = index; + }); + + if (fnIndex !== undefined) { + repeatFns[fnIndex].deferred.reject('canceled'); + repeatFns.splice(fnIndex, 1); + return true; + } + + return false; + }; - function jsonStringToDate(string) { - var match; - if (match = string.match(R_ISO8061_STR)) { - var date = new Date(0), - tzHour = 0, - tzMin = 0; - if (match[9]) { - tzHour = int(match[9] + match[10]); - tzMin = int(match[9] + match[11]); + /** + * @ngdoc method + * @name $interval#flush + * @description + * + * Runs interval tasks scheduled to be run in the next `millis` milliseconds. + * + * @param {number=} millis maximum timeout amount to flush up until. + * + * @return {number} The amount of time moved forward. + */ + $interval.flush = function(millis) { + now += millis; + while (repeatFns.length && repeatFns[0].nextTime <= now) { + var task = repeatFns[0]; + task.fn(); + task.nextTime += task.delay; + repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;}); } - date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); - date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); - return date; + return millis; + }; + + return $interval; + }]; +}; + + +/* jshint -W101 */ +/* The R_ISO8061_STR regex is never going to fit into the 100 char limit! + * This directive should go inside the anonymous function but a bug in JSHint means that it would + * not be enacted early enough to prevent the warning. + */ +var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; + +function jsonStringToDate(string) { + var match; + if (match = string.match(R_ISO8061_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0; + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); } - return string; + date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); + date.setUTCHours(int(match[4] || 0) - tzHour, + int(match[5] || 0) - tzMin, + int(match[6] || 0), + int(match[7] || 0)); + return date; } + return string; +} - function int(str) { - return parseInt(str, 10); +function int(str) { + return parseInt(str, 10); +} + +function padNumber(num, digits, trim) { + var neg = ''; + if (num < 0) { + neg = '-'; + num = -num; } + num = '' + num; + while (num.length < digits) num = '0' + num; + if (trim) + num = num.substr(num.length - digits); + return neg + num; +} - function padNumber(num, digits, trim) { - var neg = ''; - if (num < 0) { - neg = '-'; - num = -num; - } - num = '' + num; - while(num.length < digits) num = '0' + num; - if (trim) - num = num.substr(num.length - digits); - return neg + num; + +/** + * @ngdoc type + * @name angular.mock.TzDate + * @description + * + * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. + * + * Mock of the Date type which has its timezone specified via constructor arg. + * + * The main purpose is to create Date-like instances with timezone fixed to the specified timezone + * offset, so that we can test code that depends on local timezone settings without dependency on + * the time zone settings of the machine where the code is running. + * + * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) + * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* + * + * @example + * !!!! WARNING !!!!! + * This is not a complete Date object so only methods that were implemented can be called safely. + * To make matters worse, TzDate instances inherit stuff from Date via a prototype. + * + * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is + * incomplete we might be missing some non-standard methods. This can result in errors like: + * "Date.prototype.foo called on incompatible Object". + * + * ```js + * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z'); + * newYearInBratislava.getTimezoneOffset() => -60; + * newYearInBratislava.getFullYear() => 2010; + * newYearInBratislava.getMonth() => 0; + * newYearInBratislava.getDate() => 1; + * newYearInBratislava.getHours() => 0; + * newYearInBratislava.getMinutes() => 0; + * newYearInBratislava.getSeconds() => 0; + * ``` + * + */ +angular.mock.TzDate = function(offset, timestamp) { + var self = new Date(0); + if (angular.isString(timestamp)) { + var tsStr = timestamp; + + self.origDate = jsonStringToDate(timestamp); + + timestamp = self.origDate.getTime(); + if (isNaN(timestamp)) + throw { + name: "Illegal Argument", + message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" + }; + } else { + self.origDate = new Date(timestamp); } + var localOffset = new Date(timestamp).getTimezoneOffset(); + self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60; + self.date = new Date(timestamp + self.offsetDiff); - /** - * @ngdoc object - * @name angular.mock.TzDate - * @description - * - * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. - * - * Mock of the Date type which has its timezone specified via constructor arg. - * - * The main purpose is to create Date-like instances with timezone fixed to the specified timezone - * offset, so that we can test code that depends on local timezone settings without dependency on - * the time zone settings of the machine where the code is running. - * - * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) - * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* - * - * @example - * !!!! WARNING !!!!! - * This is not a complete Date object so only methods that were implemented can be called safely. - * To make matters worse, TzDate instances inherit stuff from Date via a prototype. - * - * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is - * incomplete we might be missing some non-standard methods. This can result in errors like: - * "Date.prototype.foo called on incompatible Object". - * - *
-   * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
-   * newYearInBratislava.getTimezoneOffset() => -60;
-   * newYearInBratislava.getFullYear() => 2010;
-   * newYearInBratislava.getMonth() => 0;
-   * newYearInBratislava.getDate() => 1;
-   * newYearInBratislava.getHours() => 0;
-   * newYearInBratislava.getMinutes() => 0;
-   * newYearInBratislava.getSeconds() => 0;
-   * 
- * - */ - angular.mock.TzDate = function (offset, timestamp) { - var self = new Date(0); - if (angular.isString(timestamp)) { - var tsStr = timestamp; - - self.origDate = jsonStringToDate(timestamp); - - timestamp = self.origDate.getTime(); - if (isNaN(timestamp)) - throw { - name: "Illegal Argument", - message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" - }; - } else { - self.origDate = new Date(timestamp); - } + self.getTime = function() { + return self.date.getTime() - self.offsetDiff; + }; - var localOffset = new Date(timestamp).getTimezoneOffset(); - self.offsetDiff = localOffset*60*1000 - offset*1000*60*60; - self.date = new Date(timestamp + self.offsetDiff); + self.toLocaleDateString = function() { + return self.date.toLocaleDateString(); + }; - self.getTime = function() { - return self.date.getTime() - self.offsetDiff; - }; + self.getFullYear = function() { + return self.date.getFullYear(); + }; - self.toLocaleDateString = function() { - return self.date.toLocaleDateString(); - }; + self.getMonth = function() { + return self.date.getMonth(); + }; - self.getFullYear = function() { - return self.date.getFullYear(); - }; + self.getDate = function() { + return self.date.getDate(); + }; - self.getMonth = function() { - return self.date.getMonth(); - }; + self.getHours = function() { + return self.date.getHours(); + }; - self.getDate = function() { - return self.date.getDate(); - }; + self.getMinutes = function() { + return self.date.getMinutes(); + }; - self.getHours = function() { - return self.date.getHours(); - }; + self.getSeconds = function() { + return self.date.getSeconds(); + }; - self.getMinutes = function() { - return self.date.getMinutes(); - }; + self.getMilliseconds = function() { + return self.date.getMilliseconds(); + }; - self.getSeconds = function() { - return self.date.getSeconds(); - }; + self.getTimezoneOffset = function() { + return offset * 60; + }; - self.getMilliseconds = function() { - return self.date.getMilliseconds(); - }; + self.getUTCFullYear = function() { + return self.origDate.getUTCFullYear(); + }; - self.getTimezoneOffset = function() { - return offset * 60; - }; + self.getUTCMonth = function() { + return self.origDate.getUTCMonth(); + }; - self.getUTCFullYear = function() { - return self.origDate.getUTCFullYear(); - }; + self.getUTCDate = function() { + return self.origDate.getUTCDate(); + }; - self.getUTCMonth = function() { - return self.origDate.getUTCMonth(); - }; + self.getUTCHours = function() { + return self.origDate.getUTCHours(); + }; - self.getUTCDate = function() { - return self.origDate.getUTCDate(); - }; + self.getUTCMinutes = function() { + return self.origDate.getUTCMinutes(); + }; - self.getUTCHours = function() { - return self.origDate.getUTCHours(); - }; + self.getUTCSeconds = function() { + return self.origDate.getUTCSeconds(); + }; - self.getUTCMinutes = function() { - return self.origDate.getUTCMinutes(); - }; + self.getUTCMilliseconds = function() { + return self.origDate.getUTCMilliseconds(); + }; - self.getUTCSeconds = function() { - return self.origDate.getUTCSeconds(); - }; + self.getDay = function() { + return self.date.getDay(); + }; - self.getUTCMilliseconds = function() { - return self.origDate.getUTCMilliseconds(); + // provide this method only on browsers that already have it + if (self.toISOString) { + self.toISOString = function() { + return padNumber(self.origDate.getUTCFullYear(), 4) + '-' + + padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' + + padNumber(self.origDate.getUTCDate(), 2) + 'T' + + padNumber(self.origDate.getUTCHours(), 2) + ':' + + padNumber(self.origDate.getUTCMinutes(), 2) + ':' + + padNumber(self.origDate.getUTCSeconds(), 2) + '.' + + padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z'; }; + } - self.getDay = function() { - return self.date.getDay(); + //hide all methods not implemented in this mock that the Date prototype exposes + var unimplementedMethods = ['getUTCDay', + 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', + 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', + 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', + 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', + 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; + + angular.forEach(unimplementedMethods, function(methodName) { + self[methodName] = function() { + throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock"); }; + }); - // provide this method only on browsers that already have it - if (self.toISOString) { - self.toISOString = function() { - return padNumber(self.origDate.getUTCFullYear(), 4) + '-' + - padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' + - padNumber(self.origDate.getUTCDate(), 2) + 'T' + - padNumber(self.origDate.getUTCHours(), 2) + ':' + - padNumber(self.origDate.getUTCMinutes(), 2) + ':' + - padNumber(self.origDate.getUTCSeconds(), 2) + '.' + - padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z' - } - } - - //hide all methods not implemented in this mock that the Date prototype exposes - var unimplementedMethods = ['getUTCDay', - 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', - 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', - 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', - 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', - 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; - - angular.forEach(unimplementedMethods, function(methodName) { - self[methodName] = function() { - throw Error("Method '" + methodName + "' is not implemented in the TzDate mock"); - }; - }); - - return self; - }; + return self; +}; - //make "tzDateInstance instanceof Date" return true - angular.mock.TzDate.prototype = Date.prototype; -})(); +//make "tzDateInstance instanceof Date" return true +angular.mock.TzDate.prototype = Date.prototype; +/* jshint +W101 */ -angular.mock.animate = angular.module('mock.animate', ['ng']) +angular.mock.animate = angular.module('ngAnimateMock', ['ng']) .config(['$provide', function($provide) { - $provide.decorator('$animate', function($delegate) { + var reflowQueue = []; + $provide.value('$$animateReflow', function(fn) { + var index = reflowQueue.length; + reflowQueue.push(fn); + return function cancel() { + reflowQueue.splice(index, 1); + }; + }); + + $provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser', + function($delegate, $$asyncCallback, $timeout, $browser) { var animate = { - queue : [], - enabled : $delegate.enabled, - flushNext : function(name) { - var tick = animate.queue.shift(); - expect(tick.method).toBe(name); - tick.fn(); - return tick; + queue: [], + cancel: $delegate.cancel, + enabled: $delegate.enabled, + triggerCallbackEvents: function() { + $$asyncCallback.flush(); + }, + triggerCallbackPromise: function() { + $timeout.flush(0); + }, + triggerCallbacks: function() { + this.triggerCallbackEvents(); + this.triggerCallbackPromise(); + }, + triggerReflow: function() { + angular.forEach(reflowQueue, function(fn) { + fn(); + }); + reflowQueue = []; } }; - forEach(['enter','leave','move','addClass','removeClass'], function(method) { + angular.forEach( + ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) { animate[method] = function() { - var params = arguments; animate.queue.push({ - method : method, - params : params, - element : angular.isElement(params[0]) && params[0], - parent : angular.isElement(params[1]) && params[1], - after : angular.isElement(params[2]) && params[2], - fn : function() { - $delegate[method].apply($delegate, params); - } + event: method, + element: arguments[0], + options: arguments[arguments.length - 1], + args: arguments }); + return $delegate[method].apply($delegate, arguments); }; }); return animate; - }); + }]); }]); @@ -701,9 +839,11 @@ angular.mock.animate = angular.module('mock.animate', ['ng']) * * *NOTE*: this is not an injectable instance, just a globally available function. * - * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for debugging. + * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for + * debugging. * - * This method is also available on window, where it can be used to display objects on debug console. + * This method is also available on window, where it can be used to display objects on debug + * console. * * @param {*} object - any object to turn into string. * @return {string} a serialized string of the argument @@ -733,7 +873,8 @@ angular.mock.dump = function(object) { } else if (object instanceof Error) { out = object.stack || ('' + object.name + ': ' + object.message); } else { - // TODO(i): this prevents methods to be logged, we should have a better way to serialize objects + // TODO(i): this prevents methods being logged, + // we should have a better way to serialize objects out = angular.toJson(object, true); } } else { @@ -746,13 +887,13 @@ angular.mock.dump = function(object) { function serializeScope(scope, offset) { offset = offset || ' '; var log = [offset + 'Scope(' + scope.$id + '): {']; - for ( var key in scope ) { - if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) { + for (var key in scope) { + if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) { log.push(' ' + key + ': ' + angular.toJson(scope[key])); } } var child = scope.$$childHead; - while(child) { + while (child) { log.push(serializeScope(child, offset + ' ')); child = child.$$nextSibling; } @@ -762,8 +903,8 @@ angular.mock.dump = function(object) { }; /** - * @ngdoc object - * @name ngMock.$httpBackend + * @ngdoc service + * @name $httpBackend * @description * Fake HTTP backend implementation suitable for unit testing applications that use the * {@link ng.$http $http service}. @@ -772,8 +913,8 @@ angular.mock.dump = function(object) { * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}. * * During unit testing, we want our unit tests to run quickly and have no external dependencies so - * we don’t want to send {@link https://developer.mozilla.org/en/xmlhttprequest XHR} or - * {@link http://en.wikipedia.org/wiki/JSONP JSONP} requests to a real server. All we really need is + * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or + * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is * to verify whether a certain request has been sent or not, or alternatively just let the * application make requests, respond with pre-trained responses and assert that the end result is * what we expect it to be. @@ -784,7 +925,7 @@ angular.mock.dump = function(object) { * When an Angular application needs some data from a server, it calls the $http service, which * sends the request to a real server using $httpBackend service. With dependency injection, it is * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify - * the requests and respond with some testing data without sending a request to real server. + * the requests and respond with some testing data without sending a request to a real server. * * There are two ways to specify what test data should be returned as http responses by the mock * backend when the code under test makes http requests: @@ -851,20 +992,24 @@ angular.mock.dump = function(object) { * * # Flushing HTTP requests * - * The $httpBackend used in production, always responds to requests with responses asynchronously. - * If we preserved this behavior in unit testing, we'd have to create async unit tests, which are - * hard to write, follow and maintain. At the same time the testing mock, can't respond - * synchronously because that would change the execution of the code under test. For this reason the - * mock $httpBackend has a `flush()` method, which allows the test to explicitly flush pending - * requests and thus preserving the async api of the backend, while allowing the test to execute - * synchronously. + * The $httpBackend used in production always responds to requests asynchronously. If we preserved + * this behavior in unit testing, we'd have to create async unit tests, which are hard to write, + * to follow and to maintain. But neither can the testing mock respond synchronously; that would + * change the execution of the code under test. For this reason, the mock $httpBackend has a + * `flush()` method, which allows the test to explicitly flush pending requests. This preserves + * the async api of the backend, while allowing the test to execute synchronously. * * * # Unit testing with mock $httpBackend - * The following code shows how to setup and use the mock backend in unit testing a controller. - * First we create the controller under test + * The following code shows how to setup and use the mock backend when unit testing a controller. + * First we create the controller under test: * -
+  ```js
+  // The module code
+  angular
+    .module('MyApp', [])
+    .controller('MyController', MyController);
+
   // The controller code
   function MyController($scope, $http) {
     var authToken;
@@ -885,20 +1030,24 @@ angular.mock.dump = function(object) {
       });
     };
   }
-  
+ ``` * - * Now we setup the mock backend and create the test specs. + * Now we setup the mock backend and create the test specs: * -
+  ```js
     // testing controller
     describe('MyController', function() {
-       var $httpBackend, $rootScope, createController;
+       var $httpBackend, $rootScope, createController, authRequestHandler;
+
+       // Set up the module
+       beforeEach(module('MyApp'));
 
        beforeEach(inject(function($injector) {
          // Set up the mock http service responses
          $httpBackend = $injector.get('$httpBackend');
          // backend definition common for all tests
-         $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
+         authRequestHandler = $httpBackend.when('GET', '/auth.py')
+                                .respond({userId: 'userX'}, {'A-Token': 'xxx'});
 
          // Get hold of a scope (i.e. the root scope)
          $rootScope = $injector.get('$rootScope');
@@ -924,6 +1073,18 @@ angular.mock.dump = function(object) {
        });
 
 
+       it('should fail authentication', function() {
+
+         // Notice how you can change the response even after it was set
+         authRequestHandler.respond(401, '');
+
+         $httpBackend.expectGET('/auth.py');
+         var controller = createController();
+         $httpBackend.flush();
+         expect($rootScope.status).toBe('Failed...');
+       });
+
+
        it('should send msg to server', function() {
          var controller = createController();
          $httpBackend.flush();
@@ -955,10 +1116,10 @@ angular.mock.dump = function(object) {
          $httpBackend.flush();
        });
     });
-   
+ ``` */ angular.mock.$HttpBackendProvider = function() { - this.$get = ['$rootScope', createHttpBackendMock]; + this.$get = ['$rootScope', '$timeout', createHttpBackendMock]; }; /** @@ -975,19 +1136,20 @@ angular.mock.$HttpBackendProvider = function() { * @param {Object=} $browser Auto-flushing enabled if specified * @return {Object} Instance of $httpBackend mock */ -function createHttpBackendMock($rootScope, $delegate, $browser) { +function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { var definitions = [], expectations = [], responses = [], - responsesPush = angular.bind(responses, responses.push); + responsesPush = angular.bind(responses, responses.push), + copy = angular.copy; - function createResponse(status, data, headers) { + function createResponse(status, data, headers, statusText) { if (angular.isFunction(status)) return status; return function() { return angular.isNumber(status) - ? [status, data, headers] - : [200, status, data]; + ? [status, data, headers, statusText] + : [200, status, data, headers]; }; } @@ -1004,14 +1166,17 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { } function wrapResponse(wrapped) { - if (!$browser && timeout && timeout.then) timeout.then(handleTimeout); + if (!$browser && timeout) { + timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout); + } return handleResponse; function handleResponse() { var response = wrapped.response(method, url, data, headers); xhr.$$respHeaders = response[2]; - callback(response[0], response[1], xhr.getAllResponseHeaders()); + callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(), + copy(response[3] || '')); } function handleTimeout() { @@ -1032,7 +1197,8 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { if (!expectation.matchHeaders(headers)) throw new Error('Expected ' + expectation + ' with different headers\n' + - 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + prettyPrint(headers)); + 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + + prettyPrint(headers)); expectations.shift(); @@ -1051,7 +1217,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { ($browser ? $browser.defer : responsesPush)(wrapResponse(definition)); } else if (definition.passThrough) { $delegate(method, url, data, callback, headers, timeout, withCredentials); - } else throw Error('No response defined !'); + } else throw new Error('No response defined !'); return; } } @@ -1063,36 +1229,44 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { /** * @ngdoc method - * @name ngMock.$httpBackend#when - * @methodOf ngMock.$httpBackend + * @name $httpBackend#when * @description * Creates a new backend definition. * * @param {string} method HTTP method. - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header * object and returns true if the headers match the current definition. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. * - * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` - * – The respond method takes a set of static data to be returned or a function that can return - * an array containing response status (number), response data (string) and response headers - * (Object). + * - respond – + * `{function([status,] data[, headers, statusText]) + * | function(function(method, url, data, headers)}` + * – The respond method takes a set of static data to be returned or a function that can + * return an array containing response status (number), response data (string), response + * headers (Object), and the text for the status (string). The respond method returns the + * `requestHandler` object for possible overrides. */ $httpBackend.when = function(method, url, data, headers) { var definition = new MockHttpExpectation(method, url, data, headers), chain = { - respond: function(status, data, headers) { - definition.response = createResponse(status, data, headers); + respond: function(status, data, headers, statusText) { + definition.passThrough = undefined; + definition.response = createResponse(status, data, headers, statusText); + return chain; } }; if ($browser) { chain.passThrough = function() { + definition.response = undefined; definition.passThrough = true; + return chain; }; } @@ -1102,225 +1276,244 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { /** * @ngdoc method - * @name ngMock.$httpBackend#whenGET - * @methodOf ngMock.$httpBackend + * @name $httpBackend#whenGET * @description * Creates a new backend definition for GET requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#whenHEAD - * @methodOf ngMock.$httpBackend + * @name $httpBackend#whenHEAD * @description * Creates a new backend definition for HEAD requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#whenDELETE - * @methodOf ngMock.$httpBackend + * @name $httpBackend#whenDELETE * @description * Creates a new backend definition for DELETE requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#whenPOST - * @methodOf ngMock.$httpBackend + * @name $httpBackend#whenPOST * @description * Creates a new backend definition for POST requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#whenPUT - * @methodOf ngMock.$httpBackend + * @name $httpBackend#whenPUT * @description * Creates a new backend definition for PUT requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#whenJSONP - * @methodOf ngMock.$httpBackend + * @name $httpBackend#whenJSONP * @description * Creates a new backend definition for JSONP requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ createShortMethods('when'); /** * @ngdoc method - * @name ngMock.$httpBackend#expect - * @methodOf ngMock.$httpBackend + * @name $httpBackend#expect * @description * Creates a new request expectation. * * @param {string} method HTTP method. - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that * receives data string and returns true if the data is as expected, or Object if request body * is in JSON format. * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header * object and returns true if the headers match the current expectation. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. * - * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` - * – The respond method takes a set of static data to be returned or a function that can return - * an array containing response status (number), response data (string) and response headers - * (Object). + * - respond – + * `{function([status,] data[, headers, statusText]) + * | function(function(method, url, data, headers)}` + * – The respond method takes a set of static data to be returned or a function that can + * return an array containing response status (number), response data (string), response + * headers (Object), and the text for the status (string). The respond method returns the + * `requestHandler` object for possible overrides. */ $httpBackend.expect = function(method, url, data, headers) { - var expectation = new MockHttpExpectation(method, url, data, headers); + var expectation = new MockHttpExpectation(method, url, data, headers), + chain = { + respond: function(status, data, headers, statusText) { + expectation.response = createResponse(status, data, headers, statusText); + return chain; + } + }; + expectations.push(expectation); - return { - respond: function(status, data, headers) { - expectation.response = createResponse(status, data, headers); - } - }; + return chain; }; /** * @ngdoc method - * @name ngMock.$httpBackend#expectGET - * @methodOf ngMock.$httpBackend + * @name $httpBackend#expectGET * @description * Creates a new request expectation for GET requests. For more info see `expect()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. See #expect for more info. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. See #expect for more info. */ /** * @ngdoc method - * @name ngMock.$httpBackend#expectHEAD - * @methodOf ngMock.$httpBackend + * @name $httpBackend#expectHEAD * @description * Creates a new request expectation for HEAD requests. For more info see `expect()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#expectDELETE - * @methodOf ngMock.$httpBackend + * @name $httpBackend#expectDELETE * @description * Creates a new request expectation for DELETE requests. For more info see `expect()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#expectPOST - * @methodOf ngMock.$httpBackend + * @name $httpBackend#expectPOST * @description * Creates a new request expectation for POST requests. For more info see `expect()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that * receives data string and returns true if the data is as expected, or Object if request body * is in JSON format. * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#expectPUT - * @methodOf ngMock.$httpBackend + * @name $httpBackend#expectPUT * @description * Creates a new request expectation for PUT requests. For more info see `expect()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that * receives data string and returns true if the data is as expected, or Object if request body * is in JSON format. * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#expectPATCH - * @methodOf ngMock.$httpBackend + * @name $httpBackend#expectPATCH * @description * Creates a new request expectation for PATCH requests. For more info see `expect()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that * receives data string and returns true if the data is as expected, or Object if request body * is in JSON format. * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMock.$httpBackend#expectJSONP - * @methodOf ngMock.$httpBackend + * @name $httpBackend#expectJSONP * @description * Creates a new request expectation for JSONP requests. For more info see `expect()`. * - * @param {string|RegExp} url HTTP url. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. */ createShortMethods('expect'); /** * @ngdoc method - * @name ngMock.$httpBackend#flush - * @methodOf ngMock.$httpBackend + * @name $httpBackend#flush * @description * Flushes all pending requests using the trained responses. * @@ -1328,13 +1521,13 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { * all pending requests will be flushed. If there are no pending requests when the flush method * is called an exception is thrown (as this typically a sign of programming error). */ - $httpBackend.flush = function(count) { - $rootScope.$digest(); - if (!responses.length) throw Error('No pending request to flush !'); + $httpBackend.flush = function(count, digest) { + if (digest !== false) $rootScope.$digest(); + if (!responses.length) throw new Error('No pending request to flush !'); - if (angular.isDefined(count)) { + if (angular.isDefined(count) && count !== null) { while (count--) { - if (!responses.length) throw Error('No more pending request to flush !'); + if (!responses.length) throw new Error('No more pending request to flush !'); responses.shift()(); } } else { @@ -1342,14 +1535,13 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { responses.shift()(); } } - $httpBackend.verifyNoOutstandingExpectation(); + $httpBackend.verifyNoOutstandingExpectation(digest); }; /** * @ngdoc method - * @name ngMock.$httpBackend#verifyNoOutstandingExpectation - * @methodOf ngMock.$httpBackend + * @name $httpBackend#verifyNoOutstandingExpectation * @description * Verifies that all of the requests defined via the `expect` api were made. If any of the * requests were not made, verifyNoOutstandingExpectation throws an exception. @@ -1357,12 +1549,12 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { * Typically, you would call this method following each test case that asserts requests using an * "afterEach" clause. * - *
+   * ```js
    *   afterEach($httpBackend.verifyNoOutstandingExpectation);
-   * 
+ * ``` */ - $httpBackend.verifyNoOutstandingExpectation = function() { - $rootScope.$digest(); + $httpBackend.verifyNoOutstandingExpectation = function(digest) { + if (digest !== false) $rootScope.$digest(); if (expectations.length) { throw new Error('Unsatisfied requests: ' + expectations.join(', ')); } @@ -1371,29 +1563,27 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { /** * @ngdoc method - * @name ngMock.$httpBackend#verifyNoOutstandingRequest - * @methodOf ngMock.$httpBackend + * @name $httpBackend#verifyNoOutstandingRequest * @description * Verifies that there are no outstanding requests that need to be flushed. * * Typically, you would call this method following each test case that asserts requests using an * "afterEach" clause. * - *
+   * ```js
    *   afterEach($httpBackend.verifyNoOutstandingRequest);
-   * 
+ * ``` */ $httpBackend.verifyNoOutstandingRequest = function() { if (responses.length) { - throw Error('Unflushed requests: ' + responses.length); + throw new Error('Unflushed requests: ' + responses.length); } }; /** * @ngdoc method - * @name ngMock.$httpBackend#resetExpectations - * @methodOf ngMock.$httpBackend + * @name $httpBackend#resetExpectations * @description * Resets all request expectations, but preserves all backend definitions. Typically, you would * call resetExpectations during a multiple-phase test when you want to reuse the same instance of @@ -1408,16 +1598,16 @@ function createHttpBackendMock($rootScope, $delegate, $browser) { function createShortMethods(prefix) { - angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) { + angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) { $httpBackend[prefix + method] = function(url, headers) { - return $httpBackend[prefix](method, url, undefined, headers) - } + return $httpBackend[prefix](method, url, undefined, headers); + }; }); angular.forEach(['PUT', 'POST', 'PATCH'], function(method) { $httpBackend[prefix + method] = function(url, data, headers) { - return $httpBackend[prefix](method, url, data, headers) - } + return $httpBackend[prefix](method, url, data, headers); + }; }); } } @@ -1438,6 +1628,7 @@ function MockHttpExpectation(method, url, data, headers) { this.matchUrl = function(u) { if (!url) return true; if (angular.isFunction(url.test)) return url.test(u); + if (angular.isFunction(url)) return http://github.com/url(u); return url == u; }; @@ -1451,7 +1642,9 @@ function MockHttpExpectation(method, url, data, headers) { if (angular.isUndefined(data)) return true; if (data && angular.isFunction(data.test)) return data.test(d); if (data && angular.isFunction(data)) return data(d); - if (data && !angular.isString(data)) return angular.toJson(data) == d; + if (data && !angular.isString(data)) { + return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d)); + } return data == d; }; @@ -1460,6 +1653,10 @@ function MockHttpExpectation(method, url, data, headers) { }; } +function createMockXhr() { + return new MockXhr(); +} + function MockXhr() { // hack for testing $http, $httpBackend @@ -1482,7 +1679,8 @@ function MockXhr() { }; this.getResponseHeader = function(name) { - // the lookup must be case insensitive, that's why we try two quick lookups and full scan at last + // the lookup must be case insensitive, + // that's why we try two quick lookups first and full scan last var header = this.$$respHeaders[name]; if (header) return header; @@ -1511,20 +1709,19 @@ function MockXhr() { /** - * @ngdoc function - * @name ngMock.$timeout + * @ngdoc service + * @name $timeout * @description * * This service is just a simple decorator for {@link ng.$timeout $timeout} service * that adds a "flush" and "verifyNoPendingTasks" methods. */ -angular.mock.$TimeoutDecorator = function($delegate, $browser) { +angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) { /** * @ngdoc method - * @name ngMock.$timeout#flush - * @methodOf ngMock.$timeout + * @name $timeout#flush * @description * * Flushes the queue of pending tasks. @@ -1537,22 +1734,7 @@ angular.mock.$TimeoutDecorator = function($delegate, $browser) { /** * @ngdoc method - * @name ngMock.$timeout#flushNext - * @methodOf ngMock.$timeout - * @description - * - * Flushes the next timeout in the queue and compares it to the provided delay - * - * @param {number=} expectedDelay the delay value that will be asserted against the delay of the next timeout function - */ - $delegate.flushNext = function(expectedDelay) { - $browser.defer.flushNext(expectedDelay); - }; - - /** - * @ngdoc method - * @name ngMock.$timeout#verifyNoPendingTasks - * @methodOf ngMock.$timeout + * @name $timeout#verifyNoPendingTasks * @description * * Verifies that there are no pending tasks that need to be flushed. @@ -1574,7 +1756,49 @@ angular.mock.$TimeoutDecorator = function($delegate, $browser) { } return $delegate; -}; +}]; + +angular.mock.$RAFDecorator = ['$delegate', function($delegate) { + var queue = []; + var rafFn = function(fn) { + var index = queue.length; + queue.push(fn); + return function() { + queue.splice(index, 1); + }; + }; + + rafFn.supported = $delegate.supported; + + rafFn.flush = function() { + if (queue.length === 0) { + throw new Error('No rAF callbacks present'); + } + + var length = queue.length; + for (var i = 0; i < length; i++) { + queue[i](); + } + + queue = []; + }; + + return rafFn; +}]; + +angular.mock.$AsyncCallbackDecorator = ['$delegate', function($delegate) { + var callbacks = []; + var addFn = function(fn) { + callbacks.push(fn); + }; + addFn.flush = function() { + angular.forEach(callbacks, function(fn) { + fn(); + }); + callbacks = []; + }; + return addFn; +}]; /** * @@ -1582,43 +1806,58 @@ angular.mock.$TimeoutDecorator = function($delegate, $browser) { angular.mock.$RootElementProvider = function() { this.$get = function() { return angular.element('
'); - } + }; }; /** - * @ngdoc overview + * @ngdoc module * @name ngMock + * @packageName angular-mocks * @description * - * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful - * mocks to the {@link AUTO.$injector $injector}. + * # ngMock + * + * The `ngMock` module provides support to inject and mock Angular services into unit tests. + * In addition, ngMock also extends various core ng services such that they can be + * inspected and controlled in a synchronous manner within test code. + * + * + *
+ * */ angular.module('ngMock', ['ng']).provider({ $browser: angular.mock.$BrowserProvider, $exceptionHandler: angular.mock.$ExceptionHandlerProvider, $log: angular.mock.$LogProvider, + $interval: angular.mock.$IntervalProvider, $httpBackend: angular.mock.$HttpBackendProvider, $rootElement: angular.mock.$RootElementProvider -}).config(function($provide) { +}).config(['$provide', function($provide) { $provide.decorator('$timeout', angular.mock.$TimeoutDecorator); -}); + $provide.decorator('$$rAF', angular.mock.$RAFDecorator); + $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator); + $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator); +}]); /** - * @ngdoc overview + * @ngdoc module * @name ngMockE2E + * @module ngMockE2E + * @packageName angular-mocks * @description * * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing. * Currently there is only one mock present in this module - * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock. */ -angular.module('ngMockE2E', ['ng']).config(function($provide) { +angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); -}); +}]); /** - * @ngdoc object - * @name ngMockE2E.$httpBackend + * @ngdoc service + * @name $httpBackend + * @module ngMockE2E * @description * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of * applications that use the {@link ng.$http $http service}. @@ -1638,13 +1877,13 @@ angular.module('ngMockE2E', ['ng']).config(function($provide) { * use the `passThrough` request handler of `when` instead of `respond`. * * Additionally, we don't want to manually have to flush mocked out requests like we do during unit - * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests + * testing. For this reason the e2e $httpBackend flushes mocked out requests * automatically, closely simulating the behavior of the XMLHttpRequest object. * * To setup the application to run with this http backend, you have to create a module that depends * on the `ngMockE2E` and your application modules and defines the fake backend: * - *
+ * ```js
  *   myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
  *   myAppDev.run(function($httpBackend) {
  *     phones = [{name: 'phone1'}, {name: 'phone2'}];
@@ -1654,163 +1893,279 @@ angular.module('ngMockE2E', ['ng']).config(function($provide) {
  *
  *     // adds a new phone to the phones array
  *     $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
- *       phones.push(angular.fromJson(data));
+ *       var phone = angular.fromJson(data);
+ *       phones.push(phone);
+ *       return [200, phone, {}];
  *     });
  *     $httpBackend.whenGET(/^\/templates\//).passThrough();
  *     //...
  *   });
- * 
+ * ``` * * Afterwards, bootstrap your app with this new module. */ /** * @ngdoc method - * @name ngMockE2E.$httpBackend#when - * @methodOf ngMockE2E.$httpBackend + * @name $httpBackend#when + * @module ngMockE2E * @description * Creates a new backend definition. * * @param {string} method HTTP method. - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp)=} data HTTP request body. * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header * object and returns true if the headers match the current definition. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. * - * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` + * - respond – + * `{function([status,] data[, headers, statusText]) + * | function(function(method, url, data, headers)}` * – The respond method takes a set of static data to be returned or a function that can return - * an array containing response status (number), response data (string) and response headers - * (Object). - * - passThrough – `{function()}` – Any request matching a backend definition with `passThrough` - * handler, will be pass through to the real backend (an XHR request will be made to the - * server. + * an array containing response status (number), response data (string), response headers + * (Object), and the text for the status (string). + * - passThrough – `{function()}` – Any request matching a backend definition with + * `passThrough` handler will be passed through to the real backend (an XHR request will be made + * to the server.) + * - Both methods return the `requestHandler` object for possible overrides. */ /** * @ngdoc method - * @name ngMockE2E.$httpBackend#whenGET - * @methodOf ngMockE2E.$httpBackend + * @name $httpBackend#whenGET + * @module ngMockE2E * @description * Creates a new backend definition for GET requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(Object|function(Object))=} headers HTTP headers. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMockE2E.$httpBackend#whenHEAD - * @methodOf ngMockE2E.$httpBackend + * @name $httpBackend#whenHEAD + * @module ngMockE2E * @description * Creates a new backend definition for HEAD requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(Object|function(Object))=} headers HTTP headers. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMockE2E.$httpBackend#whenDELETE - * @methodOf ngMockE2E.$httpBackend + * @name $httpBackend#whenDELETE + * @module ngMockE2E * @description * Creates a new backend definition for DELETE requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(Object|function(Object))=} headers HTTP headers. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMockE2E.$httpBackend#whenPOST - * @methodOf ngMockE2E.$httpBackend + * @name $httpBackend#whenPOST + * @module ngMockE2E * @description * Creates a new backend definition for POST requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp)=} data HTTP request body. * @param {(Object|function(Object))=} headers HTTP headers. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMockE2E.$httpBackend#whenPUT - * @methodOf ngMockE2E.$httpBackend + * @name $httpBackend#whenPUT + * @module ngMockE2E * @description * Creates a new backend definition for PUT requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp)=} data HTTP request body. * @param {(Object|function(Object))=} headers HTTP headers. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMockE2E.$httpBackend#whenPATCH - * @methodOf ngMockE2E.$httpBackend + * @name $httpBackend#whenPATCH + * @module ngMockE2E * @description * Creates a new backend definition for PATCH requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @param {(string|RegExp)=} data HTTP request body. * @param {(Object|function(Object))=} headers HTTP headers. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. */ /** * @ngdoc method - * @name ngMockE2E.$httpBackend#whenJSONP - * @methodOf ngMockE2E.$httpBackend + * @name $httpBackend#whenJSONP + * @module ngMockE2E * @description * Creates a new backend definition for JSONP requests. For more info see `when()`. * - * @param {string|RegExp} url HTTP url. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. */ angular.mock.e2e = {}; -angular.mock.e2e.$httpBackendDecorator = ['$rootScope', '$delegate', '$browser', createHttpBackendMock]; +angular.mock.e2e.$httpBackendDecorator = + ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock]; + + +/** + * @ngdoc type + * @name $rootScope.Scope + * @module ngMock + * @description + * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These + * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when + * `ngMock` module is loaded. + * + * In addition to all the regular `Scope` methods, the following helper methods are available: + */ +angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) { + + var $rootScopePrototype = Object.getPrototypeOf($delegate); + $rootScopePrototype.$countChildScopes = countChildScopes; + $rootScopePrototype.$countWatchers = countWatchers; + + return $delegate; + + // ------------------------------------------------------------------------------------------ // + + /** + * @ngdoc method + * @name $rootScope.Scope#$countChildScopes + * @module ngMock + * @description + * Counts all the direct and indirect child scopes of the current scope. + * + * The current scope is excluded from the count. The count includes all isolate child scopes. + * + * @returns {number} Total number of child scopes. + */ + function countChildScopes() { + // jshint validthis: true + var count = 0; // exclude the current scope + var pendingChildHeads = [this.$$childHead]; + var currentScope; + + while (pendingChildHeads.length) { + currentScope = pendingChildHeads.shift(); + + while (currentScope) { + count += 1; + pendingChildHeads.push(currentScope.$$childHead); + currentScope = currentScope.$$nextSibling; + } + } -angular.mock.clearDataCache = function() { - var key, - cache = angular.element.cache; + return count; + } - for(key in cache) { - if (cache.hasOwnProperty(key)) { - var handle = cache[key].handle; - handle && angular.element(handle.elem).off(); - delete cache[key]; + /** + * @ngdoc method + * @name $rootScope.Scope#$countWatchers + * @module ngMock + * @description + * Counts all the watchers of direct and indirect child scopes of the current scope. + * + * The watchers of the current scope are included in the count and so are all the watchers of + * isolate child scopes. + * + * @returns {number} Total number of watchers. + */ + function countWatchers() { + // jshint validthis: true + var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope + var pendingChildHeads = [this.$$childHead]; + var currentScope; + + while (pendingChildHeads.length) { + currentScope = pendingChildHeads.shift(); + + while (currentScope) { + count += currentScope.$$watchers ? currentScope.$$watchers.length : 0; + pendingChildHeads.push(currentScope.$$childHead); + currentScope = currentScope.$$nextSibling; + } } + + return count; } -}; +}]; +if (window.jasmine || window.mocha) { -(window.jasmine || window.mocha) && (function(window) { + var currentSpec = null, + annotatedFunctions = [], + isSpecRunning = function() { + return !!currentSpec; + }; + + angular.mock.$$annotate = angular.injector.$$annotate; + angular.injector.$$annotate = function(fn) { + if (typeof fn === 'function' && !fn.$inject) { + annotatedFunctions.push(fn); + } + return angular.mock.$$annotate.apply(this, arguments); + }; - var currentSpec = null; - beforeEach(function() { + (window.beforeEach || window.setup)(function() { + annotatedFunctions = []; currentSpec = this; }); - afterEach(function() { + (window.afterEach || window.teardown)(function() { var injector = currentSpec.$injector; + annotatedFunctions.forEach(function(fn) { + delete fn.$inject; + }); + + angular.forEach(currentSpec.$modules, function(module) { + if (module && module.$$hashKey) { + module.$$hashKey = undefined; + } + }); + currentSpec.$injector = null; currentSpec.$modules = null; currentSpec = null; @@ -1820,8 +2175,6 @@ angular.mock.clearDataCache = function() { injector.get('$browser').pollFns.length = 0; } - angular.mock.clearDataCache(); - // clean up jquery's fragment cache angular.forEach(angular.element.fragments, function(val, key) { delete angular.element.fragments[key]; @@ -1835,16 +2188,13 @@ angular.mock.clearDataCache = function() { angular.callbacks.counter = 0; }); - function isSpecRunning() { - return currentSpec && (window.mocha || currentSpec.queue.running); - } - /** * @ngdoc function * @name angular.mock.module * @description * * *NOTE*: This function is also published on window for easy access.
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha * * This function registers a module configuration code. It collects the configuration information * which will be used when the injector is created by {@link angular.mock.inject inject}. @@ -1853,8 +2203,8 @@ angular.mock.clearDataCache = function() { * * @param {...(string|Function|Object)} fns any number of modules which are represented as string * aliases or as anonymous module initialization functions. The modules are used to - * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an - * object literal is passed they will be register as values in the module, the key being + * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an + * object literal is passed they will be registered as values in the module, the key being * the module name and the value being what is returned. */ window.module = angular.mock.module = function() { @@ -1863,7 +2213,7 @@ angular.mock.clearDataCache = function() { ///////////////////// function workFn() { if (currentSpec.$injector) { - throw Error('Injector already created, can not register a module!'); + throw new Error('Injector already created, can not register a module!'); } else { var modules = currentSpec.$modules || (currentSpec.$modules = []); angular.forEach(moduleFns, function(module) { @@ -1887,15 +2237,48 @@ angular.mock.clearDataCache = function() { * @description * * *NOTE*: This function is also published on window for easy access.
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha * * The inject function wraps a function into an injectable function. The inject() creates new - * instance of {@link AUTO.$injector $injector} per test, which is then used for + * instance of {@link auto.$injector $injector} per test, which is then used for * resolving references. * - * See also {@link angular.mock.module module} * + * ## Resolving References (Underscore Wrapping) + * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this + * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable + * that is declared in the scope of the `describe()` block. Since we would, most likely, want + * the variable to have the same name of the reference we have a problem, since the parameter + * to the `inject()` function would hide the outer variable. + * + * To help with this, the injected parameters can, optionally, be enclosed with underscores. + * These are ignored by the injector when the reference name is resolved. + * + * For example, the parameter `_myService_` would be resolved as the reference `myService`. + * Since it is available in the function body as _myService_, we can then assign it to a variable + * defined in an outer scope. + * + * ``` + * // Defined out reference variable outside + * var myService; + * + * // Wrap the parameter in underscores + * beforeEach( inject( function(_myService_){ + * myService = _myService_; + * })); + * + * // Use myService in a series of tests. + * it('makes use of myService', function() { + * myService.doStuff(); + * }); + * + * ``` + * + * See also {@link angular.mock.module angular.mock.module} + * + * ## Example * Example of what a typical jasmine tests looks like with the inject method. - *
+   * ```js
    *
    *   angular.module('myApplicationModule', [])
    *       .value('mode', 'app')
@@ -1926,32 +2309,64 @@ angular.mock.clearDataCache = function() {
    *       inject(function(version) {
    *         expect(version).toEqual('overridden');
    *       });
-   *     ));
+   *     });
    *   });
    *
-   * 
+ * ``` * * @param {...Function} fns any number of functions which will be injected using the injector. */ + + + + var ErrorAddingDeclarationLocationStack = function(e, errorForStack) { + this.message = e.message; + this.name = e.name; + if (e.line) this.line = e.line; + if (e.sourceId) this.sourceId = e.sourceId; + if (e.stack && errorForStack) + this.stack = e.stack + '\n' + errorForStack.stack; + if (e.stackArray) this.stackArray = e.stackArray; + }; + ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString; + window.inject = angular.mock.inject = function() { var blockFns = Array.prototype.slice.call(arguments, 0); var errorForStack = new Error('Declaration Location'); - return isSpecRunning() ? workFn() : workFn; + return isSpecRunning() ? workFn.call(currentSpec) : workFn; ///////////////////// function workFn() { var modules = currentSpec.$modules || []; - + var strictDi = !!currentSpec.$injectorStrict; modules.unshift('ngMock'); modules.unshift('ng'); var injector = currentSpec.$injector; if (!injector) { - injector = currentSpec.$injector = angular.injector(modules); + if (strictDi) { + // If strictDi is enabled, annotate the providerInjector blocks + angular.forEach(modules, function(moduleFn) { + if (typeof moduleFn === "function") { + angular.injector.$$annotate(moduleFn); + } + }); + } + injector = currentSpec.$injector = angular.injector(modules, strictDi); + currentSpec.$injectorStrict = strictDi; } - for(var i = 0, ii = blockFns.length; i < ii; i++) { + for (var i = 0, ii = blockFns.length; i < ii; i++) { + if (currentSpec.$injectorStrict) { + // If the injector is strict / strictDi, and the spec wants to inject using automatic + // annotation, then annotate the function here. + injector.annotate(blockFns[i]); + } try { + /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */ injector.invoke(blockFns[i] || angular.noop, this); + /* jshint +W040 */ } catch (e) { - if(e.stack && errorForStack) e.stack += '\n' + errorForStack.stack; + if (e.stack && errorForStack) { + throw new ErrorAddingDeclarationLocationStack(e, errorForStack); + } throw e; } finally { errorForStack = null; @@ -1959,4 +2374,23 @@ angular.mock.clearDataCache = function() { } } }; -})(window); + + + angular.mock.inject.strictDi = function(value) { + value = arguments.length ? !!value : true; + return isSpecRunning() ? workFn() : workFn; + + function workFn() { + if (value !== currentSpec.$injectorStrict) { + if (currentSpec.$injector) { + throw new Error('Injector already created, can not modify strict annotations'); + } else { + currentSpec.$injectorStrict = value; + } + } + } + }; +} + + +})(window, window.angular); diff --git a/bower_components/angular-mocks/bower.json b/bower_components/angular-mocks/bower.json index 2f51b41..50ee07e 100644 --- a/bower_components/angular-mocks/bower.json +++ b/bower_components/angular-mocks/bower.json @@ -1,8 +1,9 @@ { "name": "angular-mocks", - "version": "1.2.0-rc.2", + "version": "1.3.14", "main": "./angular-mocks.js", + "ignore": [], "dependencies": { - "angular": "1.2.0-rc.2" + "angular": "1.3.14" } } diff --git a/bower_components/angular-mocks/ngAnimateMock.js b/bower_components/angular-mocks/ngAnimateMock.js new file mode 100644 index 0000000..6f99e62 --- /dev/null +++ b/bower_components/angular-mocks/ngAnimateMock.js @@ -0,0 +1,2 @@ +require('./angular-mocks'); +module.exports = 'ngAnimateMock'; diff --git a/bower_components/angular-mocks/ngMock.js b/bower_components/angular-mocks/ngMock.js new file mode 100644 index 0000000..7944de7 --- /dev/null +++ b/bower_components/angular-mocks/ngMock.js @@ -0,0 +1,2 @@ +require('./angular-mocks'); +module.exports = 'ngMock'; diff --git a/bower_components/angular-mocks/ngMockE2E.js b/bower_components/angular-mocks/ngMockE2E.js new file mode 100644 index 0000000..fc2e539 --- /dev/null +++ b/bower_components/angular-mocks/ngMockE2E.js @@ -0,0 +1,2 @@ +require('./angular-mocks'); +module.exports = 'ngMockE2E'; diff --git a/bower_components/angular-mocks/package.json b/bower_components/angular-mocks/package.json new file mode 100644 index 0000000..ea35c4a --- /dev/null +++ b/bower_components/angular-mocks/package.json @@ -0,0 +1,27 @@ +{ + "name": "angular-mocks", + "version": "1.3.14", + "description": "AngularJS mocks for testing", + "main": "angular-mocks.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/angular/angular.js.git" + }, + "keywords": [ + "angular", + "framework", + "browser", + "mocks", + "testing", + "client-side" + ], + "author": "Angular Core Team ", + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/angular.js/issues" + }, + "homepage": "http://angularjs.org/" +} diff --git a/bower_components/angular-scenario/.bower.json b/bower_components/angular-scenario/.bower.json index 0452541..d797d5f 100644 --- a/bower_components/angular-scenario/.bower.json +++ b/bower_components/angular-scenario/.bower.json @@ -1,16 +1,17 @@ { "name": "angular-scenario", - "version": "1.2.0-rc.2", + "version": "1.3.14", "main": "./angular-scenario.js", + "ignore": [], "dependencies": { - "angular": "1.2.0-rc.2" + "angular": "1.3.14" }, "homepage": "https://github.com/angular/bower-angular-scenario", - "_release": "1.2.0-rc.2", + "_release": "1.3.14", "_resolution": { "type": "version", - "tag": "v1.2.0-rc.2", - "commit": "d60fe144f474acf3ce6939f2d5939ecc2bfeaf1d" + "tag": "v1.3.14", + "commit": "99606da40bcee15be4698104affaa3d2d26588e9" }, "_source": "git://github.com/angular/bower-angular-scenario.git", "_target": ">= 1.0.7", diff --git a/bower_components/angular-scenario/README.md b/bower_components/angular-scenario/README.md index 1bcf564..a4a0053 100644 --- a/bower_components/angular-scenario/README.md +++ b/bower_components/angular-scenario/README.md @@ -1,4 +1,61 @@ -bower-angular-scenario -====================== +# packaged angular-scenario -bower repo for angular-scenario.js \ No newline at end of file +This tool is now in maintenance mode. If you are starting a new project, please use +[Protractor](https://github.com/angular/protractor). Existing projects using scenario runner are +advised to migrate to protractor, as this tool is unlikely to receive updates. + +This repo is for distribution on `npm` and `bower`. The source for this module is in the +[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngScenario). +Please file issues and pull requests against that repo. + +## Install + +You can install this package either with `npm` or with `bower`. + +### npm + +```shell +npm install angular-scenario +``` + +The files are then available at `node_modules/angular-scenario/`. + +Note that this package is not in CommonJS format, so doing `require('angular-scenario')` will +return `undefined`. + +### bower + +```shell +bower install angular-scenario +``` + +The files are then available at `bower_components/angular-scenario/`. + +## Documentation + +Documentation is available on the +[AngularJS docs site](http://docs.angularjs.org/). + +## License + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/bower_components/angular-scenario/angular-scenario.js b/bower_components/angular-scenario/angular-scenario.js old mode 100755 new mode 100644 index 5d53ace..72ab9a3 --- a/bower_components/angular-scenario/angular-scenario.js +++ b/bower_components/angular-scenario/angular-scenario.js @@ -1,25645 +1,33050 @@ /*! - * jQuery JavaScript Library v1.8.3 + * jQuery JavaScript Library v2.1.1 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * - * Copyright 2012 jQuery Foundation and other contributors + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time) - */ -(function( window, undefined ) {'use strict'; -var - // A central reference to the root jQuery(document) - rootjQuery, - - // The deferred used on DOM ready - readyList, - - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - location = window.location, - navigator = window.navigator, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // Save a reference to some core methods - core_push = Array.prototype.push, - core_slice = Array.prototype.slice, - core_indexOf = Array.prototype.indexOf, - core_toString = Object.prototype.toString, - core_hasOwn = Object.prototype.hasOwnProperty, - core_trim = String.prototype.trim, - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Used for matching numbers - core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source, - - // Used for detecting and trimming whitespace - core_rnotwhite = /\S/, - core_rspace = /\s+/, + * Date: 2014-05-01T17:11Z + */ + +(function( global, factory ) {'use strict'; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper window is present, + // execute the factory and get jQuery + // For environments that do not inherently posses a window with a document + // (such as Node.js), expose a jQuery-making factory as module.exports + // This accentuates the need for the creation of a real window + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +// - // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, +var arr = []; - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, +var slice = arr.slice; - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, +var concat = arr.concat; - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, - rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, +var push = arr.push; - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, +var indexOf = arr.indexOf; - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); - }, +var class2type = {}; - // The ready event handler and self cleanup method - DOMContentLoaded = function() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - } else if ( document.readyState === "complete" ) { - // we're here because readyState === "complete" in oldIE - // which is good enough for us to call the dom ready! - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } - }, +var toString = class2type.toString; - // [[Class]] -> type pairs - class2type = {}; +var hasOwn = class2type.hasOwnProperty; -jQuery.fn = jQuery.prototype = { - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem, ret, doc; +var support = {}; - // Handle $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - doc = ( context && context.nodeType ? context.ownerDocument || context : document ); - - // scripts is true for back-compat - selector = jQuery.parseHTML( match[1], doc, true ); - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - this.attr.call( selector, context, true ); - } - - return jQuery.merge( this, selector ); - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } +var + // Use the correct document accordingly with window argument (sandbox) + document = window.document, - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } + version = "2.1.1", - return jQuery.makeArray( selector, this ); - }, + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, - // Start with an empty selector - selector: "", + // Support: Android<4.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - // The current version of jQuery being used - jquery: "1.8.3", + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, - // The default length of a jQuery object is 0 - length: 0, + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; - toArray: function() { - return core_slice.call( this ); - }, +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? +jQuery.extend({ + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; + }, + + isPlainObject: function( obj ) { + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + + // If the function hasn't returned already, we're confident that + // |obj| is a plain object, created by {} or constructed with new Object + return true; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + // Support: Android < 4.0, iOS < 6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf("use strict") === 1 ) { + script = document.createElement("script"); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + indirect( code ); + } + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); - // Return a 'clean' array - this.toArray() : +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); + if ( obj.nodeType === 1 && length ) { + return true; + } - // Add the old object onto the stack (as a reference) - ret.prevObject = this; + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v1.10.19 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-18 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} - ret.context = this.context; +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} - if ( name === "find" ) { - ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} - // Return the newly-formed element set - return ret; - }, +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} - ready: function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; - return this; - }, + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} - eq: function( i ) { - i = +i; - return i === -1 ? - this.slice( i ) : - this.slice( i, i + 1 ); - }, +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} - first: function() { - return this.eq( 0 ); - }, +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} - last: function() { - return this.eq( -1 ); - }, +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} - slice: function() { - return this.pushStack( core_slice.apply( this, arguments ), - "slice", core_slice.call(arguments).join(",") ); - }, +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== strundefined && context; +} - end: function() { - return this.prevObject || this.constructor(null); - }, +// Expose support vars for convenience +support = Sizzle.support = {}; - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: core_push, - sort: [].sort, - splice: [].splice +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; }; -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowclip^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; }; -jQuery.extend({ - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 1 ); - } +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; - // Remember that the DOM is ready - jQuery.isReady = true; +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger("ready").off("ready"); - } - }, +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, +Expr = Sizzle.selectors = { - isWindow: function( obj ) { - return obj != null && obj == obj.window; - }, + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, +Expr.pseudos["nth"] = Expr.pseudos["eq"]; - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ core_toString.call(obj) ] || "object"; - }, +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); - try { - // Not own constructor property must be Object - if ( obj.constructor && - !core_hasOwn.call(obj, "constructor") && - !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} - var key; - for ( key in obj ) {} +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} - return key === undefined || core_hasOwn.call( obj, key ); - }, +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} - error: function( msg ) { - throw new Error( msg ); - }, +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} - // data: string of html - // context (optional): If specified, the fragment will be created in this context, defaults to document - // scripts (optional): If true, will include scripts passed in the html string - parseHTML: function( data, context, scripts ) { - var parsed; - if ( !data || typeof data !== "string" ) { - return null; - } - if ( typeof context === "boolean" ) { - scripts = context; - context = 0; - } - context = context || document; +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} - // Single tag - if ( (parsed = rsingleTag.exec( data )) ) { - return [ context.createElement( parsed[1] ) ]; - } +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} - parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] ); - return jQuery.merge( [], - (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes ); - }, +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} - parseJSON: function( data ) { - if ( !data || typeof data !== "string") { - return null; - } +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } +// One-time assignments - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - return ( new Function( "return " + data ) )(); +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; - } - jQuery.error( "Invalid JSON: " + data ); - }, +// Initialize against the default document +setDocument(); - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - if ( !data || typeof data !== "string" ) { - return null; - } - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); - noop: function() {}, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && core_rnotwhite.test( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} - // args is for internal usage only - each: function( obj, callback, args ) { - var name, - i = 0, - length = obj.length, - isObj = length === undefined || jQuery.isFunction( obj ); - - if ( args ) { - if ( isObj ) { - for ( name in obj ) { - if ( callback.apply( obj[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( obj[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in obj ) { - if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) { - break; - } - } - } - } +return Sizzle; - return obj; - }, +})( window ); - // Use native String.trim function wherever possible - trim: core_trim && !core_trim.call("\uFEFF\xA0") ? - function( text ) { - return text == null ? - "" : - core_trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var type, - ret = results || []; - - if ( arr != null ) { - // The window, strings (and functions) also have 'length' - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - type = jQuery.type( arr ); - - if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) { - core_push.call( ret, arr ); - } else { - jQuery.merge( ret, arr ); - } - } - return ret; - }, - inArray: function( elem, arr, i ) { - var len; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; - if ( arr ) { - if ( core_indexOf ) { - return core_indexOf.call( arr, elem, i ); - } - len = arr.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in arr && arr[ i ] === elem ) { - return i; - } - } - } +var rneedsContext = jQuery.expr.match.needsContext; - return -1; - }, +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); - merge: function( first, second ) { - var l = second.length, - i = first.length, - j = 0; - if ( typeof l === "number" ) { - for ( ; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } +var risSimple = /^.[^:#\[\.,]*$/; - first.length = i; +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); - return first; - }, + } - grep: function( elems, callback, inv ) { - var retVal, - ret = [], - i = 0, - length = elems.length; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); - return ret; - }, + } - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, key, - ret = [], - i = 0, - length = elems.length, - // jquery objects are treated as arrays - isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( key in elems ) { - value = callback( elems[ key ], key, arg ); + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } + qualifier = jQuery.filter( qualifier, elements ); + } - // Flatten any nested arrays - return ret.concat.apply( [], ret ); - }, + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; + }); +} - // A global GUID counter for objects - guid: 1, +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; + if ( not ) { + expr = ":not(" + expr + ")"; + } - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } +jQuery.fn.extend({ + find: function( selector ) { + var i, + len = this.length, + ret = [], + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +}); - // Simulated bind - args = core_slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context, args.concat( core_slice.call( arguments ) ) ); - }; - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; - return proxy; - }, +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, pass ) { - var exec, - bulk = key == null, - i = 0, - length = elems.length; - - // Sets many values - if ( key && typeof key === "object" ) { - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); - } - chainable = 1; - - // Sets one value - } else if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = pass === undefined && jQuery.isFunction( value ); - - if ( bulk ) { - // Bulk operations only iterate when executing function values - if ( exec ) { - exec = fn; - fn = function( elem, key, value ) { - return exec.call( jQuery( elem ), value ); - }; - - // Otherwise they run against the entire set - } else { - fn.call( elems, value ); - fn = null; - } - } - - if ( fn ) { - for (; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - } - - chainable = 1; - } +// Initialize central reference +rootjQuery = jQuery( document ); - return chainable ? - elems : - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; - now: function() { - return ( new Date() ).getTime(); - } +jQuery.extend({ + dir: function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } }); -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready, 1 ); +jQuery.fn.extend({ + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter(function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); +function sibling( cur, dir ) { + while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} + return cur; +} - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +var rnotwhite = (/\S+/g); - // If IE event model is used - } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var top = false; - - try { - top = window.frameElement == null && document.documentElement; - } catch(e) {} - - if ( top && top.doScroll ) { - (function doScrollCheck() { - if ( !jQuery.isReady ) { - - try { - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); - } - - // and execute any waiting functions - jQuery.ready(); - } - })(); - } - } - } - return readyList.promise( obj ); -}; -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); -// All jQuery objects should point back to these -rootjQuery = jQuery(document); // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.split( core_rspace ), function( _, flag ) { - object[ flag ] = true; - }); - return object; + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; } /* * Create a callback list using the following parameters: * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * - * once: will ensure the callback list can only be fired once (like a Deferred) + * once: will ensure the callback list can only be fired once (like a Deferred) * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) * - * unique: will ensure a callback can only be added once (no duplicate in the list) + * unique: will ensure a callback can only be added once (no duplicate in the list) * - * stopOnFalse: interrupt callings when a callback returns false + * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Control if a given callback is in the list - has: function( fn ) { - return jQuery.inArray( fn, list ) > -1; - }, - // Remove all callbacks from the list - empty: function() { - list = []; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( list && ( !fired || stack ) ) { - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; }; -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var action = tuple[ 0 ], - fn = fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? - function() { - var returned = fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); - } - } : - newDefer[ action ] - ); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] = list.fire - deferred[ tuple[0] ] = list.fire; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); - // Make the deferred a promise - promise.promise( deferred ); - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } +jQuery.extend({ - // All done! - return deferred; - }, + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = core_slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; - if( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } +// The deferred used on DOM ready +var readyList; - return deferred.promise(); - } -}); -jQuery.support = (function() { - - var support, - all, - a, - select, - opt, - input, - fragment, - eventName, - i, - isSupported, - clickFn, - div = document.createElement("div"); - - // Setup - div.setAttribute( "className", "t" ); - div.innerHTML = "
a"; - - // Support tests won't run in some limited or non-browser environments - all = div.getElementsByTagName("*"); - a = div.getElementsByTagName("a")[ 0 ]; - if ( !all || !a || !all.length ) { - return {}; - } - - // First batch of tests - select = document.createElement("select"); - opt = select.appendChild( document.createElement("option") ); - input = div.getElementsByTagName("input")[ 0 ]; - - a.style.cssText = "top:1px;float:left;opacity:.5"; - support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: ( div.firstChild.nodeType === 3 ), - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, - - // Get the style information from getAttribute - // (IE uses .cssText instead) - style: /top/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: ( a.getAttribute("href") === "/a" ), - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.5/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: ( input.value === "on" ), - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: opt.selected, - - // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) - getSetAttribute: div.className !== "t", - - // Tests for enctype support on a form (#6743) - enctype: !!document.createElement("form").enctype, - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", - - // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode - boxModel: ( document.compatMode === "CSS1Compat" ), - - // Will be defined later - submitBubbles: true, - changeBubbles: true, - focusinBubbles: false, - deleteExpando: true, - noCloneEvent: true, - inlineBlockNeedsLayout: false, - shrinkWrapBlocks: false, - reliableMarginRight: true, - boxSizingReliable: true, - pixelPosition: false - }; +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); - // Make sure checked status is properly cloned - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; + return this; +}; - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; +jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); - // Test to see if it's possible to delete an expando from an element - // Fails in Internet Explorer - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - - if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent( "onclick", clickFn = function() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - support.noCloneEvent = false; - }); - div.cloneNode( true ).fireEvent("onclick"); - div.detachEvent( "onclick", clickFn ); - } - - // Check if a radio maintains its value - // after being appended to the DOM - input = document.createElement("input"); - input.value = "t"; - input.setAttribute( "type", "radio" ); - support.radioValue = input.value === "t"; - - input.setAttribute( "checked", "checked" ); - - // #11217 - WebKit loses check when the name is after the checked attribute - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - fragment = document.createDocumentFragment(); - fragment.appendChild( div.lastChild ); - - // WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - support.appendChecked = input.checked; - - fragment.removeChild( input ); - fragment.appendChild( div ); - - // Technique from Juriy Zaytsev - // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ - // We only care about the case where non-standard event systems - // are used, namely in IE. Short-circuiting here helps us to - // avoid an eval call (in setAttribute) which can cause CSP - // to go haywire. See: https://developer.mozilla.org/en/Security/CSP - if ( div.attachEvent ) { - for ( i in { - submit: true, - change: true, - focusin: true - }) { - eventName = "on" + i; - isSupported = ( eventName in div ); - if ( !isSupported ) { - div.setAttribute( eventName, "return;" ); - isSupported = ( typeof div[ eventName ] === "function" ); - } - support[ i + "Bubbles" ] = isSupported; - } - } - - // Run tests that need a body at doc ready - jQuery(function() { - var container, div, tds, marginDiv, - divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;", - body = document.getElementsByTagName("body")[0]; - - if ( !body ) { - // Return for frameset docs that don't have a body - return; - } +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + jQuery.ready(); +} - container = document.createElement("div"); - container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px"; - body.insertBefore( container, body.firstChild ); - - // Construct the test element - div = document.createElement("div"); - container.appendChild( div ); - - // Check if table cells still have offsetWidth/Height when they are set - // to display:none and there are still other visible table cells in a - // table row; if so, offsetWidth/Height are not reliable for use when - // determining if an element has been hidden directly using - // display:none (it is still safe to use offsets if a parent element is - // hidden; don safety goggles and see bug #4512 for more information). - // (only IE 8 fails this test) - div.innerHTML = "
t
"; - tds = div.getElementsByTagName("td"); - tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; - isSupported = ( tds[ 0 ].offsetHeight === 0 ); - - tds[ 0 ].style.display = ""; - tds[ 1 ].style.display = "none"; - - // Check if empty table cells still have offsetWidth/Height - // (IE <= 8 fail this test) - support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); - - // Check box-sizing and margin behavior - div.innerHTML = ""; - div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; - support.boxSizing = ( div.offsetWidth === 4 ); - support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); - - // NOTE: To any future maintainer, we've window.getComputedStyle - // because jsdom on node.js will break without it. - if ( window.getComputedStyle ) { - support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; - support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - marginDiv = document.createElement("div"); - marginDiv.style.cssText = div.style.cssText = divReset; - marginDiv.style.marginRight = marginDiv.style.width = "0"; - div.style.width = "1px"; - div.appendChild( marginDiv ); - support.reliableMarginRight = - !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); - } +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { - if ( typeof div.style.zoom !== "undefined" ) { - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - // (IE < 8 does this) - div.innerHTML = ""; - div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; - support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); - - // Check if elements with layout shrink-wrap their children - // (IE 6 does this) - div.style.display = "block"; - div.style.overflow = "visible"; - div.innerHTML = "
"; - div.firstChild.style.width = "5px"; - support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); - - container.style.zoom = 1; - } + readyList = jQuery.Deferred(); - // Null elements to avoid leaks in IE - body.removeChild( container ); - container = div = tds = marginDiv = null; - }); + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); - // Null elements to avoid leaks in IE - fragment.removeChild( div ); - all = a = select = opt = input = fragment = div = null; + } else { - return support; -})(); -var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - rmultiDash = /([A-Z])/g; + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); -jQuery.extend({ - cache: {}, + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; - deletedIds: [], +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; - // Remove at next major release (1.9/2.0) - uuid: 0, - // Unique for each copy of jQuery on the page - // Non-digits removed to match rinlinejQuery - expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - // Ban all objects except for Flash (which handle expandos) - "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", - "applet": true - }, - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, +function Data() { + // Support: Android < 4, + // Old WebKit does not have Object.preventExtensions/freeze method, + // return new empty object instead with no [[set]] accessor + Object.defineProperty( this.cache = {}, 0, { + get: function() { + return {}; + } + }); - data: function( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } + this.expando = jQuery.expando + Math.random(); +} - var thisCache, ret, - internalKey = jQuery.expando, - getByName = typeof name === "string", +Data.uid = 1; +Data.accepts = jQuery.acceptData; + +Data.prototype = { + key: function( owner ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return the key for a frozen object. + if ( !Data.accepts( owner ) ) { + return 0; + } + + var descriptor = {}, + // Check if the owner object already has a cache key + unlock = owner[ this.expando ]; + + // If not, create one + if ( !unlock ) { + unlock = Data.uid++; + + // Secure it in a non-enumerable, non-writable property + try { + descriptor[ this.expando ] = { value: unlock }; + Object.defineProperties( owner, descriptor ); + + // Support: Android < 4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = unlock; + jQuery.extend( owner, descriptor ); + } + } + + // Ensure the cache object + if ( !this.cache[ unlock ] ) { + this.cache[ unlock ] = {}; + } + + return unlock; + }, + set: function( owner, data, value ) { + var prop, + // There may be an unlock assigned to this node, + // if there is no entry for this "owner", create one inline + // and set the unlock as though an owner entry had always existed + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + // Fresh assignments by object are shallow copied + if ( jQuery.isEmptyObject( cache ) ) { + jQuery.extend( this.cache[ unlock ], data ); + // Otherwise, copy the properties one-by-one to the cache object + } else { + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + } + return cache; + }, + get: function( owner, key ) { + // Either a valid cache is found, or will be created. + // New caches will be created and the unlock returned, + // allowing direct access to the newly created + // empty data object. A valid owner object must be provided. + var cache = this.cache[ this.key( owner ) ]; + + return key === undefined ? + cache : cache[ key ]; + }, + access: function( owner, key, value ) { + var stored; + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ((key && typeof key === "string") && value === undefined) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase(key) ); + } + + // [*]When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, name, camel, + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + if ( key === undefined ) { + this.cache[ unlock ] = {}; + + } else { + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + }, + hasData: function( owner ) { + return !jQuery.isEmptyObject( + this.cache[ owner[ this.expando ] ] || {} + ); + }, + discard: function( owner ) { + if ( owner[ this.expando ] ) { + delete this.cache[ owner[ this.expando ] ]; + } + } +}; +var data_priv = new Data(); - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, +var data_user = new Data(); - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { - return; - } +/* + Implementation Summary + + 1. Enforce API surface and semantic compatibility with 1.9.x branch + 2. Improve the module's maintainability by reducing the storage + paths to a single mechanism. + 3. Use the same single mechanism to support "private" and "user" data. + 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) + 5. Avoid exposing implementation details on user objects (eg. expando properties) + 6. Provide a clear path for implementation upgrade to WeakMap in 2014 +*/ +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++; - } else { - id = internalKey; - } - } +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} - if ( !cache[ id ] ) { - cache[ id ] = {}; +jQuery.extend({ + hasData: function( elem ) { + return data_user.hasData( elem ) || data_priv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return data_user.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + data_user.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to data_priv methods, these can be deprecated. + _data: function( elem, name, data ) { + return data_priv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + data_priv.remove( elem, name ); + } +}); - // Avoids exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = jQuery.noop; - } - } +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = data_user.get( elem ); + + if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + data_priv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + data_user.set( this, key ); + }); + } + + return access( this, function( value ) { + var data, + camelKey = jQuery.camelCase( key ); + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + // Attempt to get data from the cache + // with the key as-is + data = data_user.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to get data from the cache + // with the key camelized + data = data_user.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each(function() { + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = data_user.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + data_user.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf("-") !== -1 && data !== undefined ) { + data_user.set( this, key, value ); + } + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + data_user.remove( this, key ); + }); + } +}); - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - thisCache = cache[ id ]; +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = data_priv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = data_priv.access( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return data_priv.get( elem, key ) || data_priv.access( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + data_priv.remove( elem, [ type + "queue", key ] ); + }) + }); + } +}); - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = data_priv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; - thisCache = thisCache.data; - } +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( getByName ) { +var rcheckableType = (/^(?:checkbox|radio)$/i); - // First Try to find as-is property data - ret = thisCache[ name ]; - // Test for null|undefined property data - if ( ret == null ) { - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + // Support: Windows Web Apps (WWA) + // `name` and `type` need .setAttribute for WWA + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE9-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; - return ret; - }, - removeData: function( elem, name, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - var thisCache, i, l, +support.focusinBubbles = "onfocusin" in window; - isNode = elem.nodeType, - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } +function returnTrue() { + return true; +} - if ( name ) { +function returnFalse() { + return false; +} - thisCache = pvt ? cache[ id ] : cache[ id ].data; +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} - if ( thisCache ) { +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.hasData( elem ) && data_priv.get( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + data_priv.remove( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; + } + + // Support: Safari 6.0+, Chrome < 28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { +jQuery.removeEvent = function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } +}; - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split(" "); - } - } - } +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + // Support: Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; - for ( i = 0, l = name.length; i < l; i++ ) { - delete thisCache[ name[i] ]; - } +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { - return; - } - } - } + preventDefault: function() { + var e = this.originalEvent; - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; + this.isDefaultPrevented = returnTrue; - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject( cache[ id ] ) ) { - return; - } - } + if ( e && e.preventDefault ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; - // Destroy the cache - if ( isNode ) { - jQuery.cleanData( [ elem ], true ); + this.isPropagationStopped = returnTrue; - // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) - } else if ( jQuery.support.deleteExpando || cache != cache.window ) { - delete cache[ id ]; + if ( e && e.stopPropagation ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; - // When all else fails, null - } else { - cache[ id ] = null; - } - }, + this.isImmediatePropagationStopped = returnTrue; - // For internal use only. - _data: function( elem, name, data ) { - return jQuery.data( elem, name, data, true ); - }, + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } - // A method for determining if a DOM node can handle the data expando - acceptData: function( elem ) { - var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + this.stopPropagation(); + } +}; - // nodes accept data unless otherwise specified; rejection can be conditional - return !noData || noData !== true && elem.getAttribute("classid") === noData; - } +// Create mouseenter/leave events using mouseover/out and event-time checks +// Support: Chrome 15+ +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; }); -jQuery.fn.extend({ - data: function( key, value ) { - var parts, part, attr, name, l, - elem = this[0], - i = 0, - data = null; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = jQuery.data( elem ); - - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { - attr = elem.attributes; - for ( l = attr.length; i < l; i++ ) { - name = attr[i].name; - - if ( !name.indexOf( "data-" ) ) { - name = jQuery.camelCase( name.substring(5) ); - - dataAttr( elem, name, data[ name ] ); - } - } - jQuery._data( elem, "parsedAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } +// Create "bubbling" focus and blur events +// Support: Firefox, Chrome, Safari +if ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); + } + } + }; + }); +} - parts = key.split( ".", 2 ); - parts[1] = parts[1] ? "." + parts[1] : ""; - part = parts[1] + "!"; +jQuery.fn.extend({ - return jQuery.access( this, function( value ) { + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); - if ( value === undefined ) { - data = this.triggerHandler( "getData" + part, [ parts[0] ] ); - // Try to fetch any internally stored data first - if ( data === undefined && elem ) { - data = jQuery.data( elem, key ); - data = dataAttr( elem, key, data ); - } +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + + // Support: IE 9 + option: [ 1, "" ], + + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] + }; + +// Support: IE 9 +wrapMap.optgroup = wrapMap.option; - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; - parts[1] = value; - this.each(function() { - var self = jQuery( this ); +// Support: 1.x compatibility +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - self.triggerHandler( "setData" + part, parts ); - jQuery.data( this, key, value ); - self.triggerHandler( "changeData" + part, parts ); - }); - }, null, value, arguments.length > 1, null, false ); - }, + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute("type"); + } - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + return elem; +} - data = elem.getAttribute( name ); +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + data_user.set( dest, udataCur ); + } +} - } else { - data = undefined; - } - } +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; - return data; + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; } -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - var name; - for ( name in obj ) { +// Support: IE >= 9 +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; - return true; + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } } -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Support: IE >= 9 + // Fix Cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Fixes #12346 + // Support: Webkit, IE + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; + }, + + cleanData: function( elems ) { + var data, elem, type, key, + special = jQuery.event.special, + i = 0; + + for ( ; (elem = elems[ i ]) !== undefined; i++ ) { + if ( jQuery.acceptData( elem ) ) { + key = elem[ data_priv.expando ]; + + if ( key && (data = data_priv.cache[ key ]) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + if ( data_priv.cache[ key ] ) { + // Discard any remaining `private` data + delete data_priv.cache[ key ]; + } + } + } + // Discard any remaining `user` data + delete data_user.cache[ elem[ data_user.expando ] ]; + } + } +}); - if ( fn ) { +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each(function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + }); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return this; + } +}); - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, +var iframe, + elemdisplay = {}; - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - jQuery.removeData( elem, type + "queue", true ); - jQuery.removeData( elem, key, true ); - }) - }); - } -}); +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } + // Use of this method is a temporary fix (more like optmization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); + return display; +} - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while( i-- ) { - tmp = jQuery._data( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var nodeHook, boolHook, fixSpecified, - rclass = /[\t\r\n]/g, - rreturn = /\r/g, - rtype = /^(?:button|input)$/i, - rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea|)$/i, - rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - getSetAttribute = jQuery.support.getSetAttribute; + if ( !display ) { + display = actualDisplay( nodeName, doc ); -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, + // Use the already-created iframe if possible + iframe = (iframe || jQuery( " + + + diff --git a/bower_components/jquery/test/data/dashboard.xml b/bower_components/jquery/test/data/dashboard.xml new file mode 100644 index 0000000..5a6f561 --- /dev/null +++ b/bower_components/jquery/test/data/dashboard.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bower_components/jquery/test/data/dimensions/documentLarge.html b/bower_components/jquery/test/data/dimensions/documentLarge.html new file mode 100644 index 0000000..a6598fc --- /dev/null +++ b/bower_components/jquery/test/data/dimensions/documentLarge.html @@ -0,0 +1,17 @@ + + + + + + + +
+ +
+ + diff --git a/bower_components/jquery/test/data/dimensions/documentSmall.html b/bower_components/jquery/test/data/dimensions/documentSmall.html new file mode 100644 index 0000000..63e1c2a --- /dev/null +++ b/bower_components/jquery/test/data/dimensions/documentSmall.html @@ -0,0 +1,21 @@ + + + + + + + +
+ +
+ + diff --git a/bower_components/jquery/test/data/echoData.php b/bower_components/jquery/test/data/echoData.php new file mode 100644 index 0000000..a37ba51 --- /dev/null +++ b/bower_components/jquery/test/data/echoData.php @@ -0,0 +1 @@ + diff --git a/bower_components/jquery/test/data/echoQuery.php b/bower_components/jquery/test/data/echoQuery.php new file mode 100644 index 0000000..b72f329 --- /dev/null +++ b/bower_components/jquery/test/data/echoQuery.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bower_components/jquery/test/data/errorWithJSON.php b/bower_components/jquery/test/data/errorWithJSON.php new file mode 100644 index 0000000..62b187e --- /dev/null +++ b/bower_components/jquery/test/data/errorWithJSON.php @@ -0,0 +1,6 @@ + diff --git a/bower_components/jquery/test/data/evalScript.php b/bower_components/jquery/test/data/evalScript.php new file mode 100644 index 0000000..ea9b8c5 --- /dev/null +++ b/bower_components/jquery/test/data/evalScript.php @@ -0,0 +1 @@ +ok( "" === "GET", "request method is " ); \ No newline at end of file diff --git a/bower_components/jquery/test/data/event/focusElem.html b/bower_components/jquery/test/data/event/focusElem.html new file mode 100644 index 0000000..10726b4 --- /dev/null +++ b/bower_components/jquery/test/data/event/focusElem.html @@ -0,0 +1,16 @@ + + + + + .focus() (activeElement access #13393) + + + + + + + + diff --git a/bower_components/jquery/test/data/event/longLoadScript.php b/bower_components/jquery/test/data/event/longLoadScript.php new file mode 100644 index 0000000..ba47168 --- /dev/null +++ b/bower_components/jquery/test/data/event/longLoadScript.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/bower_components/jquery/test/data/event/onbeforeunload.html b/bower_components/jquery/test/data/event/onbeforeunload.html new file mode 100644 index 0000000..11ad196 --- /dev/null +++ b/bower_components/jquery/test/data/event/onbeforeunload.html @@ -0,0 +1,20 @@ + + + + + diff --git a/bower_components/jquery/test/data/event/promiseReady.html b/bower_components/jquery/test/data/event/promiseReady.html new file mode 100644 index 0000000..17b6e7f --- /dev/null +++ b/bower_components/jquery/test/data/event/promiseReady.html @@ -0,0 +1,17 @@ + + + + +Test case for jQuery ticket #11470 + + + + + + + diff --git a/bower_components/jquery/test/data/event/syncReady.html b/bower_components/jquery/test/data/event/syncReady.html new file mode 100644 index 0000000..e088570 --- /dev/null +++ b/bower_components/jquery/test/data/event/syncReady.html @@ -0,0 +1,23 @@ + + + + +Test case for jQuery ticket #10067 + + + + + + + + +
+ + diff --git a/bower_components/jquery/test/data/headers.php b/bower_components/jquery/test/data/headers.php new file mode 100644 index 0000000..968f13f --- /dev/null +++ b/bower_components/jquery/test/data/headers.php @@ -0,0 +1,18 @@ + $value ) { + + $key = str_replace( "_" , "-" , substr( $key , 0 , 5 ) == "HTTP_" ? substr( $key , 5 ) : $key ); + $headers[ $key ] = $value; + +} + +foreach( explode( "_" , $_GET[ "keys" ] ) as $key ) { + echo "$key: " . @$headers[ strtoupper( $key ) ] . "\n"; +} diff --git a/bower_components/jquery/test/data/if_modified_since.php b/bower_components/jquery/test/data/if_modified_since.php new file mode 100644 index 0000000..098b7da --- /dev/null +++ b/bower_components/jquery/test/data/if_modified_since.php @@ -0,0 +1,20 @@ + diff --git a/bower_components/jquery/test/data/iframe.html b/bower_components/jquery/test/data/iframe.html new file mode 100644 index 0000000..ad646c4 --- /dev/null +++ b/bower_components/jquery/test/data/iframe.html @@ -0,0 +1,8 @@ + + + iframe + + +
span text
+ + diff --git a/bower_components/jquery/test/data/jquery-1.9.1.ajax_xhr.min.js b/bower_components/jquery/test/data/jquery-1.9.1.ajax_xhr.min.js new file mode 100644 index 0000000..0c5f64c --- /dev/null +++ b/bower_components/jquery/test/data/jquery-1.9.1.ajax_xhr.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.9.1 -css,-event-alias,-ajax/script,-ajax/jsonp,-effects,-offset,-dimensions,-deprecated | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery.min.map +*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],d="1.9.1 -css,-event-alias,-ajax/script,-ajax/jsonp,-effects,-offset,-dimensions,-deprecated",p=c.concat,f=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=d.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,N=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,w=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,k=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,S=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,j=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(M(),b.ready())},M=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:d,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:w.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&E.test(n.replace(A,"@").replace(S,"]").replace(k,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,j)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=B(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(N,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(B(Object(e))?b.merge(n,"string"==typeof e?[e]:e):f.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=B(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return p.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}M(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function B(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function O(e){var t=_[e]={};return b.each(e.match(T)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||O(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:d.disable())},d={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&d.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||d.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return d.fireWith(this,arguments),this},fired:function(){return!!i}};return d},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,d,p,f=o.createElement("div");if(f.setAttribute("className","t"),f.innerHTML="
a",n=f.getElementsByTagName("*"),r=f.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=f.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==f.className,leadingWhitespace:3===f.firstChild.nodeType,tbody:!f.getElementsByTagName("tbody").length,htmlSerialize:!!f.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete f.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,f.attachEvent&&(f.attachEvent("onclick",function(){t.noCloneEvent=!1}),f.cloneNode(!0).click());for(p in{submit:!0,change:!0,focusin:!0})f.setAttribute(c="on"+p,"t"),t[p+"Bubbles"]=c in e||f.attributes[c].expando===!1;return f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===f.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(f),f.innerHTML="
t
",a=f.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",d=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=d&&0===a[0].offsetHeight,f.innerHTML="",f.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===f.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(f,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(f,null)||{width:"4px"}).width,r=f.appendChild(o.createElement("div")),r.style.cssText=f.style.cssText=s,r.style.marginRight=r.style.width="0",f.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof f.style.zoom!==i&&(f.innerHTML="",f.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===f.offsetWidth,f.style.display="block",f.innerHTML="
",f.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==f.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=f=a=r=null)}),n=s=u=l=r=a=null,t}();var F=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,q=/([A-Z])/g;function R(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,d=l?b.cache:e,p=l?e[s]:e[s]&&s;if(p&&d[p]&&(i||d[p].data)||!u||r!==t)return p||(l?e[s]=p=c.pop()||b.guid++:p=s),d[p]||(d[p]={},l||(d[p].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?d[p]=b.extend(d[p],n):d[p].data=b.extend(d[p].data,n)),o=d[p],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function P(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?I:b.isEmptyObject)(o))return}(n||(delete s[u].data,I(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(d+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return P(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return P(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),$(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?$(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(q,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:F.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var W,X,z=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,Q=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,K=b.support.getSetAttribute,Y=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(z," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(z," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(T)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(z," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(Q.test(n)?X:W)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,Q.test(n)?!K&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(K?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:t}}}}),X={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?Y&&K?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):Y&&K||!G.test(n)?e.setAttribute(!K&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},Y&&K||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):W&&W.set(e,n,r)}}),K||(W=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:W.get,set:function(e,t,n){W.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,d,p,f,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(p=v.handle)||(p=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(p.elem,arguments)},p.elem=e),n=(n||"").match(T)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),d=b.event.special[g]||{},g=(a?d.delegateType:d.bindType)||g,d=b.event.special[g]||{},f=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,d.setup&&d.setup.call(e,o,m,p)!==!1||(e.addEventListener?e.addEventListener(g,p,!1):e.attachEvent&&e.attachEvent("on"+g,p))),d.add&&(d.add.call(e,f),f.handler.guid||(f.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,f):h.push(f),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,d,p,f,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],f=g=s[1],h=(s[2]||"").split(".").sort(),f){d=b.event.special[f]||{},f=(r?d.delegateType:d.bindType)||f,p=c[f]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=p.length;while(o--)a=p[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(p.splice(o,1),a.selector&&p.delegateCount--,d.remove&&d.remove.call(e,a));u&&!p.length&&(d.teardown&&d.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,f,m.handle),delete c[f])}else for(f in c)b.event.remove(e,f+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,d,p,f,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=p=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),d=b.event.special[g]||{},a||!d.trigger||d.trigger.apply(i,r)!==!1)){if(!a&&!d.noBubble&&!b.isWindow(i)){for(c=d.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),p=l;p===(i.ownerDocument||o)&&h.push(p.defaultView||p.parentWindow||e)}f=0;while((l=h[f++])&&!n.isPropagationStopped())n.type=f>1?c:d.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||d._default&&d._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){p=i[u],p&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,p&&(i[u]=p)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; +return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,d,p,f,h,g,m,y,v,x="sizzle"+-new Date,T=e.document,N={},w=0,C=0,E=it(),k=it(),A=it(),S=typeof t,D=1<<31,L=[],j=L.pop,H=L.push,M=L.slice,B=L.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",O="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",F=O.replace("w","w#"),q="([*^$|!~]?=)",R="\\["+_+"*("+O+")"+_+"*(?:"+q+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+F+")|)|)"+_+"*\\]",P=":("+O+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+R.replace(3,8)+")*)|.*)\\)|)",$=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),I=RegExp("^"+_+"*,"+_+"*"),W=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),X=RegExp(P),z=RegExp("^"+F+"$"),U={ID:RegExp("^#("+O+")"),CLASS:RegExp("^\\.("+O+")"),NAME:RegExp("^\\[name=['\"]?("+O+")['\"]?\\]"),TAG:RegExp("^("+O.replace("w","w*")+")"),ATTR:RegExp("^"+R),PSEUDO:RegExp("^"+P),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,J=/^[^{]+\{\s*\[native code/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,Y=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{M.call(T.documentElement.childNodes,0)[0].nodeType}catch(nt){M=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return J.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=d.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,p,g,m,v;if((t?t.ownerDocument||t:T)!==d&&c(t),t=t||d,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!f&&!r){if(i=Q.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,M.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&N.getByClassName&&t.getElementsByClassName)return H.apply(n,M.call(t.getElementsByClassName(a),0)),n}if(N.qsa&&!h.test(e)){if(p=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=pt(e),(p=t.getAttribute("id"))?g=p.replace(Y,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+ft(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,M.call(m.querySelectorAll(v),0)),n}catch(b){}finally{p||t.removeAttribute("id")}}}return Tt(e.replace($,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:T;return n!==d&&9===n.nodeType&&n.documentElement?(d=n,p=n.documentElement,f=a(n),N.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),N.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),N.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),N.getByName=at(function(e){e.id=x+0,e.innerHTML="
",p.insertBefore(e,p.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return N.getIdNotName=!n.getElementById(x),p.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==S&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},N.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==S&&!f){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==S&&!f){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==S&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==S&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=N.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==S?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=N.getByName&&function(e,n){return typeof n.getElementsByName!==S?n.getElementsByName(name):t},i.find.CLASS=N.getByClassName&&function(e,n){return typeof n.getElementsByClassName===S||f?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(N.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(N.matchesSelector=rt(m=p.matchesSelector||p.mozMatchesSelector||p.webkitMatchesSelector||p.oMatchesSelector||p.msMatchesSelector))&&at(function(e){N.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",P)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(p.contains)||p.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=p.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(T,e)?-1:t===n||y(T,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===T?-1:l[i]===T?1:0},u=!1,[0,0].sort(v),N.detectDuplicates=u,d):d},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&c(e),t=t.replace(Z,"='$1']"),!(!N.matchesSelector||f||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||N.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,d,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==d&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==d&&c(e),f||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):f||N.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!N.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function dt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&X.test(n)&&(t=pt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=E[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&E(e,function(e){return t.test(e.className||typeof e.getAttribute!==S&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,d,p,f,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){d=t;while(d=d[g])if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],f=l[0]===w&&l[1],p=l[0]===w&&l[2],d=f&&m.childNodes[f];while(d=++f&&d&&d[g]||(p=f=0)||h.pop())if(1===d.nodeType&&++p&&d===t){c[e]=[w,f,p];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===w)p=l[1];else while(d=++f&&d&&d[g]||(p=f=0)||h.pop())if((s?d.nodeName.toLowerCase()===y:1===d.nodeType)&&++p&&(v&&((d[x]||(d[x]={}))[e]=[w,p]),d===t))break;return p-=i,p===r||0===p%r&&p/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=B.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace($,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return z.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=f?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===p},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return K.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:dt(function(){return[0]}),last:dt(function(e,t){return[t-1]}),eq:dt(function(e,t,n){return[0>n?n+t:n]}),even:dt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:dt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:dt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:dt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function pt(e,t){var n,r,o,a,s,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=I.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=W.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace($," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):k(e,u).slice(0)}function ft(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,d=w+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===d){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[d],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,d,p=[],f=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,p,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,f),r(l,[],s,u),c=l.length;while(c--)(d=l[c])&&(y[f[c]]=!(m[f[c]]=d))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(d=y[c])&&l.push(m[c]=d);i(null,y=[],l,u)}c=y.length;while(c--)(d=y[c])&&(l=i?B.call(o,d):p[c])>-1&&(o[l]=!(a[l]=d))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),d=ht(function(e){return B.call(t,e)>-1},s,!0),p=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):d(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])p=[ht(gt(p),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(p),u>1&&ft(e.slice(0,u-1)).replace($,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&ft(e))}p.push(n)}return gt(p)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,p,f){var h,g,m,y=[],v=0,b="0",x=s&&[],T=null!=f,N=l,C=s||a&&i.find.TAG("*",f&&u.parentNode||u),E=w+=null==N?1:Math.random()||.1;for(T&&(l=u!==d&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){p.push(h);break}T&&(w=E,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=j.call(p));y=mt(y)}H.apply(p,y),T&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(p)}return T&&(w=E,l=N),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=A[e+" "];if(!o){t||(t=pt(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=A(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function Tt(e,t,n,r){var o,a,u,l,c,d=pt(e);if(!r&&1===d.length){if(a=d[0]=d[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!f&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&ft(a),!e)return H.apply(n,M.call(r,0)),n;break}}}return s(e,d)(r,t,f,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Nt(){}i.filters=Nt.prototype=i.pseudos,i.setFilters=new Nt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(pt(this,e,!1))},filter:function(e){return this.pushStack(pt(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function dt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return dt(e,"nextSibling")},prev:function(e){return dt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function pt(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function ft(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,St={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},Dt=ft(o),Lt=Dt.appendChild(o.createElement("div"));St.optgroup=St.option,St.tbody=St.tfoot=St.colgroup=St.caption=St.thead,St.th=St.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ft(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Bt(Ft(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Nt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||St[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=p.apply([],e);var i,o,a,s,u,l,c=0,d=this.length,f=this,h=d-1,g=e[0],m=b.isFunction(g);if(m||!(1>=d||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=f.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(d&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ft(l,"script"),Ht),a=s.length;d>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ft(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?jt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,Mt),c=0;a>c;c++)o=s[c],Et.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(At,"")));l=i=null}return this}});function jt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function Mt(e){var t=kt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Bt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,Mt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&wt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),f.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ft(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function qt(e){wt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Lt.innerHTML=e.outerHTML,Lt.removeChild(o=Lt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ft(o,"script"),r.length>0&&Bt(r,!u&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,d=e.length,p=ft(t),f=[],h=0;for(;d>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(f,o.nodeType?[o]:o);else if(Tt.test(o)){s=s||p.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=St[u]||St._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&f.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) +}b.merge(f,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=p.lastChild}else f.push(t.createTextNode(o));s&&p.removeChild(s),b.support.appendChecked||b.grep(Ft(f,"input"),qt),h=0;while(o=f[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ft(p.appendChild(o),"script"),a&&Bt(s),n)){i=0;while(o=s[i++])Et.test(o.type||"")&&n.push(o)}return s=null,p},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,d=b.support.deleteExpando,p=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)p[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],d?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Rt=/%20/g,Pt=/\[\]$/,$t=/\r?\n/g,It=/^(?:submit|button|image|reset|file)$/i,Wt=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&Wt.test(this.nodeName)&&!It.test(e)&&(this.checked||!wt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace($t,"\r\n")}}):{name:t.name,value:n.replace($t,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)Xt(r,e[r],n,o);return i.join("&").replace(Rt,"+")};function Xt(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||Pt.test(e)?r(e,i):Xt(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)Xt(e+"["+i+"]",t[i],n,r)}var zt,Ut,Vt=b.now(),Jt=/\?/,Qt=/#.*$/,Gt=/([?&])_=[^&]*/,Kt=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Yt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Zt=/^(?:GET|HEAD)$/,en=/^\/\//,tn=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,nn=b.fn.load,rn={},on={},an="*/".concat("*");try{Ut=a.href}catch(sn){Ut=o.createElement("a"),Ut.href="",Ut=Ut.href}zt=tn.exec(Ut.toLowerCase())||[];function un(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(T)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function ln(e,n,r,i){var o={},a=e===on;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function cn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&nn)return nn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("
").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ut,type:"GET",isLocal:Yt.test(zt[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":an,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?cn(cn(e,b.ajaxSettings),t):cn(b.ajaxSettings,e)},ajaxPrefilter:un(rn),ajaxTransport:un(on),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,d=b.ajaxSetup({},n),p=d.context||d,f=d.context&&(p.nodeType||p.jquery)?b(p):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=d.statusCode||{},y={},v={},x=0,N="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Kt.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||N;return l&&l.abort(t),E(0,t),this}};if(h.promise(w).complete=g.add,w.success=w.done,w.error=w.fail,d.url=((e||d.url||Ut)+"").replace(Qt,"").replace(en,zt[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=b.trim(d.dataType||"*").toLowerCase().match(T)||[""],null==d.crossDomain&&(r=tn.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]===zt[1]&&r[2]===zt[2]&&(r[3]||("http:"===r[1]?80:443))==(zt[3]||("http:"===zt[1]?80:443)))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=b.param(d.data,d.traditional)),ln(rn,d,n,w),2===x)return w;u=d.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Zt.test(d.type),o=d.url,d.hasContent||(d.data&&(o=d.url+=(Jt.test(o)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Gt.test(o)?o.replace(Gt,"$1_="+Vt++):o+(Jt.test(o)?"&":"?")+"_="+Vt++)),d.ifModified&&(b.lastModified[o]&&w.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&w.setRequestHeader("If-None-Match",b.etag[o])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&w.setRequestHeader("Content-Type",d.contentType),w.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+an+"; q=0.01":""):d.accepts["*"]);for(i in d.headers)w.setRequestHeader(i,d.headers[i]);if(d.beforeSend&&(d.beforeSend.call(p,w,d)===!1||2===x))return w.abort();N="abort";for(i in{success:1,error:1,complete:1})w[i](d[i]);if(l=ln(on,d,n,w)){w.readyState=1,u&&f.trigger("ajaxSend",[w,d]),d.async&&d.timeout>0&&(s=setTimeout(function(){w.abort("timeout")},d.timeout));try{x=1,l.send(y,E)}catch(C){if(!(2>x))throw C;E(-1,C)}}else E(-1,"No Transport");function E(e,n,r,i){var c,y,v,T,N,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",w.readyState=e>0?4:0,r&&(T=dn(d,w,r)),e>=200&&300>e||304===e?(d.ifModified&&(N=w.getResponseHeader("Last-Modified"),N&&(b.lastModified[o]=N),N=w.getResponseHeader("etag"),N&&(b.etag[o]=N)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=pn(d,T),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),w.status=e,w.statusText=(n||C)+"",c?h.resolveWith(p,[y,C,w]):h.rejectWith(p,[w,C,v]),w.statusCode(m),m=t,u&&f.trigger(c?"ajaxSuccess":"ajaxError",[w,d,c?y:v]),g.fireWith(p,[w,C]),u&&(f.trigger("ajaxComplete",[w,d]),--b.active||b.event.trigger("ajaxStop")))}return w},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function dn(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function pn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}var fn,hn,gn=0,mn=e.ActiveXObject&&function(){var e;for(e in fn)fn[e](t,!0)};function yn(){try{return new e.XMLHttpRequest}catch(t){}}function vn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&yn()||vn()}:yn,hn=b.ajaxSettings.xhr(),b.support.cors=!!hn&&"withCredentials"in hn,hn=b.support.ajax=!!hn,hn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,d;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,mn&&delete fn[a]),i)4!==u.readyState&&u.abort();else{d={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(d.text=u.responseText);try{c=u.statusText}catch(p){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=d.text?200:404}}catch(f){i||o(-1,f)}d&&o(s,c,d,l)},n.async?4===u.readyState?setTimeout(r):(a=++gn,mn&&(fn||(fn={},b(e).unload(mn)),fn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window); \ No newline at end of file diff --git a/bower_components/jquery/test/data/json.php b/bower_components/jquery/test/data/json.php new file mode 100644 index 0000000..d6e0f2f --- /dev/null +++ b/bower_components/jquery/test/data/json.php @@ -0,0 +1,13 @@ + diff --git a/bower_components/jquery/test/data/json_obj.js b/bower_components/jquery/test/data/json_obj.js new file mode 100644 index 0000000..7fa6182 --- /dev/null +++ b/bower_components/jquery/test/data/json_obj.js @@ -0,0 +1 @@ +{ "data": {"lang": "en", "length": 25} } diff --git a/bower_components/jquery/test/data/jsonp.php b/bower_components/jquery/test/data/jsonp.php new file mode 100644 index 0000000..6c13d72 --- /dev/null +++ b/bower_components/jquery/test/data/jsonp.php @@ -0,0 +1,14 @@ + diff --git a/bower_components/jquery/test/data/manipulation/iframe-denied.html b/bower_components/jquery/test/data/manipulation/iframe-denied.html new file mode 100644 index 0000000..14df26a --- /dev/null +++ b/bower_components/jquery/test/data/manipulation/iframe-denied.html @@ -0,0 +1,36 @@ + + + + + body + + +
+ + + + diff --git a/bower_components/jquery/test/data/name.html b/bower_components/jquery/test/data/name.html new file mode 100644 index 0000000..0fa32d1 --- /dev/null +++ b/bower_components/jquery/test/data/name.html @@ -0,0 +1 @@ +ERROR diff --git a/bower_components/jquery/test/data/name.php b/bower_components/jquery/test/data/name.php new file mode 100644 index 0000000..6402858 --- /dev/null +++ b/bower_components/jquery/test/data/name.php @@ -0,0 +1,24 @@ +$xml$result"; + die(); +} +$name = $_REQUEST['name']; +if($name == 'foo') { + echo "bar"; + die(); +} else if($name == 'peter') { + echo "pan"; + die(); +} + +echo 'ERROR '; +?> \ No newline at end of file diff --git a/bower_components/jquery/test/data/nocontent.php b/bower_components/jquery/test/data/nocontent.php new file mode 100644 index 0000000..9c8431b --- /dev/null +++ b/bower_components/jquery/test/data/nocontent.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/bower_components/jquery/test/data/offset/absolute.html b/bower_components/jquery/test/data/offset/absolute.html new file mode 100644 index 0000000..7665d7a --- /dev/null +++ b/bower_components/jquery/test/data/offset/absolute.html @@ -0,0 +1,41 @@ + + + + + absolute + + + + + +
absolute-1 +
absolute-1-1 +
absolute-1-1-1
+
+
+
absolute-2
+
Has absolute position but no values set for the location ('auto').
+
+

Click the white box to move the marker to it. Clicking the box also changes the position to absolute (if not already) and sets the position according to the position method.

+ + diff --git a/bower_components/jquery/test/data/offset/body.html b/bower_components/jquery/test/data/offset/body.html new file mode 100644 index 0000000..6dc3d37 --- /dev/null +++ b/bower_components/jquery/test/data/offset/body.html @@ -0,0 +1,26 @@ + + + + + body + + + + + +
+
+ + diff --git a/bower_components/jquery/test/data/offset/fixed.html b/bower_components/jquery/test/data/offset/fixed.html new file mode 100644 index 0000000..7564f08 --- /dev/null +++ b/bower_components/jquery/test/data/offset/fixed.html @@ -0,0 +1,34 @@ + + + + + fixed + + + + + +
+
+
+
+
+

Click the white box to move the marker to it.

+ + diff --git a/bower_components/jquery/test/data/offset/relative.html b/bower_components/jquery/test/data/offset/relative.html new file mode 100644 index 0000000..3ac0548 --- /dev/null +++ b/bower_components/jquery/test/data/offset/relative.html @@ -0,0 +1,31 @@ + + + + + relative + + + + + +
+
+
+

Click the white box to move the marker to it. Clicking the box also changes the position to absolute (if not already) and sets the position according to the position method.

+ + diff --git a/bower_components/jquery/test/data/offset/scroll.html b/bower_components/jquery/test/data/offset/scroll.html new file mode 100644 index 0000000..113400c --- /dev/null +++ b/bower_components/jquery/test/data/offset/scroll.html @@ -0,0 +1,39 @@ + + + + + scroll + + + + + +
+
+
+
+
+
+
+

Click the white box to move the marker to it.

+ + diff --git a/bower_components/jquery/test/data/offset/static.html b/bower_components/jquery/test/data/offset/static.html new file mode 100644 index 0000000..1e6ab7c --- /dev/null +++ b/bower_components/jquery/test/data/offset/static.html @@ -0,0 +1,31 @@ + + + + + static + + + + + +
+
+
+

Click the white box to move the marker to it. Clicking the box also changes the position to absolute (if not already) and sets the position according to the position method.

+ + diff --git a/bower_components/jquery/test/data/offset/table.html b/bower_components/jquery/test/data/offset/table.html new file mode 100644 index 0000000..5510e2b --- /dev/null +++ b/bower_components/jquery/test/data/offset/table.html @@ -0,0 +1,43 @@ + + + + + table + + + + + +
+ + + + + + + + + + + + + + +
th-1th-2th-3
td-1td-2td-3
+
+

Click the white box to move the marker to it.

+ + diff --git a/bower_components/jquery/test/data/params_html.php b/bower_components/jquery/test/data/params_html.php new file mode 100644 index 0000000..e88ef15 --- /dev/null +++ b/bower_components/jquery/test/data/params_html.php @@ -0,0 +1,12 @@ +
+$value ) + echo "$value"; +?> +
+
+$value ) + echo "$value"; +?> +
\ No newline at end of file diff --git a/bower_components/jquery/test/data/readywaitasset.js b/bower_components/jquery/test/data/readywaitasset.js new file mode 100644 index 0000000..2308965 --- /dev/null +++ b/bower_components/jquery/test/data/readywaitasset.js @@ -0,0 +1 @@ +var delayedMessage = "It worked!"; diff --git a/bower_components/jquery/test/data/readywaitloader.js b/bower_components/jquery/test/data/readywaitloader.js new file mode 100644 index 0000000..e07dac7 --- /dev/null +++ b/bower_components/jquery/test/data/readywaitloader.js @@ -0,0 +1,25 @@ +// Simple script loader that uses jQuery.readyWait via jQuery.holdReady() + +//Hold on jQuery! +jQuery.holdReady(true); + +var readyRegExp = /^(complete|loaded)$/; + +function assetLoaded( evt ){ + var node = evt.currentTarget || evt.srcElement; + if ( evt.type === "load" || readyRegExp.test(node.readyState) ) { + jQuery.holdReady(false); + } +} + +setTimeout( function() { + var script = document.createElement("script"); + script.type = "text/javascript"; + if ( script.addEventListener ) { + script.addEventListener( "load", assetLoaded, false ); + } else { + script.attachEvent( "onreadystatechange", assetLoaded ); + } + script.src = "data/readywaitasset.js"; + document.getElementsByTagName("head")[0].appendChild(script); +}, 2000 ); diff --git a/bower_components/jquery/test/data/script.php b/bower_components/jquery/test/data/script.php new file mode 100644 index 0000000..fb71104 --- /dev/null +++ b/bower_components/jquery/test/data/script.php @@ -0,0 +1,11 @@ + +ok( true, "Script executed correctly." ); diff --git a/bower_components/jquery/test/data/selector/html5_selector.html b/bower_components/jquery/test/data/selector/html5_selector.html new file mode 100644 index 0000000..30f25c9 --- /dev/null +++ b/bower_components/jquery/test/data/selector/html5_selector.html @@ -0,0 +1,114 @@ + + + + + jQuery selector - attributes + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + + + +
    + +
    + +
    + + + + + + + + + + + + + + + +
    +
    Term
    This is the first definition in compact format.
    +
    Term
    This is the second definition in compact format.
    +
    + + + + Scrolling text (non-standard) + + diff --git a/bower_components/jquery/test/data/selector/sizzle_cache.html b/bower_components/jquery/test/data/selector/sizzle_cache.html new file mode 100644 index 0000000..1055c75 --- /dev/null +++ b/bower_components/jquery/test/data/selector/sizzle_cache.html @@ -0,0 +1,21 @@ + + + + + jQuery selector - sizzle cache + + + + + + + + + diff --git a/bower_components/jquery/test/data/statusText.php b/bower_components/jquery/test/data/statusText.php new file mode 100644 index 0000000..daf58ce --- /dev/null +++ b/bower_components/jquery/test/data/statusText.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/bower_components/jquery/test/data/support/bodyBackground.html b/bower_components/jquery/test/data/support/bodyBackground.html new file mode 100644 index 0000000..8991007 --- /dev/null +++ b/bower_components/jquery/test/data/support/bodyBackground.html @@ -0,0 +1,28 @@ + + + + + + + +
    + +
    + + + diff --git a/bower_components/jquery/test/data/support/boxSizing.html b/bower_components/jquery/test/data/support/boxSizing.html new file mode 100644 index 0000000..63c4c90 --- /dev/null +++ b/bower_components/jquery/test/data/support/boxSizing.html @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/bower_components/jquery/test/data/support/csp.js b/bower_components/jquery/test/data/support/csp.js new file mode 100644 index 0000000..8bce34f --- /dev/null +++ b/bower_components/jquery/test/data/support/csp.js @@ -0,0 +1,3 @@ +jQuery(function() { + parent.iframeCallback( jQuery.support ); +}); diff --git a/bower_components/jquery/test/data/support/csp.php b/bower_components/jquery/test/data/support/csp.php new file mode 100644 index 0000000..f72aa07 --- /dev/null +++ b/bower_components/jquery/test/data/support/csp.php @@ -0,0 +1,22 @@ + + + + + + CSP Test Page + + + + +

    CSP Test Page

    + + diff --git a/bower_components/jquery/test/data/support/shrinkWrapBlocks.html b/bower_components/jquery/test/data/support/shrinkWrapBlocks.html new file mode 100644 index 0000000..a2097cb --- /dev/null +++ b/bower_components/jquery/test/data/support/shrinkWrapBlocks.html @@ -0,0 +1,23 @@ + + + + + + + +
    + +
    + + + diff --git a/bower_components/jquery/test/data/support/testElementCrash.html b/bower_components/jquery/test/data/support/testElementCrash.html new file mode 100644 index 0000000..16de48c --- /dev/null +++ b/bower_components/jquery/test/data/support/testElementCrash.html @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/bower_components/jquery/test/data/test.html b/bower_components/jquery/test/data/test.html new file mode 100644 index 0000000..eec028e --- /dev/null +++ b/bower_components/jquery/test/data/test.html @@ -0,0 +1,7 @@ +html text
    + + +blabla diff --git a/bower_components/jquery/test/data/test.js b/bower_components/jquery/test/data/test.js new file mode 100644 index 0000000..fb33952 --- /dev/null +++ b/bower_components/jquery/test/data/test.js @@ -0,0 +1,3 @@ +this.testBar = "bar"; +jQuery("#ap").html("bar"); +ok( true, "test.js executed"); diff --git a/bower_components/jquery/test/data/test.php b/bower_components/jquery/test/data/test.php new file mode 100644 index 0000000..3d08f32 --- /dev/null +++ b/bower_components/jquery/test/data/test.php @@ -0,0 +1,7 @@ +html text
    + + +blabla \ No newline at end of file diff --git a/bower_components/jquery/test/data/test2.html b/bower_components/jquery/test/data/test2.html new file mode 100644 index 0000000..1df6151 --- /dev/null +++ b/bower_components/jquery/test/data/test2.html @@ -0,0 +1,5 @@ + diff --git a/bower_components/jquery/test/data/test3.html b/bower_components/jquery/test/data/test3.html new file mode 100644 index 0000000..909d417 --- /dev/null +++ b/bower_components/jquery/test/data/test3.html @@ -0,0 +1,3 @@ +
    This is a user
    +
    This is a user
    +
    This is a teacher
    diff --git a/bower_components/jquery/test/data/testinit.js b/bower_components/jquery/test/data/testinit.js new file mode 100644 index 0000000..0351f02 --- /dev/null +++ b/bower_components/jquery/test/data/testinit.js @@ -0,0 +1,264 @@ +/*jshint multistr:true, quotmark:false */ + +var amdDefined, fireNative, + originaljQuery = this.jQuery || "jQuery", + original$ = this.$ || "$", + // see RFC 2606 + externalHost = "example.com"; + +this.hasPHP = true; +this.isLocal = window.location.protocol === "file:"; + +// For testing .noConflict() +this.jQuery = originaljQuery; +this.$ = original$; + +/** + * Set up a mock AMD define function for testing AMD registration. + */ +function define( name, dependencies, callback ) { + amdDefined = callback(); +} + +define.amd = {}; + +/** + * Returns an array of elements with the given IDs + * @example q("main", "foo", "bar") + * @result [
    , , ] + */ +this.q = function() { + var r = [], + i = 0; + + for ( ; i < arguments.length; i++ ) { + r.push( document.getElementById( arguments[i] ) ); + } + return r; +}; + +/** + * Asserts that a select matches the given IDs + * @param {String} a - Assertion name + * @param {String} b - Sizzle selector + * @param {String} c - Array of ids to construct what is expected + * @example t("Check for something", "//[a]", ["foo", "baar"]); + * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baar' + */ +this.t = function( a, b, c ) { + var f = jQuery(b).get(), + s = "", + i = 0; + + for ( ; i < f.length; i++ ) { + s += ( s && "," ) + '"' + f[ i ].id + '"'; + } + + deepEqual(f, q.apply( q, c ), a + " (" + b + ")"); +}; + +this.createDashboardXML = function() { + var string = ' \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + '; + + return jQuery.parseXML(string); +}; + +this.createWithFriesXML = function() { + var string = ' \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + 1 \ + \ + \ + \ + \ + foo \ + \ + \ + \ + \ + \ + \ + '; + + return jQuery.parseXML( string.replace( /\{\{\s*externalHost\s*\}\}/g, externalHost ) ); +}; + +this.createXMLFragment = function() { + var xml, frag; + if ( window.ActiveXObject ) { + xml = new ActiveXObject("msxml2.domdocument"); + } else { + xml = document.implementation.createDocument( "", "", null ); + } + + if ( xml ) { + frag = xml.createElement("data"); + } + + return frag; +}; + +fireNative = document.createEvent ? + function( node, type ) { + var event = document.createEvent('HTMLEvents'); + event.initEvent( type, true, true ); + node.dispatchEvent( event ); + } : + function( node, type ) { + var event = document.createEventObject(); + node.fireEvent( 'on' + type, event ); + }; + +/** + * Add random number to url to stop caching + * + * @example url("http://github.com/data/test.html") + * @result "data/test.html?10538358428943" + * + * @example url("http://github.com/data/test.php?foo=bar") + * @result "data/test.php?foo=bar&10538358345554" + */ +function url(http://github.com/value) { + return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random() * 100000, 10); +} + +// Ajax testing helper +this.ajaxTest = function( title, expect, options ) { + var requestOptions; + if ( jQuery.isFunction( options ) ) { + options = options(); + } + options = options || []; + requestOptions = options.requests || options.request || options; + if ( !jQuery.isArray( requestOptions ) ) { + requestOptions = [ requestOptions ]; + } + asyncTest( title, expect, function() { + if ( options.setup ) { + options.setup(); + } + + var completed = false, + remaining = requestOptions.length, + complete = function() { + if ( !completed && --remaining === 0 ) { + completed = true; + delete ajaxTest.abort; + if ( options.teardown ) { + options.teardown(); + } + start(); + } + }, + requests = jQuery.map( requestOptions, function( options ) { + var request = ( options.create || jQuery.ajax )( options ), + callIfDefined = function( deferType, optionType ) { + var handler = options[ deferType ] || !!options[ optionType ]; + return function( _, status ) { + if ( !completed ) { + if ( !handler ) { + ok( false, "unexpected " + status ); + } else if ( jQuery.isFunction( handler ) ) { + handler.apply( this, arguments ); + } + } + }; + }; + + if ( options.afterSend ) { + options.afterSend( request ); + } + + return request + .done( callIfDefined( "done", "success" ) ) + .fail( callIfDefined( "fail", "error" ) ) + .always( complete ); + }); + + ajaxTest.abort = function( reason ) { + if ( !completed ) { + completed = true; + delete ajaxTest.abort; + ok( false, "aborted " + reason ); + jQuery.each( requests, function( i, request ) { + request.abort(); + }); + } + }; + }); +}; + + +this.testIframe = function( fileName, name, fn ) { + + test(name, function() { + // pause execution for now + stop(); + + // load fixture in iframe + var iframe = loadFixture(), + win = iframe.contentWindow, + interval = setInterval( function() { + if ( win && win.jQuery && win.jQuery.isReady ) { + clearInterval( interval ); + // continue + start(); + // call actual tests passing the correct jQuery instance to use + fn.call( this, win.jQuery, win, win.document ); + document.body.removeChild( iframe ); + iframe = null; + } + }, 15 ); + }); + + function loadFixture() { + var src = url("http://github.com/data/%22%20+%20fileName%20+%20%22.html"), + iframe = jQuery(" +
    +
    +

    See this blog entry for more information.

    +

    + Here are some links in a normal paragraph: Google, + Google Groups (Link). + This link has class="blog": + diveintomark + +

    +
    +

    Everything inside the red border is inside a div with id="foo".

    +

    This is a normal link: Yahoo

    +

    This link has class="blog": Simon Willison's Weblog

    + +
    +
    +
    +
    + +

    Try them out:

    +
      +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + test element +
        + Float test. + +
        + + +
        +
        + +
        + + + + +
        + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        +
        +
        + +
        +
        hi there
        +
        +
        +
        +
        +
        +
        +
        +
        + +
        +
          +
        1. Rice
        2. +
        3. Beans
        4. +
        5. Blinis
        6. +
        7. Tofu
        8. +
        + +
        I'm hungry. I should...
        + ...Eat lots of food... | + ...Eat a little food... | + ...Eat no food... + ...Eat a burger... + ...Eat some funyuns... + ...Eat some funyuns... + + + + + + +
        + +
        + + +
        + +
        + 1 + 2 + + + + + + + + +
        +
        +
        +
        fadeIn
        fadeIn
        +
        fadeOut
        fadeOut
        + +
        show
        show
        +
        hide
        hide
        +
        hide
        hide
        + +
        togglein
        togglein
        +
        toggleout
        toggleout
        +
        toggleout
        toggleout
        + +
        slideUp
        slideUp
        +
        slideDown
        slideDown
        +
        slideUp
        slideUp
        + +
        slideToggleIn
        slideToggleIn
        +
        slideToggleOut
        slideToggleOut
        + +
        fadeToggleIn
        fadeToggleIn
        +
        fadeToggleOut
        fadeToggleOut
        + +
        fadeTo
        fadeTo
        +
        + +
        + +
        +
        +
        + + + + + + diff --git a/bower_components/jquery/test/jquery.js b/bower_components/jquery/test/jquery.js new file mode 100644 index 0000000..93d1095 --- /dev/null +++ b/bower_components/jquery/test/jquery.js @@ -0,0 +1,5 @@ +// Use the right jQuery source in iframe tests +document.write( " + + + +

        jQuery Local File Test

        +

        + Introduction +

        +
          +
        • + Access this file using the "file:" protocol, +
        • +
        • + two green "OK" strings must appear below, +
        • +
        • + Empty local files will issue errors, it's a known limitation. +
        • +
        +

        + Results +

        +
          +
        • + Success: + + +
        • +
        • + Error: + + +
        • +
        +

        + Logs: +

        +
          +
        + + diff --git a/bower_components/jquery/test/networkerror.html b/bower_components/jquery/test/networkerror.html new file mode 100644 index 0000000..edbaaba --- /dev/null +++ b/bower_components/jquery/test/networkerror.html @@ -0,0 +1,84 @@ + + + + + + jQuery Network Error Test for Firefox + + + + + + +

        + jQuery Network Error Test for Firefox +

        +
        + This is a test page for + + #8135 + + which was reported in Firefox when accessing properties + of an XMLHttpRequest object after a network error occurred. +
        +
        Take the following steps:
        +
          +
        1. + make sure you accessed this page through a web server, +
        2. +
        3. + stop the web server, +
        4. +
        5. + open the console, +
        6. +
        7. + click this + + , +
        8. +
        9. + wait for both requests to fail. +
        10. +
        +
        + Test passes if you get two log lines: +
          +
        • + the first starting with "abort", +
        • +
        • + the second starting with "complete", +
        • +
        +
        +
        + Test fails if the browser notifies an exception. +
        + + diff --git a/bower_components/jquery/test/readywait.html b/bower_components/jquery/test/readywait.html new file mode 100644 index 0000000..7a736be --- /dev/null +++ b/bower_components/jquery/test/readywait.html @@ -0,0 +1,70 @@ + + + + + + jQuery.holdReady Test + + + + + + + + + + +

        + jQuery.holdReady Test +

        +

        + This is a test page for jQuery.readyWait and jQuery.holdReady, + see + #6781 + and + #8803. +

        +

        + Test for jQuery.holdReady, which can be used + by plugins and other scripts to indicate something + important to the page is still loading and needs + to block the DOM ready callbacks that are registered + with jQuery. +

        +

        + Script loaders are the most likely kind of script + to use jQuery.holdReady, but it could be used by + other things like a script that loads a CSS file + and wants to pause the DOM ready callbacks. +

        +

        + Expected Result: The text + It Worked! + appears below after about 2 seconds. +

        +

        + If there is an error in the console, + or the text does not show up, then the test failed. +

        +
        + + diff --git a/bower_components/jquery/test/unit/ajax.js b/bower_components/jquery/test/unit/ajax.js new file mode 100644 index 0000000..761885c --- /dev/null +++ b/bower_components/jquery/test/unit/ajax.js @@ -0,0 +1,1954 @@ +module( "ajax", { + setup: function() { + var jsonpCallback = this.jsonpCallback = jQuery.ajaxSettings.jsonpCallback; + jQuery.ajaxSettings.jsonpCallback = function() { + var callback = jsonpCallback.apply( this, arguments ); + Globals.register( callback ); + return callback; + }; + }, + teardown: function() { + jQuery( document ).off( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError ajaxSuccess" ); + moduleTeardown.apply( this, arguments ); + } +}); + +(function() { + + if ( !jQuery.ajax || ( isLocal && !hasPHP ) ) { + return; + } + + function addGlobalEvents( expected ) { + return function() { + expected = expected || ""; + jQuery( document ).on( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError ajaxSuccess", function( e ) { + ok( expected.indexOf(e.type) !== -1, e.type ); + }); + }; + } + +//----------- jQuery.ajax() + + testIframeWithCallback( "XMLHttpRequest - Attempt to block tests because of dangling XHR requests (IE)", "ajax/unreleasedXHR.html", function() { + expect( 1 ); + ok( true, "done" ); + }); + + ajaxTest( "jQuery.ajax() - success callbacks", 8, { + setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess"), + url: url("http://github.com/data/name.html"), + beforeSend: function() { + ok( true, "beforeSend" ); + }, + success: function() { + ok( true, "success" ); + }, + complete: function() { + ok( true, "complete"); + } + }); + + ajaxTest( "jQuery.ajax() - success callbacks - (url, options) syntax", 8, { + setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess"), + create: function( options ) { + return jQuery.ajax( url("http://github.com/data/name.html"), options ); + }, + beforeSend: function() { + ok( true, "beforeSend" ); + }, + success: function() { + ok( true, "success" ); + }, + complete: function() { + ok( true, "complete" ); + } + }); + + ajaxTest( "jQuery.ajax() - success callbacks (late binding)", 8, { + setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess"), + url: url("http://github.com/data/name.html"), + beforeSend: function() { + ok( true, "beforeSend" ); + }, + success: true, + afterSend: function( request ) { + request.complete(function() { + ok( true, "complete" ); + }).success(function() { + ok( true, "success" ); + }).error(function() { + ok( false, "error" ); + }); + } + }); + + ajaxTest( "jQuery.ajax() - success callbacks (oncomplete binding)", 8, { + setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess"), + url: url("http://github.com/data/name.html"), + beforeSend: function() { + ok( true, "beforeSend" ); + }, + success: true, + complete: function( xhr ) { + xhr.complete(function() { + ok( true, "complete" ); + }).success(function() { + ok( true, "success" ); + }).error(function() { + ok( false, "error" ); + }); + } + }); + + ajaxTest( "jQuery.ajax() - error callbacks", 8, { + setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError"), + url: url("http://github.com/data/name.php?wait=5"), + beforeSend: function() { + ok( true, "beforeSend" ); + }, + afterSend: function( request ) { + request.abort(); + }, + error: function() { + ok( true, "error" ); + }, + complete: function() { + ok( true, "complete" ); + } + }); + + ajaxTest( "jQuery.ajax() - textStatus and errorThrown values", 4, [ + { + url: url("http://github.com/data/name.php?wait=5"), + error: function( _, textStatus, errorThrown ) { + strictEqual( textStatus, "abort", "textStatus is 'abort' for abort" ); + strictEqual( errorThrown, "abort", "errorThrown is 'abort' for abort" ); + }, + afterSend: function( request ) { + request.abort(); + } + }, + { + url: url("http://github.com/data/name.php?wait=5"), + error: function( _, textStatus, errorThrown ) { + strictEqual( textStatus, "mystatus", "textStatus is 'mystatus' for abort('mystatus')" ); + strictEqual( errorThrown, "mystatus", "errorThrown is 'mystatus' for abort('mystatus')" ); + }, + afterSend: function( request ) { + request.abort("mystatus"); + } + } + ]); + + ajaxTest( "jQuery.ajax() - responseText on error", 1, { + url: url("http://github.com/data/errorWithText.php"), + error: function( xhr ) { + strictEqual( xhr.responseText, "plain text message", "Test jqXHR.responseText is filled for HTTP errors" ); + } + }); + + asyncTest( "jQuery.ajax() - retry with jQuery.ajax( this )", 2, function() { + var previousUrl, + firstTime = true; + jQuery.ajax({ + url: url("http://github.com/data/errorWithText.php"), + error: function() { + if ( firstTime ) { + firstTime = false; + jQuery.ajax( this ); + } else { + ok ( true, "Test retrying with jQuery.ajax(this) works" ); + jQuery.ajax({ + url: url("http://github.com/data/errorWithText.php"), + data: { + "x": 1 + }, + beforeSend: function() { + if ( !previousUrl ) { + previousUrl = this.url; + } else { + strictEqual( this.url, previousUrl, "url parameters are not re-appended" ); + start(); + return false; + } + }, + error: function() { + jQuery.ajax( this ); + } + }); + } + } + }); + }); + + ajaxTest( "jQuery.ajax() - headers", 4, { + setup: function() { + jQuery( document ).ajaxSend(function( evt, xhr ) { + xhr.setRequestHeader( "ajax-send", "test" ); + }); + }, + url: url("http://github.com/data/headers.php?keys=siMPle_SometHing-elsE_OthEr_ajax-send"), + headers: { + "siMPle": "value", + "SometHing-elsE": "other value", + "OthEr": "something else" + }, + success: function( data, _, xhr ) { + var i, emptyHeader, + requestHeaders = jQuery.extend( this.headers, { + "ajax-send": "test" + }), + tmp = []; + for ( i in requestHeaders ) { + tmp.push( i, ": ", requestHeaders[ i ], "\n" ); + } + tmp = tmp.join(""); + + strictEqual( data, tmp, "Headers were sent" ); + strictEqual( xhr.getResponseHeader("Sample-Header"), "Hello World", "Sample header received" ); + + emptyHeader = xhr.getResponseHeader("Empty-Header"); + if ( emptyHeader === null ) { + ok( true, "Firefox doesn't support empty headers" ); + } else { + strictEqual( emptyHeader, "", "Empty header received" ); + } + strictEqual( xhr.getResponseHeader("Sample-Header2"), "Hello World 2", "Second sample header received" ); + } + }); + + ajaxTest( "jQuery.ajax() - Accept header", 1, { + url: url("http://github.com/data/headers.php?keys=accept"), + headers: { + Accept: "very wrong accept value" + }, + beforeSend: function( xhr ) { + xhr.setRequestHeader("Accept", "*/*"); + }, + success: function( data ) { + strictEqual( data, "accept: */*\n", "Test Accept header is set to last value provided" ); + } + }); + + ajaxTest( "jQuery.ajax() - contentType", 2, [ + { + url: url("http://github.com/data/headers.php?keys=content-type"), + contentType: "test", + success: function( data ) { + strictEqual( data, "content-type: test\n", "Test content-type is sent when options.contentType is set" ); + } + }, + { + url: url("http://github.com/data/headers.php?keys=content-type"), + contentType: false, + success: function( data ) { + strictEqual( data, "content-type: \n", "Test content-type is not sent when options.contentType===false" ); + } + } + ]); + + ajaxTest( "jQuery.ajax() - protocol-less urls", 1, { + url: "//somedomain.com", + beforeSend: function( xhr, settings ) { + equal( settings.url, location.protocol + "//somedomain.com", "Make sure that the protocol is added." ); + return false; + }, + error: true + }); + + ajaxTest( "jQuery.ajax() - hash", 3, [ + { + url: "data/name.html#foo", + beforeSend: function( xhr, settings ) { + equal( settings.url, "data/name.html", "Make sure that the URL is trimmed." ); + return false; + }, + error: true + }, + { + url: "data/name.html?abc#foo", + beforeSend: function( xhr, settings ) { + equal( settings.url, "data/name.html?abc", "Make sure that the URL is trimmed." ); + return false; + }, + error: true + }, + { + url: "data/name.html?abc#foo", + data: { + "test": 123 + }, + beforeSend: function( xhr, settings ) { + equal( settings.url, "data/name.html?abc&test=123", "Make sure that the URL is trimmed." ); + return false; + }, + error: true + } + ]); + + ajaxTest( "jQuery.ajax() - cross-domain detection", 7, function() { + function request( url, title, crossDomainOrOptions ) { + return jQuery.extend( { + dataType: "jsonp", + url: url, + beforeSend: function( _, s ) { + ok( crossDomainOrOptions === false ? !s.crossDomain : s.crossDomain, title ); + return false; + }, + error: true + }, crossDomainOrOptions ); + } + + var loc = document.location, + samePort = loc.port || ( loc.protocol === "http:" ? 80 : 443 ), + otherPort = loc.port === 666 ? 667 : 666, + otherProtocol = loc.protocol === "http:" ? "https:" : "http:"; + + return [ + request( + loc.protocol + "//" + loc.host + ":" + samePort, + "Test matching ports are not detected as cross-domain", + false + ), + request( + otherProtocol + "//" + loc.host, + "Test different protocols are detected as cross-domain" + ), + request( + "app:/path", + "Adobe AIR app:/ URL detected as cross-domain" + ), + request( + loc.protocol + "//example.invalid:" + ( loc.port || 80 ), + "Test different hostnames are detected as cross-domain" + ), + request( + loc.protocol + "//" + loc.hostname + ":" + otherPort, + "Test different ports are detected as cross-domain" + ), + request( + "about:blank", + "Test about:blank is detected as cross-domain" + ), + request( + loc.protocol + "//" + loc.host, + "Test forced crossDomain is detected as cross-domain", + { + crossDomain: true + } + ) + ]; + }); + + ajaxTest( "jQuery.ajax() - abort", 9, { + setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxError ajaxComplete"), + url: url("http://github.com/data/name.php?wait=5"), + beforeSend: function() { + ok( true, "beforeSend" ); + }, + afterSend: function( xhr ) { + strictEqual( xhr.readyState, 1, "XHR readyState indicates successful dispatch" ); + xhr.abort(); + strictEqual( xhr.readyState, 0, "XHR readyState indicates successful abortion" ); + }, + error: true, + complete: function() { + ok( true, "complete" ); + } + }); + + ajaxTest( "jQuery.ajax() - events with context", 12, function() { + + var context = document.createElement("div"); + + function event( e ) { + equal( this, context, e.type ); + } + + function callback( msg ) { + return function() { + equal( this, context, "context is preserved on callback " + msg ); + }; + } + + return { + setup: function() { + jQuery( context ).appendTo("#foo") + .ajaxSend( event ) + .ajaxComplete( event ) + .ajaxError( event ) + .ajaxSuccess( event ); + }, + requests: [{ + url: url("http://github.com/data/name.html"), + context: context, + beforeSend: callback("beforeSend"), + success: callback("success"), + complete: callback("complete") + }, { + url: url("http://github.com/data/404.html"), + context: context, + beforeSend: callback("beforeSend"), + error: callback("error"), + complete: callback("complete") + }] + }; + }); + + ajaxTest( "jQuery.ajax() - events without context", 3, function() { + function nocallback( msg ) { + return function() { + equal( typeof this.url, "string", "context is settings on callback " + msg ); + }; + } + return { + url: url("http://github.com/data/404.html"), + beforeSend: nocallback("beforeSend"), + error: nocallback("error"), + complete: nocallback("complete") + }; + }); + + ajaxTest( "jQuery.ajax() - context modification", 1, { + url: url("http://github.com/data/name.html"), + context: {}, + beforeSend: function() { + this.test = "foo"; + }, + afterSend: function() { + strictEqual( this.context.test, "foo", "Make sure the original object is maintained." ); + }, + success: true + }); + + ajaxTest( "jQuery.ajax() - context modification through ajaxSetup", 3, function() { + var obj = {}; + return { + setup: function() { + jQuery.ajaxSetup({ + context: obj + }); + strictEqual( jQuery.ajaxSettings.context, obj, "Make sure the context is properly set in ajaxSettings." ); + }, + requests: [{ + url: url("http://github.com/data/name.html"), + success: function() { + strictEqual( this, obj, "Make sure the original object is maintained." ); + } + }, { + url: url("http://github.com/data/name.html"), + context: {}, + success: function() { + ok( this !== obj, "Make sure overriding context is possible." ); + } + }] + }; + }); + + ajaxTest( "jQuery.ajax() - disabled globals", 3, { + setup: addGlobalEvents(""), + global: false, + url: url("http://github.com/data/name.html"), + beforeSend: function() { + ok( true, "beforeSend" ); + }, + success: function() { + ok( true, "success" ); + }, + complete: function() { + ok( true, "complete" ); + } + }); + + ajaxTest( "jQuery.ajax() - xml: non-namespace elements inside namespaced elements", 3, { + url: url("http://github.com/data/with_fries.xml"), + dataType: "xml", + success: function( resp ) { + equal( jQuery( "properties", resp ).length, 1, "properties in responseXML" ); + equal( jQuery( "jsconf", resp ).length, 1, "jsconf in responseXML" ); + equal( jQuery( "thing", resp ).length, 2, "things in responseXML" ); + } + }); + + ajaxTest( "jQuery.ajax() - xml: non-namespace elements inside namespaced elements (over JSONP)", 3, { + url: url("http://github.com/data/with_fries_over_jsonp.php"), + dataType: "jsonp xml", + success: function( resp ) { + equal( jQuery( "properties", resp ).length, 1, "properties in responseXML" ); + equal( jQuery( "jsconf", resp ).length, 1, "jsconf in responseXML" ); + equal( jQuery( "thing", resp ).length, 2, "things in responseXML" ); + } + }); + + ajaxTest( "jQuery.ajax() - HEAD requests", 2, [ + { + url: url("http://github.com/data/name.html"), + type: "HEAD", + success: function( data, status, xhr ) { + ok( /Date/i.test( xhr.getAllResponseHeaders() ), "No Date in HEAD response" ); + } + }, + { + url: url("http://github.com/data/name.html"), + data: { + "whip_it": "good" + }, + type: "HEAD", + success: function( data, status, xhr ) { + ok( /Date/i.test( xhr.getAllResponseHeaders() ), "No Date in HEAD response with data" ); + } + } + ]); + + ajaxTest( "jQuery.ajax() - beforeSend", 1, { + url: url("http://github.com/data/name.html"), + beforeSend: function() { + this.check = true; + }, + success: function() { + ok( this.check, "check beforeSend was executed" ); + } + }); + + ajaxTest( "jQuery.ajax() - beforeSend, cancel request manually", 2, { + create: function() { + return jQuery.ajax({ + url: url("http://github.com/data/name.html"), + beforeSend: function( xhr ) { + ok( true, "beforeSend got called, canceling" ); + xhr.abort(); + }, + success: function() { + ok( false, "request didn't get canceled" ); + }, + complete: function() { + ok( false, "request didn't get canceled" ); + }, + error: function() { + ok( false, "request didn't get canceled" ); + } + }); + }, + fail: function( _, reason ) { + strictEqual( reason, "canceled", "canceled request must fail with 'canceled' status text" ); + } + }); + + ajaxTest( "jQuery.ajax() - dataType html", 5, { + setup: function() { + Globals.register("testFoo"); + Globals.register("testBar"); + }, + dataType: "html", + url: url("http://github.com/data/test.html"), + success: function( data ) { + ok( data.match( /^html text/ ), "Check content for datatype html" ); + jQuery("#ap").html( data ); + strictEqual( window["testFoo"], "foo", "Check if script was evaluated for datatype html" ); + strictEqual( window["testBar"], "bar", "Check if script src was evaluated for datatype html" ); + } + }); + + ajaxTest( "jQuery.ajax() - synchronous request", 1, { + url: url("http://github.com/data/json_obj.js"), + dataType: "text", + async: false, + success: true, + afterSend: function( xhr ) { + ok( /^\{ "data"/.test( xhr.responseText ), "check returned text" ); + } + }); + + ajaxTest( "jQuery.ajax() - synchronous request with callbacks", 2, { + url: url("http://github.com/data/json_obj.js"), + async: false, + dataType: "text", + success: true, + afterSend: function( xhr ) { + var result; + xhr.done(function( data ) { + ok( true, "success callback executed" ); + result = data; + }); + ok( /^\{ "data"/.test( result ), "check returned text" ); + } + }); + + asyncTest( "jQuery.ajax(), jQuery.get[Script|JSON](), jQuery.post(), pass-through request object", 8, function() { + var target = "data/name.html", + successCount = 0, + errorCount = 0, + errorEx = "", + success = function() { + successCount++; + }; + jQuery( document ).on( "ajaxError.passthru", function( e, xml ) { + errorCount++; + errorEx += ": " + xml.status; + }); + jQuery( document ).one( "ajaxStop", function() { + equal( successCount, 5, "Check all ajax calls successful" ); + equal( errorCount, 0, "Check no ajax errors (status" + errorEx + ")" ); + jQuery( document ).off("ajaxError.passthru"); + start(); + }); + Globals.register("testBar"); + + ok( jQuery.get( url(http://github.com/target), success ), "get" ); + ok( jQuery.post( url(http://github.com/target), success ), "post" ); + ok( jQuery.getScript( url("http://github.com/data/test.js"), success ), "script" ); + ok( jQuery.getJSON( url("http://github.com/data/json_obj.js"), success ), "json" ); + ok( jQuery.ajax({ + url: url(http://github.com/target), + success: success + }), "generic" ); + }); + + ajaxTest( "jQuery.ajax() - cache", 12, function() { + + var re = /_=(.*?)(&|$)/g; + + function request( url, title ) { + return { + url: url, + cache: false, + beforeSend: function() { + var parameter, tmp; + while(( tmp = re.exec( this.url ) )) { + strictEqual( parameter, undefined, title + ": only one 'no-cache' parameter" ); + parameter = tmp[ 1 ]; + notStrictEqual( parameter, "tobereplaced555", title + ": parameter (if it was there) was replaced" ); + } + return false; + }, + error: true + }; + } + + return [ + request( + "data/text.php", + "no parameter" + ), + request( + "data/text.php?pizza=true", + "1 parameter" + ), + request( + "data/text.php?_=tobereplaced555", + "_= parameter" + ), + request( + "data/text.php?pizza=true&_=tobereplaced555", + "1 parameter and _=" + ), + request( + "data/text.php?_=tobereplaced555&tv=false", + "_= and 1 parameter" + ), + request( + "data/text.php?name=David&_=tobereplaced555&washere=true", + "2 parameters surrounding _=" + ) + ]; + }); + + jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) { + + ajaxTest( "jQuery.ajax() - JSONP - Query String (?n)" + label, 4, [ + { + url: "data/jsonp.php?callback=?", + dataType: "jsonp", + crossDomain: crossDomain, + success: function( data ) { + ok( data.data, "JSON results returned (GET, url callback)" ); + } + }, + { + url: "data/jsonp.php?callback=??", + dataType: "jsonp", + crossDomain: crossDomain, + success: function( data ) { + ok( data.data, "JSON results returned (GET, url context-free callback)" ); + } + }, + { + url: "data/jsonp.php/??", + dataType: "jsonp", + crossDomain: crossDomain, + success: function( data ) { + ok( data.data, "JSON results returned (GET, REST-like)" ); + } + }, + { + url: "data/jsonp.php/???json=1", + dataType: "jsonp", + crossDomain: crossDomain, + success: function( data ) { + strictEqual( jQuery.type( data ), "array", "JSON results returned (GET, REST-like with param)" ); + } + } + ]); + + ajaxTest( "jQuery.ajax() - JSONP - Explicit callback param" + label, 9, { + setup: function() { + Globals.register("functionToCleanUp"); + Globals.register("XXX"); + Globals.register("jsonpResults"); + window["jsonpResults"] = function( data ) { + ok( data["data"], "JSON results returned (GET, custom callback function)" ); + }; + }, + requests: [{ + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + jsonp: "callback", + success: function( data ) { + ok( data["data"], "JSON results returned (GET, data obj callback)" ); + } + }, { + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + jsonpCallback: "jsonpResults", + success: function( data ) { + ok( data.data, "JSON results returned (GET, custom callback name)" ); + } + }, { + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + jsonpCallback: "functionToCleanUp", + success: function( data ) { + ok( data["data"], "JSON results returned (GET, custom callback name to be cleaned up)" ); + strictEqual( window["functionToCleanUp"], undefined, "Callback was removed (GET, custom callback name to be cleaned up)" ); + var xhr; + jQuery.ajax({ + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + jsonpCallback: "functionToCleanUp", + beforeSend: function( jqXHR ) { + xhr = jqXHR; + return false; + } + }); + xhr.fail(function() { + ok( true, "Ajax error JSON (GET, custom callback name to be cleaned up)" ); + strictEqual( window["functionToCleanUp"], undefined, "Callback was removed after early abort (GET, custom callback name to be cleaned up)" ); + }); + } + }, { + url: "data/jsonp.php?callback=XXX", + dataType: "jsonp", + jsonp: false, + jsonpCallback: "XXX", + crossDomain: crossDomain, + beforeSend: function() { + ok( /^data\/jsonp.php\?callback=XXX&_=\d+$/.test( this.url ), "The URL wasn't messed with (GET, custom callback name with no url manipulation)" ); + }, + success: function( data ) { + ok( data["data"], "JSON results returned (GET, custom callback name with no url manipulation)" ); + } + }] + }); + + ajaxTest( "jQuery.ajax() - JSONP - Callback in data" + label, 2, [ + { + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + data: "callback=?", + success: function( data ) { + ok( data.data, "JSON results returned (GET, data callback)" ); + } + }, + { + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + data: "callback=??", + success: function( data ) { + ok( data.data, "JSON results returned (GET, data context-free callback)" ); + } + } + ]); + + + ajaxTest( "jQuery.ajax() - JSONP - POST" + label, 3, [ + { + type: "POST", + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + success: function( data ) { + ok( data["data"], "JSON results returned (POST, no callback)" ); + } + }, + { + type: "POST", + url: "data/jsonp.php", + data: "callback=?", + dataType: "jsonp", + crossDomain: crossDomain, + success: function( data ) { + ok( data["data"], "JSON results returned (POST, data callback)" ); + } + }, + { + type: "POST", + url: "data/jsonp.php", + jsonp: "callback", + dataType: "jsonp", + crossDomain: crossDomain, + success: function( data ) { + ok( data["data"], "JSON results returned (POST, data obj callback)" ); + } + } + ]); + + ajaxTest( "jQuery.ajax() - JSONP" + label, 3, [ + { + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + success: function( data ) { + ok( data.data, "JSON results returned (GET, no callback)" ); + } + }, + { + create: function( options ) { + var request = jQuery.ajax( options ), + promise = request.then(function( data ) { + ok( data.data, "first request: JSON results returned (GET, no callback)" ); + request = jQuery.ajax( this ).done(function( data ) { + ok( data.data, "this re-used: JSON results returned (GET, no callback)" ); + }); + promise.abort = request.abort; + return request; + }); + promise.abort = request.abort; + return promise; + }, + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + success: true + } + ]); + + }); + + ajaxTest( "jQuery.ajax() - script, Remote", 2, { + setup: function() { + Globals.register("testBar"); + }, + url: window.location.href.replace( /[^\/]*$/, "" ) + "data/test.js", + dataType: "script", + success: function() { + strictEqual( window["testBar"], "bar", "Script results returned (GET, no callback)" ); + } + }); + + ajaxTest( "jQuery.ajax() - script, Remote with POST", 3, { + setup: function() { + Globals.register("testBar"); + }, + url: window.location.href.replace( /[^\/]*$/, "" ) + "data/test.js", + type: "POST", + dataType: "script", + success: function( data, status ) { + strictEqual( window["testBar"], "bar", "Script results returned (POST, no callback)" ); + strictEqual( status, "success", "Script results returned (POST, no callback)" ); + } + }); + + ajaxTest( "jQuery.ajax() - script, Remote with scheme-less URL", 2, { + setup: function() { + Globals.register("testBar"); + }, + url: window.location.href.replace( /[^\/]*$/, "" ).replace( /^.*?\/\//, "//" ) + "data/test.js", + dataType: "script", + success: function() { + strictEqual( window["testBar"], "bar", "Script results returned (GET, no callback)" ); + } + }); + + ajaxTest( "jQuery.ajax() - malformed JSON", 2, { + url: "data/badjson.js", + dataType: "json", + error: function( xhr, msg, detailedMsg ) { + strictEqual( msg, "parsererror", "A parse error occurred." ); + ok( /(invalid|error|exception)/i.test( detailedMsg ), "Detailed parsererror message provided" ); + } + }); + + ajaxTest( "jQuery.ajax() - script by content-type", 2, [ + { + url: "data/script.php", + data: { + "header": "script" + }, + success: true + }, + { + url: "data/script.php", + data: { + "header": "ecma" + }, + success: true + } + ]); + + ajaxTest( "jQuery.ajax() - JSON by content-type", 5, { + url: "data/json.php", + data: { + "header": "json", + "json": "array" + }, + success: function( json ) { + ok( json.length >= 2, "Check length" ); + strictEqual( json[ 0 ]["name"], "John", "Check JSON: first, name" ); + strictEqual( json[ 0 ]["age"], 21, "Check JSON: first, age" ); + strictEqual( json[ 1 ]["name"], "Peter", "Check JSON: second, name" ); + strictEqual( json[ 1 ]["age"], 25, "Check JSON: second, age" ); + } + }); + + ajaxTest( "jQuery.ajax() - JSON by content-type disabled with options", 6, { + url: url("http://github.com/data/json.php"), + data: { + "header": "json", + "json": "array" + }, + contents: { + "json": false + }, + success: function( text ) { + strictEqual( typeof text, "string", "json wasn't auto-determined" ); + var json = jQuery.parseJSON( text ); + ok( json.length >= 2, "Check length"); + strictEqual( json[ 0 ]["name"], "John", "Check JSON: first, name" ); + strictEqual( json[ 0 ]["age"], 21, "Check JSON: first, age" ); + strictEqual( json[ 1 ]["name"], "Peter", "Check JSON: second, name" ); + strictEqual( json[ 1 ]["age"], 25, "Check JSON: second, age" ); + } + }); + + ajaxTest( "jQuery.ajax() - simple get", 1, { + type: "GET", + url: url("http://github.com/data/name.php?name=foo"), + success: function( msg ) { + strictEqual( msg, "bar", "Check for GET" ); + } + }); + + ajaxTest( "jQuery.ajax() - simple post", 1, { + type: "POST", + url: url("http://github.com/data/name.php"), + data: "name=peter", + success: function( msg ) { + strictEqual( msg, "pan", "Check for POST" ); + } + }); + + ajaxTest( "jQuery.ajax() - data option - empty bodies for non-GET requests", 1, { + url: "data/echoData.php", + data: undefined, + type: "post", + success: function( result ) { + strictEqual( result, "" ); + } + }); + + var ifModifiedNow = new Date(); + + jQuery.each( + /* jQuery.each arguments start */ + { + " (cache)": true, + " (no cache)": false + }, + function( label, cache ) { + // Support: Opera 12.0 + // In Opera 12.0, XHR doesn't notify 304 back to the user properly + var opera = window.opera && window.opera.version(); + jQuery.each( + { + "If-Modified-Since": "if_modified_since.php", + "Etag": "etag.php" + }, + function( type, url ) { + url = "data/" + url + "?ts=" + ifModifiedNow++; + asyncTest( "jQuery.ajax() - " + type + " support" + label, 4, function() { + jQuery.ajax({ + url: url, + ifModified: true, + cache: cache, + success: function( _, status ) { + strictEqual( status, "success", "Initial status is 'success'" ); + jQuery.ajax({ + url: url, + ifModified: true, + cache: cache, + success: function( data, status, jqXHR ) { + if ( status === "success" && opera === "12.00" ) { + strictEqual( status, "success", "Opera 12.0: Following status is 'success'" ); + strictEqual( jqXHR.status, 200, "Opera 12.0: XHR status is 200, not 304" ); + strictEqual( data, "", "Opera 12.0: response body is empty" ); + } else { + strictEqual( status, "notmodified", "Following status is 'notmodified'" ); + strictEqual( jqXHR.status, 304, "XHR status is 304" ); + equal( data, null, "no response body is given" ); + } + }, + complete: function() { + start(); + } + }); + } + }); + }); + } + ); + } + /* jQuery.each arguments end */ + ); + + ajaxTest( "jQuery.ajax() - failing cross-domain (non-existing)", 1, { + // see RFC 2606 + url: "http://example.invalid/", + error: function( xhr, _, e ) { + ok( true, "file not found: " + xhr.status + " => " + e ); + } + }); + + ajaxTest( "jQuery.ajax() - failing cross-domain", 1, { + url: "http://" + externalHost, + error: function( xhr, _, e ) { + ok( true, "access denied: " + xhr.status + " => " + e ); + } + }); + + ajaxTest( "jQuery.ajax() - atom+xml", 1, { + url: url("http://github.com/data/atom+xml.php"), + success: function() { + ok( true, "success" ); + } + }); + + asyncTest( "jQuery.ajax() - statusText", 3, function() { + jQuery.ajax( url("http://github.com/data/statusText.php?status=200&text=Hello") ).done(function( _, statusText, jqXHR ) { + strictEqual( statusText, "success", "callback status text ok for success" ); + ok( jqXHR.statusText === "Hello" || jqXHR.statusText === "OK", "jqXHR status text ok for success (" + jqXHR.statusText + ")" ); + jQuery.ajax( url("http://github.com/data/statusText.php?status=404&text=World") ).fail(function( jqXHR, statusText ) { + strictEqual( statusText, "error", "callback status text ok for error" ); + start(); + }); + }); + }); + + asyncTest( "jQuery.ajax() - statusCode", 20, function() { + + var count = 12; + + function countComplete() { + if ( ! --count ) { + start(); + } + } + + function createStatusCodes( name, isSuccess ) { + name = "Test " + name + " " + ( isSuccess ? "success" : "error" ); + return { + 200: function() { + ok( isSuccess, name ); + }, + 404: function() { + ok( !isSuccess, name ); + } + }; + } + + jQuery.each( + /* jQuery.each arguments start */ + { + "data/name.html": true, + "data/someFileThatDoesNotExist.html": false + }, + function( uri, isSuccess ) { + + jQuery.ajax( url(http://github.com/uri), { + statusCode: createStatusCodes( "in options", isSuccess ), + complete: countComplete + }); + + jQuery.ajax( url(http://github.com/uri), { + complete: countComplete + }).statusCode( createStatusCodes("immediately with method", isSuccess) ); + + jQuery.ajax( url(http://github.com/uri), { + complete: function( jqXHR ) { + jqXHR.statusCode( createStatusCodes("on complete", isSuccess) ); + countComplete(); + } + }); + + jQuery.ajax( url(http://github.com/uri), { + complete: function( jqXHR ) { + setTimeout(function() { + jqXHR.statusCode( createStatusCodes("very late binding", isSuccess) ); + countComplete(); + }, 100 ); + } + }); + + jQuery.ajax( url(http://github.com/uri), { + statusCode: createStatusCodes( "all (options)", isSuccess ), + complete: function( jqXHR ) { + jqXHR.statusCode( createStatusCodes("all (on complete)", isSuccess) ); + setTimeout(function() { + jqXHR.statusCode( createStatusCodes("all (very late binding)", isSuccess) ); + countComplete(); + }, 100 ); + } + }).statusCode( createStatusCodes("all (immediately with method)", isSuccess) ); + + var testString = ""; + + jQuery.ajax( url(http://github.com/uri), { + success: function( a, b, jqXHR ) { + ok( isSuccess, "success" ); + var statusCode = {}; + statusCode[ jqXHR.status ] = function() { + testString += "B"; + }; + jqXHR.statusCode( statusCode ); + testString += "A"; + }, + error: function( jqXHR ) { + ok( !isSuccess, "error" ); + var statusCode = {}; + statusCode[ jqXHR.status ] = function() { + testString += "B"; + }; + jqXHR.statusCode( statusCode ); + testString += "A"; + }, + complete: function() { + strictEqual( + testString, + "AB", + "Test statusCode callbacks are ordered like " + ( isSuccess ? "success" : "error" ) + " callbacks" + ); + countComplete(); + } + }); + + } + /* jQuery.each arguments end*/ + ); + }); + + ajaxTest( "jQuery.ajax() - transitive conversions", 8, [ + { + url: url("http://github.com/data/json.php"), + converters: { + "json myJson": function( data ) { + ok( true, "converter called" ); + return data; + } + }, + dataType: "myJson", + success: function() { + ok( true, "Transitive conversion worked" ); + strictEqual( this.dataTypes[ 0 ], "text", "response was retrieved as text" ); + strictEqual( this.dataTypes[ 1 ], "myjson", "request expected myjson dataType" ); + } + }, + { + url: url("http://github.com/data/json.php"), + converters: { + "json myJson": function( data ) { + ok( true, "converter called (*)" ); + return data; + } + }, + contents: false, /* headers are wrong so we ignore them */ + dataType: "* myJson", + success: function() { + ok( true, "Transitive conversion worked (*)" ); + strictEqual( this.dataTypes[ 0 ], "text", "response was retrieved as text (*)" ); + strictEqual( this.dataTypes[ 1 ], "myjson", "request expected myjson dataType (*)" ); + } + } + ]); + + ajaxTest( "jQuery.ajax() - overrideMimeType", 2, [ + { + url: url("http://github.com/data/json.php"), + beforeSend: function( xhr ) { + xhr.overrideMimeType( "application/json" ); + }, + success: function( json ) { + ok( json.data, "Mimetype overridden using beforeSend" ); + } + }, + { + url: url("http://github.com/data/json.php"), + mimeType: "application/json", + success: function( json ) { + ok( json.data, "Mimetype overridden using mimeType option" ); + } + } + ]); + + ajaxTest( "jQuery.ajax() - empty json gets to error callback instead of success callback.", 1, { + url: url("http://github.com/data/echoData.php"), + error: function( _, __, error ) { + equal( typeof error === "object", true, "Didn't get back error object for empty json response" ); + }, + dataType: "json" + }); + + ajaxTest( "#2688 - jQuery.ajax() - beforeSend, cancel request", 2, { + create: function() { + return jQuery.ajax({ + url: url("http://github.com/data/name.html"), + beforeSend: function() { + ok( true, "beforeSend got called, canceling" ); + return false; + }, + success: function() { + ok( false, "request didn't get canceled" ); + }, + complete: function() { + ok( false, "request didn't get canceled" ); + }, + error: function() { + ok( false, "request didn't get canceled" ); + } + }); + }, + fail: function( _, reason ) { + strictEqual( reason, "canceled", "canceled request must fail with 'canceled' status text" ); + } + }); + + ajaxTest( "#2806 - jQuery.ajax() - data option - evaluate function values", 1, { + url: "data/echoQuery.php", + data: { + key: function() { + return "value"; + } + }, + success: function( result ) { + strictEqual( result, "key=value" ); + } + }); + + test( "#7531 - jQuery.ajax() - Location object as url", 1, function () { + var xhr, + success = false; + try { + xhr = jQuery.ajax({ + url: window.location + }); + success = true; + xhr.abort(); + } catch (e) { + + } + ok( success, "document.location did not generate exception" ); + }); + + jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) { + ajaxTest( "#7578 - jQuery.ajax() - JSONP - default for cache option" + label, 1, { + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + beforeSend: function() { + strictEqual( this.cache, false, "cache must be false on JSON request" ); + return false; + }, + error: true + }); + }); + + ajaxTest( "#8107 - jQuery.ajax() - multiple method signatures introduced in 1.5", 4, [ + { + create: function() { + return jQuery.ajax(); + }, + done: function() { + ok( true, "With no arguments" ); + } + }, + { + create: function() { + return jQuery.ajax("data/name.html"); + }, + done: function() { + ok( true, "With only string URL argument" ); + } + }, + { + create: function() { + return jQuery.ajax( "data/name.html", {}); + }, + done: function() { + ok( true, "With string URL param and map" ); + } + }, + { + create: function( options ) { + return jQuery.ajax( options ); + }, + url: "data/name.html", + success: function() { + ok( true, "With only map" ); + } + } + ]); + + jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) { + ajaxTest( "#8205 - jQuery.ajax() - JSONP - re-use callbacks name" + label, 2, { + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + beforeSend: function( jqXHR, s ) { + s.callback = s.jsonpCallback; + }, + success: function() { + var previous = this; + strictEqual( previous.jsonpCallback, undefined, "jsonpCallback option is set back to default in callbacks" ); + jQuery.ajax({ + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + beforeSend: function() { + strictEqual( this.jsonpCallback, previous.callback, "JSONP callback name is re-used" ); + return false; + } + }); + } + }); + }); + + test( "#9887 - jQuery.ajax() - Context with circular references (#9887)", 2, function () { + var success = false, + context = {}; + context.field = context; + try { + jQuery.ajax( "non-existing", { + context: context, + beforeSend: function() { + ok( this === context, "context was not deep extended" ); + return false; + } + }); + success = true; + } catch ( e ) { + console.log( e ); + } + ok( success, "context with circular reference did not generate an exception" ); + }); + + jQuery.each( [ "as argument", "in settings object" ], function( inSetting, title ) { + + function request( url, test ) { + return { + create: function() { + return jQuery.ajax( inSetting ? { url: url } : url ); + }, + done: function() { + ok( true, ( test || url ) + " " + title ); + } + }; + } + + ajaxTest( "#10093 - jQuery.ajax() - falsy url " + title, 4, [ + request( "", "empty string" ), + request( false ), + request( null ), + request( undefined ) + ]); + + }); + + ajaxTest( "#11151 - jQuery.ajax() - parse error body", 2, { + url: url("http://github.com/data/errorWithJSON.php"), + dataFilter: function( string ) { + ok( false, "dataFilter called" ); + return string; + }, + error: function( jqXHR ) { + strictEqual( jqXHR.responseText, "{ \"code\": 40, \"message\": \"Bad Request\" }", "Error body properly set" ); + deepEqual( jqXHR.responseJSON, { code: 40, message: "Bad Request" }, "Error body properly parsed" ); + } + }); + + ajaxTest( "#11426 - jQuery.ajax() - loading binary data shouldn't throw an exception in IE", 1, { + url: url("http://github.com/data/1x1.jpg"), + success: function( data ) { + ok( data === undefined || /JFIF/.test( data ), "success callback reached" ); + } + }); + + asyncTest( "#11743 - jQuery.ajax() - script, throws exception", 1, function() { + var onerror = window.onerror; + window.onerror = function() { + ok( true, "Exception thrown" ); + window.onerror = onerror; + start(); + }; + jQuery.ajax({ + url: "data/badjson.js", + dataType: "script", + throws: true, + // Global events get confused by the exception + global: false, + success: function() { + ok( false, "Success." ); + }, + error: function() { + ok( false, "Error." ); + } + }); + }); + + jQuery.each( [ "method", "type" ], function( _, globalOption ) { + + function request( option ) { + var options = { + url: url("http://github.com/data/echoData.php"), + data: "hello", + success: function( msg ) { + strictEqual( msg, "hello", "Check for POST (no override)" ); + } + }; + if ( option ) { + options[ option ] = "GET"; + options.success = function( msg ) { + strictEqual( msg, "", "Check for no POST (overriding with " + option + ")" ); + }; + } + return options; + } + + ajaxTest( "#12004 - jQuery.ajax() - method is an alias of type - " + globalOption + " set globally", 3, { + setup: function() { + var options = {}; + options[ globalOption ] = "POST"; + jQuery.ajaxSetup( options ); + }, + requests: [ + request("type"), + request("method"), + request() + ] + }); + + }); + + ajaxTest( "#13276 - jQuery.ajax() - compatibility between XML documents from ajax requests and parsed string", 1, { + url: "data/dashboard.xml", + dataType: "xml", + success: function( ajaxXML ) { + var parsedXML = jQuery( jQuery.parseXML("blibli") ).find("tab"); + ajaxXML = jQuery( ajaxXML ); + try { + ajaxXML.find("infowindowtab").append( parsedXML ); + } catch( e ) { + strictEqual( e, undefined, "error" ); + return; + } + strictEqual( ajaxXML.find("tab").length, 3, "Parsed node was added properly" ); + } + }); + + ajaxTest( "#13292 - jQuery.ajax() - converter is bypassed for 204 requests", 3, { + url: "data/nocontent.php", + dataType: "testing", + converters: { + "* testing": function() { + throw "converter was called"; + } + }, + success: function( data, status, jqXHR ) { + strictEqual( jqXHR.status, 204, "status code is 204" ); + strictEqual( status, "nocontent", "status text is 'nocontent'" ); + strictEqual( data, undefined, "data is undefined" ); + }, + error: function( _, status, error ) { + ok( false, "error" ); + strictEqual( status, "parsererror", "Parser Error" ); + strictEqual( error, "converter was called", "Converter was called" ); + } + }); + + ajaxTest( "#13388 - jQuery.ajax() - responseXML", 3, { + url: url("http://github.com/data/with_fries.xml"), + dataType: "xml", + success: function( resp, _, jqXHR ) { + notStrictEqual( resp, undefined, "XML document exists" ); + ok( "responseXML" in jqXHR, "jqXHR.responseXML exists" ); + strictEqual( resp, jqXHR.responseXML, "jqXHR.responseXML is set correctly" ); + } + }); + + ajaxTest( "#13922 - jQuery.ajax() - converter is bypassed for HEAD requests", 3, { + url: "data/json.php", + method: "HEAD", + data: { + header: "yes" + }, + converters: { + "text json": function() { + throw "converter was called"; + } + }, + success: function( data, status ) { + ok( true, "success" ); + strictEqual( status, "nocontent", "data is undefined" ); + strictEqual( data, undefined, "data is undefined" ); + }, + error: function( _, status, error ) { + ok( false, "error" ); + strictEqual( status, "parsererror", "Parser Error" ); + strictEqual( error, "converter was called", "Converter was called" ); + } + } ); + +//----------- jQuery.ajaxPrefilter() + + ajaxTest( "jQuery.ajaxPrefilter() - abort", 1, { + setup: function() { + jQuery.ajaxPrefilter(function( options, _, jqXHR ) { + if ( options.abortInPrefilter ) { + jqXHR.abort(); + } + }); + }, + abortInPrefilter: true, + error: function() { + ok( false, "error callback called" ); + }, + fail: function( _, reason ) { + strictEqual( reason, "canceled", "Request aborted by the prefilter must fail with 'canceled' status text" ); + } + }); + +//----------- jQuery.ajaxSetup() + + asyncTest( "jQuery.ajaxSetup()", 1, function() { + jQuery.ajaxSetup({ + url: url("http://github.com/data/name.php?name=foo"), + success: function( msg ) { + strictEqual( msg, "bar", "Check for GET" ); + start(); + } + }); + jQuery.ajax(); + }); + + asyncTest( "jQuery.ajaxSetup({ timeout: Number }) - with global timeout", 2, function() { + var passed = 0, + pass = function() { + ok( passed++ < 2, "Error callback executed" ); + if ( passed === 2 ) { + jQuery( document ).off("ajaxError.setupTest"); + start(); + } + }, + fail = function( a, b ) { + ok( false, "Check for timeout failed " + a + " " + b ); + start(); + }; + + jQuery( document ).on( "ajaxError.setupTest", pass ); + + jQuery.ajaxSetup({ + timeout: 1000 + }); + + jQuery.ajax({ + type: "GET", + url: url("http://github.com/data/name.php?wait=5"), + error: pass, + success: fail + }); + }); + + asyncTest( "jQuery.ajaxSetup({ timeout: Number }) with localtimeout", 1, function() { + jQuery.ajaxSetup({ + timeout: 50 + }); + jQuery.ajax({ + type: "GET", + timeout: 15000, + url: url("http://github.com/data/name.php?wait=1"), + error: function() { + ok( false, "Check for local timeout failed" ); + start(); + }, + success: function() { + ok( true, "Check for local timeout" ); + start(); + } + }); + }); + +//----------- jQuery.domManip() + + test( "#11264 - jQuery.domManip() - no side effect because of ajaxSetup or global events", 1, function() { + jQuery.ajaxSetup({ + type: "POST" + }); + + jQuery( document ).on( "ajaxStart ajaxStop", function() { + ok( false, "Global event triggered" ); + }); + + jQuery("#qunit-fixture").append(""); + + jQuery( document ).off("ajaxStart ajaxStop"); + }); + + asyncTest( "#11402 - jQuery.domManip() - script in comments are properly evaluated", 2, function() { + jQuery("#qunit-fixture").load( "data/cleanScript.html", start ); + }); + +//----------- jQuery.get() + + asyncTest( "jQuery.get( String, Hash, Function ) - parse xml and use text() on nodes", 2, function() { + jQuery.get( url("http://github.com/data/dashboard.xml"), function( xml ) { + var content = []; + jQuery( "tab", xml ).each(function() { + content.push( jQuery( this ).text() ); + }); + strictEqual( content[ 0 ], "blabla", "Check first tab" ); + strictEqual( content[ 1 ], "blublu", "Check second tab" ); + start(); + }); + }); + + asyncTest( "#8277 - jQuery.get( String, Function ) - data in ajaxSettings", 1, function() { + jQuery.ajaxSetup({ + data: "helloworld" + }); + jQuery.get( url("http://github.com/data/echoQuery.php"), function( data ) { + ok( /helloworld$/.test( data ), "Data from ajaxSettings was used" ); + start(); + }); + }); + +//----------- jQuery.getJSON() + + asyncTest( "jQuery.getJSON( String, Hash, Function ) - JSON array", 5, function() { + jQuery.getJSON( + url("http://github.com/data/json.php"), + { + "json": "array" + }, + function( json ) { + ok( json.length >= 2, "Check length" ); + strictEqual( json[ 0 ]["name"], "John", "Check JSON: first, name" ); + strictEqual( json[ 0 ]["age"], 21, "Check JSON: first, age" ); + strictEqual( json[ 1 ]["name"], "Peter", "Check JSON: second, name" ); + strictEqual( json[ 1 ]["age"], 25, "Check JSON: second, age" ); + start(); + } + ); + }); + + asyncTest( "jQuery.getJSON( String, Function ) - JSON object", 2, function() { + jQuery.getJSON( url("http://github.com/data/json.php"), function( json ) { + if ( json && json["data"] ) { + strictEqual( json["data"]["lang"], "en", "Check JSON: lang" ); + strictEqual( json["data"].length, 25, "Check JSON: length" ); + start(); + } + }); + }); + + asyncTest( "jQuery.getJSON( String, Function ) - JSON object with absolute url to local content", 2, function() { + jQuery.getJSON( url(http://github.com/window.location.href.replace(%20/[%5E//]*$/,%20%22%22) + "data/json.php" ), function( json ) { + strictEqual( json.data.lang, "en", "Check JSON: lang" ); + strictEqual( json.data.length, 25, "Check JSON: length" ); + start(); + }); + }); + +//----------- jQuery.getScript() + + asyncTest( "jQuery.getScript( String, Function ) - with callback", 2, function() { + Globals.register("testBar"); + jQuery.getScript( url("http://github.com/data/test.js"), function() { + strictEqual( window["testBar"], "bar", "Check if script was evaluated" ); + start(); + }); + }); + + asyncTest( "jQuery.getScript( String, Function ) - no callback", 1, function() { + Globals.register("testBar"); + jQuery.getScript( url("http://github.com/data/test.js") ).done( start ); + }); + + asyncTest( "#8082 - jQuery.getScript( String, Function ) - source as responseText", 2, function() { + Globals.register("testBar"); + jQuery.getScript( url("http://github.com/data/test.js"), function( data, _, jqXHR ) { + strictEqual( data, jqXHR.responseText, "Same-domain script requests returns the source of the script" ); + start(); + }); + }); + +//----------- jQuery.fn.load() + + // check if load can be called with only url + asyncTest( "jQuery.fn.load( String )", 2, function() { + jQuery.ajaxSetup({ + beforeSend: function() { + strictEqual( this.type, "GET", "no data means GET request" ); + } + }); + jQuery("#first").load( "data/name.html", start ); + }); + + asyncTest( "jQuery.fn.load() - 404 error callbacks", 6, function() { + addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError")(); + jQuery( document ).ajaxStop( start ); + jQuery("
        ").load( "data/404.html", function() { + ok( true, "complete" ); + }); + }); + + // check if load can be called with url and null data + asyncTest( "jQuery.fn.load( String, null )", 2, function() { + jQuery.ajaxSetup({ + beforeSend: function() { + strictEqual( this.type, "GET", "no data means GET request" ); + } + }); + jQuery("#first").load( "data/name.html", null, start ); + }); + + // check if load can be called with url and undefined data + asyncTest( "jQuery.fn.load( String, undefined )", 2, function() { + jQuery.ajaxSetup({ + beforeSend: function() { + strictEqual( this.type, "GET", "no data means GET request" ); + } + }); + jQuery("#first").load( "data/name.html", undefined, start ); + }); + + // check if load can be called with only url + asyncTest( "jQuery.fn.load( URL_SELECTOR )", 1, function() { + jQuery("#first").load( "data/test3.html div.user", function() { + strictEqual( jQuery( this ).children("div").length, 2, "Verify that specific elements were injected" ); + start(); + }); + }); + + asyncTest( "jQuery.fn.load( String, Function ) - simple: inject text into DOM", 2, function() { + jQuery("#first").load( url("http://github.com/data/name.html"), function() { + ok( /^ERROR/.test(jQuery("#first").text()), "Check if content was injected into the DOM" ); + start(); + }); + }); + + asyncTest( "jQuery.fn.load( String, Function ) - check scripts", 7, function() { + var verifyEvaluation = function() { + strictEqual( window["testBar"], "bar", "Check if script src was evaluated after load" ); + strictEqual( jQuery("#ap").html(), "bar", "Check if script evaluation has modified DOM"); + start(); + }; + + Globals.register("testFoo"); + Globals.register("testBar"); + + jQuery("#first").load( url("http://github.com/data/test.html"), function() { + ok( jQuery("#first").html().match( /^html text/ ), "Check content after loading html" ); + strictEqual( jQuery("#foo").html(), "foo", "Check if script evaluation has modified DOM" ); + strictEqual( window["testFoo"], "foo", "Check if script was evaluated after load" ); + setTimeout( verifyEvaluation, 600 ); + }); + }); + + asyncTest( "jQuery.fn.load( String, Function ) - check file with only a script tag", 3, function() { + Globals.register("testFoo"); + + jQuery("#first").load( url("http://github.com/data/test2.html"), function() { + strictEqual( jQuery("#foo").html(), "foo", "Check if script evaluation has modified DOM"); + strictEqual( window["testFoo"], "foo", "Check if script was evaluated after load" ); + start(); + }); + }); + + asyncTest( "jQuery.fn.load( String, Function ) - dataFilter in ajaxSettings", 2, function() { + jQuery.ajaxSetup({ + dataFilter: function() { + return "Hello World"; + } + }); + jQuery("
        ").load( url("http://github.com/data/name.html"), function( responseText ) { + strictEqual( jQuery( this ).html(), "Hello World", "Test div was filled with filtered data" ); + strictEqual( responseText, "Hello World", "Test callback receives filtered data" ); + start(); + }); + }); + + asyncTest( "jQuery.fn.load( String, Object, Function )", 2, function() { + jQuery("
        ").load( url("http://github.com/data/params_html.php"), { + "foo": 3, + "bar": "ok" + }, function() { + var $post = jQuery( this ).find("#post"); + strictEqual( $post.find("#foo").text(), "3", "Check if a hash of data is passed correctly" ); + strictEqual( $post.find("#bar").text(), "ok", "Check if a hash of data is passed correctly" ); + start(); + }); + }); + + asyncTest( "jQuery.fn.load( String, String, Function )", 2, function() { + jQuery("
        ").load( url("http://github.com/data/params_html.php"), "foo=3&bar=ok", function() { + var $get = jQuery( this ).find("#get"); + strictEqual( $get.find("#foo").text(), "3", "Check if a string of data is passed correctly" ); + strictEqual( $get.find("#bar").text(), "ok", "Check if a of data is passed correctly" ); + start(); + }); + }); + + asyncTest( "jQuery.fn.load() - callbacks get the correct parameters", 8, function() { + var completeArgs = {}; + + jQuery.ajaxSetup({ + success: function( _, status, jqXHR ) { + completeArgs[ this.url ] = [ jqXHR.responseText, status, jqXHR ]; + }, + error: function( jqXHR, status ) { + completeArgs[ this.url ] = [ jqXHR.responseText, status, jqXHR ]; + } + }); + + jQuery.when.apply( + jQuery, + jQuery.map([ + { + type: "success", + url: "data/echoQuery.php?arg=pop" + }, + { + type: "error", + url: "data/404.php" + } + ], + function( options ) { + return jQuery.Deferred(function( defer ) { + jQuery("#foo").load( options.url, function() { + var args = arguments; + strictEqual( completeArgs[ options.url ].length, args.length, "same number of arguments (" + options.type + ")" ); + jQuery.each( completeArgs[ options.url ], function( i, value ) { + strictEqual( args[ i ], value, "argument #" + i + " is the same (" + options.type + ")" ); + }); + defer.resolve(); + }); + }); + }) + ).always( start ); + }); + + asyncTest( "#2046 - jQuery.fn.load( String, Function ) with ajaxSetup on dataType json", 1, function() { + jQuery.ajaxSetup({ + dataType: "json" + }); + jQuery( document ).ajaxComplete(function( e, xml, s ) { + strictEqual( s.dataType, "html", "Verify the load() dataType was html" ); + jQuery( document ).off("ajaxComplete"); + start(); + }); + jQuery("#first").load("data/test3.html"); + }); + + asyncTest( "#10524 - jQuery.fn.load() - data specified in ajaxSettings is merged in", 1, function() { + var data = { + "baz": 1 + }; + jQuery.ajaxSetup({ + data: { + "foo": "bar" + } + }); + jQuery("#foo").load( "data/echoQuery.php", data ); + jQuery( document ).ajaxComplete(function( event, jqXHR, options ) { + ok( ~options.data.indexOf("foo=bar"), "Data from ajaxSettings was used" ); + start(); + }); + }); + +//----------- jQuery.post() + + asyncTest( "jQuery.post() - data", 3, function() { + jQuery.when( + jQuery.post( + url("http://github.com/data/name.php"), + { + xml: "5-2", + length: 3 + }, + function( xml ) { + jQuery( "math", xml ).each(function() { + strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); + strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); + }); + } + ), + jQuery.ajax({ + url: url("http://github.com/data/echoData.php"), + type: "POST", + data: { + "test": { + "length": 7, + "foo": "bar" + } + }, + success: function( data ) { + strictEqual( data, "test%5Blength%5D=7&test%5Bfoo%5D=bar", "Check if a sub-object with a length param is serialized correctly" ); + } + }) + ).always( start ); + }); + + asyncTest( "jQuery.post( String, Hash, Function ) - simple with xml", 4, function() { + jQuery.when( + jQuery.post( + url("http://github.com/data/name.php"), + { + "xml": "5-2" + }, + function( xml ) { + jQuery( "math", xml ).each(function() { + strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); + strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); + }); + } + ), + jQuery.post( url("http://github.com/data/name.php?xml=5-2"), {}, function( xml ) { + jQuery( "math", xml ).each(function() { + strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); + strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); + }); + }) + ).always( start ); + }); + +//----------- jQuery.active + + test( "jQuery.active", 1, function() { + ok( jQuery.active === 0, "ajax active counter should be zero: " + jQuery.active ); + }); + +})(); diff --git a/bower_components/jquery/test/unit/attributes.js b/bower_components/jquery/test/unit/attributes.js new file mode 100644 index 0000000..84ac8c2 --- /dev/null +++ b/bower_components/jquery/test/unit/attributes.js @@ -0,0 +1,1384 @@ +module( "attributes", { + teardown: moduleTeardown +}); + +function bareObj( value ) { + return value; +} + +function functionReturningObj( value ) { + return function() { + return value; + }; +} + +/* + ======== local reference ======= + bareObj and functionReturningObj can be used to test passing functions to setters + See testVal below for an example + + bareObj( value ); + This function returns whatever value is passed in + + functionReturningObj( value ); + Returns a function that returns the value +*/ + +test( "jQuery.propFix integrity test", function() { + expect( 1 ); + + // This must be maintained and equal jQuery.attrFix when appropriate + // Ensure that accidental or erroneous property + // overwrites don't occur + // This is simply for better code coverage and future proofing. + var props = { + "tabindex": "tabIndex", + "readonly": "readOnly", + "for": "htmlFor", + "class": "className", + "maxlength": "maxLength", + "cellspacing": "cellSpacing", + "cellpadding": "cellPadding", + "rowspan": "rowSpan", + "colspan": "colSpan", + "usemap": "useMap", + "frameborder": "frameBorder", + "contenteditable": "contentEditable" + }; + + deepEqual( props, jQuery.propFix, "jQuery.propFix passes integrity check" ); +}); + +test( "attr(String)", function() { + expect( 50 ); + + var extras, body, $body, + select, optgroup, option, $img, styleElem, + $button, $form, $a; + + equal( jQuery("#text1").attr("type"), "text", "Check for type attribute" ); + equal( jQuery("#radio1").attr("type"), "radio", "Check for type attribute" ); + equal( jQuery("#check1").attr("type"), "checkbox", "Check for type attribute" ); + equal( jQuery("#simon1").attr("rel"), "bookmark", "Check for rel attribute" ); + equal( jQuery("#google").attr("title"), "Google!", "Check for title attribute" ); + equal( jQuery("#mark").attr("hreflang"), "en", "Check for hreflang attribute" ); + equal( jQuery("#en").attr("lang"), "en", "Check for lang attribute" ); + equal( jQuery("#simon").attr("class"), "blog link", "Check for class attribute" ); + equal( jQuery("#name").attr("name"), "name", "Check for name attribute" ); + equal( jQuery("#text1").attr("name"), "action", "Check for name attribute" ); + ok( jQuery("#form").attr("action").indexOf("formaction") >= 0, "Check for action attribute" ); + equal( jQuery("#text1").attr("value", "t").attr("value"), "t", "Check setting the value attribute" ); + equal( jQuery("#text1").attr("value", "").attr("value"), "", "Check setting the value attribute to empty string" ); + equal( jQuery("
        ").attr("value"), "t", "Check setting custom attr named 'value' on a div" ); + equal( jQuery("#form").attr("blah", "blah").attr("blah"), "blah", "Set non-existent attribute on a form" ); + equal( jQuery("#foo").attr("height"), undefined, "Non existent height attribute should return undefined" ); + + // [7472] & [3113] (form contains an input with name="action" or name="id") + extras = jQuery("").appendTo("#testForm"); + equal( jQuery("#form").attr("action","newformaction").attr("action"), "newformaction", "Check that action attribute was changed" ); + equal( jQuery("#testForm").attr("target"), undefined, "Retrieving target does not equal the input with name=target" ); + equal( jQuery("#testForm").attr("target", "newTarget").attr("target"), "newTarget", "Set target successfully on a form" ); + equal( jQuery("#testForm").removeAttr("id").attr("id"), undefined, "Retrieving id does not equal the input with name=id after id is removed [#7472]" ); + // Bug #3685 (form contains input with name="name") + equal( jQuery("#testForm").attr("name"), undefined, "Retrieving name does not retrieve input with name=name" ); + extras.remove(); + + equal( jQuery("#text1").attr("maxlength"), "30", "Check for maxlength attribute" ); + equal( jQuery("#text1").attr("maxLength"), "30", "Check for maxLength attribute" ); + equal( jQuery("#area1").attr("maxLength"), "30", "Check for maxLength attribute" ); + + // using innerHTML in IE causes href attribute to be serialized to the full path + jQuery("").attr({ + "id": "tAnchor5", + "href": "#5" + }).appendTo("#qunit-fixture"); + equal( jQuery("#tAnchor5").attr("href"), "#5", "Check for non-absolute href (an anchor)" ); + jQuery("").appendTo("#qunit-fixture"); + equal( jQuery("#tAnchor5").prop("href"), jQuery("#tAnchor6").prop("href"), "Check for absolute href prop on an anchor" ); + + $("").appendTo("#qunit-fixture"); + equal( jQuery("#tAnchor5").prop("href"), jQuery("#scriptSrc").prop("src"), "Check for absolute src prop on a script" ); + + // list attribute is readonly by default in browsers that support it + jQuery("#list-test").attr( "list", "datalist" ); + equal( jQuery("#list-test").attr("list"), "datalist", "Check setting list attribute" ); + + // Related to [5574] and [5683] + body = document.body; + $body = jQuery( body ); + + strictEqual( $body.attr("foo"), undefined, "Make sure that a non existent attribute returns undefined" ); + + body.setAttribute( "foo", "baz" ); + equal( $body.attr("foo"), "baz", "Make sure the dom attribute is retrieved when no expando is found" ); + + $body.attr( "foo","cool" ); + equal( $body.attr("foo"), "cool", "Make sure that setting works well when both expando and dom attribute are available" ); + + body.removeAttribute("foo"); // Cleanup + + select = document.createElement("select"); + optgroup = document.createElement("optgroup"); + option = document.createElement("option"); + + optgroup.appendChild( option ); + select.appendChild( optgroup ); + + equal( jQuery( option ).prop("selected"), true, "Make sure that a single option is selected, even when in an optgroup." ); + + $img = jQuery("").appendTo("body"); + equal( $img.attr("width"), "215", "Retrieve width attribute an an element with display:none." ); + equal( $img.attr("height"), "53", "Retrieve height attribute an an element with display:none." ); + + // Check for style support + styleElem = jQuery("
        ").appendTo("#qunit-fixture").css({ + background: "url(http://github.com/UPPERlower.gif)" + }); + ok( !!~styleElem.attr("style").indexOf("UPPERlower.gif"), "Check style attribute getter" ); + ok( !!~styleElem.attr("style", "position:absolute;").attr("style").indexOf("absolute"), "Check style setter" ); + + // Check value on button element (#1954) + $button = jQuery("").insertAfter("#button"); + strictEqual( $button.attr("value"), undefined, "Absence of value attribute on a button" ); + equal( $button.attr( "value", "foobar" ).attr("value"), "foobar", "Value attribute on a button does not return innerHTML" ); + equal( $button.attr("value", "baz").html(), "text", "Setting the value attribute does not change innerHTML" ); + + // Attributes with a colon on a table element (#1591) + equal( jQuery("#table").attr("test:attrib"), undefined, "Retrieving a non-existent attribute on a table with a colon does not throw an error." ); + equal( jQuery("#table").attr( "test:attrib", "foobar" ).attr("test:attrib"), "foobar", "Setting an attribute on a table with a colon does not throw an error." ); + + $form = jQuery("
        ").appendTo("#qunit-fixture"); + equal( $form.attr("class"), "something", "Retrieve the class attribute on a form." ); + + $a = jQuery("
        Click").appendTo("#qunit-fixture"); + equal( $a.attr("onclick"), "something()", "Retrieve ^on attribute without anonymous function wrapper." ); + + ok( jQuery("
        ").attr("doesntexist") === undefined, "Make sure undefined is returned when no attribute is found." ); + ok( jQuery("
        ").attr("title") === undefined, "Make sure undefined is returned when no attribute is found." ); + equal( jQuery("
        ").attr( "title", "something" ).attr("title"), "something", "Set the title attribute." ); + ok( jQuery().attr("doesntexist") === undefined, "Make sure undefined is returned when no element is there." ); + equal( jQuery("
        ").attr("value"), undefined, "An unset value on a div returns undefined." ); + strictEqual( jQuery("").attr("value"), undefined, "An unset value on a select returns undefined." ); + + $form = jQuery("#form").attr( "enctype", "multipart/form-data" ); + equal( $form.prop("enctype"), "multipart/form-data", "Set the enctype of a form (encoding in IE6/7 #6743)" ); + +}); + +test( "attr(String) on cloned elements, #9646", function() { + expect( 4 ); + + var div, + input = jQuery(""); + + input.attr("name"); + + strictEqual( input.clone( true ).attr( "name", "test" )[ 0 ].name, "test", "Name attribute should be changed on cloned element" ); + + div = jQuery("
        "); + div.attr("id"); + + strictEqual( div.clone( true ).attr( "id", "test" )[ 0 ].id, "test", "Id attribute should be changed on cloned element" ); + + input = jQuery(""); + input.attr("value"); + + strictEqual( input.clone( true ).attr( "value", "test" )[ 0 ].value, "test", "Value attribute should be changed on cloned element" ); + + strictEqual( input.clone( true ).attr( "value", 42 )[ 0 ].value, "42", "Value attribute should be changed on cloned element" ); +}); + +test( "attr(String) in XML Files", function() { + expect( 3 ); + var xml = createDashboardXML(); + equal( jQuery( "locations", xml ).attr("class"), "foo", "Check class attribute in XML document" ); + equal( jQuery( "location", xml ).attr("for"), "bar", "Check for attribute in XML document" ); + equal( jQuery( "location", xml ).attr("checked"), "different", "Check that hooks are not attached in XML document" ); +}); + +test( "attr(String, Function)", function() { + expect( 2 ); + + equal( + jQuery("#text1").attr( "value", function() { + return this.id; + }).attr("value"), + "text1", + "Set value from id" + ); + + equal( + jQuery("#text1").attr( "title", function(i) { + return i; + }).attr("title"), + "0", + "Set value with an index" + ); +}); + +test( "attr(Hash)", function() { + expect( 3 ); + var pass = true; + jQuery("div").attr({ + "foo": "baz", + "zoo": "ping" + }).each(function() { + if ( this.getAttribute("foo") !== "baz" && this.getAttribute("zoo") !== "ping" ) { + pass = false; + } + }); + + ok( pass, "Set Multiple Attributes" ); + + equal( + jQuery("#text1").attr({ + "value": function() { + return this["id"]; + }}).attr("value"), + "text1", + "Set attribute to computed value #1" + ); + + equal( + jQuery("#text1").attr({ + "title": function(i) { + return i; + } + }).attr("title"), + "0", + "Set attribute to computed value #2" + ); +}); + +test( "attr(String, Object)", function() { + expect( 71 ); + + var $input, $text, $details, + attributeNode, commentNode, textNode, obj, + table, td, j, type, + check, thrown, button, $radio, $radios, $svg, + div = jQuery("div").attr("foo", "bar"), + i = 0, + fail = false; + + for ( ; i < div.length; i++ ) { + if ( div[ i ].getAttribute("foo") !== "bar" ) { + fail = i; + break; + } + } + + equal( fail, false, "Set Attribute, the #" + fail + " element didn't get the attribute 'foo'" ); + + ok( + jQuery("#foo").attr({ + "width": null + }), + "Try to set an attribute to nothing" + ); + + jQuery("#name").attr( "name", "something" ); + equal( jQuery("#name").attr("name"), "something", "Set name attribute" ); + jQuery("#name").attr( "name", null ); + equal( jQuery("#name").attr("name"), undefined, "Remove name attribute" ); + + $input = jQuery( "", { + name: "something", + id: "specified" + }); + equal( $input.attr("name"), "something", "Check element creation gets/sets the name attribute." ); + equal( $input.attr("id"), "specified", "Check element creation gets/sets the id attribute." ); + + // As of fixing #11115, we only guarantee boolean property update for checked and selected + $input = jQuery("").attr( "checked", true ); + equal( $input.prop("checked"), true, "Setting checked updates property (verified by .prop)" ); + equal( $input[0].checked, true, "Setting checked updates property (verified by native property)" ); + $input = jQuery(""); + $select1.val( valueObj( 4 ) ); + equal( $select1.val(), "4", "Should be possible to set the val() to a newly created option" ); + + // using contents will get comments regular, text, and comment nodes + j = jQuery("#nonnodes").contents(); + j.val( valueObj( "asdf" ) ); + equal( j.val(), "asdf", "Check node,textnode,comment with val()" ); + j.removeAttr("value"); +}; + +test( "val(String/Number)", function() { + testVal( bareObj ); +}); + +test( "val(Function)", function() { + testVal( functionReturningObj ); +}); + +test( "val(Array of Numbers) (Bug #7123)", function() { + expect( 4 ); + jQuery("#form").append(""); + var elements = jQuery("input[name=arrayTest]").val([ 1, 2 ]); + ok( elements[ 0 ].checked, "First element was checked" ); + ok( elements[ 1 ].checked, "Second element was checked" ); + ok( !elements[ 2 ].checked, "Third element was unchecked" ); + ok( !elements[ 3 ].checked, "Fourth element remained unchecked" ); + + elements.remove(); +}); + +test( "val(Function) with incoming value", function() { + expect( 10 ); + + QUnit.reset(); + var oldVal = jQuery("#text1").val(); + + jQuery("#text1").val(function( i, val ) { + equal( val, oldVal, "Make sure the incoming value is correct." ); + return "test"; + }); + + equal( document.getElementById("text1").value, "test", "Check for modified (via val(String)) value of input element" ); + + oldVal = jQuery("#text1").val(); + + jQuery("#text1").val(function( i, val ) { + equal( val, oldVal, "Make sure the incoming value is correct." ); + return 67; + }); + + equal( document.getElementById("text1").value, "67", "Check for modified (via val(Number)) value of input element" ); + + oldVal = jQuery("#select1").val(); + + jQuery("#select1").val(function( i, val ) { + equal( val, oldVal, "Make sure the incoming value is correct." ); + return "3"; + }); + + equal( jQuery("#select1").val(), "3", "Check for modified (via val(String)) value of select element" ); + + oldVal = jQuery("#select1").val(); + + jQuery("#select1").val(function( i, val ) { + equal( val, oldVal, "Make sure the incoming value is correct." ); + return 2; + }); + + equal( jQuery("#select1").val(), "2", "Check for modified (via val(Number)) value of select element" ); + + jQuery("#select1").append(""); + + oldVal = jQuery("#select1").val(); + + jQuery("#select1").val(function( i, val ) { + equal( val, oldVal, "Make sure the incoming value is correct." ); + return 4; + }); + + equal( jQuery("#select1").val(), "4", "Should be possible to set the val() to a newly created option" ); +}); + +// testing if a form.reset() breaks a subsequent call to a select element's .val() (in IE only) +test( "val(select) after form.reset() (Bug #2551)", function() { + expect( 3 ); + + jQuery("
        ").appendTo("#qunit-fixture"); + + jQuery("#kkk").val("gf"); + + document["kk"].reset(); + + equal( jQuery("#kkk")[ 0 ].value, "cf", "Check value of select after form reset." ); + equal( jQuery("#kkk").val(), "cf", "Check value of select after form reset." ); + + // re-verify the multi-select is not broken (after form.reset) by our fix for single-select + deepEqual( jQuery("#select3").val(), ["1", "2"], "Call val() on a multiple='multiple' select" ); + + jQuery("#kk").remove(); +}); + +var testAddClass = function( valueObj ) { + expect( 9 ); + + var pass, j, i, + div = jQuery("#qunit-fixture div"); + div.addClass( valueObj("test") ); + pass = true; + for ( i = 0; i < div.length; i++ ) { + if ( !~div.get( i ).className.indexOf("test") ) { + pass = false; + } + } + ok( pass, "Add Class" ); + + // using contents will get regular, text, and comment nodes + j = jQuery("#nonnodes").contents(); + j.addClass( valueObj("asdf") ); + ok( j.hasClass("asdf"), "Check node,textnode,comment for addClass" ); + + div = jQuery("
        "); + + div.addClass( valueObj("test") ); + equal( div.attr("class"), "test", "Make sure there's no extra whitespace." ); + + div.attr( "class", " foo" ); + div.addClass( valueObj("test") ); + equal( div.attr("class"), "foo test", "Make sure there's no extra whitespace." ); + + div.attr( "class", "foo" ); + div.addClass( valueObj("bar baz") ); + equal( div.attr("class"), "foo bar baz", "Make sure there isn't too much trimming." ); + + div.removeClass(); + div.addClass( valueObj("foo") ).addClass( valueObj("foo") ); + equal( div.attr("class"), "foo", "Do not add the same class twice in separate calls." ); + + div.addClass( valueObj("fo") ); + equal( div.attr("class"), "foo fo", "Adding a similar class does not get interrupted." ); + div.removeClass().addClass("wrap2"); + ok( div.addClass("wrap").hasClass("wrap"), "Can add similarly named classes"); + + div.removeClass(); + div.addClass( valueObj("bar bar") ); + equal( div.attr("class"), "bar", "Do not add the same class twice in the same call." ); +}; + +test( "addClass(String)", function() { + testAddClass( bareObj ); +}); + +test( "addClass(Function)", function() { + testAddClass( functionReturningObj ); +}); + +test( "addClass(Function) with incoming value", function() { + expect( 52 ); + var pass, i, + div = jQuery("#qunit-fixture div"), + old = div.map(function() { + return jQuery(this).attr("class") || ""; + }); + + div.addClass(function( i, val ) { + if ( this.id !== "_firebugConsole" ) { + equal( val, old[ i ], "Make sure the incoming value is correct." ); + return "test"; + } + }); + + pass = true; + for ( i = 0; i < div.length; i++ ) { + if ( div.get(i).className.indexOf("test") === -1 ) { + pass = false; + } + } + ok( pass, "Add Class" ); +}); + +var testRemoveClass = function(valueObj) { + expect( 8 ); + + var $set = jQuery("#qunit-fixture div"), + div = document.createElement("div"); + + $set.addClass("test").removeClass( valueObj("test") ); + + ok( !$set.is(".test"), "Remove Class" ); + + $set.addClass("test").addClass("foo").addClass("bar"); + $set.removeClass( valueObj("test") ).removeClass( valueObj("bar") ).removeClass( valueObj("foo") ); + + ok( !$set.is(".test,.bar,.foo"), "Remove multiple classes" ); + + // Make sure that a null value doesn't cause problems + $set.eq( 0 ).addClass("expected").removeClass( valueObj( null ) ); + ok( $set.eq( 0 ).is(".expected"), "Null value passed to removeClass" ); + + $set.eq( 0 ).addClass("expected").removeClass( valueObj("") ); + ok( $set.eq( 0 ).is(".expected"), "Empty string passed to removeClass" ); + + // using contents will get regular, text, and comment nodes + $set = jQuery("#nonnodes").contents(); + $set.removeClass( valueObj("asdf") ); + ok( !$set.hasClass("asdf"), "Check node,textnode,comment for removeClass" ); + + + jQuery( div ).removeClass( valueObj("foo") ); + strictEqual( jQuery( div ).attr("class"), undefined, "removeClass doesn't create a class attribute" ); + + div.className = " test foo "; + + jQuery( div ).removeClass( valueObj("foo") ); + equal( div.className, "test", "Make sure remaining className is trimmed." ); + + div.className = " test "; + + jQuery( div ).removeClass( valueObj("test") ); + equal( div.className, "", "Make sure there is nothing left after everything is removed." ); +}; + +test( "removeClass(String) - simple", function() { + testRemoveClass( bareObj ); +}); + +test( "removeClass(Function) - simple", function() { + testRemoveClass( functionReturningObj ); +}); + +test( "removeClass(Function) with incoming value", function() { + expect( 52 ); + + var $divs = jQuery("#qunit-fixture div").addClass("test"), old = $divs.map(function() { + return jQuery( this ).attr("class"); + }); + + $divs.removeClass(function( i, val ) { + if ( this.id !== "_firebugConsole" ) { + equal( val, old[ i ], "Make sure the incoming value is correct." ); + return "test"; + } + }); + + ok( !$divs.is(".test"), "Remove Class" ); +}); + +test( "removeClass() removes duplicates", function() { + expect( 1 ); + + var $div = jQuery( jQuery.parseHTML("
        ") ); + + $div.removeClass("x"); + + ok( !$div.hasClass("x"), "Element with multiple same classes does not escape the wrath of removeClass()" ); +}); + +test("removeClass(undefined) is a no-op", function() { + expect( 1 ); + + var $div = jQuery("
        "); + $div.removeClass( undefined ); + + ok( $div.hasClass("base") && $div.hasClass("second"), "Element still has classes after removeClass(undefined)" ); +}); + +var testToggleClass = function(valueObj) { + expect( 17 ); + + var e = jQuery("#firstp"); + ok( !e.is(".test"), "Assert class not present" ); + e.toggleClass( valueObj("test") ); + ok( e.is(".test"), "Assert class present" ); + e.toggleClass( valueObj("test") ); + ok( !e.is(".test"), "Assert class not present" ); + + // class name with a boolean + e.toggleClass( valueObj("test"), false ); + ok( !e.is(".test"), "Assert class not present" ); + e.toggleClass( valueObj("test"), true ); + ok( e.is(".test"), "Assert class present" ); + e.toggleClass( valueObj("test"), false ); + ok( !e.is(".test"), "Assert class not present" ); + + // multiple class names + e.addClass("testA testB"); + ok( e.is(".testA.testB"), "Assert 2 different classes present" ); + e.toggleClass( valueObj("testB testC") ); + ok( (e.is(".testA.testC") && !e.is(".testB")), "Assert 1 class added, 1 class removed, and 1 class kept" ); + e.toggleClass( valueObj("testA testC") ); + ok( (!e.is(".testA") && !e.is(".testB") && !e.is(".testC")), "Assert no class present" ); + + // toggleClass storage + e.toggleClass( true ); + ok( e[ 0 ].className === "", "Assert class is empty (data was empty)" ); + e.addClass("testD testE"); + ok( e.is(".testD.testE"), "Assert class present" ); + e.toggleClass(); + ok( !e.is(".testD.testE"), "Assert class not present" ); + ok( jQuery._data(e[ 0 ], "__className__") === "testD testE", "Assert data was stored" ); + e.toggleClass(); + ok( e.is(".testD.testE"), "Assert class present (restored from data)" ); + e.toggleClass( false ); + ok( !e.is(".testD.testE"), "Assert class not present" ); + e.toggleClass( true ); + ok( e.is(".testD.testE"), "Assert class present (restored from data)" ); + e.toggleClass(); + e.toggleClass( false ); + e.toggleClass(); + ok( e.is(".testD.testE"), "Assert class present (restored from data)" ); + + // Cleanup + e.removeClass("testD"); + QUnit.expectJqData( e[ 0 ], "__className__" ); +}; + +test( "toggleClass(String|boolean|undefined[, boolean])", function() { + testToggleClass( bareObj ); +}); + +test( "toggleClass(Function[, boolean])", function() { + testToggleClass( functionReturningObj ); +}); + +test( "toggleClass(Function[, boolean]) with incoming value", function() { + expect( 14 ); + + var e = jQuery("#firstp"), + old = e.attr("class") || ""; + + ok( !e.is(".test"), "Assert class not present" ); + + e.toggleClass(function( i, val ) { + equal( old, val, "Make sure the incoming value is correct." ); + return "test"; + }); + ok( e.is(".test"), "Assert class present" ); + + old = e.attr("class"); + + e.toggleClass(function( i, val ) { + equal( old, val, "Make sure the incoming value is correct." ); + return "test"; + }); + ok( !e.is(".test"), "Assert class not present" ); + + old = e.attr("class") || ""; + + // class name with a boolean + e.toggleClass(function( i, val, state ) { + equal( old, val, "Make sure the incoming value is correct." ); + equal( state, false, "Make sure that the state is passed in." ); + return "test"; + }, false ); + ok( !e.is(".test"), "Assert class not present" ); + + old = e.attr("class") || ""; + + e.toggleClass(function( i, val, state ) { + equal( old, val, "Make sure the incoming value is correct." ); + equal( state, true, "Make sure that the state is passed in." ); + return "test"; + }, true ); + ok( e.is(".test"), "Assert class present" ); + + old = e.attr("class"); + + e.toggleClass(function( i, val, state ) { + equal( old, val, "Make sure the incoming value is correct." ); + equal( state, false, "Make sure that the state is passed in." ); + return "test"; + }, false ); + ok( !e.is(".test"), "Assert class not present" ); +}); + +test( "addClass, removeClass, hasClass", function() { + expect( 17 ); + + var jq = jQuery("

        Hi

        "), x = jq[ 0 ]; + + jq.addClass("hi"); + equal( x.className, "hi", "Check single added class" ); + + jq.addClass("foo bar"); + equal( x.className, "hi foo bar", "Check more added classes" ); + + jq.removeClass(); + equal( x.className, "", "Remove all classes" ); + + jq.addClass("hi foo bar"); + jq.removeClass("foo"); + equal( x.className, "hi bar", "Check removal of one class" ); + + ok( jq.hasClass("hi"), "Check has1" ); + ok( jq.hasClass("bar"), "Check has2" ); + + jq = jQuery("

        "); + + ok( jq.hasClass("class1"), "Check hasClass with line feed" ); + ok( jq.is(".class1"), "Check is with line feed" ); + ok( jq.hasClass("class2"), "Check hasClass with tab" ); + ok( jq.is(".class2"), "Check is with tab" ); + ok( jq.hasClass("cla.ss3"), "Check hasClass with dot" ); + ok( jq.hasClass("class4"), "Check hasClass with carriage return" ); + ok( jq.is(".class4"), "Check is with carriage return" ); + + jq.removeClass("class2"); + ok( jq.hasClass("class2") === false, "Check the class has been properly removed" ); + jq.removeClass("cla"); + ok( jq.hasClass("cla.ss3"), "Check the dotted class has not been removed" ); + jq.removeClass("cla.ss3"); + ok( jq.hasClass("cla.ss3") === false, "Check the dotted class has been removed" ); + jq.removeClass("class4"); + ok( jq.hasClass("class4") === false, "Check the class has been properly removed" ); +}); + +test( "contents().hasClass() returns correct values", function() { + expect( 2 ); + + var $div = jQuery("
        text
        "), + $contents = $div.contents(); + + ok( $contents.hasClass("foo"), "Found 'foo' in $contents" ); + ok( !$contents.hasClass("undefined"), "Did not find 'undefined' in $contents (correctly)" ); +}); + +test( "hasClass correctly interprets non-space separators (#13835)", function() { + expect( 4 ); + + var + map = { + tab: " ", + "line-feed": " ", + "form-feed": " ", + "carriage-return": " " + }, + classes = jQuery.map( map, function( separator, label ) { + return " " + separator + label + separator + " "; + }), + $div = jQuery( "
        " ); + + jQuery.each( map, function( label ) { + ok( $div.hasClass( label ), label.replace( "-", " " ) ); + }); +}); + +test( "coords returns correct values in IE6/IE7, see #10828", function() { + expect( 1 ); + + var area, + map = jQuery(""); + + area = map.html("a").find("area"); + equal( area.attr("coords"), "0,0,0,0", "did not retrieve coords correctly" ); +}); diff --git a/bower_components/jquery/test/unit/callbacks.js b/bower_components/jquery/test/unit/callbacks.js new file mode 100644 index 0000000..843c958 --- /dev/null +++ b/bower_components/jquery/test/unit/callbacks.js @@ -0,0 +1,342 @@ +module( "callbacks", { + teardown: moduleTeardown +}); + +(function() { + +var output, + addToOutput = function( string ) { + return function() { + output += string; + }; + }, + outputA = addToOutput("A"), + outputB = addToOutput("B"), + outputC = addToOutput("C"), + tests = { + "": "XABC X XABCABCC X XBB X XABA X XX", + "once": "XABC X X X X X XABA X XX", + "memory": "XABC XABC XABCABCCC XA XBB XB XABA XC XX", + "unique": "XABC X XABCA X XBB X XAB X X", + "stopOnFalse": "XABC X XABCABCC X XBB X XA X XX", + "once memory": "XABC XABC X XA X XA XABA XC XX", + "once unique": "XABC X X X X X XAB X X", + "once stopOnFalse": "XABC X X X X X XA X XX", + "memory unique": "XABC XA XABCA XA XBB XB XAB XC X", + "memory stopOnFalse": "XABC XABC XABCABCCC XA XBB XB XA X XX", + "unique stopOnFalse": "XABC X XABCA X XBB X XA X X" + }, + filters = { + "no filter": undefined, + "filter": function( fn ) { + return function() { + return fn.apply( this, arguments ); + }; + } + }; + + function showFlags( flags ) { + if ( typeof flags === "string" ) { + return "'" + flags + "'"; + } + var output = [], key; + for ( key in flags ) { + output.push( "'" + key + "': " + flags[ key ] ); + } + return "{ " + output.join( ", " ) + " }"; + } + +jQuery.each( tests, function( strFlags, resultString ) { + + var objectFlags = {}; + + jQuery.each( strFlags.split( " " ), function() { + if ( this.length ) { + objectFlags[ this ] = true; + } + }); + + jQuery.each( filters, function( filterLabel ) { + + jQuery.each({ + "string": strFlags, + "object": objectFlags + }, function( flagsTypes, flags ) { + + test( "jQuery.Callbacks( " + showFlags( flags ) + " ) - " + filterLabel, function() { + + expect( 21 ); + + // Give qunit a little breathing room + stop(); + setTimeout( start, 0 ); + + var cblist, + results = resultString.split( /\s+/ ); + + // Basic binding and firing + output = "X"; + cblist = jQuery.Callbacks( flags ); + cblist.add(function( str ) { + output += str; + }); + cblist.fire("A"); + strictEqual( output, "XA", "Basic binding and firing" ); + strictEqual( cblist.fired(), true, ".fired() detects firing" ); + output = "X"; + cblist.disable(); + cblist.add(function( str ) { + output += str; + }); + strictEqual( output, "X", "Adding a callback after disabling" ); + cblist.fire("A"); + strictEqual( output, "X", "Firing after disabling" ); + + // #13517 - Emptying while firing + cblist = jQuery.Callbacks( flags ); + cblist.add( cblist.empty ); + cblist.add( function() { + ok( false, "not emptied" ); + } ); + cblist.fire(); + + // Disabling while firing + cblist = jQuery.Callbacks( flags ); + cblist.add( cblist.disable ); + cblist.add( function() { + ok( false, "not disabled" ); + } ); + cblist.fire(); + + // Basic binding and firing (context, arguments) + output = "X"; + cblist = jQuery.Callbacks( flags ); + cblist.add(function() { + equal( this, window, "Basic binding and firing (context)" ); + output += Array.prototype.join.call( arguments, "" ); + }); + cblist.fireWith( window, [ "A", "B" ] ); + strictEqual( output, "XAB", "Basic binding and firing (arguments)" ); + + // fireWith with no arguments + output = ""; + cblist = jQuery.Callbacks( flags ); + cblist.add(function() { + equal( this, window, "fireWith with no arguments (context is window)" ); + strictEqual( arguments.length, 0, "fireWith with no arguments (no arguments)" ); + }); + cblist.fireWith(); + + // Basic binding, removing and firing + output = "X"; + cblist = jQuery.Callbacks( flags ); + cblist.add( outputA, outputB, outputC ); + cblist.remove( outputB, outputC ); + cblist.fire(); + strictEqual( output, "XA", "Basic binding, removing and firing" ); + + // Empty + output = "X"; + cblist = jQuery.Callbacks( flags ); + cblist.add( outputA ); + cblist.add( outputB ); + cblist.add( outputC ); + cblist.empty(); + cblist.fire(); + strictEqual( output, "X", "Empty" ); + + // Locking + output = "X"; + cblist = jQuery.Callbacks( flags ); + cblist.add(function( str ) { + output += str; + }); + cblist.lock(); + cblist.add(function( str ) { + output += str; + }); + cblist.fire("A"); + cblist.add(function( str ) { + output += str; + }); + strictEqual( output, "X", "Lock early" ); + + // Ordering + output = "X"; + cblist = jQuery.Callbacks( flags ); + cblist.add(function() { + cblist.add( outputC ); + outputA(); + }, outputB ); + cblist.fire(); + strictEqual( output, results.shift(), "Proper ordering" ); + + // Add and fire again + output = "X"; + cblist.add(function() { + cblist.add( outputC ); + outputA(); + }, outputB ); + strictEqual( output, results.shift(), "Add after fire" ); + + output = "X"; + cblist.fire(); + strictEqual( output, results.shift(), "Fire again" ); + + // Multiple fire + output = "X"; + cblist = jQuery.Callbacks( flags ); + cblist.add(function( str ) { + output += str; + }); + cblist.fire("A"); + strictEqual( output, "XA", "Multiple fire (first fire)" ); + output = "X"; + cblist.add(function( str ) { + output += str; + }); + strictEqual( output, results.shift(), "Multiple fire (first new callback)" ); + output = "X"; + cblist.fire("B"); + strictEqual( output, results.shift(), "Multiple fire (second fire)" ); + output = "X"; + cblist.add(function( str ) { + output += str; + }); + strictEqual( output, results.shift(), "Multiple fire (second new callback)" ); + + // Return false + output = "X"; + cblist = jQuery.Callbacks( flags ); + cblist.add( outputA, function() { return false; }, outputB ); + cblist.add( outputA ); + cblist.fire(); + strictEqual( output, results.shift(), "Callback returning false" ); + + // Add another callback (to control lists with memory do not fire anymore) + output = "X"; + cblist.add( outputC ); + strictEqual( output, results.shift(), "Adding a callback after one returned false" ); + + // Callbacks are not iterated + output = ""; + function handler() { + output += "X"; + } + handler.method = function() { + output += "!"; + }; + cblist = jQuery.Callbacks( flags ); + cblist.add( handler ); + cblist.add( handler ); + cblist.fire(); + strictEqual( output, results.shift(), "No callback iteration" ); + }); + }); + }); +}); + +})(); + +test( "jQuery.Callbacks( options ) - options are copied", function() { + + expect( 1 ); + + var options = { + "unique": true + }, + cb = jQuery.Callbacks( options ), + count = 0, + fn = function() { + ok( !( count++ ), "called once" ); + }; + options["unique"] = false; + cb.add( fn, fn ); + cb.fire(); +}); + +test( "jQuery.Callbacks.fireWith - arguments are copied", function() { + + expect( 1 ); + + var cb = jQuery.Callbacks("memory"), + args = ["hello"]; + + cb.fireWith( null, args ); + args[ 0 ] = "world"; + + cb.add(function( hello ) { + strictEqual( hello, "hello", "arguments are copied internally" ); + }); +}); + +test( "jQuery.Callbacks.remove - should remove all instances", function() { + + expect( 1 ); + + var cb = jQuery.Callbacks(); + + function fn() { + ok( false, "function wasn't removed" ); + } + + cb.add( fn, fn, function() { + ok( true, "end of test" ); + }).remove( fn ).fire(); +}); + +test( "jQuery.Callbacks.has", function() { + + expect( 13 ); + + var cb = jQuery.Callbacks(); + function getA() { + return "A"; + } + function getB() { + return "B"; + } + function getC() { + return "C"; + } + cb.add(getA, getB, getC); + strictEqual( cb.has(), true, "No arguments to .has() returns whether callback function(s) are attached or not" ); + strictEqual( cb.has(getA), true, "Check if a specific callback function is in the Callbacks list" ); + + cb.remove(getB); + strictEqual( cb.has(getB), false, "Remove a specific callback function and make sure its no longer there" ); + strictEqual( cb.has(getA), true, "Remove a specific callback function and make sure other callback function is still there" ); + + cb.empty(); + strictEqual( cb.has(), false, "Empty list and make sure there are no callback function(s)" ); + strictEqual( cb.has(getA), false, "Check for a specific function in an empty() list" ); + + cb.add(getA, getB, function(){ + strictEqual( cb.has(), true, "Check if list has callback function(s) from within a callback function" ); + strictEqual( cb.has(getA), true, "Check if list has a specific callback from within a callback function" ); + }).fire(); + + strictEqual( cb.has(), true, "Callbacks list has callback function(s) after firing" ); + + cb.disable(); + strictEqual( cb.has(), false, "disabled() list has no callback functions (returns false)" ); + strictEqual( cb.has(getA), false, "Check for a specific function in a disabled() list" ); + + cb = jQuery.Callbacks("unique"); + cb.add(getA); + cb.add(getA); + strictEqual( cb.has(), true, "Check if unique list has callback function(s) attached" ); + cb.lock(); + strictEqual( cb.has(), false, "locked() list is empty and returns false" ); + + +}); + +test( "jQuery.Callbacks() - adding a string doesn't cause a stack overflow", function() { + + expect( 1 ); + + jQuery.Callbacks().add( "hello world" ); + + ok( true, "no stack overflow" ); +}); diff --git a/bower_components/jquery/test/unit/core.js b/bower_components/jquery/test/unit/core.js new file mode 100644 index 0000000..5fba3cf --- /dev/null +++ b/bower_components/jquery/test/unit/core.js @@ -0,0 +1,1387 @@ +module("core", { teardown: moduleTeardown }); + +test("Unit Testing Environment", function () { + expect(2); + ok( hasPHP, "Running in an environment with PHP support. The AJAX tests only run if the environment supports PHP!" ); + ok( !isLocal, "Unit tests are not ran from file:// (especially in Chrome. If you must test from file:// with Chrome, run it with the --allow-file-access-from-files flag!)" ); +}); + +test("Basic requirements", function() { + expect(7); + ok( Array.prototype.push, "Array.push()" ); + ok( Function.prototype.apply, "Function.apply()" ); + ok( document.getElementById, "getElementById" ); + ok( document.getElementsByTagName, "getElementsByTagName" ); + ok( RegExp, "RegExp" ); + ok( jQuery, "jQuery" ); + ok( $, "$" ); +}); + +testIframeWithCallback( "Conditional compilation compatibility (#13274)", "core/cc_on.html", function( cc_on, errors, $ ) { + expect( 3 ); + ok( true, "JScript conditional compilation " + ( cc_on ? "supported" : "not supported" ) ); + deepEqual( errors, [], "No errors" ); + ok( $(), "jQuery executes" ); +}); + +testIframeWithCallback( "document ready when jQuery loaded asynchronously (#13655)", "core/dynamic_ready.html", function( ready ) { + expect( 1 ); + equal( true, ready, "document ready correctly fired when jQuery is loaded after DOMContentLoaded" ); +}); + +test("jQuery()", function() { + + var elem, i, + obj = jQuery("div"), + code = jQuery(""), + img = jQuery(""), + div = jQuery("

        "), + exec = false, + lng = "", + expected = 22, + attrObj = { + "text": "test", + "class": "test2", + "id": "test3" + }; + + // The $(html, props) signature can stealth-call any $.fn method, check for a + // few here but beware of modular builds where these methods may be excluded. + if ( jQuery.fn.click ) { + expected++; + attrObj["click"] = function() { ok( exec, "Click executed." ); }; + } + if ( jQuery.fn.width ) { + expected++; + attrObj["width"] = 10; + } + if ( jQuery.fn.offset ) { + expected++; + attrObj["offset"] = { "top": 1, "left": 1 }; + } + if ( jQuery.fn.css ) { + expected += 2; + attrObj["css"] = { "paddingLeft": 1, "paddingRight": 1 }; + } + if ( jQuery.fn.attr ) { + expected++; + attrObj.attr = { "desired": "very" }; + } + + expect( expected ); + + // Basic constructor's behavior + equal( jQuery().length, 0, "jQuery() === jQuery([])" ); + equal( jQuery(undefined).length, 0, "jQuery(undefined) === jQuery([])" ); + equal( jQuery(null).length, 0, "jQuery(null) === jQuery([])" ); + equal( jQuery("").length, 0, "jQuery('') === jQuery([])" ); + equal( jQuery("#").length, 0, "jQuery('#') === jQuery([])" ); + + equal( jQuery(obj).selector, "div", "jQuery(jQueryObj) == jQueryObj" ); + + // can actually yield more than one, when iframes are included, the window is an array as well + equal( jQuery(window).length, 1, "Correct number of elements generated for jQuery(window)" ); + +/* + // disabled since this test was doing nothing. i tried to fix it but i'm not sure + // what the expected behavior should even be. FF returns "\n" for the text node + // make sure this is handled + var crlfContainer = jQuery('

        \r\n

        '); + var x = crlfContainer.contents().get(0).nodeValue; + equal( x, what???, "Check for \\r and \\n in jQuery()" ); +*/ + + /* // Disabled until we add this functionality in + var pass = true; + try { + jQuery("
        Testing
        ").appendTo(document.getElementById("iframe").contentDocument.body); + } catch(e){ + pass = false; + } + ok( pass, "jQuery('<tag>') needs optional document parameter to ease cross-frame DOM wrangling, see #968" );*/ + + equal( code.length, 1, "Correct number of elements generated for code" ); + equal( code.parent().length, 0, "Make sure that the generated HTML has no parent." ); + + equal( img.length, 1, "Correct number of elements generated for img" ); + equal( img.parent().length, 0, "Make sure that the generated HTML has no parent." ); + + equal( div.length, 4, "Correct number of elements generated for div hr code b" ); + equal( div.parent().length, 0, "Make sure that the generated HTML has no parent." ); + + equal( jQuery([1,2,3]).get(1), 2, "Test passing an array to the factory" ); + + equal( jQuery(document.body).get(0), jQuery("body").get(0), "Test passing an html node to the factory" ); + + elem = jQuery(" hello")[0]; + equal( elem.nodeName.toLowerCase(), "em", "leading space" ); + + elem = jQuery("\n\nworld")[0]; + equal( elem.nodeName.toLowerCase(), "em", "leading newlines" ); + + elem = jQuery("
        ", attrObj ); + + if ( jQuery.fn.width ) { + equal( elem[0].style.width, "10px", "jQuery() quick setter width"); + } + + if ( jQuery.fn.offset ) { + equal( elem[0].style.top, "1px", "jQuery() quick setter offset"); + } + + if ( jQuery.fn.css ) { + equal( elem[0].style.paddingLeft, "1px", "jQuery quick setter css"); + equal( elem[0].style.paddingRight, "1px", "jQuery quick setter css"); + } + + if ( jQuery.fn.attr ) { + equal( elem[0].getAttribute("desired"), "very", "jQuery quick setter attr"); + } + + equal( elem[0].childNodes.length, 1, "jQuery quick setter text"); + equal( elem[0].firstChild.nodeValue, "test", "jQuery quick setter text"); + equal( elem[0].className, "test2", "jQuery() quick setter class"); + equal( elem[0].id, "test3", "jQuery() quick setter id"); + + exec = true; + elem.trigger("click"); + + // manually clean up detached elements + elem.remove(); + + for ( i = 0; i < 3; ++i ) { + elem = jQuery(""); + } + equal( elem[0].defaultValue, "TEST", "Ensure cached nodes are cloned properly (Bug #6655)" ); + + // manually clean up detached elements + elem.remove(); + + for ( i = 0; i < 128; i++ ) { + lng += "12345678"; + } +}); + +test("jQuery(selector, context)", function() { + expect(3); + deepEqual( jQuery("div p", "#qunit-fixture").get(), q("sndp", "en", "sap"), "Basic selector with string as context" ); + deepEqual( jQuery("div p", q("qunit-fixture")[0]).get(), q("sndp", "en", "sap"), "Basic selector with element as context" ); + deepEqual( jQuery("div p", jQuery("#qunit-fixture")).get(), q("sndp", "en", "sap"), "Basic selector with jQuery object as context" ); +}); + +test( "selector state", function() { + expect( 18 ); + + var test; + + test = jQuery( undefined ); + equal( test.selector, "", "Empty jQuery Selector" ); + equal( test.context, undefined, "Empty jQuery Context" ); + + test = jQuery( document ); + equal( test.selector, "", "Document Selector" ); + equal( test.context, document, "Document Context" ); + + test = jQuery( document.body ); + equal( test.selector, "", "Body Selector" ); + equal( test.context, document.body, "Body Context" ); + + test = jQuery("#qunit-fixture"); + equal( test.selector, "#qunit-fixture", "#qunit-fixture Selector" ); + equal( test.context, document, "#qunit-fixture Context" ); + + test = jQuery("#notfoundnono"); + equal( test.selector, "#notfoundnono", "#notfoundnono Selector" ); + equal( test.context, document, "#notfoundnono Context" ); + + test = jQuery( "#qunit-fixture", document ); + equal( test.selector, "#qunit-fixture", "#qunit-fixture Selector" ); + equal( test.context, document, "#qunit-fixture Context" ); + + test = jQuery( "#qunit-fixture", document.body ); + equal( test.selector, "#qunit-fixture", "#qunit-fixture Selector" ); + equal( test.context, document.body, "#qunit-fixture Context" ); + + // Test cloning + test = jQuery( test ); + equal( test.selector, "#qunit-fixture", "#qunit-fixture Selector" ); + equal( test.context, document.body, "#qunit-fixture Context" ); + + test = jQuery( document.body ).find("#qunit-fixture"); + equal( test.selector, "#qunit-fixture", "#qunit-fixture find Selector" ); + equal( test.context, document.body, "#qunit-fixture find Context" ); +}); + +test( "globalEval", function() { + expect( 3 ); + Globals.register("globalEvalTest"); + + jQuery.globalEval("globalEvalTest = 1;"); + equal( window.globalEvalTest, 1, "Test variable assignments are global" ); + + jQuery.globalEval("var globalEvalTest = 2;"); + equal( window.globalEvalTest, 2, "Test variable declarations are global" ); + + jQuery.globalEval("this.globalEvalTest = 3;"); + equal( window.globalEvalTest, 3, "Test context (this) is the window object" ); +}); + +test( "globalEval with 'use strict'", function() { + expect( 1 ); + Globals.register("strictEvalTest"); + + jQuery.globalEval("'use strict'; var strictEvalTest = 1;"); + equal( window.strictEvalTest, 1, "Test variable declarations are global (strict mode)" ); +}); + +test("noConflict", function() { + expect(7); + + var $$ = jQuery; + + strictEqual( jQuery, jQuery.noConflict(), "noConflict returned the jQuery object" ); + strictEqual( window["jQuery"], $$, "Make sure jQuery wasn't touched." ); + strictEqual( window["$"], original$, "Make sure $ was reverted." ); + + jQuery = $ = $$; + + strictEqual( jQuery.noConflict(true), $$, "noConflict returned the jQuery object" ); + strictEqual( window["jQuery"], originaljQuery, "Make sure jQuery was reverted." ); + strictEqual( window["$"], original$, "Make sure $ was reverted." ); + ok( $$().pushStack([]), "Make sure that jQuery still works." ); + + window["jQuery"] = jQuery = $$; +}); + +test("trim", function() { + expect(13); + + var nbsp = String.fromCharCode(160); + + equal( jQuery.trim("hello "), "hello", "trailing space" ); + equal( jQuery.trim(" hello"), "hello", "leading space" ); + equal( jQuery.trim(" hello "), "hello", "space on both sides" ); + equal( jQuery.trim(" " + nbsp + "hello " + nbsp + " "), "hello", " " ); + + equal( jQuery.trim(), "", "Nothing in." ); + equal( jQuery.trim( undefined ), "", "Undefined" ); + equal( jQuery.trim( null ), "", "Null" ); + equal( jQuery.trim( 5 ), "5", "Number" ); + equal( jQuery.trim( false ), "false", "Boolean" ); + + equal( jQuery.trim(" "), "", "space should be trimmed" ); + equal( jQuery.trim("ipad\xA0"), "ipad", "nbsp should be trimmed" ); + equal( jQuery.trim("\uFEFF"), "", "zwsp should be trimmed" ); + equal( jQuery.trim("\uFEFF \xA0! | \uFEFF"), "! |", "leading/trailing should be trimmed" ); +}); + +test("type", function() { + expect( 28 ); + + equal( jQuery.type(null), "null", "null" ); + equal( jQuery.type(undefined), "undefined", "undefined" ); + equal( jQuery.type(true), "boolean", "Boolean" ); + equal( jQuery.type(false), "boolean", "Boolean" ); + equal( jQuery.type(Boolean(true)), "boolean", "Boolean" ); + equal( jQuery.type(0), "number", "Number" ); + equal( jQuery.type(1), "number", "Number" ); + equal( jQuery.type(Number(1)), "number", "Number" ); + equal( jQuery.type(""), "string", "String" ); + equal( jQuery.type("a"), "string", "String" ); + equal( jQuery.type(String("a")), "string", "String" ); + equal( jQuery.type({}), "object", "Object" ); + equal( jQuery.type(/foo/), "regexp", "RegExp" ); + equal( jQuery.type(new RegExp("asdf")), "regexp", "RegExp" ); + equal( jQuery.type([1]), "array", "Array" ); + equal( jQuery.type(new Date()), "date", "Date" ); + equal( jQuery.type(new Function("return;")), "function", "Function" ); + equal( jQuery.type(function(){}), "function", "Function" ); + equal( jQuery.type(new Error()), "error", "Error" ); + equal( jQuery.type(window), "object", "Window" ); + equal( jQuery.type(document), "object", "Document" ); + equal( jQuery.type(document.body), "object", "Element" ); + equal( jQuery.type(document.createTextNode("foo")), "object", "TextNode" ); + equal( jQuery.type(document.getElementsByTagName("*")), "object", "NodeList" ); + + // Avoid Lint complaints + var MyString = String, + MyNumber = Number, + MyBoolean = Boolean, + MyObject = Object; + equal( jQuery.type(new MyBoolean(true)), "boolean", "Boolean" ); + equal( jQuery.type(new MyNumber(1)), "number", "Number" ); + equal( jQuery.type(new MyString("a")), "string", "String" ); + equal( jQuery.type(new MyObject()), "object", "Object" ); +}); + +asyncTest("isPlainObject", function() { + expect(15); + + var pass, iframe, doc, + fn = function() {}; + + // The use case that we want to match + ok( jQuery.isPlainObject({}), "{}" ); + + // Not objects shouldn't be matched + ok( !jQuery.isPlainObject(""), "string" ); + ok( !jQuery.isPlainObject(0) && !jQuery.isPlainObject(1), "number" ); + ok( !jQuery.isPlainObject(true) && !jQuery.isPlainObject(false), "boolean" ); + ok( !jQuery.isPlainObject(null), "null" ); + ok( !jQuery.isPlainObject(undefined), "undefined" ); + + // Arrays shouldn't be matched + ok( !jQuery.isPlainObject([]), "array" ); + + // Instantiated objects shouldn't be matched + ok( !jQuery.isPlainObject(new Date()), "new Date" ); + + // Functions shouldn't be matched + ok( !jQuery.isPlainObject(fn), "fn" ); + + // Again, instantiated objects shouldn't be matched + ok( !jQuery.isPlainObject(new fn()), "new fn (no methods)" ); + + // Makes the function a little more realistic + // (and harder to detect, incidentally) + fn.prototype["someMethod"] = function(){}; + + // Again, instantiated objects shouldn't be matched + ok( !jQuery.isPlainObject(new fn()), "new fn" ); + + // DOM Element + ok( !jQuery.isPlainObject( document.createElement("div") ), "DOM Element" ); + + // Window + ok( !jQuery.isPlainObject( window ), "window" ); + + pass = false; + try { + jQuery.isPlainObject( window.location ); + pass = true; + } catch ( e ) {} + ok( pass, "Does not throw exceptions on host objects" ); + + // Objects from other windows should be matched + window.iframeCallback = function( otherObject, detail ) { + window.iframeCallback = undefined; + iframe.parentNode.removeChild( iframe ); + ok( jQuery.isPlainObject(new otherObject()), "new otherObject" + ( detail ? " - " + detail : "" ) ); + start(); + }; + + try { + iframe = jQuery("#qunit-fixture")[0].appendChild( document.createElement("iframe") ); + doc = iframe.contentDocument || iframe.contentWindow.document; + doc.open(); + doc.write(""); + doc.close(); + } catch(e) { + window.iframeDone( Object, "iframes not supported" ); + } +}); + +test("isFunction", function() { + expect(19); + + var mystr, myarr, myfunction, fn, obj, nodes, first, input, a; + + // Make sure that false values return false + ok( !jQuery.isFunction(), "No Value" ); + ok( !jQuery.isFunction( null ), "null Value" ); + ok( !jQuery.isFunction( undefined ), "undefined Value" ); + ok( !jQuery.isFunction( "" ), "Empty String Value" ); + ok( !jQuery.isFunction( 0 ), "0 Value" ); + + // Check built-ins + // Safari uses "(Internal Function)" + ok( jQuery.isFunction(String), "String Function("+String+")" ); + ok( jQuery.isFunction(Array), "Array Function("+Array+")" ); + ok( jQuery.isFunction(Object), "Object Function("+Object+")" ); + ok( jQuery.isFunction(Function), "Function Function("+Function+")" ); + + // When stringified, this could be misinterpreted + mystr = "function"; + ok( !jQuery.isFunction(mystr), "Function String" ); + + // When stringified, this could be misinterpreted + myarr = [ "function" ]; + ok( !jQuery.isFunction(myarr), "Function Array" ); + + // When stringified, this could be misinterpreted + myfunction = { "function": "test" }; + ok( !jQuery.isFunction(myfunction), "Function Object" ); + + // Make sure normal functions still work + fn = function(){}; + ok( jQuery.isFunction(fn), "Normal Function" ); + + obj = document.createElement("object"); + + // Firefox says this is a function + ok( !jQuery.isFunction(obj), "Object Element" ); + + // IE says this is an object + // Since 1.3, this isn't supported (#2968) + //ok( jQuery.isFunction(obj.getAttribute), "getAttribute Function" ); + + nodes = document.body.childNodes; + + // Safari says this is a function + ok( !jQuery.isFunction(nodes), "childNodes Property" ); + + first = document.body.firstChild; + + // Normal elements are reported ok everywhere + ok( !jQuery.isFunction(first), "A normal DOM Element" ); + + input = document.createElement("input"); + input.type = "text"; + document.body.appendChild( input ); + + // IE says this is an object + // Since 1.3, this isn't supported (#2968) + //ok( jQuery.isFunction(input.focus), "A default function property" ); + + document.body.removeChild( input ); + + a = document.createElement("a"); + a.href = "some-function"; + document.body.appendChild( a ); + + // This serializes with the word 'function' in it + ok( !jQuery.isFunction(a), "Anchor Element" ); + + document.body.removeChild( a ); + + // Recursive function calls have lengths and array-like properties + function callme(callback){ + function fn(response){ + callback(response); + } + + ok( jQuery.isFunction(fn), "Recursive Function Call" ); + + fn({ some: "data" }); + } + + callme(function(){ + callme(function(){}); + }); +}); + +test( "isNumeric", function() { + expect( 36 ); + + var t = jQuery.isNumeric, + Traditionalists = /** @constructor */ function(n) { + this.value = n; + this.toString = function(){ + return String(this.value); + }; + }, + answer = new Traditionalists( "42" ), + rong = new Traditionalists( "Devo" ); + + ok( t("-10"), "Negative integer string"); + ok( t("0"), "Zero string"); + ok( t("5"), "Positive integer string"); + ok( t(-16), "Negative integer number"); + ok( t(0), "Zero integer number"); + ok( t(32), "Positive integer number"); + ok( t("040"), "Octal integer literal string"); + // OctalIntegerLiteral has been deprecated since ES3/1999 + // It doesn't pass lint, so disabling until a solution can be found + //ok( t(0144), "Octal integer literal"); + ok( t("0xFF"), "Hexadecimal integer literal string"); + ok( t(0xFFF), "Hexadecimal integer literal"); + ok( t("-1.6"), "Negative floating point string"); + ok( t("4.536"), "Positive floating point string"); + ok( t(-2.6), "Negative floating point number"); + ok( t(3.1415), "Positive floating point number"); + ok( t(8e5), "Exponential notation"); + ok( t("123e-2"), "Exponential notation string"); + ok( t(answer), "Custom .toString returning number"); + equal( t(""), false, "Empty string"); + equal( t(" "), false, "Whitespace characters string"); + equal( t("\t\t"), false, "Tab characters string"); + equal( t("abcdefghijklm1234567890"), false, "Alphanumeric character string"); + equal( t("xabcdefx"), false, "Non-numeric character string"); + equal( t(true), false, "Boolean true literal"); + equal( t(false), false, "Boolean false literal"); + equal( t("bcfed5.2"), false, "Number with preceding non-numeric characters"); + equal( t("7.2acdgs"), false, "Number with trailling non-numeric characters"); + equal( t(undefined), false, "Undefined value"); + equal( t(null), false, "Null value"); + equal( t(NaN), false, "NaN value"); + equal( t(Infinity), false, "Infinity primitive"); + equal( t(Number.POSITIVE_INFINITY), false, "Positive Infinity"); + equal( t(Number.NEGATIVE_INFINITY), false, "Negative Infinity"); + equal( t(rong), false, "Custom .toString returning non-number"); + equal( t({}), false, "Empty object"); + equal( t(function(){} ), false, "Instance of a function"); + equal( t( new Date() ), false, "Instance of a Date"); + equal( t(function(){} ), false, "Instance of a function"); +}); + +test("isXMLDoc - HTML", function() { + expect(4); + + ok( !jQuery.isXMLDoc( document ), "HTML document" ); + ok( !jQuery.isXMLDoc( document.documentElement ), "HTML documentElement" ); + ok( !jQuery.isXMLDoc( document.body ), "HTML Body Element" ); + + var body, + iframe = document.createElement("iframe"); + document.body.appendChild( iframe ); + + try { + body = jQuery(iframe).contents()[0]; + + try { + ok( !jQuery.isXMLDoc( body ), "Iframe body element" ); + } catch(e) { + ok( false, "Iframe body element exception" ); + } + + } catch(e) { + ok( true, "Iframe body element - iframe not working correctly" ); + } + + document.body.removeChild( iframe ); +}); + +test("XSS via location.hash", function() { + expect(1); + + stop(); + jQuery["_check9521"] = function(x){ + ok( x, "script called from #id-like selector with inline handler" ); + jQuery("#check9521").remove(); + delete jQuery["_check9521"]; + start(); + }; + try { + // This throws an error because it's processed like an id + jQuery( "#" ).appendTo("#qunit-fixture"); + } catch (err) { + jQuery["_check9521"](true); + } +}); + +test("isXMLDoc - XML", function() { + expect(3); + var xml = createDashboardXML(); + ok( jQuery.isXMLDoc( xml ), "XML document" ); + ok( jQuery.isXMLDoc( xml.documentElement ), "XML documentElement" ); + ok( jQuery.isXMLDoc( jQuery("tab", xml)[0] ), "XML Tab Element" ); +}); + +test("isWindow", function() { + expect( 14 ); + + ok( jQuery.isWindow(window), "window" ); + ok( jQuery.isWindow(document.getElementsByTagName("iframe")[0].contentWindow), "iframe.contentWindow" ); + ok( !jQuery.isWindow(), "empty" ); + ok( !jQuery.isWindow(null), "null" ); + ok( !jQuery.isWindow(undefined), "undefined" ); + ok( !jQuery.isWindow(document), "document" ); + ok( !jQuery.isWindow(document.documentElement), "documentElement" ); + ok( !jQuery.isWindow(""), "string" ); + ok( !jQuery.isWindow(1), "number" ); + ok( !jQuery.isWindow(true), "boolean" ); + ok( !jQuery.isWindow({}), "object" ); + ok( !jQuery.isWindow({ setInterval: function(){} }), "fake window" ); + ok( !jQuery.isWindow(/window/), "regexp" ); + ok( !jQuery.isWindow(function(){}), "function" ); +}); + +test("jQuery('html')", function() { + expect( 15 ); + + var s, div, j; + + QUnit.reset(); + jQuery["foo"] = false; + s = jQuery("")[0]; + ok( s, "Creating a script" ); + ok( !jQuery["foo"], "Make sure the script wasn't executed prematurely" ); + jQuery("body").append(""); + ok( jQuery["foo"], "Executing a scripts contents in the right context" ); + + // Test multi-line HTML + div = jQuery("
        \r\nsome text\n

        some p

        \nmore text\r\n
        ")[0]; + equal( div.nodeName.toUpperCase(), "DIV", "Make sure we're getting a div." ); + equal( div.firstChild.nodeType, 3, "Text node." ); + equal( div.lastChild.nodeType, 3, "Text node." ); + equal( div.childNodes[1].nodeType, 1, "Paragraph." ); + equal( div.childNodes[1].firstChild.nodeType, 3, "Paragraph text." ); + + QUnit.reset(); + ok( jQuery("")[0], "Creating a link" ); + + ok( !jQuery(""; + equal( jQuery.parseHTML( html ).length, 0, "Ignore scripts by default" ); + equal( jQuery.parseHTML( html, true )[0].nodeName.toLowerCase(), "script", "Preserve scripts when requested" ); + + html += "
        "; + equal( jQuery.parseHTML( html )[0].nodeName.toLowerCase(), "div", "Preserve non-script nodes" ); + equal( jQuery.parseHTML( html, true )[0].nodeName.toLowerCase(), "script", "Preserve script position"); + + equal( jQuery.parseHTML("text")[0].nodeType, 3, "Parsing text returns a text node" ); + equal( jQuery.parseHTML( "\t
        " )[0].nodeValue, "\t", "Preserve leading whitespace" ); + + equal( jQuery.parseHTML("
        ")[0].nodeType, 3, "Leading spaces are treated as text nodes (#11290)" ); + + html = jQuery.parseHTML( "
        test div
        " ); + + equal( html[ 0 ].parentNode.nodeType, 11, "parentNode should be documentFragment" ); + equal( html[ 0 ].innerHTML, "test div", "Content should be preserved" ); + + equal( jQuery.parseHTML("").length, 1, "Incorrect html-strings should not break anything" ); + equal( jQuery.parseHTML("")[ 1 ].parentNode.nodeType, 11, + "parentNode should be documentFragment for wrapMap (variable in manipulation module) elements too" ); + ok( jQuery.parseHTML("<#if>

        This is a test.

        <#/if>") || true, "Garbage input should not cause error" ); +}); + +test("jQuery.parseJSON", function(){ + expect( 9 ); + + equal( jQuery.parseJSON( null ), null, "Actual null returns null" ); + equal( jQuery.isEmptyObject( jQuery.parseJSON("{}") ), true, "Empty object returns empty object" ); + deepEqual( jQuery.parseJSON("{\"test\":1}"), { "test": 1 }, "Plain object parses" ); + deepEqual( jQuery.parseJSON("\n{\"test\":1}"), { "test": 1 }, "Leading whitespaces are ignored." ); + raises(function() { + jQuery.parseJSON(); + }, null, "Undefined raises an error" ); + raises( function() { + jQuery.parseJSON( "" ); + }, null, "Empty string raises an error" ); + raises(function() { + jQuery.parseJSON("''"); + }, null, "Single-quoted string raises an error" ); + raises(function() { + jQuery.parseJSON("{a:1}"); + }, null, "Unquoted property raises an error" ); + raises(function() { + jQuery.parseJSON("{'a':1}"); + }, null, "Single-quoted property raises an error" ); +}); + +test("jQuery.parseXML", 8, function(){ + var xml, tmp; + try { + xml = jQuery.parseXML( "

        A well-formed xml string

        " ); + tmp = xml.getElementsByTagName( "p" )[ 0 ]; + ok( !!tmp, "

        present in document" ); + tmp = tmp.getElementsByTagName( "b" )[ 0 ]; + ok( !!tmp, " present in document" ); + strictEqual( tmp.childNodes[ 0 ].nodeValue, "well-formed", " text is as expected" ); + } catch (e) { + strictEqual( e, undefined, "unexpected error" ); + } + try { + xml = jQuery.parseXML( "

        Not a <well-formed xml string

        " ); + ok( false, "invalid xml not detected" ); + } catch( e ) { + strictEqual( e.message, "Invalid XML:

        Not a <well-formed xml string

        ", "invalid xml detected" ); + } + try { + xml = jQuery.parseXML( "" ); + strictEqual( xml, null, "empty string => null document" ); + xml = jQuery.parseXML(); + strictEqual( xml, null, "undefined string => null document" ); + xml = jQuery.parseXML( null ); + strictEqual( xml, null, "null string => null document" ); + xml = jQuery.parseXML( true ); + strictEqual( xml, null, "non-string => null document" ); + } catch( e ) { + ok( false, "empty input throws exception" ); + } +}); + +test("jQuery.camelCase()", function() { + + var tests = { + "foo-bar": "fooBar", + "foo-bar-baz": "fooBarBaz", + "girl-u-want": "girlUWant", + "the-4th-dimension": "the4thDimension", + "-o-tannenbaum": "OTannenbaum", + "-moz-illa": "MozIlla", + "-ms-take": "msTake" + }; + + expect(7); + + jQuery.each( tests, function( key, val ) { + equal( jQuery.camelCase( key ), val, "Converts: " + key + " => " + val ); + }); +}); diff --git a/bower_components/jquery/test/unit/css.js b/bower_components/jquery/test/unit/css.js new file mode 100644 index 0000000..297fbec --- /dev/null +++ b/bower_components/jquery/test/unit/css.js @@ -0,0 +1,1017 @@ +if ( jQuery.css ) { + +module("css", { teardown: moduleTeardown }); + +test("css(String|Hash)", function() { + expect( 40 ); + + equal( jQuery("#qunit-fixture").css("display"), "block", "Check for css property \"display\"" ); + + var $child, div, div2, width, height, child, prctval, checkval, old; + + $child = jQuery("#nothiddendivchild").css({ "width": "20%", "height": "20%" }); + notEqual( $child.css("width"), "20px", "Retrieving a width percentage on the child of a hidden div returns percentage" ); + notEqual( $child.css("height"), "20px", "Retrieving a height percentage on the child of a hidden div returns percentage" ); + + div = jQuery( "
        " ); + + // These should be "auto" (or some better value) + // temporarily provide "0px" for backwards compat + equal( div.css("width"), "0px", "Width on disconnected node." ); + equal( div.css("height"), "0px", "Height on disconnected node." ); + + div.css({ "width": 4, "height": 4 }); + + equal( div.css("width"), "4px", "Width on disconnected node." ); + equal( div.css("height"), "4px", "Height on disconnected node." ); + + div2 = jQuery( "
        "); + clone = element.clone(); + equal( clone[ 0 ].defaultValue, "foo", "Textarea defaultValue cloned correctly" ); +}); + +test( "clone(multiple selected options) (Bug #8129)", function() { + + expect( 1 ); + + var element = jQuery(""); + + equal( element.clone().find("option:selected").length, element.find("option:selected").length, "Multiple selected options cloned correctly" ); + +}); + +test( "clone() on XML nodes", function() { + + expect( 2 ); + + var xml = createDashboardXML(), + root = jQuery(xml.documentElement).clone(), + origTab = jQuery("tab", xml).eq( 0 ), + cloneTab = jQuery("tab", root).eq( 0 ); + + origTab.text("origval"); + cloneTab.text("cloneval"); + equal( origTab.text(), "origval", "Check original XML node was correctly set" ); + equal( cloneTab.text(), "cloneval", "Check cloned XML node was correctly set" ); +}); + +test( "clone() on local XML nodes with html5 nodename", function() { + + expect( 2 ); + + var $xmlDoc = jQuery( jQuery.parseXML( "" ) ), + $meter = $xmlDoc.find( "meter" ).clone(); + + equal( $meter[ 0 ].nodeName, "meter", "Check if nodeName was not changed due to cloning" ); + equal( $meter[ 0 ].nodeType, 1, "Check if nodeType is not changed due to cloning" ); +}); + +test( "html(undefined)", function() { + + expect( 1 ); + + equal( jQuery("#foo").html("test").html(undefined).html().toLowerCase(), "test", ".html(undefined) is chainable (#5571)" ); +}); + +test( "html() on empty set", function() { + + expect( 1 ); + + strictEqual( jQuery().html(), undefined, ".html() returns undefined for empty sets (#11962)" ); +}); + +function childNodeNames( node ) { + return jQuery.map( node.childNodes, function( child ) { + return child.nodeName.toUpperCase(); + }).join(" "); +} + +function testHtml( valueObj ) { + expect( 37 ); + + var actual, expected, tmp, + div = jQuery("
        "), + fixture = jQuery("#qunit-fixture"); + + div.html( valueObj("
        ") ); + equal( div.children().length, 2, "Found children" ); + equal( div.children().children().length, 1, "Found grandchild" ); + + actual = []; expected = []; + tmp = jQuery("").html( valueObj("area") ).each(function() { + expected.push("AREA"); + actual.push( childNodeNames( this ) ); + }); + equal( expected.length, 1, "Expecting one parent" ); + deepEqual( actual, expected, "Found the inserted area element" ); + + equal( div.html(valueObj(5)).html(), "5", "Setting a number as html" ); + equal( div.html(valueObj(0)).html(), "0", "Setting a zero as html" ); + + div.html( valueObj(" &") ); + equal( + div[ 0 ].innerHTML.replace( /\xA0/, " " ), + " &", + "Entities are passed through correctly" + ); + + tmp = "<div>hello1</div>"; + equal( div.html(valueObj(tmp) ).html().replace( />/g, ">" ), tmp, "Escaped html" ); + tmp = "x" + tmp; + equal( div.html(valueObj( tmp )).html().replace( />/g, ">" ), tmp, "Escaped html, leading x" ); + tmp = " " + tmp.slice( 1 ); + equal( div.html(valueObj( tmp )).html().replace( />/g, ">" ), tmp, "Escaped html, leading space" ); + + actual = []; expected = []; tmp = {}; + jQuery("#nonnodes").contents().html( valueObj("bold") ).each(function() { + var html = jQuery( this ).html(); + tmp[ this.nodeType ] = true; + expected.push( this.nodeType === 1 ? "bold" : undefined ); + actual.push( html ? html.toLowerCase() : html ); + }); + deepEqual( actual, expected, "Set containing element, text node, comment" ); + ok( tmp[ 1 ], "element" ); + ok( tmp[ 3 ], "text node" ); + ok( tmp[ 8 ], "comment" ); + + actual = []; expected = []; + fixture.children("div").html( valueObj("test") ).each(function() { + expected.push("B"); + actual.push( childNodeNames( this ) ); + }); + equal( expected.length, 7, "Expecting many parents" ); + deepEqual( actual, expected, "Correct childNodes after setting HTML" ); + + actual = []; expected = []; + fixture.html( valueObj("") ).each(function() { + expected.push("STYLE"); + actual.push( childNodeNames( this ) ); + }); + equal( expected.length, 1, "Expecting one parent" ); + deepEqual( actual, expected, "Found the inserted style element" ); + + fixture.html( valueObj("", {} ); + ok( true, "Does not allow attribute object to be treated like a doc object" ); + } catch ( e ) {} +}); + +test( "jQuery.clone - no exceptions for object elements #9587", function() { + + expect( 1 ); + + try { + jQuery("#no-clone-exception").clone(); + ok( true, "cloned with no exceptions" ); + } catch( e ) { + ok( false, e.message ); + } +}); + +test( "Cloned, detached HTML5 elems (#10667,10670)", function() { + + expect( 7 ); + + var $clone, + $section = jQuery( "
        " ).appendTo( "#qunit-fixture" ); + + // First clone + $clone = $section.clone(); + + // Infer that the test is being run in IE<=8 + if ( $clone[ 0 ].outerHTML && !jQuery.support.opacity ) { + // This branch tests cloning nodes by reading the outerHTML, used only in IE<=8 + equal( $clone[ 0 ].outerHTML, "
        ", "detached clone outerHTML matches '
        '" ); + } else { + // This branch tests a known behaviour in modern browsers that should never fail. + // Included for expected test count symmetry (expecting 1) + equal( $clone[ 0 ].nodeName, "SECTION", "detached clone nodeName matches 'SECTION' in modern browsers" ); + } + + // Bind an event + $section.on( "click", function() { + ok( true, "clone fired event" ); + }); + + // Second clone (will have an event bound) + $clone = $section.clone( true ); + + // Trigger an event from the first clone + $clone.trigger("click"); + $clone.off("click"); + + // Add a child node with text to the original + $section.append("

        Hello

        "); + + // Third clone (will have child node and text) + $clone = $section.clone( true ); + + equal( $clone.find("p").text(), "Hello", "Assert text in child of clone" ); + + // Trigger an event from the third clone + $clone.trigger("click"); + $clone.off("click"); + + // Add attributes to copy + $section.attr({ + "class": "foo bar baz", + "title": "This is a title" + }); + + // Fourth clone (will have newly added attributes) + $clone = $section.clone( true ); + + equal( $clone.attr("class"), $section.attr("class"), "clone and element have same class attribute" ); + equal( $clone.attr("title"), $section.attr("title"), "clone and element have same title attribute" ); + + // Remove the original + $section.remove(); + + // Clone the clone + $section = $clone.clone( true ); + + // Remove the clone + $clone.remove(); + + // Trigger an event from the clone of the clone + $section.trigger("click"); + + // Unbind any remaining events + $section.off("click"); + $clone.off("click"); +}); + +test( "Guard against exceptions when clearing safeChildNodes", function() { + + expect( 1 ); + + var div; + + try { + div = jQuery("

        "); + } catch(e) {} + + ok( div && div.jquery, "Created nodes safely, guarded against exceptions on safeChildNodes[ -1 ]" ); +}); + +test( "Ensure oldIE creates a new set on appendTo (#8894)", function() { + + expect( 5 ); + + strictEqual( jQuery("
        ").clone().addClass("test").appendTo("
        ").end().end().hasClass("test"), false, "Check jQuery.fn.appendTo after jQuery.clone" ); + strictEqual( jQuery("
        ").find("p").end().addClass("test").appendTo("
        ").end().end().hasClass("test"), false, "Check jQuery.fn.appendTo after jQuery.fn.find" ); + strictEqual( jQuery("
        ").text("test").addClass("test").appendTo("
        ").end().end().hasClass("test"), false, "Check jQuery.fn.appendTo after jQuery.fn.text" ); + strictEqual( jQuery("").clone().addClass("test").appendTo("
        ").end().end().hasClass("test"), false, "Check jQuery.fn.appendTo after clone html5 element" ); + strictEqual( jQuery("

        ").appendTo("

        ").end().length, jQuery("

        test

        ").appendTo("
        ").end().length, "Elements created with createElement and with createDocumentFragment should be treated alike" ); +}); + +test( "html() - script exceptions bubble (#11743)", function() { + + expect( 3 ); + + raises(function() { + jQuery("#qunit-fixture").html(""); + ok( false, "Exception ignored" ); + }, "Exception bubbled from inline script" ); + + if ( jQuery.ajax ) { + var onerror = window.onerror; + window.onerror = function() { + ok( true, "Exception thrown in remote script" ); + }; + + jQuery("#qunit-fixture").html(""); + ok( true, "Exception ignored" ); + window.onerror = onerror; + } else { + ok( true, "No jQuery.ajax" ); + ok( true, "No jQuery.ajax" ); + } +}); + +test( "checked state is cloned with clone()", function() { + + expect( 2 ); + + var elem = jQuery.parseHTML("")[ 0 ]; + elem.checked = false; + equal( jQuery(elem).clone().attr("id","clone")[ 0 ].checked, false, "Checked false state correctly cloned" ); + + elem = jQuery.parseHTML("")[ 0 ]; + elem.checked = true; + equal( jQuery(elem).clone().attr("id","clone")[ 0 ].checked, true, "Checked true state correctly cloned" ); +}); + +test( "manipulate mixed jQuery and text (#12384, #12346)", function() { + + expect( 2 ); + + var div = jQuery("
        a
        ").append( " ", jQuery("b"), " ", jQuery("c") ), + nbsp = String.fromCharCode( 160 ); + + equal( div.text(), "a" + nbsp + "b" + nbsp+ "c", "Appending mixed jQuery with text nodes" ); + + div = jQuery("
        ") + .find("div") + .after( "

        a

        ", "

        b

        " ) + .parent(); + equal( div.find("*").length, 3, "added 2 paragraphs after inner div" ); +}); + +testIframeWithCallback( "buildFragment works even if document[0] is iframe's window object in IE9/10 (#12266)", "manipulation/iframe-denied.html", function( test ) { + expect( 1 ); + + ok( test.status, test.description ); +}); + +test( "script evaluation (#11795)", function() { + + expect( 13 ); + + var scriptsIn, scriptsOut, + fixture = jQuery("#qunit-fixture").empty(), + objGlobal = (function() { + return this; + })(), + isOk = objGlobal.ok, + notOk = function() { + var args = arguments; + args[ 0 ] = !args[ 0 ]; + return isOk.apply( this, args ); + }; + + objGlobal.ok = notOk; + scriptsIn = jQuery([ + "", + "", + "", + "", + "
        ", + "", + "", + "", + "", + "
        " + ].join("")); + scriptsIn.appendTo( jQuery("
        ") ); + objGlobal.ok = isOk; + + scriptsOut = fixture.append( scriptsIn ).find("script"); + equal( scriptsOut[ 0 ].type, "something/else", "Non-evaluated type." ); + equal( scriptsOut[ 1 ].type, "text/javascript", "Evaluated type." ); + deepEqual( scriptsOut.get(), fixture.find("script").get(), "All script tags remain." ); + + objGlobal.ok = notOk; + scriptsOut = scriptsOut.add( scriptsOut.clone() ).appendTo( fixture.find("div") ); + deepEqual( fixture.find("div script").get(), scriptsOut.get(), "Scripts cloned without reevaluation" ); + fixture.append( scriptsOut.detach() ); + deepEqual( fixture.children("script").get(), scriptsOut.get(), "Scripts detached without reevaluation" ); + objGlobal.ok = isOk; + + if ( jQuery.ajax ) { + Globals.register("testBar"); + jQuery("#qunit-fixture").append( " + + + + + + + + +
        +
        +

        AngularJS - Single Timer

        + + +
        +
        +
        +<!DOCTYPE html>
        +<html>
        +<head>
        +    <title>AngularJS Example - Single Timer Example</title>
        +    <script src="http://github.com/angular/angular.min.js"></script>
        +    <script src="http://github.com/app/js/timer.js"></script>
        +    <script>
        +        angular.module('MyApp', ['timer']);
        +        function MyAppController($scope) {
        +            $scope.timerRunning = true;
        +
        +            $scope.startTimer = function (){
        +                $scope.$broadcast('timer-start');
        +                $scope.timerRunning = true;
        +            };
        +
        +            $scope.stopTimer = function (){
        +                $scope.$broadcast('timer-stop');
        +                $scope.timerRunning = false;
        +            };
        +
        +            $scope.$on('timer-stopped', function (event, data){
        +                console.log('Timer Stopped - data = ', data);
        +            });
        +        }
        +        MyAppController.$inject = ['$scope'];
        +    </script>
        +</head>
        +<body ng-app="MyApp">
        +    <div ng-controller="MyAppController">
        +        <h1>AngularJS - Single Timer Example</h1>
        +        <h3><timer/></h3>
        +        <button ng-click="startTimer()" ng-disabled="timerRunning">Start Timer</button>
        +        <button ng-click="stopTimer()" ng-disabled="!timerRunning">Stop Timer</button>
        +    </div>
        +    <br/>
        +</body>
        +</html>
        +
        +
        + +
        +
        +
        + +
        +

        AngularJS - Multiple Timer

        + + +
        +
        +
        +<!DOCTYPE html>
        +<html>
        +<head>
        +    <title>AngularJS Example - Multiple Timers Example</title>
        +    <script src="http://github.com/angular/angular.min.js"></script>
        +    <script src="http://github.com/app/js/timer.js"></script>
        +    <script>
        +        angular.module('MyApp', ['timer']);
        +        function MyAppController($scope) {
        +            $scope.timerRunning = true;
        +
        +            $scope.startTimer = function (){
        +                $scope.$broadcast('timer-start');
        +                $scope.timerRunning = true;
        +            };
        +
        +            $scope.stopTimer = function (){
        +                $scope.$broadcast('timer-stop');
        +                $scope.timerRunning = false;
        +            };
        +        }
        +        MyAppController.$inject = ['$scope'];
        +    </script>
        +</head>
        +<body ng-app="MyApp">
        +    <div ng-controller="MyAppController">
        +        <h2>AngularJS - Multiple Timers Example</h2>
        +        <h3>Timer 1: <timer/></h3>
        +        <h3>Timer 2: <timer interval="2000"/></h3>
        +        <h3>Timer 3: <timer>{{minutes}} minutes, {{seconds}} seconds.</timer></h3>
        +        <button ng-click="startTimer()" ng-disabled="timerRunning">Start Timers</button>
        +        <button ng-click="stopTimer()" ng-disabled="!timerRunning">Stop Timers</button>
        +    </div>
        +</body>
        +</html>
        +
        +
        + +
        +
        +
        + +
        +

        AngularJS - Polling Timers

        + + +
        +
        +
        +<!DOCTYPE html>
        +<html>
        +<head>
        +    <title>AngularJS Example - Polling Timer Example</title>
        +    <script src="http://github.com/angular/angular.min.js"></script>
        +    <script src="http://github.com/app/js/timer.js"></script>
        +    <script>
        +        angular.module('MyApp', ['timer']);
        +        function PollingController($scope) {
        +            $scope.timerRunning = true;
        +            $scope.timerConsole = '';
        +
        +            $scope.timerType = '';
        +
        +            $scope.startTimer = function (){
        +                $scope.$broadcast('timer-start');
        +                $scope.timerRunning = true;
        +            };
        +
        +            $scope.stopTimer = function (){
        +                $scope.$broadcast('timer-stop');
        +                $scope.timerRunning = false;
        +            };
        +
        +            $scope.$on('timer-tick', function (event, args) {
        +                $scope.timerConsole += $scope.timerType  + ' - event.name = '+ event.name + ', timeoutId = ' + args.timeoutId + ', millis = ' + args.millis +'\n';
        +            });
        +        }
        +
        +        PollingController.$inject = ['$scope'];
        +    </script>
        +</head>
        +<body ng-app="MyApp">
        +    <div>
        +        <h1>AngularJS - Polling Timer Example using <code>timer-tick</code> event</h1>
        +        <div ng-init="timerType = 'Polling Server'" ng-controller="PollingController" style="border: 1px darkgray dashed; padding: 15px;margin:15px">
        +        <h2>Polling Server every 5 seconds</h2>
        +        <h3><timer interval="5000"/></h3>
        +        <textarea style="height: 100px;" row=20 cols="200">{{timerConsole}}</textarea>
        +        <br/>
        +        <button ng-click="startTimer('poll-server')" ng-disabled="timerRunning">Start Timer</button>
        +        <button ng-click="stopTimer('poll-server')" ng-disabled="!timerRunning">Stop Timer</button>
        +        </div>
        +        <br/>
        +
        +        <div ng-init="timerType = 'Saving Documents'" ng-controller="PollingController" style="border: 1px darkgray dashed; padding: 15px">
        +        <h2>Saving Document every 3 seconds</h2>
        +        <h3><timer interval="3000"/></h3>
        +        <textarea style="height: 100px;" row=20 cols="200">{{timerConsole}}</textarea>
        +        <br/>
        +        <button ng-click="startTimer('poll-server')" ng-disabled="timerRunning">Start Timer</button>
        +        <button ng-click="stopTimer('poll-server')" ng-disabled="!timerRunning">Stop Timer</button>
        +        </div>
        +
        +    </div>
        +    <br/>
        +</body>
        +</html>
        +
        +
        + +
        +
        +
        + +
        +

        JQuery Timer

        + + +
        +
        +
        +<!DOCTYPE html>
        +<html>
        +<head>
        +    <title>JQuery Timer Example</title>
        +    <script src="http://github.com/jquery/jquery-1.9.1.min.js"></script>
        +    <script src="http://github.com/angular/angular.min.js"></script>
        +    <script src="http://github.com/app/js/timer.js"></script>
        +    <script>
        +        function startTimer() {
        +            $('timer')[0].start();
        +        }
        +
        +        function stopTimer() {
        +            $('timer')[0].stop();
        +        }
        +    </script>
        +</head>
        +<body>
        +<div ng-controller="MyAppController">
        +    <h2>JQuery - Timer Example</h2>
        +    <h3 ng-app="timer"><timer/></h3>
        +    <button onclick="startTimer()">Start Timer</button>
        +    <button onclick="stopTimer()">Stop Timer</button>
        +</div>
        +<br/>
        +</body>
        +</html>
        +
        +
        + +
        +
        +
        + +
        +

        Plain JavaScript

        + + +
        +
        +
        +<!DOCTYPE html>
        +<html>
        +<head>
        +    <title>Plain Javascript Timer Example</title>
        +    <script src="http://github.com/angular/angular.min.js"></script>
        +    <script src="http://github.com/app/js/timer.js"></script>
        +    <script>
        +        function startTimer() {
        +            document.getElementsByTagName('timer')[0].start();
        +        }
        +
        +        function stopTimer() {
        +            document.getElementsByTagName('timer')[0].stop();
        +        }
        +    </script>
        +</head>
        +<body>
        +<div>
        +    <h2>Plain JavaScript - Timer Example</h2>
        +    <h3><timer ng-app="timer"/></h3>
        +    <button onclick="startTimer()">Start Timer</button>
        +    <button onclick="stopTimer()">Stop Timer</button>
        +</div>
        +<br/>
        +</body>
        +</html>
        +
        +
        + +
        +
        +
        + + +
        +

        © Siddique Hameed 2013

        +
        + +
        + + diff --git a/dist/examples/angularjs-add-countdown-seconds.html b/dist/examples/angularjs-add-countdown-seconds.html new file mode 100644 index 0000000..3238779 --- /dev/null +++ b/dist/examples/angularjs-add-countdown-seconds.html @@ -0,0 +1,28 @@ + + + + AngularJS Example - Single Timer Example + + + + + + + + + +
        +

        AngularJS - Add Countdown Seconds Example

        +

        {{countdown}}

        + +
        +
        + + \ No newline at end of file diff --git a/dist/examples/angularjs-multiple-timers.html b/dist/examples/angularjs-multiple-timers.html new file mode 100644 index 0000000..b8e4102 --- /dev/null +++ b/dist/examples/angularjs-multiple-timers.html @@ -0,0 +1,40 @@ + + + + AngularJS Example - Multiple Timers Example + + + + + + + +
        +

        AngularJS - Multiple Timers Example

        +

        Timer 1:

        +

        Timer 2:

        +

        Timer 3: {{minutes}} minutes, {{seconds}} seconds.

        + + +
        + + \ No newline at end of file diff --git a/dist/examples/angularjs-polling-timer.html b/dist/examples/angularjs-polling-timer.html new file mode 100644 index 0000000..6acc3f0 --- /dev/null +++ b/dist/examples/angularjs-polling-timer.html @@ -0,0 +1,60 @@ + + + + AngularJS Example - Polling Timer Example + + + + + + + +
        +

        AngularJS - Polling Timer Example using timer-tick event

        +
        +

        Polling Server every 5 seconds

        +

        + +
        + + +
        +
        + +
        +

        Saving Document every 3 seconds

        +

        + +
        + + +
        + +
        +
        + + \ No newline at end of file diff --git a/dist/examples/angularjs-reset-timer b/dist/examples/angularjs-reset-timer new file mode 100644 index 0000000..03b28c2 --- /dev/null +++ b/dist/examples/angularjs-reset-timer @@ -0,0 +1,92 @@ + + + + + AngularJS Example - Reset Countdown + + + + + + + + + + + + +
        +
          +
        • + {{millis | date:'mm:ss'}} +
        • + +
        • Start/Resume
        • +
        • Stop
        • +
        • Reset
        • + +
        + + +
        + + + diff --git a/dist/examples/angularjs-single-timer.html b/dist/examples/angularjs-single-timer.html new file mode 100644 index 0000000..c8f886a --- /dev/null +++ b/dist/examples/angularjs-single-timer.html @@ -0,0 +1,39 @@ + + + + AngularJS Example - Single Timer Example + + + + + + + +
        +

        AngularJS - Single Timer Example

        +

        + + +
        +
        + + \ No newline at end of file diff --git a/dist/examples/jquery-timer.html b/dist/examples/jquery-timer.html new file mode 100644 index 0000000..6db9ba4 --- /dev/null +++ b/dist/examples/jquery-timer.html @@ -0,0 +1,27 @@ + + + + JQuery Timer Example + + + + + + +
        +

        JQuery - Timer Example

        +

        + + +
        +
        + + \ No newline at end of file diff --git a/dist/examples/plain-javascript.html b/dist/examples/plain-javascript.html new file mode 100644 index 0000000..fb9bff5 --- /dev/null +++ b/dist/examples/plain-javascript.html @@ -0,0 +1,27 @@ + + + + Plain Javascript Timer Example + + + + + + +
        +

        Plain JavaScript - Timer Example

        +

        + + +
        +
        + + \ No newline at end of file diff --git a/dist/examples/timer-with-autostart-false-and-starttime.html b/dist/examples/timer-with-autostart-false-and-starttime.html new file mode 100644 index 0000000..5b4995c --- /dev/null +++ b/dist/examples/timer-with-autostart-false-and-starttime.html @@ -0,0 +1,17 @@ + + + + Start Time and Auto Start set - Timer Example + + + + + + +
        +

        Start Time and Auto Start set - Timer Example

        +

        +
        +
        + + \ No newline at end of file diff --git a/dist/navbar.html b/dist/navbar.html new file mode 100644 index 0000000..bc73405 --- /dev/null +++ b/dist/navbar.html @@ -0,0 +1,73 @@ + diff --git a/docs/docs.js b/docs/docs.js index 6543d48..5878302 100644 --- a/docs/docs.js +++ b/docs/docs.js @@ -1,30 +1,3 @@ -function startTimer(sectionId) { - document.getElementById(sectionId).getElementsByTagName('timer')[0].start(); -} - -function stopTimer(sectionId) { - document.getElementById(sectionId).getElementsByTagName('timer')[0].stop(); -} - - -function addCDSeconds(sectionId, extraTime) { - document.getElementById(sectionId).getElementsByTagName('timer')[0].addCDSeconds(extraTime); -} - -function stopResumeTimer(sectionId, btn) { - if (btn.innerHTML === 'Start') { - document.getElementById(sectionId).getElementsByTagName('timer')[0].start(); - btn.innerHTML = 'Stop'; - } - else if (btn.innerHTML === 'Stop') { - document.getElementById(sectionId).getElementsByTagName('timer')[0].stop(); - btn.innerHTML = 'Resume'; - } - else { - document.getElementById(sectionId).getElementsByTagName('timer')[0].resume(); - btn.innerHTML = 'Stop'; - } -} angular.module('timer-demo',['timer']).controller('TimerDemoController',['$scope', function ($scope) { $scope.linkAnchors = function () { $('ul.nav a').click(function (){ @@ -34,4 +7,24 @@ angular.module('timer-demo',['timer']).controller('TimerDemoController',['$scope } }); }; + + $scope.btnText = { + reset: "Start", + started: "Stop", + stopped: "Resume" + }; + + $scope.currentYear = (new Date).getFullYear(); + $scope.startTime = (new Date($scope.currentYear, 0, 1)).getTime(); + $scope.endYear = $scope.currentYear+1; + $scope.endTime = (new Date($scope.endYear, 0, 1)).getTime(); + + $scope.callbackTimer={}; + $scope.callbackTimer.status='Running'; + $scope.callbackTimer.callbackCount=0; + $scope.callbackTimer.finished=function(){ + $scope.callbackTimer.status='COMPLETE!!'; + $scope.callbackTimer.callbackCount++; + $scope.$apply(); + }; }]); diff --git a/examples.html b/examples.html index e0d4732..0c66dbb 100644 --- a/examples.html +++ b/examples.html @@ -4,15 +4,18 @@ Angular Timer, a simple, inter-operable AngularJS directive - - - - - - - - - + + + + + + + + + + + + @@ -293,8 +296,5 @@

        Plain JavaScript

        - - - diff --git a/examples/angularjs-add-countdown-seconds.html b/examples/angularjs-add-countdown-seconds.html index f4bda14..3238779 100644 --- a/examples/angularjs-add-countdown-seconds.html +++ b/examples/angularjs-add-countdown-seconds.html @@ -2,16 +2,19 @@ AngularJS Example - Single Timer Example - - + + + + + + diff --git a/examples/angularjs-multiple-timers.html b/examples/angularjs-multiple-timers.html index 73c6c42..b8e4102 100644 --- a/examples/angularjs-multiple-timers.html +++ b/examples/angularjs-multiple-timers.html @@ -2,28 +2,29 @@ AngularJS Example - Multiple Timers Example - - + + + + diff --git a/examples/angularjs-polling-timer.html b/examples/angularjs-polling-timer.html index 2270535..6acc3f0 100644 --- a/examples/angularjs-polling-timer.html +++ b/examples/angularjs-polling-timer.html @@ -2,34 +2,34 @@ AngularJS Example - Polling Timer Example - - - + + diff --git a/examples/angularjs-reset-timer b/examples/angularjs-reset-timer new file mode 100644 index 0000000..03b28c2 --- /dev/null +++ b/examples/angularjs-reset-timer @@ -0,0 +1,92 @@ + + + + + AngularJS Example - Reset Countdown + + + + + + + + + + + + +
        +
          +
        • + {{millis | date:'mm:ss'}} +
        • + +
        • Start/Resume
        • +
        • Stop
        • +
        • Reset
        • + +
        + + +
        + + + diff --git a/examples/angularjs-single-timer.html b/examples/angularjs-single-timer.html index bec08ef..c8f886a 100644 --- a/examples/angularjs-single-timer.html +++ b/examples/angularjs-single-timer.html @@ -2,11 +2,13 @@ AngularJS Example - Single Timer Example - - + + + + diff --git a/examples/jquery-timer.html b/examples/jquery-timer.html index a2a543c..6db9ba4 100644 --- a/examples/jquery-timer.html +++ b/examples/jquery-timer.html @@ -2,9 +2,9 @@ JQuery Timer Example - - - + + + - + + + + + + +
        +

        Start Time and Auto Start set - Timer Example

        +

        +
        +
        + + \ No newline at end of file diff --git a/index.html b/index.html index e3f4e44..a7f5193 100644 --- a/index.html +++ b/index.html @@ -1,437 +1,562 @@ + Angular Timer, a simple, inter-operable AngularJS directive - - - - - - - - - + + + + + + + + + + + +
        -
        -

        - Introduction

        - -

        - Directives in AngularJS is a powerful way of building - reusable UI components. This simple project will serve as a sample/reference implementation - demonstrating its flexibilities by making it inter-operable across runtime (AngularJS, plain simple - JavaScript & jQuery)

        - -

        - For basic understanding of how directives work in AngularJS, please head to this developer guide.

        -
        -
        -

        - Basic Example

        - -
        +
        +

        + Introduction

        +

        - This simple directive <timer /> will start the timer with the default option of - ticking every 1 millisecond

        + Directives in AngularJS is a powerful way of building + reusable UI components. This simple project will serve as a sample/reference implementation + demonstrating its flexibilities by making it inter-operable across runtime (AngularJS, plain simple + JavaScript & jQuery)

        -

        - -

        - - -
        -
        -
        -

        - Timer with hours, minutes & seconds

        - -

        - This markup <timer interval="1000">{{hours}} hours, {{minutes}} - minutes, {{seconds}} seconds.</timer> will run the clock timer ticking every second

        + For basic understanding of how directives work in AngularJS, please head to this developer guide.

        +
        +
        +

        + Basic Example

        + +
        +

        + This simple directive <timer /> will start the timer with the default option of + ticking every 1 millisecond

        +

        + +

        + + +
        +
        +
        +

        Timer with start time and auto start set

        + +
        +

        This will start a timer with 1410914940000 milliseconds and stopped

        + +

        + +

        +
        +
        +

        - {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds. -

        - -
        -
        -
        -

        - Timer with leading zero

        - -
        -

        - This markup <timer interval="1000">{{hhours}} hours, {{mminutes}} - minutes, {{sseconds}} seconds.</timer> will run the clock timer ticking every second with an additional zero at the beginning if unit is smaller than 10

        + Timer with hours, minutes & seconds + +
        +

        + This markup <timer interval="1000">{{hours}} hour{{hoursS}}, {{minutes}} + minute{{minutesS}}, {{seconds}} second{{secondsS}}.</timer> will run the clock timer ticking every second

        +

        + {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}. +

        + +
        +
        +

        - {{hhours}} hours, {{mminutes}} minutes, {{sseconds}} seconds. -

        - -
        - -
        -

        - Timer initialised with some predefined start time.

        - -
        -

        - Following is the timer clock setting for the days, hours, minutes & seconds elapsed since January 1, 2013 (GMT-6) -

        (01 Jan 2013 06:00:00 GMT = 1357020000000 milliseconds)

        - <timer start-time="1357020000000">{{days}} days, {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds.</timer> + Timer i18n + +
        +

        + This markup <timer interval="1000" language="fr" >{{yearUnit}} will run the clock timer ticking every second. +

        +

        + You can use a controller variable as the language attribut or a string. If a scope variable is used, the value will be watched, that is to say if your app language changes, the change will also affects the timer. +

        +

        + Based on HumanizeDuration with more than 16 languages available. +

        + +

        Spanish

        +
        +

        Year max unit time : {{yearUnit}}

        +

        Hour max unit time: {{hourUnit}}

        +

        Second max unit time: {{secondUnit}}

        +
        + + +

        French

        +
        +

        Year max unit time : {{yearUnit}}

        +

        Hour max unit time: {{hourUnit}}

        +

        Second max unit time: {{secondUnit}}

        +
        + +

        Available units

        +
          +
        • secondUnit: 8 164 816 seconds
        • +
        • minuteUnit: 136 089 minutes, 16 seconds
        • +
        • hourUnit: 18 126 hours,9 minutes, 16 seconds
        • +
        • dayUnit: 755 days, 6 hours, 9 minutes, 16 seconds
        • +
        • monthUnit: 25 month, 5 days, 6 hours, 9 minutes, 16 seconds
        • +
        • yearUnit : 2 years, 1 month, 5 days, 6 hours, 9 minutes, 16 seconds
        • +
        +
        +
        +

        - {{days}} days, {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds. -

        - -
        - -
        -

        - Timer initialised with some predefined end time.

        - -
        -

        - Following is the countdown timer setting for the days, hours, minutes & seconds to January 1, 2015 (GMT-6) -

        (01 Jan 2015 06:00:00 GMT = 1420070400000 milliseconds)

        - <timer end-time="1420070400000">{{days}} days, {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds.</timer> + Timer with leading zero + +
        +

        + This markup <timer interval="1000">{{hhours}} hour{{hhoursS}}, {{mminutes}} + minute{{minutesS}}, {{sseconds}} second{{secondsS}}.</timer> will run the clock timer ticking every second with an additional zero at the beginning if unit is smaller than 10

        + +

        + {{hhours}} hour{{hoursS}}, {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} +

        + +
        +
        +

        - {{days}} days, {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds. -

        -
        - -
        -

        - Progressbar Timer

        - -
        -

        - Timer directive along with Twitter Bootstrap's Progressbar will set the timer - on Progressbar control.

        - <timer interval="1000"><div class="progress - progress-striped active"> <div class="bar" style="width: {{seconds}}%;"></div> - </div></timer> + Timer initialised with some predefined start time. +
        +

        + Following is the timer clock setting for the days, hours, minutes & seconds elapsed since January 1, {{currentYear}} (GMT-6) +

        (01 Jan {{currentYear}} 06:00:00 GMT = {{startTime}} milliseconds)

        + <timer start-time="{{startTime}}">{{days}} days, {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds.</timer> +

        + {{days}} days, {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds. +

        + +
        +
        +

        - -
        -
        -  
        -
        -
        -

        - - -
        - -
        -

        - Countdown Timer

        - -
        -

        - The countdown timer <timer interval="1000" countdown="100">{{countdown}}</timer> - will start its countdown from 100 until it reaches 0 by ticking every second

        + Timer initialised with some predefined end time. +
        +

        + Following is the countdown timer setting for the days, hours, minutes & seconds to January 1, {{endYear}} (GMT) +

        (01 Jan {{endYear}} 00:00:00 GMT = {{endTime}} milliseconds)

        + <timer end-time="{{endTime}}">{{days}} days, {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds.</timer> +

        + {{days}} days, {{hours}} hours, {{minutes}} minutes, {{seconds}} seconds. +

        +
        +
        +

        - {{countdown}} -

        - -
        - -
        -

        - Timer with autostart = false

        - -
        -

        - Click on the start button to start the timer. <timer autostart="false" interval="1000">{{seconds}}</timer>

        + Progressbar Timer + +
        +

        + Timer directive along with Twitter Bootstrap's Progressbar will set the timer + on Progressbar control.

        + + <timer countdown="30" interval="1000"><div class="progress + progress-striped active {{displayProgressActive}}"style="height: 30px;"> + + + Remaining time : {{countdown}} second{{secondsS}} ({{progressBar}}%). Activity? {{displayProgressActive}} + + + <div class="bar" style="min-width: 2em;width: {{progressBar}}%;"></div> + + + </div></timer> + + +

        + + Remaining time : {{countdown}} second{{secondsS}} ({{progressBar}}%). Activity? {{displayProgressActive}} +
        +
        + {{ progressBar }}% +
        +
        +
        +

        + + + <timer end-time="1451628000000" interval="1000"><div class="progress + progress-striped active {{displayProgressActive}}"style="height: 30px;"> + + + <div class="bar" style="min-width: 2em;width: {{progressBar}}%;"></div> + + + </div></timer> + +

        + + ({{progressBar}}%). Progress bar activity : {{displayProgressActive}} +
        +
        + {{ progressBar }}%
        +
        +
        +

        + + +
        +
        +

        - {{seconds}} -

        - -
        - + Countdown Timer -
        -

        Plural / Singular units

        +
        +

        + The countdown timer <timer interval="1000" countdown="100">{{countdown}}</timer> + will start its countdown from 100 until it reaches 0 by ticking every second

        -
        -

        - Two stopped countdown timers to illustrate how to handle pluralization of time units. - - <timer autostart="false" countdown="90061">{{days}} day{{daysS}}, {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}.</timer> - -

        - {{days}} day{{daysS}}, {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}. -

        - - <timer autostart="false" countdown="190061">{{days}} day{{daysS}}, {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}.</timer> - -

        - {{days}} day{{daysS}}, {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}. -

        -
        -
        - -
        -

        Countdown time display according to specified max-time-unit

        - -
        -

        - This markup will display countdown time in minute and seconds only. This attribute can be applied to regular clock timer as well. +

        + {{countdown}} +

        + +
        +
        +
        +

        + Timer with autostart = false

        + +
        +

        + Click on the start button to start the timer. <timer autostart="false" interval="1000">{{seconds}}</timer>

        +

        + {{seconds}} +

        + +
        +
        + +
        +

        Plural / Singular units

        + +
        +

        + Two stopped countdown timers to illustrate how to handle pluralization of time units. + + <timer autostart="false" countdown="90061">{{days}} day{{daysS}}, {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}.</timer> + +

        + {{days}} day{{daysS}}, {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}. +

        - <timer countdown="10041" max-time-unit="'minute'" interval="1000">{{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}}</timer> + <timer autostart="false" countdown="190061">{{days}} day{{daysS}}, {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}.</timer> +

        + {{days}} day{{daysS}}, {{hours}} hour{{hoursS}}, {{minutes}} minute{{minutesS}}, {{seconds}} second{{secondsS}}. +

        +
        +
        + +
        +

        Countdown time display according to specified max-time-unit

        + +
        +

        + This markup will display countdown time in minute and seconds only. This attribute can be applied to regular clock timer as well. + + <timer countdown="10041" max-time-unit="'minute'" interval="1000">{{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}}</timer> + +

        +

        countdown Time with max time unit option - year

        +

        + {{yyears}} year{{yearsS}}, {{mmonths}} month{{monthsS}}, {{ddays}} day{{daysS}}, {{hhours}} hour{{hoursS}}, {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} +

        +

        countdown Time with max time unit option - minute

        +

        + {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} +

        + +

        countdown Time with max time unit option - second

        +

        + {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} +

        + +

        countdown Time without max time unit option - minute

        +

        + {{ddays}} day{{daysS}}, {{hhours}} hour{{hoursS}}, {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} +

        +
        +
        + + +
        +

        Countdown Finished Callback

        + +
        +

        + A countdown timer that updates a value once the callback is reached + + <timer countdown="3" interval="1000" finish-callback="callbackTimer.finished()">{{seconds}} second{{secondsS}}</timer> + + +

        + {{seconds}} second{{secondsS}} +

        + Timer: {{callbackTimer.status}} + Callbacks: {{callbackTimer.callbackCount}} + +
        +
        + +
        +

        + Markup

        + +

        + Timer directive can be declared using following options. By default, it will display milliseconds inside + span tag. It can also take template string to display user-defined formats.

        + +
        +

        + <timer interval="1000" />

        +
        +
        +

        + <timer interval="1000">{{hours}} hours, {{minutes}} minutes, + {{seconds}} seconds, {{millis}} milliseconds.</timer>

        +
        +

        + Attributes

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + Name + + Required + + Default value +
        + interval + + false + + 1 millisecond +
        + autostart
        + Formerly called 'auto-start'. Please see this issue +
        + false + + true +
        + countdown + + false + +  
        + start-time + + false + starts the timer with predefined time (in milliseconds). +
        + end-time + + false + Sets the countdown based on predefined end time (in milliseconds). +
        + max-time-unit + + false + no default value. But you can give value, 'minute', 'second', or 'hour'. +
        + language + + false + 'en' for English. Please see supported languages. +
        +

        + Methods

        + +

        + Following DOM methods can be invoked to timer. Append to timer- for scope based + events when calling from AngularJS controllers.

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + Method name + + Description +
        + start + + Starts the timer +
        + stop + + Stops the timer +
        + clear + + Same as stop. But, without the event being triggered +
        + reset + + Resets a timer to its initial value, then clears + the timer. +
        + resume + + Resumes the timer. Will NOT reset the start time +
        + addCDSeconds + + Add seconds to running countdown +
        +

        + Events

        + + + + + + + + + + + + + + + + + + +
        + Event name + + Description +
        + timer-tick + + Tick event that gets emitted for every timer tick for specified interval. Please refer to Polling Timer + example for its usage. +
        + timer-stopped + + Tick event that gets emitted when the timer stops. Please refer to Single Timer + example for its usage. +
        +
        +
        +

        + Install using Bower

        +

        + bower install angular-timer

        -

        countdown Time with max time unit option - year

        -

        - {{yyears}} year{{yearsS}}, {{mmonths}} month{{monthsS}}, {{ddays}} day{{daysS}}, {{hhours}} hour{{hoursS}}, {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} -

        -

        countdown Time with max time unit option - minute

        -

        - {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} -

        - -

        countdown Time with max time unit option - second

        -

        - {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} -

        - -

        countdown Time without max time unit option - minute

        -

        - {{ddays}} day{{daysS}}, {{hhours}} hour{{hoursS}}, {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} -

        -
        - - -
        -

        - Markup

        - -

        - Timer directive can be declared using following options. By default, it will display milliseconds inside - span tag. It can also take template string to display user-defined formats.

        - -
        +
        +
        +

        + Contributions welcome!

        +

        - <timer interval="1000" />

        -
        -
        + We welcome any or all kinds of contributions! Please submit pull requests or create issues to contribute to this + project :)

        + +

        - <timer interval="1000">{{hours}} hours, {{minutes}} minutes, - {{seconds}} seconds, {{millis}} milliseconds.</timer>

        -
        -

        - Attributes

        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        - Name - - Required - - Default value -
        - interval - - false - - 1 millisecond -
        - autostart
        - Formerly called 'auto-start'. Please see this issue -
        - false - - true -
        - countdown - - false - -  
        - start-time - - false - starts the timer with predefined time (in milliseconds). -
        - end-time - - false - Sets the countdown based on predefined end time (in milliseconds). -
        - max-time-unit - - false - no default value. But you can give value, 'minute', 'second', or 'hour'. -
        -

        - Methods

        - -

        - Following DOM methods can be invoked to timer. Prepend to timer- for scope based - events when calling from AngularJS controllers.

        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        - Method name - - Description -
        - start - - Starts the timer -
        - stop - - Stops the timer -
        - clear - - Same as stop. But, without the event being triggered -
        - resume - - Resumes the timer. Will NOT reset the start time -
        - addCDSeconds - - Add seconds to running countdown -
        -

        - Events

        - - - - - - - - - - - - - - - - - - -
        - Event name - - Description -
        - timer-tick - - Tick event that gets emitted for every timer tick for specified interval. Please refer to Polling Timer - example for its usage. -
        - timer-stopped - - Tick event that gets emitted when the timer stops. Please refer to Single Timer - example for its usage. -
        - -
        -

        - Install using Bower

        -

        - bower install angular-timer -

        -
        -
        -

        - Contributions welcome!

        - -

        - We welcome any or all kinds of contributions! Please submit pull requests or create issues to contribute to this - project :)

        -
        -
        -

        - © Siddique Hameed 2013

        -
        + © Siddique Hameed 2013

        +
        - - diff --git a/navbar.html b/navbar.html index e6b9859..bc73405 100644 --- a/navbar.html +++ b/navbar.html @@ -19,6 +19,9 @@ Basic Example
      1. Clock Timer
      2. +
      3. +
      4. + Clock Timer with i18n
      5. Timer with start time
      6. @@ -34,7 +37,7 @@
      7. <timer/>