summaryrefslogtreecommitdiffstats
path: root/apps/it/static/js/ui/external
diff options
context:
space:
mode:
authorFilipp Lepalaan <f@230.to>2014-02-19 18:02:09 +0200
committerFilipp Lepalaan <f@230.to>2014-02-19 18:02:09 +0200
commit75ad7e4bd7d69243e7e5281c2642f00478fb072d (patch)
treeeaaaf360902d369f42d94778aac8803db7973ce0 /apps/it/static/js/ui/external
parentcfc7c3f52544af8a71d3fa3988a06fee200d2c24 (diff)
downloadpudding-75ad7e4bd7d69243e7e5281c2642f00478fb072d.tar.gz
pudding-75ad7e4bd7d69243e7e5281c2642f00478fb072d.tar.bz2
pudding-75ad7e4bd7d69243e7e5281c2642f00478fb072d.zip
Added tags, jquery UI
Diffstat (limited to 'apps/it/static/js/ui/external')
-rw-r--r--apps/it/static/js/ui/external/globalize.culture.de-DE.js81
-rw-r--r--apps/it/static/js/ui/external/globalize.culture.ja-JP.js100
-rw-r--r--apps/it/static/js/ui/external/globalize.js1585
-rw-r--r--apps/it/static/js/ui/external/jquery.mousewheel.js101
-rw-r--r--apps/it/static/js/ui/external/jshint.js4835
-rw-r--r--apps/it/static/js/ui/external/qunit.css244
-rw-r--r--apps/it/static/js/ui/external/qunit.js2152
7 files changed, 9098 insertions, 0 deletions
diff --git a/apps/it/static/js/ui/external/globalize.culture.de-DE.js b/apps/it/static/js/ui/external/globalize.culture.de-DE.js
new file mode 100644
index 0000000..5466bd7
--- /dev/null
+++ b/apps/it/static/js/ui/external/globalize.culture.de-DE.js
@@ -0,0 +1,81 @@
+/*
+ * Globalize Culture de-DE
+ *
+ * http://github.com/jquery/globalize
+ *
+ * Copyright Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * This file was generated by the Globalize Culture Generator
+ * Translation: bugs found in this file need to be fixed in the generator
+ */
+
+(function( window, undefined ) {
+
+var Globalize;
+
+if ( typeof require !== "undefined"
+ && typeof exports !== "undefined"
+ && typeof module !== "undefined" ) {
+ // Assume CommonJS
+ Globalize = require( "globalize" );
+} else {
+ // Global variable
+ Globalize = window.Globalize;
+}
+
+Globalize.addCultureInfo( "de-DE", "default", {
+ name: "de-DE",
+ englishName: "German (Germany)",
+ nativeName: "Deutsch (Deutschland)",
+ language: "de",
+ numberFormat: {
+ ",": ".",
+ ".": ",",
+ NaN: "n. def.",
+ negativeInfinity: "-unendlich",
+ positiveInfinity: "+unendlich",
+ percent: {
+ pattern: ["-n%","n%"],
+ ",": ".",
+ ".": ","
+ },
+ currency: {
+ pattern: ["-n $","n $"],
+ ",": ".",
+ ".": ",",
+ symbol: "€"
+ }
+ },
+ calendars: {
+ standard: {
+ "/": ".",
+ firstDay: 1,
+ days: {
+ names: ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],
+ namesAbbr: ["So","Mo","Di","Mi","Do","Fr","Sa"],
+ namesShort: ["So","Mo","Di","Mi","Do","Fr","Sa"]
+ },
+ months: {
+ names: ["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember",""],
+ namesAbbr: ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez",""]
+ },
+ AM: null,
+ PM: null,
+ eras: [{"name":"n. Chr.","start":null,"offset":0}],
+ patterns: {
+ d: "dd.MM.yyyy",
+ D: "dddd, d. MMMM yyyy",
+ t: "HH:mm",
+ T: "HH:mm:ss",
+ f: "dddd, d. MMMM yyyy HH:mm",
+ F: "dddd, d. MMMM yyyy HH:mm:ss",
+ M: "dd MMMM",
+ Y: "MMMM yyyy"
+ }
+ }
+ }
+});
+
+}( this ));
diff --git a/apps/it/static/js/ui/external/globalize.culture.ja-JP.js b/apps/it/static/js/ui/external/globalize.culture.ja-JP.js
new file mode 100644
index 0000000..a9469d7
--- /dev/null
+++ b/apps/it/static/js/ui/external/globalize.culture.ja-JP.js
@@ -0,0 +1,100 @@
+/*
+ * Globalize Culture ja-JP
+ *
+ * http://github.com/jquery/globalize
+ *
+ * Copyright Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * This file was generated by the Globalize Culture Generator
+ * Translation: bugs found in this file need to be fixed in the generator
+ */
+
+(function( window, undefined ) {
+
+var Globalize;
+
+if ( typeof require !== "undefined"
+ && typeof exports !== "undefined"
+ && typeof module !== "undefined" ) {
+ // Assume CommonJS
+ Globalize = require( "globalize" );
+} else {
+ // Global variable
+ Globalize = window.Globalize;
+}
+
+Globalize.addCultureInfo( "ja-JP", "default", {
+ name: "ja-JP",
+ englishName: "Japanese (Japan)",
+ nativeName: "日本語 (日本)",
+ language: "ja",
+ numberFormat: {
+ NaN: "NaN (非数値)",
+ negativeInfinity: "-∞",
+ positiveInfinity: "+∞",
+ percent: {
+ pattern: ["-n%","n%"]
+ },
+ currency: {
+ pattern: ["-$n","$n"],
+ decimals: 0,
+ symbol: "¥"
+ }
+ },
+ calendars: {
+ standard: {
+ days: {
+ names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],
+ namesAbbr: ["日","月","火","水","木","金","土"],
+ namesShort: ["日","月","火","水","木","金","土"]
+ },
+ months: {
+ names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""],
+ namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""]
+ },
+ AM: ["午前","午前","午前"],
+ PM: ["午後","午後","午後"],
+ eras: [{"name":"西暦","start":null,"offset":0}],
+ patterns: {
+ d: "yyyy/MM/dd",
+ D: "yyyy'年'M'月'd'日'",
+ t: "H:mm",
+ T: "H:mm:ss",
+ f: "yyyy'年'M'月'd'日' H:mm",
+ F: "yyyy'年'M'月'd'日' H:mm:ss",
+ M: "M'月'd'日'",
+ Y: "yyyy'年'M'月'"
+ }
+ },
+ Japanese: {
+ name: "Japanese",
+ days: {
+ names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],
+ namesAbbr: ["日","月","火","水","木","金","土"],
+ namesShort: ["日","月","火","水","木","金","土"]
+ },
+ months: {
+ names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""],
+ namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""]
+ },
+ AM: ["午前","午前","午前"],
+ PM: ["午後","午後","午後"],
+ eras: [{"name":"平成","start":null,"offset":1867},{"name":"昭和","start":-1812153600000,"offset":1911},{"name":"大正","start":-1357603200000,"offset":1925},{"name":"明治","start":60022080000,"offset":1988}],
+ twoDigitYearMax: 99,
+ patterns: {
+ d: "gg y/M/d",
+ D: "gg y'年'M'月'd'日'",
+ t: "H:mm",
+ T: "H:mm:ss",
+ f: "gg y'年'M'月'd'日' H:mm",
+ F: "gg y'年'M'月'd'日' H:mm:ss",
+ M: "M'月'd'日'",
+ Y: "gg y'年'M'月'"
+ }
+ }
+ }
+});
+
+}( this ));
diff --git a/apps/it/static/js/ui/external/globalize.js b/apps/it/static/js/ui/external/globalize.js
new file mode 100644
index 0000000..a38a326
--- /dev/null
+++ b/apps/it/static/js/ui/external/globalize.js
@@ -0,0 +1,1585 @@
+/*!
+ * Globalize
+ *
+ * http://github.com/jquery/globalize
+ *
+ * Copyright Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+
+(function( window, undefined ) {
+
+var Globalize,
+ // private variables
+ regexHex,
+ regexInfinity,
+ regexParseFloat,
+ regexTrim,
+ // private JavaScript utility functions
+ arrayIndexOf,
+ endsWith,
+ extend,
+ isArray,
+ isFunction,
+ isObject,
+ startsWith,
+ trim,
+ truncate,
+ zeroPad,
+ // private Globalization utility functions
+ appendPreOrPostMatch,
+ expandFormat,
+ formatDate,
+ formatNumber,
+ getTokenRegExp,
+ getEra,
+ getEraYear,
+ parseExact,
+ parseNegativePattern;
+
+// Global variable (Globalize) or CommonJS module (globalize)
+Globalize = function( cultureSelector ) {
+ return new Globalize.prototype.init( cultureSelector );
+};
+
+if ( typeof require !== "undefined" &&
+ typeof exports !== "undefined" &&
+ typeof module !== "undefined" ) {
+ // Assume CommonJS
+ module.exports = Globalize;
+} else {
+ // Export as global variable
+ window.Globalize = Globalize;
+}
+
+Globalize.cultures = {};
+
+Globalize.prototype = {
+ constructor: Globalize,
+ init: function( cultureSelector ) {
+ this.cultures = Globalize.cultures;
+ this.cultureSelector = cultureSelector;
+
+ return this;
+ }
+};
+Globalize.prototype.init.prototype = Globalize.prototype;
+
+// 1. When defining a culture, all fields are required except the ones stated as optional.
+// 2. Each culture should have a ".calendars" object with at least one calendar named "standard"
+// which serves as the default calendar in use by that culture.
+// 3. Each culture should have a ".calendar" object which is the current calendar being used,
+// it may be dynamically changed at any time to one of the calendars in ".calendars".
+Globalize.cultures[ "default" ] = {
+ // A unique name for the culture in the form <language code>-<country/region code>
+ name: "en",
+ // the name of the culture in the english language
+ englishName: "English",
+ // the name of the culture in its own language
+ nativeName: "English",
+ // whether the culture uses right-to-left text
+ isRTL: false,
+ // "language" is used for so-called "specific" cultures.
+ // For example, the culture "es-CL" means "Spanish, in Chili".
+ // It represents the Spanish-speaking culture as it is in Chili,
+ // which might have different formatting rules or even translations
+ // than Spanish in Spain. A "neutral" culture is one that is not
+ // specific to a region. For example, the culture "es" is the generic
+ // Spanish culture, which may be a more generalized version of the language
+ // that may or may not be what a specific culture expects.
+ // For a specific culture like "es-CL", the "language" field refers to the
+ // neutral, generic culture information for the language it is using.
+ // This is not always a simple matter of the string before the dash.
+ // For example, the "zh-Hans" culture is netural (Simplified Chinese).
+ // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage
+ // field is "zh-CHS", not "zh".
+ // This field should be used to navigate from a specific culture to it's
+ // more general, neutral culture. If a culture is already as general as it
+ // can get, the language may refer to itself.
+ language: "en",
+ // numberFormat defines general number formatting rules, like the digits in
+ // each grouping, the group separator, and how negative numbers are displayed.
+ numberFormat: {
+ // [negativePattern]
+ // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency,
+ // but is still defined as an array for consistency with them.
+ // negativePattern: one of "(n)|-n|- n|n-|n -"
+ pattern: [ "-n" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // symbol used for positive numbers
+ "+": "+",
+ // symbol used for negative numbers
+ "-": "-",
+ // symbol used for NaN (Not-A-Number)
+ "NaN": "NaN",
+ // symbol used for Negative Infinity
+ negativeInfinity: "-Infinity",
+ // symbol used for Positive Infinity
+ positiveInfinity: "Infinity",
+ percent: {
+ // [negativePattern, positivePattern]
+ // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
+ // positivePattern: one of "n %|n%|%n|% n"
+ pattern: [ "-n %", "n %" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // symbol used to represent a percentage
+ symbol: "%"
+ },
+ currency: {
+ // [negativePattern, positivePattern]
+ // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
+ // positivePattern: one of "$n|n$|$ n|n $"
+ pattern: [ "($n)", "$n" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // symbol used to represent currency
+ symbol: "$"
+ }
+ },
+ // calendars defines all the possible calendars used by this culture.
+ // There should be at least one defined with name "standard", and is the default
+ // calendar used by the culture.
+ // A calendar contains information about how dates are formatted, information about
+ // the calendar's eras, a standard set of the date formats,
+ // translations for day and month names, and if the calendar is not based on the Gregorian
+ // calendar, conversion functions to and from the Gregorian calendar.
+ calendars: {
+ standard: {
+ // name that identifies the type of calendar this is
+ name: "Gregorian_USEnglish",
+ // separator of parts of a date (e.g. "/" in 11/05/1955)
+ "/": "/",
+ // separator of parts of a time (e.g. ":" in 05:44 PM)
+ ":": ":",
+ // the first day of the week (0 = Sunday, 1 = Monday, etc)
+ firstDay: 0,
+ days: {
+ // full day names
+ names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
+ // abbreviated day names
+ namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
+ // shortest day names
+ namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ]
+ },
+ months: {
+ // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
+ names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ],
+ // abbreviated month names
+ namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ]
+ },
+ // AM and PM designators in one of these forms:
+ // The usual view, and the upper and lower case versions
+ // [ standard, lowercase, uppercase ]
+ // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
+ // null
+ AM: [ "AM", "am", "AM" ],
+ PM: [ "PM", "pm", "PM" ],
+ eras: [
+ // eras in reverse chronological order.
+ // name: the name of the era in this culture (e.g. A.D., C.E.)
+ // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
+ // offset: offset in years from gregorian calendar
+ {
+ "name": "A.D.",
+ "start": null,
+ "offset": 0
+ }
+ ],
+ // when a two digit year is given, it will never be parsed as a four digit
+ // year greater than this year (in the appropriate era for the culture)
+ // Set it as a full year (e.g. 2029) or use an offset format starting from
+ // the current year: "+19" would correspond to 2029 if the current year 2010.
+ twoDigitYearMax: 2029,
+ // set of predefined date and time patterns used by the culture
+ // these represent the format someone in this culture would expect
+ // to see given the portions of the date that are shown.
+ patterns: {
+ // short date pattern
+ d: "M/d/yyyy",
+ // long date pattern
+ D: "dddd, MMMM dd, yyyy",
+ // short time pattern
+ t: "h:mm tt",
+ // long time pattern
+ T: "h:mm:ss tt",
+ // long date, short time pattern
+ f: "dddd, MMMM dd, yyyy h:mm tt",
+ // long date, long time pattern
+ F: "dddd, MMMM dd, yyyy h:mm:ss tt",
+ // month/day pattern
+ M: "MMMM dd",
+ // month/year pattern
+ Y: "yyyy MMMM",
+ // S is a sortable format that does not vary by culture
+ S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
+ }
+ // optional fields for each calendar:
+ /*
+ monthsGenitive:
+ Same as months but used when the day preceeds the month.
+ Omit if the culture has no genitive distinction in month names.
+ For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
+ convert:
+ Allows for the support of non-gregorian based calendars. This convert object is used to
+ to convert a date to and from a gregorian calendar date to handle parsing and formatting.
+ The two functions:
+ fromGregorian( date )
+ Given the date as a parameter, return an array with parts [ year, month, day ]
+ corresponding to the non-gregorian based year, month, and day for the calendar.
+ toGregorian( year, month, day )
+ Given the non-gregorian year, month, and day, return a new Date() object
+ set to the corresponding date in the gregorian calendar.
+ */
+ }
+ },
+ // For localized strings
+ messages: {}
+};
+
+Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard;
+
+Globalize.cultures.en = Globalize.cultures[ "default" ];
+
+Globalize.cultureSelector = "en";
+
+//
+// private variables
+//
+
+regexHex = /^0x[a-f0-9]+$/i;
+regexInfinity = /^[+\-]?infinity$/i;
+regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/;
+regexTrim = /^\s+|\s+$/g;
+
+//
+// private JavaScript utility functions
+//
+
+arrayIndexOf = function( array, item ) {
+ if ( array.indexOf ) {
+ return array.indexOf( item );
+ }
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[i] === item ) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+endsWith = function( value, pattern ) {
+ return value.substr( value.length - pattern.length ) === pattern;
+};
+
+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" && !isFunction(target) ) {
+ target = {};
+ }
+
+ 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 && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && isArray(src) ? src : [];
+
+ } else {
+ clone = src && isObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+isArray = Array.isArray || function( obj ) {
+ return Object.prototype.toString.call( obj ) === "[object Array]";
+};
+
+isFunction = function( obj ) {
+ return Object.prototype.toString.call( obj ) === "[object Function]";
+};
+
+isObject = function( obj ) {
+ return Object.prototype.toString.call( obj ) === "[object Object]";
+};
+
+startsWith = function( value, pattern ) {
+ return value.indexOf( pattern ) === 0;
+};
+
+trim = function( value ) {
+ return ( value + "" ).replace( regexTrim, "" );
+};
+
+truncate = function( value ) {
+ if ( isNaN( value ) ) {
+ return NaN;
+ }
+ return Math[ value < 0 ? "ceil" : "floor" ]( value );
+};
+
+zeroPad = function( str, count, left ) {
+ var l;
+ for ( l = str.length; l < count; l += 1 ) {
+ str = ( left ? ("0" + str) : (str + "0") );
+ }
+ return str;
+};
+
+//
+// private Globalization utility functions
+//
+
+appendPreOrPostMatch = function( preMatch, strings ) {
+ // appends pre- and post- token match strings while removing escaped characters.
+ // Returns a single quote count which is used to determine if the token occurs
+ // in a string literal.
+ var quoteCount = 0,
+ escaped = false;
+ for ( var i = 0, il = preMatch.length; i < il; i++ ) {
+ var c = preMatch.charAt( i );
+ switch ( c ) {
+ case "\'":
+ if ( escaped ) {
+ strings.push( "\'" );
+ }
+ else {
+ quoteCount++;
+ }
+ escaped = false;
+ break;
+ case "\\":
+ if ( escaped ) {
+ strings.push( "\\" );
+ }
+ escaped = !escaped;
+ break;
+ default:
+ strings.push( c );
+ escaped = false;
+ break;
+ }
+ }
+ return quoteCount;
+};
+
+expandFormat = function( cal, format ) {
+ // expands unspecified or single character date formats into the full pattern.
+ format = format || "F";
+ var pattern,
+ patterns = cal.patterns,
+ len = format.length;
+ if ( len === 1 ) {
+ pattern = patterns[ format ];
+ if ( !pattern ) {
+ throw "Invalid date format string \'" + format + "\'.";
+ }
+ format = pattern;
+ }
+ else if ( len === 2 && format.charAt(0) === "%" ) {
+ // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
+ format = format.charAt( 1 );
+ }
+ return format;
+};
+
+formatDate = function( value, format, culture ) {
+ var cal = culture.calendar,
+ convert = cal.convert,
+ ret;
+
+ if ( !format || !format.length || format === "i" ) {
+ if ( culture && culture.name.length ) {
+ if ( convert ) {
+ // non-gregorian calendar, so we cannot use built-in toLocaleString()
+ ret = formatDate( value, cal.patterns.F, culture );
+ }
+ else {
+ var eraDate = new Date( value.getTime() ),
+ era = getEra( value, cal.eras );
+ eraDate.setFullYear( getEraYear(value, cal, era) );
+ ret = eraDate.toLocaleString();
+ }
+ }
+ else {
+ ret = value.toString();
+ }
+ return ret;
+ }
+
+ var eras = cal.eras,
+ sortable = format === "s";
+ format = expandFormat( cal, format );
+
+ // Start with an empty string
+ ret = [];
+ var hour,
+ zeros = [ "0", "00", "000" ],
+ foundDay,
+ checkedDay,
+ dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
+ quoteCount = 0,
+ tokenRegExp = getTokenRegExp(),
+ converted;
+
+ function padZeros( num, c ) {
+ var r, s = num + "";
+ if ( c > 1 && s.length < c ) {
+ r = ( zeros[c - 2] + s);
+ return r.substr( r.length - c, c );
+ }
+ else {
+ r = s;
+ }
+ return r;
+ }
+
+ function hasDay() {
+ if ( foundDay || checkedDay ) {
+ return foundDay;
+ }
+ foundDay = dayPartRegExp.test( format );
+ checkedDay = true;
+ return foundDay;
+ }
+
+ function getPart( date, part ) {
+ if ( converted ) {
+ return converted[ part ];
+ }
+ switch ( part ) {
+ case 0:
+ return date.getFullYear();
+ case 1:
+ return date.getMonth();
+ case 2:
+ return date.getDate();
+ default:
+ throw "Invalid part value " + part;
+ }
+ }
+
+ if ( !sortable && convert ) {
+ converted = convert.fromGregorian( value );
+ }
+
+ for ( ; ; ) {
+ // Save the current index
+ var index = tokenRegExp.lastIndex,
+ // Look for the next pattern
+ ar = tokenRegExp.exec( format );
+
+ // Append the text before the pattern (or the end of the string if not found)
+ var preMatch = format.slice( index, ar ? ar.index : format.length );
+ quoteCount += appendPreOrPostMatch( preMatch, ret );
+
+ if ( !ar ) {
+ break;
+ }
+
+ // do not replace any matches that occur inside a string literal.
+ if ( quoteCount % 2 ) {
+ ret.push( ar[0] );
+ continue;
+ }
+
+ var current = ar[ 0 ],
+ clength = current.length;
+
+ switch ( current ) {
+ case "ddd":
+ //Day of the week, as a three-letter abbreviation
+ case "dddd":
+ // Day of the week, using the full name
+ var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names;
+ ret.push( names[value.getDay()] );
+ break;
+ case "d":
+ // Day of month, without leading zero for single-digit days
+ case "dd":
+ // Day of month, with leading zero for single-digit days
+ foundDay = true;
+ ret.push(
+ padZeros( getPart(value, 2), clength )
+ );
+ break;
+ case "MMM":
+ // Month, as a three-letter abbreviation
+ case "MMMM":
+ // Month, using the full name
+ var part = getPart( value, 1 );
+ ret.push(
+ ( cal.monthsGenitive && hasDay() ) ?
+ ( cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] ) :
+ ( cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] )
+ );
+ break;
+ case "M":
+ // Month, as digits, with no leading zero for single-digit months
+ case "MM":
+ // Month, as digits, with leading zero for single-digit months
+ ret.push(
+ padZeros( getPart(value, 1) + 1, clength )
+ );
+ break;
+ case "y":
+ // Year, as two digits, but with no leading zero for years less than 10
+ case "yy":
+ // Year, as two digits, with leading zero for years less than 10
+ case "yyyy":
+ // Year represented by four full digits
+ part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable );
+ if ( clength < 4 ) {
+ part = part % 100;
+ }
+ ret.push(
+ padZeros( part, clength )
+ );
+ break;
+ case "h":
+ // Hours with no leading zero for single-digit hours, using 12-hour clock
+ case "hh":
+ // Hours with leading zero for single-digit hours, using 12-hour clock
+ hour = value.getHours() % 12;
+ if ( hour === 0 ) hour = 12;
+ ret.push(
+ padZeros( hour, clength )
+ );
+ break;
+ case "H":
+ // Hours with no leading zero for single-digit hours, using 24-hour clock
+ case "HH":
+ // Hours with leading zero for single-digit hours, using 24-hour clock
+ ret.push(
+ padZeros( value.getHours(), clength )
+ );
+ break;
+ case "m":
+ // Minutes with no leading zero for single-digit minutes
+ case "mm":
+ // Minutes with leading zero for single-digit minutes
+ ret.push(
+ padZeros( value.getMinutes(), clength )
+ );
+ break;
+ case "s":
+ // Seconds with no leading zero for single-digit seconds
+ case "ss":
+ // Seconds with leading zero for single-digit seconds
+ ret.push(
+ padZeros( value.getSeconds(), clength )
+ );
+ break;
+ case "t":
+ // One character am/pm indicator ("a" or "p")
+ case "tt":
+ // Multicharacter am/pm indicator
+ part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " );
+ ret.push( clength === 1 ? part.charAt(0) : part );
+ break;
+ case "f":
+ // Deciseconds
+ case "ff":
+ // Centiseconds
+ case "fff":
+ // Milliseconds
+ ret.push(
+ padZeros( value.getMilliseconds(), 3 ).substr( 0, clength )
+ );
+ break;
+ case "z":
+ // Time zone offset, no leading zero
+ case "zz":
+ // Time zone offset with leading zero
+ hour = value.getTimezoneOffset() / 60;
+ ret.push(
+ ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength )
+ );
+ break;
+ case "zzz":
+ // Time zone offset with leading zero
+ hour = value.getTimezoneOffset() / 60;
+ ret.push(
+ ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) +
+ // Hard coded ":" separator, rather than using cal.TimeSeparator
+ // Repeated here for consistency, plus ":" was already assumed in date parsing.
+ ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 )
+ );
+ break;
+ case "g":
+ case "gg":
+ if ( cal.eras ) {
+ ret.push(
+ cal.eras[ getEra(value, eras) ].name
+ );
+ }
+ break;
+ case "/":
+ ret.push( cal["/"] );
+ break;
+ default:
+ throw "Invalid date format pattern \'" + current + "\'.";
+ }
+ }
+ return ret.join( "" );
+};
+
+// formatNumber
+(function() {
+ var expandNumber;
+
+ expandNumber = function( number, precision, formatInfo ) {
+ var groupSizes = formatInfo.groupSizes,
+ curSize = groupSizes[ 0 ],
+ curGroupIndex = 1,
+ factor = Math.pow( 10, precision ),
+ rounded = Math.round( number * factor ) / factor;
+
+ if ( !isFinite(rounded) ) {
+ rounded = number;
+ }
+ number = rounded;
+
+ var numberString = number+"",
+ right = "",
+ split = numberString.split( /e/i ),
+ exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0;
+ numberString = split[ 0 ];
+ split = numberString.split( "." );
+ numberString = split[ 0 ];
+ right = split.length > 1 ? split[ 1 ] : "";
+
+ if ( exponent > 0 ) {
+ right = zeroPad( right, exponent, false );
+ numberString += right.slice( 0, exponent );
+ right = right.substr( exponent );
+ }
+ else if ( exponent < 0 ) {
+ exponent = -exponent;
+ numberString = zeroPad( numberString, exponent + 1, true );
+ right = numberString.slice( -exponent, numberString.length ) + right;
+ numberString = numberString.slice( 0, -exponent );
+ }
+
+ if ( precision > 0 ) {
+ right = formatInfo[ "." ] +
+ ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) );
+ }
+ else {
+ right = "";
+ }
+
+ var stringIndex = numberString.length - 1,
+ sep = formatInfo[ "," ],
+ ret = "";
+
+ while ( stringIndex >= 0 ) {
+ if ( curSize === 0 || curSize > stringIndex ) {
+ return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right );
+ }
+ ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" );
+
+ stringIndex -= curSize;
+
+ if ( curGroupIndex < groupSizes.length ) {
+ curSize = groupSizes[ curGroupIndex ];
+ curGroupIndex++;
+ }
+ }
+
+ return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right;
+ };
+
+ formatNumber = function( value, format, culture ) {
+ if ( !isFinite(value) ) {
+ if ( value === Infinity ) {
+ return culture.numberFormat.positiveInfinity;
+ }
+ if ( value === -Infinity ) {
+ return culture.numberFormat.negativeInfinity;
+ }
+ return culture.numberFormat.NaN;
+ }
+ if ( !format || format === "i" ) {
+ return culture.name.length ? value.toLocaleString() : value.toString();
+ }
+ format = format || "D";
+
+ var nf = culture.numberFormat,
+ number = Math.abs( value ),
+ precision = -1,
+ pattern;
+ if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 );
+
+ var current = format.charAt( 0 ).toUpperCase(),
+ formatInfo;
+
+ switch ( current ) {
+ case "D":
+ pattern = "n";
+ number = truncate( number );
+ if ( precision !== -1 ) {
+ number = zeroPad( "" + number, precision, true );
+ }
+ if ( value < 0 ) number = "-" + number;
+ break;
+ case "N":
+ formatInfo = nf;
+ /* falls through */
+ case "C":
+ formatInfo = formatInfo || nf.currency;
+ /* falls through */
+ case "P":
+ formatInfo = formatInfo || nf.percent;
+ pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" );
+ if ( precision === -1 ) precision = formatInfo.decimals;
+ number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo );
+ break;
+ default:
+ throw "Bad number format specifier: " + current;
+ }
+
+ var patternParts = /n|\$|-|%/g,
+ ret = "";
+ for ( ; ; ) {
+ var index = patternParts.lastIndex,
+ ar = patternParts.exec( pattern );
+
+ ret += pattern.slice( index, ar ? ar.index : pattern.length );
+
+ if ( !ar ) {
+ break;
+ }
+
+ switch ( ar[0] ) {
+ case "n":
+ ret += number;
+ break;
+ case "$":
+ ret += nf.currency.symbol;
+ break;
+ case "-":
+ // don't make 0 negative
+ if ( /[1-9]/.test(number) ) {
+ ret += nf[ "-" ];
+ }
+ break;
+ case "%":
+ ret += nf.percent.symbol;
+ break;
+ }
+ }
+
+ return ret;
+ };
+
+}());
+
+getTokenRegExp = function() {
+ // regular expression for matching date and time tokens in format strings.
+ return (/\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g);
+};
+
+getEra = function( date, eras ) {
+ if ( !eras ) return 0;
+ var start, ticks = date.getTime();
+ for ( var i = 0, l = eras.length; i < l; i++ ) {
+ start = eras[ i ].start;
+ if ( start === null || ticks >= start ) {
+ return i;
+ }
+ }
+ return 0;
+};
+
+getEraYear = function( date, cal, era, sortable ) {
+ var year = date.getFullYear();
+ if ( !sortable && cal.eras ) {
+ // convert normal gregorian year to era-shifted gregorian
+ // year by subtracting the era offset
+ year -= cal.eras[ era ].offset;
+ }
+ return year;
+};
+
+// parseExact
+(function() {
+ var expandYear,
+ getDayIndex,
+ getMonthIndex,
+ getParseRegExp,
+ outOfRange,
+ toUpper,
+ toUpperArray;
+
+ expandYear = function( cal, year ) {
+ // expands 2-digit year into 4 digits.
+ if ( year < 100 ) {
+ var now = new Date(),
+ era = getEra( now ),
+ curr = getEraYear( now, cal, era ),
+ twoDigitYearMax = cal.twoDigitYearMax;
+ twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax;
+ year += curr - ( curr % 100 );
+ if ( year > twoDigitYearMax ) {
+ year -= 100;
+ }
+ }
+ return year;
+ };
+
+ getDayIndex = function ( cal, value, abbr ) {
+ var ret,
+ days = cal.days,
+ upperDays = cal._upperDays;
+ if ( !upperDays ) {
+ cal._upperDays = upperDays = [
+ toUpperArray( days.names ),
+ toUpperArray( days.namesAbbr ),
+ toUpperArray( days.namesShort )
+ ];
+ }
+ value = toUpper( value );
+ if ( abbr ) {
+ ret = arrayIndexOf( upperDays[1], value );
+ if ( ret === -1 ) {
+ ret = arrayIndexOf( upperDays[2], value );
+ }
+ }
+ else {
+ ret = arrayIndexOf( upperDays[0], value );
+ }
+ return ret;
+ };
+
+ getMonthIndex = function( cal, value, abbr ) {
+ var months = cal.months,
+ monthsGen = cal.monthsGenitive || cal.months,
+ upperMonths = cal._upperMonths,
+ upperMonthsGen = cal._upperMonthsGen;
+ if ( !upperMonths ) {
+ cal._upperMonths = upperMonths = [
+ toUpperArray( months.names ),
+ toUpperArray( months.namesAbbr )
+ ];
+ cal._upperMonthsGen = upperMonthsGen = [
+ toUpperArray( monthsGen.names ),
+ toUpperArray( monthsGen.namesAbbr )
+ ];
+ }
+ value = toUpper( value );
+ var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value );
+ if ( i < 0 ) {
+ i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value );
+ }
+ return i;
+ };
+
+ getParseRegExp = function( cal, format ) {
+ // converts a format string into a regular expression with groups that
+ // can be used to extract date fields from a date string.
+ // check for a cached parse regex.
+ var re = cal._parseRegExp;
+ if ( !re ) {
+ cal._parseRegExp = re = {};
+ }
+ else {
+ var reFormat = re[ format ];
+ if ( reFormat ) {
+ return reFormat;
+ }
+ }
+
+ // expand single digit formats, then escape regular expression characters.
+ var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ),
+ regexp = [ "^" ],
+ groups = [],
+ index = 0,
+ quoteCount = 0,
+ tokenRegExp = getTokenRegExp(),
+ match;
+
+ // iterate through each date token found.
+ while ( (match = tokenRegExp.exec(expFormat)) !== null ) {
+ var preMatch = expFormat.slice( index, match.index );
+ index = tokenRegExp.lastIndex;
+
+ // don't replace any matches that occur inside a string literal.
+ quoteCount += appendPreOrPostMatch( preMatch, regexp );
+ if ( quoteCount % 2 ) {
+ regexp.push( match[0] );
+ continue;
+ }
+
+ // add a regex group for the token.
+ var m = match[ 0 ],
+ len = m.length,
+ add;
+ switch ( m ) {
+ case "dddd": case "ddd":
+ case "MMMM": case "MMM":
+ case "gg": case "g":
+ add = "(\\D+)";
+ break;
+ case "tt": case "t":
+ add = "(\\D*)";
+ break;
+ case "yyyy":
+ case "fff":
+ case "ff":
+ case "f":
+ add = "(\\d{" + len + "})";
+ break;
+ case "dd": case "d":
+ case "MM": case "M":
+ case "yy": case "y":
+ case "HH": case "H":
+ case "hh": case "h":
+ case "mm": case "m":
+ case "ss": case "s":
+ add = "(\\d\\d?)";
+ break;
+ case "zzz":
+ add = "([+-]?\\d\\d?:\\d{2})";
+ break;
+ case "zz": case "z":
+ add = "([+-]?\\d\\d?)";
+ break;
+ case "/":
+ add = "(\\/)";
+ break;
+ default:
+ throw "Invalid date format pattern \'" + m + "\'.";
+ }
+ if ( add ) {
+ regexp.push( add );
+ }
+ groups.push( match[0] );
+ }
+ appendPreOrPostMatch( expFormat.slice(index), regexp );
+ regexp.push( "$" );
+
+ // allow whitespace to differ when matching formats.
+ var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ),
+ parseRegExp = { "regExp": regexpStr, "groups": groups };
+
+ // cache the regex for this format.
+ return re[ format ] = parseRegExp;
+ };
+
+ outOfRange = function( value, low, high ) {
+ return value < low || value > high;
+ };
+
+ toUpper = function( value ) {
+ // "he-IL" has non-breaking space in weekday names.
+ return value.split( "\u00A0" ).join( " " ).toUpperCase();
+ };
+
+ toUpperArray = function( arr ) {
+ var results = [];
+ for ( var i = 0, l = arr.length; i < l; i++ ) {
+ results[ i ] = toUpper( arr[i] );
+ }
+ return results;
+ };
+
+ parseExact = function( value, format, culture ) {
+ // try to parse the date string by matching against the format string
+ // while using the specified culture for date field names.
+ value = trim( value );
+ var cal = culture.calendar,
+ // convert date formats into regular expressions with groupings.
+ // use the regexp to determine the input format and extract the date fields.
+ parseInfo = getParseRegExp( cal, format ),
+ match = new RegExp( parseInfo.regExp ).exec( value );
+ if ( match === null ) {
+ return null;
+ }
+ // found a date format that matches the input.
+ var groups = parseInfo.groups,
+ era = null, year = null, month = null, date = null, weekDay = null,
+ hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
+ pmHour = false;
+ // iterate the format groups to extract and set the date fields.
+ for ( var j = 0, jl = groups.length; j < jl; j++ ) {
+ var matchGroup = match[ j + 1 ];
+ if ( matchGroup ) {
+ var current = groups[ j ],
+ clength = current.length,
+ matchInt = parseInt( matchGroup, 10 );
+ switch ( current ) {
+ case "dd": case "d":
+ // Day of month.
+ date = matchInt;
+ // check that date is generally in valid range, also checking overflow below.
+ if ( outOfRange(date, 1, 31) ) return null;
+ break;
+ case "MMM": case "MMMM":
+ month = getMonthIndex( cal, matchGroup, clength === 3 );
+ if ( outOfRange(month, 0, 11) ) return null;
+ break;
+ case "M": case "MM":
+ // Month.
+ month = matchInt - 1;
+ if ( outOfRange(month, 0, 11) ) return null;
+ break;
+ case "y": case "yy":
+ case "yyyy":
+ year = clength < 4 ? expandYear( cal, matchInt ) : matchInt;
+ if ( outOfRange(year, 0, 9999) ) return null;
+ break;
+ case "h": case "hh":
+ // Hours (12-hour clock).
+ hour = matchInt;
+ if ( hour === 12 ) hour = 0;
+ if ( outOfRange(hour, 0, 11) ) return null;
+ break;
+ case "H": case "HH":
+ // Hours (24-hour clock).
+ hour = matchInt;
+ if ( outOfRange(hour, 0, 23) ) return null;
+ break;
+ case "m": case "mm":
+ // Minutes.
+ min = matchInt;
+ if ( outOfRange(min, 0, 59) ) return null;
+ break;
+ case "s": case "ss":
+ // Seconds.
+ sec = matchInt;
+ if ( outOfRange(sec, 0, 59) ) return null;
+ break;
+ case "tt": case "t":
+ // AM/PM designator.
+ // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
+ // the AM tokens. If not, fail the parse for this format.
+ pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] );
+ if (
+ !pmHour && (
+ !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] )
+ )
+ ) return null;
+ break;
+ case "f":
+ // Deciseconds.
+ case "ff":
+ // Centiseconds.
+ case "fff":
+ // Milliseconds.
+ msec = matchInt * Math.pow( 10, 3 - clength );
+ if ( outOfRange(msec, 0, 999) ) return null;
+ break;
+ case "ddd":
+ // Day of week.
+ case "dddd":
+ // Day of week.
+ weekDay = getDayIndex( cal, matchGroup, clength === 3 );
+ if ( outOfRange(weekDay, 0, 6) ) return null;
+ break;
+ case "zzz":
+ // Time zone offset in +/- hours:min.
+ var offsets = matchGroup.split( /:/ );
+ if ( offsets.length !== 2 ) return null;
+ hourOffset = parseInt( offsets[0], 10 );
+ if ( outOfRange(hourOffset, -12, 13) ) return null;
+ var minOffset = parseInt( offsets[1], 10 );
+ if ( outOfRange(minOffset, 0, 59) ) return null;
+ tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset );
+ break;
+ case "z": case "zz":
+ // Time zone offset in +/- hours.
+ hourOffset = matchInt;
+ if ( outOfRange(hourOffset, -12, 13) ) return null;
+ tzMinOffset = hourOffset * 60;
+ break;
+ case "g": case "gg":
+ var eraName = matchGroup;
+ if ( !eraName || !cal.eras ) return null;
+ eraName = trim( eraName.toLowerCase() );
+ for ( var i = 0, l = cal.eras.length; i < l; i++ ) {
+ if ( eraName === cal.eras[i].name.toLowerCase() ) {
+ era = i;
+ break;
+ }
+ }
+ // could not find an era with that name
+ if ( era === null ) return null;
+ break;
+ }
+ }
+ }
+ var result = new Date(), defaultYear, convert = cal.convert;
+ defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear();
+ if ( year === null ) {
+ year = defaultYear;
+ }
+ else if ( cal.eras ) {
+ // year must be shifted to normal gregorian year
+ // but not if year was not specified, its already normal gregorian
+ // per the main if clause above.
+ year += cal.eras[( era || 0 )].offset;
+ }
+ // set default day and month to 1 and January, so if unspecified, these are the defaults
+ // instead of the current day/month.
+ if ( month === null ) {
+ month = 0;
+ }
+ if ( date === null ) {
+ date = 1;
+ }
+ // now have year, month, and date, but in the culture's calendar.
+ // convert to gregorian if necessary
+ if ( convert ) {
+ result = convert.toGregorian( year, month, date );
+ // conversion failed, must be an invalid match
+ if ( result === null ) return null;
+ }
+ else {
+ // have to set year, month and date together to avoid overflow based on current date.
+ result.setFullYear( year, month, date );
+ // check to see if date overflowed for specified month (only checked 1-31 above).
+ if ( result.getDate() !== date ) return null;
+ // invalid day of week.
+ if ( weekDay !== null && result.getDay() !== weekDay ) {
+ return null;
+ }
+ }
+ // if pm designator token was found make sure the hours fit the 24-hour clock.
+ if ( pmHour && hour < 12 ) {
+ hour += 12;
+ }
+ result.setHours( hour, min, sec, msec );
+ if ( tzMinOffset !== null ) {
+ // adjust timezone to utc before applying local offset.
+ var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() );
+ // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours
+ // to ensure both these fields will not exceed this range. adjustedMin will range
+ // somewhere between -1440 and 1500, so we only need to split this into hours.
+ result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 );
+ }
+ return result;
+ };
+}());
+
+parseNegativePattern = function( value, nf, negativePattern ) {
+ var neg = nf[ "-" ],
+ pos = nf[ "+" ],
+ ret;
+ switch ( negativePattern ) {
+ case "n -":
+ neg = " " + neg;
+ pos = " " + pos;
+ /* falls through */
+ case "n-":
+ if ( endsWith(value, neg) ) {
+ ret = [ "-", value.substr(0, value.length - neg.length) ];
+ }
+ else if ( endsWith(value, pos) ) {
+ ret = [ "+", value.substr(0, value.length - pos.length) ];
+ }
+ break;
+ case "- n":
+ neg += " ";
+ pos += " ";
+ /* falls through */
+ case "-n":
+ if ( startsWith(value, neg) ) {
+ ret = [ "-", value.substr(neg.length) ];
+ }
+ else if ( startsWith(value, pos) ) {
+ ret = [ "+", value.substr(pos.length) ];
+ }
+ break;
+ case "(n)":
+ if ( startsWith(value, "(") && endsWith(value, ")") ) {
+ ret = [ "-", value.substr(1, value.length - 2) ];
+ }
+ break;
+ }
+ return ret || [ "", value ];
+};
+
+//
+// public instance functions
+//
+
+Globalize.prototype.findClosestCulture = function( cultureSelector ) {
+ return Globalize.findClosestCulture.call( this, cultureSelector );
+};
+
+Globalize.prototype.format = function( value, format, cultureSelector ) {
+ return Globalize.format.call( this, value, format, cultureSelector );
+};
+
+Globalize.prototype.localize = function( key, cultureSelector ) {
+ return Globalize.localize.call( this, key, cultureSelector );
+};
+
+Globalize.prototype.parseInt = function( value, radix, cultureSelector ) {
+ return Globalize.parseInt.call( this, value, radix, cultureSelector );
+};
+
+Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) {
+ return Globalize.parseFloat.call( this, value, radix, cultureSelector );
+};
+
+Globalize.prototype.culture = function( cultureSelector ) {
+ return Globalize.culture.call( this, cultureSelector );
+};
+
+//
+// public singleton functions
+//
+
+Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) {
+
+ var base = {},
+ isNew = false;
+
+ if ( typeof cultureName !== "string" ) {
+ // cultureName argument is optional string. If not specified, assume info is first
+ // and only argument. Specified info deep-extends current culture.
+ info = cultureName;
+ cultureName = this.culture().name;
+ base = this.cultures[ cultureName ];
+ } else if ( typeof baseCultureName !== "string" ) {
+ // baseCultureName argument is optional string. If not specified, assume info is second
+ // argument. Specified info deep-extends specified culture.
+ // If specified culture does not exist, create by deep-extending default
+ info = baseCultureName;
+ isNew = ( this.cultures[ cultureName ] == null );
+ base = this.cultures[ cultureName ] || this.cultures[ "default" ];
+ } else {
+ // cultureName and baseCultureName specified. Assume a new culture is being created
+ // by deep-extending an specified base culture
+ isNew = true;
+ base = this.cultures[ baseCultureName ];
+ }
+
+ this.cultures[ cultureName ] = extend(true, {},
+ base,
+ info
+ );
+ // Make the standard calendar the current culture if it's a new culture
+ if ( isNew ) {
+ this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard;
+ }
+};
+
+Globalize.findClosestCulture = function( name ) {
+ var match;
+ if ( !name ) {
+ return this.findClosestCulture( this.cultureSelector ) || this.cultures[ "default" ];
+ }
+ if ( typeof name === "string" ) {
+ name = name.split( "," );
+ }
+ if ( isArray(name) ) {
+ var lang,
+ cultures = this.cultures,
+ list = name,
+ i, l = list.length,
+ prioritized = [];
+ for ( i = 0; i < l; i++ ) {
+ name = trim( list[i] );
+ var pri, parts = name.split( ";" );
+ lang = trim( parts[0] );
+ if ( parts.length === 1 ) {
+ pri = 1;
+ }
+ else {
+ name = trim( parts[1] );
+ if ( name.indexOf("q=") === 0 ) {
+ name = name.substr( 2 );
+ pri = parseFloat( name );
+ pri = isNaN( pri ) ? 0 : pri;
+ }
+ else {
+ pri = 1;
+ }
+ }
+ prioritized.push({ lang: lang, pri: pri });
+ }
+ prioritized.sort(function( a, b ) {
+ if ( a.pri < b.pri ) {
+ return 1;
+ } else if ( a.pri > b.pri ) {
+ return -1;
+ }
+ return 0;
+ });
+ // exact match
+ for ( i = 0; i < l; i++ ) {
+ lang = prioritized[ i ].lang;
+ match = cultures[ lang ];
+ if ( match ) {
+ return match;
+ }
+ }
+
+ // neutral language match
+ for ( i = 0; i < l; i++ ) {
+ lang = prioritized[ i ].lang;
+ do {
+ var index = lang.lastIndexOf( "-" );
+ if ( index === -1 ) {
+ break;
+ }
+ // strip off the last part. e.g. en-US => en
+ lang = lang.substr( 0, index );
+ match = cultures[ lang ];
+ if ( match ) {
+ return match;
+ }
+ }
+ while ( 1 );
+ }
+
+ // last resort: match first culture using that language
+ for ( i = 0; i < l; i++ ) {
+ lang = prioritized[ i ].lang;
+ for ( var cultureKey in cultures ) {
+ var culture = cultures[ cultureKey ];
+ if ( culture.language === lang ) {
+ return culture;
+ }
+ }
+ }
+ }
+ else if ( typeof name === "object" ) {
+ return name;
+ }
+ return match || null;
+};
+
+Globalize.format = function( value, format, cultureSelector ) {
+ var culture = this.findClosestCulture( cultureSelector );
+ if ( value instanceof Date ) {
+ value = formatDate( value, format, culture );
+ }
+ else if ( typeof value === "number" ) {
+ value = formatNumber( value, format, culture );
+ }
+ return value;
+};
+
+Globalize.localize = function( key, cultureSelector ) {
+ return this.findClosestCulture( cultureSelector ).messages[ key ] ||
+ this.cultures[ "default" ].messages[ key ];
+};
+
+Globalize.parseDate = function( value, formats, culture ) {
+ culture = this.findClosestCulture( culture );
+
+ var date, prop, patterns;
+ if ( formats ) {
+ if ( typeof formats === "string" ) {
+ formats = [ formats ];
+ }
+ if ( formats.length ) {
+ for ( var i = 0, l = formats.length; i < l; i++ ) {
+ var format = formats[ i ];
+ if ( format ) {
+ date = parseExact( value, format, culture );
+ if ( date ) {
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ patterns = culture.calendar.patterns;
+ for ( prop in patterns ) {
+ date = parseExact( value, patterns[prop], culture );
+ if ( date ) {
+ break;
+ }
+ }
+ }
+
+ return date || null;
+};
+
+Globalize.parseInt = function( value, radix, cultureSelector ) {
+ return truncate( Globalize.parseFloat(value, radix, cultureSelector) );
+};
+
+Globalize.parseFloat = function( value, radix, cultureSelector ) {
+ // radix argument is optional
+ if ( typeof radix !== "number" ) {
+ cultureSelector = radix;
+ radix = 10;
+ }
+
+ var culture = this.findClosestCulture( cultureSelector );
+ var ret = NaN,
+ nf = culture.numberFormat;
+
+ if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) {
+ // remove currency symbol
+ value = value.replace( culture.numberFormat.currency.symbol, "" );
+ // replace decimal seperator
+ value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] );
+ }
+
+ //Remove percentage character from number string before parsing
+ if ( value.indexOf(culture.numberFormat.percent.symbol) > -1){
+ value = value.replace( culture.numberFormat.percent.symbol, "" );
+ }
+
+ // remove spaces: leading, trailing and between - and number. Used for negative currency pt-BR
+ value = value.replace( / /g, "" );
+
+ // allow infinity or hexidecimal
+ if ( regexInfinity.test(value) ) {
+ ret = parseFloat( value );
+ }
+ else if ( !radix && regexHex.test(value) ) {
+ ret = parseInt( value, 16 );
+ }
+ else {
+
+ // determine sign and number
+ var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ),
+ sign = signInfo[ 0 ],
+ num = signInfo[ 1 ];
+
+ // #44 - try parsing as "(n)"
+ if ( sign === "" && nf.pattern[0] !== "(n)" ) {
+ signInfo = parseNegativePattern( value, nf, "(n)" );
+ sign = signInfo[ 0 ];
+ num = signInfo[ 1 ];
+ }
+
+ // try parsing as "-n"
+ if ( sign === "" && nf.pattern[0] !== "-n" ) {
+ signInfo = parseNegativePattern( value, nf, "-n" );
+ sign = signInfo[ 0 ];
+ num = signInfo[ 1 ];
+ }
+
+ sign = sign || "+";
+
+ // determine exponent and number
+ var exponent,
+ intAndFraction,
+ exponentPos = num.indexOf( "e" );
+ if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" );
+ if ( exponentPos < 0 ) {
+ intAndFraction = num;
+ exponent = null;
+ }
+ else {
+ intAndFraction = num.substr( 0, exponentPos );
+ exponent = num.substr( exponentPos + 1 );
+ }
+ // determine decimal position
+ var integer,
+ fraction,
+ decSep = nf[ "." ],
+ decimalPos = intAndFraction.indexOf( decSep );
+ if ( decimalPos < 0 ) {
+ integer = intAndFraction;
+ fraction = null;
+ }
+ else {
+ integer = intAndFraction.substr( 0, decimalPos );
+ fraction = intAndFraction.substr( decimalPos + decSep.length );
+ }
+ // handle groups (e.g. 1,000,000)
+ var groupSep = nf[ "," ];
+ integer = integer.split( groupSep ).join( "" );
+ var altGroupSep = groupSep.replace( /\u00A0/g, " " );
+ if ( groupSep !== altGroupSep ) {
+ integer = integer.split( altGroupSep ).join( "" );
+ }
+ // build a natively parsable number string
+ var p = sign + integer;
+ if ( fraction !== null ) {
+ p += "." + fraction;
+ }
+ if ( exponent !== null ) {
+ // exponent itself may have a number patternd
+ var expSignInfo = parseNegativePattern( exponent, nf, "-n" );
+ p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ];
+ }
+ if ( regexParseFloat.test(p) ) {
+ ret = parseFloat( p );
+ }
+ }
+ return ret;
+};
+
+Globalize.culture = function( cultureSelector ) {
+ // setter
+ if ( typeof cultureSelector !== "undefined" ) {
+ this.cultureSelector = cultureSelector;
+ }
+ // getter
+ return this.findClosestCulture( cultureSelector ) || this.cultures[ "default" ];
+};
+
+}( this )); \ No newline at end of file
diff --git a/apps/it/static/js/ui/external/jquery.mousewheel.js b/apps/it/static/js/ui/external/jquery.mousewheel.js
new file mode 100644
index 0000000..8c60304
--- /dev/null
+++ b/apps/it/static/js/ui/external/jquery.mousewheel.js
@@ -0,0 +1,101 @@
+/*! Copyright (c) 2013 Brandon Aaron (http://brandonaaron.net)
+ * Licensed under the MIT License (LICENSE.txt).
+ *
+ * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
+ * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
+ * Thanks to: Seamus Leahy for adding deltaX and deltaY
+ *
+ * Version: 3.1.0
+ *
+ * Requires: 1.2.2+
+ */
+
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+}(function ($) {
+
+ var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll'];
+ var toBind = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'];
+ var lowestDelta, lowestDeltaXY;
+
+ if ($.event.fixHooks) {
+ for ( var i=toFix.length; i; ) {
+ $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
+ }
+ }
+
+ $.event.special.mousewheel = {
+ setup: function() {
+ if ( this.addEventListener ) {
+ for ( var i=toBind.length; i; ) {
+ this.addEventListener( toBind[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = handler;
+ }
+ },
+
+ teardown: function() {
+ if ( this.removeEventListener ) {
+ for ( var i=toBind.length; i; ) {
+ this.removeEventListener( toBind[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = null;
+ }
+ }
+ };
+
+ $.fn.extend({
+ mousewheel: function(fn) {
+ return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
+ },
+
+ unmousewheel: function(fn) {
+ return this.unbind("mousewheel", fn);
+ }
+ });
+
+
+ function handler(event) {
+ var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, deltaX = 0, deltaY = 0, absDelta = 0, absDeltaXY = 0;
+ event = $.event.fix(orgEvent);
+ event.type = "mousewheel";
+
+ // Old school scrollwheel delta
+ if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta; }
+ if ( orgEvent.detail ) { delta = orgEvent.detail * -1; }
+
+ // New school wheel delta (wheel event)
+ if ( orgEvent.deltaY ) {
+ deltaY = orgEvent.deltaY * -1;
+ delta = deltaY;
+ }
+ if ( orgEvent.deltaX ) {
+ deltaX = orgEvent.deltaX;
+ delta = deltaX * -1;
+ }
+
+ // Webkit
+ if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY; }
+ if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = orgEvent.wheelDeltaX * -1; }
+
+ absDelta = Math.abs(delta);
+ if ( !lowestDelta || absDelta < lowestDelta ) { lowestDelta = absDelta; }
+
+ absDeltaXY = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
+ if ( !lowestDeltaXY || absDeltaXY < lowestDeltaXY ) { lowestDeltaXY = absDeltaXY; }
+
+ // Add event and delta to the front of the arguments
+ args.unshift(event, Math.floor(delta/lowestDelta), Math.floor(deltaX/lowestDeltaXY), Math.floor(deltaY/lowestDeltaXY));
+
+ return ($.event.dispatch || $.event.handle).apply(this, args);
+ }
+
+}));
diff --git a/apps/it/static/js/ui/external/jshint.js b/apps/it/static/js/ui/external/jshint.js
new file mode 100644
index 0000000..8f06d9b
--- /dev/null
+++ b/apps/it/static/js/ui/external/jshint.js
@@ -0,0 +1,4835 @@
+/*!
+ * JSHint, by JSHint Community.
+ *
+ * This file (and this file only) is licensed under the same slightly modified
+ * MIT license that JSLint is. It stops evil-doers everywhere.
+ *
+ * JSHint is a derivative work of JSLint:
+ *
+ * Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
+ *
+ * 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 shall be used for Good, not Evil.
+ *
+ * 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.
+ *
+ */
+
+/*
+ JSHINT is a global function. It takes two parameters.
+
+ var myResult = JSHINT(source, option);
+
+ The first parameter is either a string or an array of strings. If it is a
+ string, it will be split on '\n' or '\r'. If it is an array of strings, it
+ is assumed that each string represents one line. The source can be a
+ JavaScript text or a JSON text.
+
+ The second parameter is an optional object of options which control the
+ operation of JSHINT. Most of the options are booleans: They are all
+ optional and have a default value of false. One of the options, predef,
+ can be an array of names, which will be used to declare global variables,
+ or an object whose keys are used as global names, with a boolean value
+ that determines if they are assignable.
+
+ If it checks out, JSHINT returns true. Otherwise, it returns false.
+
+ If false, you can inspect JSHINT.errors to find out the problems.
+ JSHINT.errors is an array of objects containing these members:
+
+ {
+ line : The line (relative to 1) at which the lint was found
+ character : The character (relative to 1) at which the lint was found
+ reason : The problem
+ evidence : The text line in which the problem occurred
+ raw : The raw message before the details were inserted
+ a : The first detail
+ b : The second detail
+ c : The third detail
+ d : The fourth detail
+ }
+
+ If a fatal error was found, a null will be the last element of the
+ JSHINT.errors array.
+
+ You can request a data structure which contains JSHint's results.
+
+ var myData = JSHINT.data();
+
+ It returns a structure with this form:
+
+ {
+ errors: [
+ {
+ line: NUMBER,
+ character: NUMBER,
+ reason: STRING,
+ evidence: STRING
+ }
+ ],
+ functions: [
+ name: STRING,
+ line: NUMBER,
+ character: NUMBER,
+ last: NUMBER,
+ lastcharacter: NUMBER,
+ param: [
+ STRING
+ ],
+ closure: [
+ STRING
+ ],
+ var: [
+ STRING
+ ],
+ exception: [
+ STRING
+ ],
+ outer: [
+ STRING
+ ],
+ unused: [
+ STRING
+ ],
+ global: [
+ STRING
+ ],
+ label: [
+ STRING
+ ]
+ ],
+ globals: [
+ STRING
+ ],
+ member: {
+ STRING: NUMBER
+ },
+ unused: [
+ {
+ name: STRING,
+ line: NUMBER
+ }
+ ],
+ implieds: [
+ {
+ name: STRING,
+ line: NUMBER
+ }
+ ],
+ urls: [
+ STRING
+ ],
+ json: BOOLEAN
+ }
+
+ Empty arrays will not be included.
+
+*/
+
+/*jshint
+ evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true,
+ undef: true, maxlen: 100, indent: 4, quotmark: double, unused: true
+*/
+
+/*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)",
+ "(breakage)", "(character)", "(context)", "(error)", "(explicitNewcap)", "(global)",
+ "(identifier)", "(last)", "(lastcharacter)", "(line)", "(loopage)", "(metrics)",
+ "(name)", "(onevar)", "(params)", "(scope)", "(statement)", "(verb)", "(tokens)", "(catch)",
+ "*", "+", "++", "-", "--", "\/", "<", "<=", "==",
+ "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax,
+ __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView, Audio,
+ Autocompleter, Asset, Boolean, Builder, Buffer, Browser, Blob, COM, CScript, Canvas,
+ CustomAnimation, Class, Control, ComplexityCount, Chain, Color, Cookie, Core, DataView, Date,
+ Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMEvent, DOMReady, DOMParser,
+ Drag, E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event,
+ Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form,
+ FormField, Frame, FormData, Function, Fx, GetObject, Group, Hash, HotKey,
+ HTMLElement, HTMLAnchorElement, HTMLBaseElement, HTMLBlockquoteElement,
+ HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDirectoryElement,
+ HTMLDivElement, HTMLDListElement, HTMLFieldSetElement,
+ HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement,
+ HTMLHeadElement, HTMLHeadingElement, HTMLHRElement, HTMLHtmlElement,
+ HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLIsIndexElement,
+ HTMLLabelElement, HTMLLayerElement, HTMLLegendElement, HTMLLIElement,
+ HTMLLinkElement, HTMLMapElement, HTMLMenuElement, HTMLMetaElement,
+ HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement,
+ HTMLOptionElement, HTMLParagraphElement, HTMLParamElement, HTMLPreElement,
+ HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLStyleElement,
+ HtmlTable, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement,
+ HTMLTableElement, HTMLTableRowElement, HTMLTableSectionElement,
+ HTMLTextAreaElement, HTMLTitleElement, HTMLUListElement, HTMLVideoElement,
+ Iframe, IframeShim, Image, importScripts, Int16Array, Int32Array, Int8Array,
+ Insertion, InputValidator, JSON, Keyboard, Locale, LN10, LN2, LOG10E, LOG2E,
+ MAX_VALUE, MIN_VALUE, Map, Mask, Math, MenuItem, MessageChannel, MessageEvent, MessagePort,
+ MoveAnimation, MooTools, MutationObserver, NaN, Native, NEGATIVE_INFINITY, Node, NodeFilter,
+ Number, Object, ObjectRange,
+ Option, Options, OverText, PI, POSITIVE_INFINITY, PeriodicalExecuter, Point, Position, Prototype,
+ RangeError, Rectangle, ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation, Set,
+ SQRT1_2, SQRT2, ScrollBar, ScriptEngine, ScriptEngineBuildVersion,
+ ScriptEngineMajorVersion, ScriptEngineMinorVersion, Scriptaculous, Scroller,
+ Slick, Slider, Selector, SharedWorker, String, Style, SyntaxError, Sortable, Sortables,
+ SortableObserver, Sound, Spinner, System, Swiff, Text, TextArea, Template,
+ Timer, Tips, Type, TypeError, Toggle, Try, "use strict", unescape, URI, URIError, URL,
+ VBArray, WeakMap, WSH, WScript, XDomainRequest, Web, Window, XMLDOM, XMLHttpRequest, XMLSerializer,
+ XPathEvaluator, XPathException, XPathExpression, XPathNamespace, XPathNSResolver, XPathResult,
+ "\\", a, abs, addEventListener, address, alert, apply, applicationCache, arguments, arity,
+ asi, atob, b, basic, basicToken, bitwise, blacklist, block, blur, boolOptions, boss,
+ browser, btoa, c, call, callee, caller, camelcase, cases, charAt, charCodeAt, character,
+ clearInterval, clearTimeout, close, closed, closure, comment, complexityCount, condition,
+ confirm, console, constructor, content, couch, create, css, curly, d, data, datalist, dd, debug,
+ decodeURI, decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document,
+ dojo, dijit, dojox, define, else, emit, encodeURI, encodeURIComponent, elem,
+ eqeq, eqeqeq, eqnull, errors, es5, escape, esnext, eval, event, evidence, evil,
+ ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus, forEach,
+ forin, fragment, frames, from, fromCharCode, fud, funcscope, funct, function, functions,
+ g, gc, getComputedStyle, getRow, getter, getterToken, GLOBAL, global, globals, globalstrict,
+ hasOwnProperty, help, history, i, id, identifier, immed, implieds, importPackage, include,
+ indent, indexOf, init, ins, internals, instanceOf, isAlpha, isApplicationRunning, isArray,
+ isDigit, isFinite, isNaN, iterator, java, join, jshint,
+ JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastcharacter, lastsemic, laxbreak,
+ laxcomma, latedef, lbp, led, left, length, line, load, loadClass, localStorage, location,
+ log, loopfunc, m, match, max, maxcomplexity, maxdepth, maxerr, maxlen, maxstatements, maxparams,
+ member, message, meta, module, moveBy, moveTo, mootools, multistr, name, navigator, new, newcap,
+ nestedBlockDepth, noarg, node, noempty, nomen, nonew, nonstandard, nud, onbeforeunload, onblur,
+ onerror, onevar, onecase, onfocus, onload, onresize, onunload, open, openDatabase, openURL,
+ opener, opera, options, outer, param, parent, parseFloat, parseInt, passfail, plusplus,
+ postMessage, pop, predef, print, process, prompt, proto, prototype, prototypejs, provides, push,
+ quit, quotmark, range, raw, reach, reason, regexp, readFile, readUrl, regexdash,
+ removeEventListener, replace, report, require, reserved, resizeBy, resizeTo, resolvePath,
+ resumeUpdates, respond, rhino, right, runCommand, scroll, scope, screen, scripturl, scrollBy,
+ scrollTo, scrollbar, search, seal, self, send, serialize, sessionStorage, setInterval, setTimeout,
+ setter, setterToken, shift, slice, smarttabs, sort, spawn, split, statement, statementCount, stack,
+ status, start, strict, sub, substr, supernew, shadow, supplant, sum, sync, test, toLowerCase,
+ toString, toUpperCase, toint32, token, tokens, top, trailing, type, typeOf, Uint16Array,
+ Uint32Array, Uint8Array, undef, undefs, unused, urls, validthis, value, valueOf, var, vars,
+ version, verifyMaxParametersPerFunction, verifyMaxStatementsPerFunction,
+ verifyMaxComplexityPerFunction, verifyMaxNestedBlockDepthPerFunction, WebSocket, withstmt, white,
+ window, windows, Worker, worker, wsh, yui, YUI, Y, YUI_config*/
+
+/*global exports: false */
+
+// We build the application inside a function so that we produce only a single
+// global variable. That function will be invoked immediately, and its return
+// value is the JSHINT function itself.
+
+var JSHINT = (function () {
+ "use strict";
+
+ var anonname, // The guessed name for anonymous functions.
+
+// These are operators that should not be used with the ! operator.
+
+ bang = {
+ "<" : true,
+ "<=" : true,
+ "==" : true,
+ "===": true,
+ "!==": true,
+ "!=" : true,
+ ">" : true,
+ ">=" : true,
+ "+" : true,
+ "-" : true,
+ "*" : true,
+ "/" : true,
+ "%" : true
+ },
+
+ // These are the JSHint boolean options.
+ boolOptions = {
+ asi : true, // if automatic semicolon insertion should be tolerated
+ bitwise : true, // if bitwise operators should not be allowed
+ boss : true, // if advanced usage of assignments should be allowed
+ browser : true, // if the standard browser globals should be predefined
+ camelcase : true, // if identifiers should be required in camel case
+ couch : true, // if CouchDB globals should be predefined
+ curly : true, // if curly braces around all blocks should be required
+ debug : true, // if debugger statements should be allowed
+ devel : true, // if logging globals should be predefined (console,
+ // alert, etc.)
+ dojo : true, // if Dojo Toolkit globals should be predefined
+ eqeqeq : true, // if === should be required
+ eqnull : true, // if == null comparisons should be tolerated
+ es5 : true, // if ES5 syntax should be allowed
+ esnext : true, // if es.next specific syntax should be allowed
+ evil : true, // if eval should be allowed
+ expr : true, // if ExpressionStatement should be allowed as Programs
+ forin : true, // if for in statements must filter
+ funcscope : true, // if only function scope should be used for scope tests
+ globalstrict: true, // if global "use strict"; should be allowed (also
+ // enables 'strict')
+ immed : true, // if immediate invocations must be wrapped in parens
+ iterator : true, // if the `__iterator__` property should be allowed
+ jquery : true, // if jQuery globals should be predefined
+ lastsemic : true, // if semicolons may be ommitted for the trailing
+ // statements inside of a one-line blocks.
+ latedef : true, // if the use before definition should not be tolerated
+ laxbreak : true, // if line breaks should not be checked
+ laxcomma : true, // if line breaks should not be checked around commas
+ loopfunc : true, // if functions should be allowed to be defined within
+ // loops
+ mootools : true, // if MooTools globals should be predefined
+ multistr : true, // allow multiline strings
+ newcap : true, // if constructor names must be capitalized
+ noarg : true, // if arguments.caller and arguments.callee should be
+ // disallowed
+ node : true, // if the Node.js environment globals should be
+ // predefined
+ noempty : true, // if empty blocks should be disallowed
+ nonew : true, // if using `new` for side-effects should be disallowed
+ nonstandard : true, // if non-standard (but widely adopted) globals should
+ // be predefined
+ nomen : true, // if names should be checked
+ onevar : true, // if only one var statement per function should be
+ // allowed
+ onecase : true, // if one case switch statements should be allowed
+ passfail : true, // if the scan should stop on first error
+ plusplus : true, // if increment/decrement should not be allowed
+ proto : true, // if the `__proto__` property should be allowed
+ prototypejs : true, // if Prototype and Scriptaculous globals should be
+ // predefined
+ regexdash : true, // if unescaped first/last dash (-) inside brackets
+ // should be tolerated
+ regexp : true, // if the . should not be allowed in regexp literals
+ rhino : true, // if the Rhino environment globals should be predefined
+ undef : true, // if variables should be declared before used
+ unused : true, // if variables should be always used
+ scripturl : true, // if script-targeted URLs should be tolerated
+ shadow : true, // if variable shadowing should be tolerated
+ smarttabs : true, // if smarttabs should be tolerated
+ // (http://www.emacswiki.org/emacs/SmartTabs)
+ strict : true, // require the "use strict"; pragma
+ sub : true, // if all forms of subscript notation are tolerated
+ supernew : true, // if `new function () { ... };` and `new Object;`
+ // should be tolerated
+ trailing : true, // if trailing whitespace rules apply
+ validthis : true, // if 'this' inside a non-constructor function is valid.
+ // This is a function scoped option only.
+ withstmt : true, // if with statements should be allowed
+ white : true, // if strict whitespace rules apply
+ worker : true, // if Web Worker script symbols should be allowed
+ wsh : true, // if the Windows Scripting Host environment globals
+ // should be predefined
+ yui : true // YUI variables should be predefined
+ },
+
+ // These are the JSHint options that can take any value
+ // (we use this object to detect invalid options)
+ valOptions = {
+ maxlen : false,
+ indent : false,
+ maxerr : false,
+ predef : false,
+ quotmark : false, //'single'|'double'|true
+ scope : false,
+ maxstatements: false, // {int} max statements per function
+ maxdepth : false, // {int} max nested block depth per function
+ maxparams : false, // {int} max params per function
+ maxcomplexity: false // {int} max cyclomatic complexity per function
+ },
+
+ // These are JSHint boolean options which are shared with JSLint
+ // where the definition in JSHint is opposite JSLint
+ invertedOptions = {
+ bitwise : true,
+ forin : true,
+ newcap : true,
+ nomen : true,
+ plusplus : true,
+ regexp : true,
+ undef : true,
+ white : true,
+
+ // Inverted and renamed, use JSHint name here
+ eqeqeq : true,
+ onevar : true
+ },
+
+ // These are JSHint boolean options which are shared with JSLint
+ // where the name has been changed but the effect is unchanged
+ renamedOptions = {
+ eqeq : "eqeqeq",
+ vars : "onevar",
+ windows : "wsh"
+ },
+
+
+ // browser contains a set of global names which are commonly provided by a
+ // web browser environment.
+ browser = {
+ ArrayBuffer : false,
+ ArrayBufferView : false,
+ Audio : false,
+ Blob : false,
+ addEventListener : false,
+ applicationCache : false,
+ atob : false,
+ blur : false,
+ btoa : false,
+ clearInterval : false,
+ clearTimeout : false,
+ close : false,
+ closed : false,
+ DataView : false,
+ DOMParser : false,
+ defaultStatus : false,
+ document : false,
+ event : false,
+ FileReader : false,
+ Float32Array : false,
+ Float64Array : false,
+ FormData : false,
+ focus : false,
+ frames : false,
+ getComputedStyle : false,
+ HTMLElement : false,
+ HTMLAnchorElement : false,
+ HTMLBaseElement : false,
+ HTMLBlockquoteElement : false,
+ HTMLBodyElement : false,
+ HTMLBRElement : false,
+ HTMLButtonElement : false,
+ HTMLCanvasElement : false,
+ HTMLDirectoryElement : false,
+ HTMLDivElement : false,
+ HTMLDListElement : false,
+ HTMLFieldSetElement : false,
+ HTMLFontElement : false,
+ HTMLFormElement : false,
+ HTMLFrameElement : false,
+ HTMLFrameSetElement : false,
+ HTMLHeadElement : false,
+ HTMLHeadingElement : false,
+ HTMLHRElement : false,
+ HTMLHtmlElement : false,
+ HTMLIFrameElement : false,
+ HTMLImageElement : false,
+ HTMLInputElement : false,
+ HTMLIsIndexElement : false,
+ HTMLLabelElement : false,
+ HTMLLayerElement : false,
+ HTMLLegendElement : false,
+ HTMLLIElement : false,
+ HTMLLinkElement : false,
+ HTMLMapElement : false,
+ HTMLMenuElement : false,
+ HTMLMetaElement : false,
+ HTMLModElement : false,
+ HTMLObjectElement : false,
+ HTMLOListElement : false,
+ HTMLOptGroupElement : false,
+ HTMLOptionElement : false,
+ HTMLParagraphElement : false,
+ HTMLParamElement : false,
+ HTMLPreElement : false,
+ HTMLQuoteElement : false,
+ HTMLScriptElement : false,
+ HTMLSelectElement : false,
+ HTMLStyleElement : false,
+ HTMLTableCaptionElement : false,
+ HTMLTableCellElement : false,
+ HTMLTableColElement : false,
+ HTMLTableElement : false,
+ HTMLTableRowElement : false,
+ HTMLTableSectionElement : false,
+ HTMLTextAreaElement : false,
+ HTMLTitleElement : false,
+ HTMLUListElement : false,
+ HTMLVideoElement : false,
+ history : false,
+ Int16Array : false,
+ Int32Array : false,
+ Int8Array : false,
+ Image : false,
+ length : false,
+ localStorage : false,
+ location : false,
+ MessageChannel : false,
+ MessageEvent : false,
+ MessagePort : false,
+ moveBy : false,
+ moveTo : false,
+ MutationObserver : false,
+ name : false,
+ Node : false,
+ NodeFilter : false,
+ navigator : false,
+ onbeforeunload : true,
+ onblur : true,
+ onerror : true,
+ onfocus : true,
+ onload : true,
+ onresize : true,
+ onunload : true,
+ open : false,
+ openDatabase : false,
+ opener : false,
+ Option : false,
+ parent : false,
+ print : false,
+ removeEventListener : false,
+ resizeBy : false,
+ resizeTo : false,
+ screen : false,
+ scroll : false,
+ scrollBy : false,
+ scrollTo : false,
+ sessionStorage : false,
+ setInterval : false,
+ setTimeout : false,
+ SharedWorker : false,
+ status : false,
+ top : false,
+ Uint16Array : false,
+ Uint32Array : false,
+ Uint8Array : false,
+ WebSocket : false,
+ window : false,
+ Worker : false,
+ XMLHttpRequest : false,
+ XMLSerializer : false,
+ XPathEvaluator : false,
+ XPathException : false,
+ XPathExpression : false,
+ XPathNamespace : false,
+ XPathNSResolver : false,
+ XPathResult : false
+ },
+
+ couch = {
+ "require" : false,
+ respond : false,
+ getRow : false,
+ emit : false,
+ send : false,
+ start : false,
+ sum : false,
+ log : false,
+ exports : false,
+ module : false,
+ provides : false
+ },
+
+ declared, // Globals that were declared using /*global ... */ syntax.
+
+ devel = {
+ alert : false,
+ confirm : false,
+ console : false,
+ Debug : false,
+ opera : false,
+ prompt : false
+ },
+
+ dojo = {
+ dojo : false,
+ dijit : false,
+ dojox : false,
+ define : false,
+ "require" : false
+ },
+
+ funct, // The current function
+
+ functionicity = [
+ "closure", "exception", "global", "label",
+ "outer", "unused", "var"
+ ],
+
+ functions, // All of the functions
+
+ global, // The global scope
+ implied, // Implied globals
+ inblock,
+ indent,
+ jsonmode,
+
+ jquery = {
+ "$" : false,
+ jQuery : false
+ },
+
+ lines,
+ lookahead,
+ member,
+ membersOnly,
+
+ mootools = {
+ "$" : false,
+ "$$" : false,
+ Asset : false,
+ Browser : false,
+ Chain : false,
+ Class : false,
+ Color : false,
+ Cookie : false,
+ Core : false,
+ Document : false,
+ DomReady : false,
+ DOMEvent : false,
+ DOMReady : false,
+ Drag : false,
+ Element : false,
+ Elements : false,
+ Event : false,
+ Events : false,
+ Fx : false,
+ Group : false,
+ Hash : false,
+ HtmlTable : false,
+ Iframe : false,
+ IframeShim : false,
+ InputValidator : false,
+ instanceOf : false,
+ Keyboard : false,
+ Locale : false,
+ Mask : false,
+ MooTools : false,
+ Native : false,
+ Options : false,
+ OverText : false,
+ Request : false,
+ Scroller : false,
+ Slick : false,
+ Slider : false,
+ Sortables : false,
+ Spinner : false,
+ Swiff : false,
+ Tips : false,
+ Type : false,
+ typeOf : false,
+ URI : false,
+ Window : false
+ },
+
+ nexttoken,
+
+ node = {
+ __filename : false,
+ __dirname : false,
+ Buffer : false,
+ console : false,
+ exports : true, // In Node it is ok to exports = module.exports = foo();
+ GLOBAL : false,
+ global : false,
+ module : false,
+ process : false,
+ require : false,
+ setTimeout : false,
+ clearTimeout : false,
+ setInterval : false,
+ clearInterval : false
+ },
+
+ noreach,
+ option,
+ predefined, // Global variables defined by option
+ prereg,
+ prevtoken,
+
+ prototypejs = {
+ "$" : false,
+ "$$" : false,
+ "$A" : false,
+ "$F" : false,
+ "$H" : false,
+ "$R" : false,
+ "$break" : false,
+ "$continue" : false,
+ "$w" : false,
+ Abstract : false,
+ Ajax : false,
+ Class : false,
+ Enumerable : false,
+ Element : false,
+ Event : false,
+ Field : false,
+ Form : false,
+ Hash : false,
+ Insertion : false,
+ ObjectRange : false,
+ PeriodicalExecuter: false,
+ Position : false,
+ Prototype : false,
+ Selector : false,
+ Template : false,
+ Toggle : false,
+ Try : false,
+ Autocompleter : false,
+ Builder : false,
+ Control : false,
+ Draggable : false,
+ Draggables : false,
+ Droppables : false,
+ Effect : false,
+ Sortable : false,
+ SortableObserver : false,
+ Sound : false,
+ Scriptaculous : false
+ },
+
+ quotmark,
+
+ rhino = {
+ defineClass : false,
+ deserialize : false,
+ gc : false,
+ help : false,
+ importPackage: false,
+ "java" : false,
+ load : false,
+ loadClass : false,
+ print : false,
+ quit : false,
+ readFile : false,
+ readUrl : false,
+ runCommand : false,
+ seal : false,
+ serialize : false,
+ spawn : false,
+ sync : false,
+ toint32 : false,
+ version : false
+ },
+
+ scope, // The current scope
+ stack,
+
+ // standard contains the global names that are provided by the
+ // ECMAScript standard.
+ standard = {
+ Array : false,
+ Boolean : false,
+ Date : false,
+ decodeURI : false,
+ decodeURIComponent : false,
+ encodeURI : false,
+ encodeURIComponent : false,
+ Error : false,
+ "eval" : false,
+ EvalError : false,
+ Function : false,
+ hasOwnProperty : false,
+ isFinite : false,
+ isNaN : false,
+ JSON : false,
+ Map : false,
+ Math : false,
+ NaN : false,
+ Number : false,
+ Object : false,
+ parseInt : false,
+ parseFloat : false,
+ RangeError : false,
+ ReferenceError : false,
+ RegExp : false,
+ Set : false,
+ String : false,
+ SyntaxError : false,
+ TypeError : false,
+ URIError : false,
+ WeakMap : false
+ },
+
+ // widely adopted global names that are not part of ECMAScript standard
+ nonstandard = {
+ escape : false,
+ unescape : false
+ },
+
+ directive,
+ syntax = {},
+ tab,
+ token,
+ unuseds,
+ urls,
+ useESNextSyntax,
+ warnings,
+
+ worker = {
+ importScripts : true,
+ postMessage : true,
+ self : true
+ },
+
+ wsh = {
+ ActiveXObject : true,
+ Enumerator : true,
+ GetObject : true,
+ ScriptEngine : true,
+ ScriptEngineBuildVersion : true,
+ ScriptEngineMajorVersion : true,
+ ScriptEngineMinorVersion : true,
+ VBArray : true,
+ WSH : true,
+ WScript : true,
+ XDomainRequest : true
+ },
+
+ yui = {
+ YUI : false,
+ Y : false,
+ YUI_config : false
+ };
+ // Regular expressions. Some of these are stupidly long.
+ var ax, cx, tx, nx, nxg, lx, ix, jx, ft;
+ (function () {
+ /*jshint maxlen:300 */
+
+ // unsafe comment or string
+ ax = /@cc|<\/?|script|\]\s*\]|<\s*!|&lt/i;
+
+ // unsafe characters that are silently deleted by one or more browsers
+ cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
+
+ // token
+ tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/=(?!(\S*\/[gim]?))|\/(\*(jshint|jslint|members?|global)?|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/;
+
+ // characters in strings that need escapement
+ nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
+ nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+
+ // star slash
+ lx = /\*\//;
+
+ // identifier
+ ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
+
+ // javascript url
+ jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i;
+
+ // catches /* falls through */ comments
+ ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/;
+ }());
+
+ function F() {} // Used by Object.create
+
+ function is_own(object, name) {
+ // The object.hasOwnProperty method fails when the property under consideration
+ // is named 'hasOwnProperty'. So we have to use this more convoluted form.
+ return Object.prototype.hasOwnProperty.call(object, name);
+ }
+
+ function checkOption(name, t) {
+ if (valOptions[name] === undefined && boolOptions[name] === undefined) {
+ warning("Bad option: '" + name + "'.", t);
+ }
+ }
+
+ function isString(obj) {
+ return Object.prototype.toString.call(obj) === "[object String]";
+ }
+
+ // Provide critical ES5 functions to ES3.
+
+ if (typeof Array.isArray !== "function") {
+ Array.isArray = function (o) {
+ return Object.prototype.toString.apply(o) === "[object Array]";
+ };
+ }
+
+ if (!Array.prototype.forEach) {
+ Array.prototype.forEach = function (fn, scope) {
+ var len = this.length;
+
+ for (var i = 0; i < len; i++) {
+ fn.call(scope || this, this[i], i, this);
+ }
+ };
+ }
+
+ if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
+ if (this === null || this === undefined) {
+ throw new TypeError();
+ }
+
+ var t = new Object(this);
+ var len = t.length >>> 0;
+
+ if (len === 0) {
+ return -1;
+ }
+
+ var n = 0;
+ if (arguments.length > 0) {
+ n = Number(arguments[1]);
+ if (n != n) { // shortcut for verifying if it's NaN
+ n = 0;
+ } else if (n !== 0 && n != Infinity && n != -Infinity) {
+ n = (n > 0 || -1) * Math.floor(Math.abs(n));
+ }
+ }
+
+ if (n >= len) {
+ return -1;
+ }
+
+ var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
+ for (; k < len; k++) {
+ if (k in t && t[k] === searchElement) {
+ return k;
+ }
+ }
+
+ return -1;
+ };
+ }
+
+ if (typeof Object.create !== "function") {
+ Object.create = function (o) {
+ F.prototype = o;
+ return new F();
+ };
+ }
+
+ if (typeof Object.keys !== "function") {
+ Object.keys = function (o) {
+ var a = [], k;
+ for (k in o) {
+ if (is_own(o, k)) {
+ a.push(k);
+ }
+ }
+ return a;
+ };
+ }
+
+ // Non standard methods
+
+ function isAlpha(str) {
+ return (str >= "a" && str <= "z\uffff") ||
+ (str >= "A" && str <= "Z\uffff");
+ }
+
+ function isDigit(str) {
+ return (str >= "0" && str <= "9");
+ }
+
+ function isIdentifier(token, value) {
+ if (!token)
+ return false;
+
+ if (!token.identifier || token.value !== value)
+ return false;
+
+ return true;
+ }
+
+ function supplant(str, data) {
+ return str.replace(/\{([^{}]*)\}/g, function (a, b) {
+ var r = data[b];
+ return typeof r === "string" || typeof r === "number" ? r : a;
+ });
+ }
+
+ function combine(t, o) {
+ var n;
+ for (n in o) {
+ if (is_own(o, n) && !is_own(JSHINT.blacklist, n)) {
+ t[n] = o[n];
+ }
+ }
+ }
+
+ function updatePredefined() {
+ Object.keys(JSHINT.blacklist).forEach(function (key) {
+ delete predefined[key];
+ });
+ }
+
+ function assume() {
+ if (option.couch) {
+ combine(predefined, couch);
+ }
+
+ if (option.rhino) {
+ combine(predefined, rhino);
+ }
+
+ if (option.prototypejs) {
+ combine(predefined, prototypejs);
+ }
+
+ if (option.node) {
+ combine(predefined, node);
+ option.globalstrict = true;
+ }
+
+ if (option.devel) {
+ combine(predefined, devel);
+ }
+
+ if (option.dojo) {
+ combine(predefined, dojo);
+ }
+
+ if (option.browser) {
+ combine(predefined, browser);
+ }
+
+ if (option.nonstandard) {
+ combine(predefined, nonstandard);
+ }
+
+ if (option.jquery) {
+ combine(predefined, jquery);
+ }
+
+ if (option.mootools) {
+ combine(predefined, mootools);
+ }
+
+ if (option.worker) {
+ combine(predefined, worker);
+ }
+
+ if (option.wsh) {
+ combine(predefined, wsh);
+ }
+
+ if (option.esnext) {
+ useESNextSyntax();
+ }
+
+ if (option.globalstrict && option.strict !== false) {
+ option.strict = true;
+ }
+
+ if (option.yui) {
+ combine(predefined, yui);
+ }
+ }
+
+
+ // Produce an error warning.
+ function quit(message, line, chr) {
+ var percentage = Math.floor((line / lines.length) * 100);
+
+ throw {
+ name: "JSHintError",
+ line: line,
+ character: chr,
+ message: message + " (" + percentage + "% scanned).",
+ raw: message
+ };
+ }
+
+ function isundef(scope, m, t, a) {
+ return JSHINT.undefs.push([scope, m, t, a]);
+ }
+
+ function warning(m, t, a, b, c, d) {
+ var ch, l, w;
+ t = t || nexttoken;
+ if (t.id === "(end)") { // `~
+ t = token;
+ }
+ l = t.line || 0;
+ ch = t.from || 0;
+ w = {
+ id: "(error)",
+ raw: m,
+ evidence: lines[l - 1] || "",
+ line: l,
+ character: ch,
+ scope: JSHINT.scope,
+ a: a,
+ b: b,
+ c: c,
+ d: d
+ };
+ w.reason = supplant(m, w);
+ JSHINT.errors.push(w);
+ if (option.passfail) {
+ quit("Stopping. ", l, ch);
+ }
+ warnings += 1;
+ if (warnings >= option.maxerr) {
+ quit("Too many errors.", l, ch);
+ }
+ return w;
+ }
+
+ function warningAt(m, l, ch, a, b, c, d) {
+ return warning(m, {
+ line: l,
+ from: ch
+ }, a, b, c, d);
+ }
+
+ function error(m, t, a, b, c, d) {
+ warning(m, t, a, b, c, d);
+ }
+
+ function errorAt(m, l, ch, a, b, c, d) {
+ return error(m, {
+ line: l,
+ from: ch
+ }, a, b, c, d);
+ }
+
+ // Tracking of "internal" scripts, like eval containing a static string
+ function addInternalSrc(elem, src) {
+ var i;
+ i = {
+ id: "(internal)",
+ elem: elem,
+ value: src
+ };
+ JSHINT.internals.push(i);
+ return i;
+ }
+
+
+// lexical analysis and token construction
+
+ var lex = (function lex() {
+ var character, from, line, s;
+
+// Private lex methods
+
+ function nextLine() {
+ var at,
+ match,
+ tw; // trailing whitespace check
+
+ if (line >= lines.length)
+ return false;
+
+ character = 1;
+ s = lines[line];
+ line += 1;
+
+ // If smarttabs option is used check for spaces followed by tabs only.
+ // Otherwise check for any occurence of mixed tabs and spaces.
+ // Tabs and one space followed by block comment is allowed.
+ if (option.smarttabs) {
+ // negative look-behind for "//"
+ match = s.match(/(\/\/)? \t/);
+ at = match && !match[1] ? 0 : -1;
+ } else {
+ at = s.search(/ \t|\t [^\*]/);
+ }
+
+ if (at >= 0)
+ warningAt("Mixed spaces and tabs.", line, at + 1);
+
+ s = s.replace(/\t/g, tab);
+ at = s.search(cx);
+
+ if (at >= 0)
+ warningAt("Unsafe character.", line, at);
+
+ if (option.maxlen && option.maxlen < s.length)
+ warningAt("Line too long.", line, s.length);
+
+ // Check for trailing whitespaces
+ tw = option.trailing && s.match(/^(.*?)\s+$/);
+ if (tw && !/^\s+$/.test(s)) {
+ warningAt("Trailing whitespace.", line, tw[1].length + 1);
+ }
+ return true;
+ }
+
+// Produce a token object. The token inherits from a syntax symbol.
+
+ function it(type, value) {
+ var i, t;
+
+ function checkName(name) {
+ if (!option.proto && name === "__proto__") {
+ warningAt("The '{a}' property is deprecated.", line, from, name);
+ return;
+ }
+
+ if (!option.iterator && name === "__iterator__") {
+ warningAt("'{a}' is only available in JavaScript 1.7.", line, from, name);
+ return;
+ }
+
+ // Check for dangling underscores unless we're in Node
+ // environment and this identifier represents built-in
+ // Node globals with underscores.
+
+ var hasDangling = /^(_+.*|.*_+)$/.test(name);
+
+ if (option.nomen && hasDangling && name !== "_") {
+ if (option.node && token.id !== "." && /^(__dirname|__filename)$/.test(name))
+ return;
+
+ warningAt("Unexpected {a} in '{b}'.", line, from, "dangling '_'", name);
+ return;
+ }
+
+ // Check for non-camelcase names. Names like MY_VAR and
+ // _myVar are okay though.
+
+ if (option.camelcase) {
+ if (name.replace(/^_+/, "").indexOf("_") > -1 && !name.match(/^[A-Z0-9_]*$/)) {
+ warningAt("Identifier '{a}' is not in camel case.", line, from, value);
+ }
+ }
+ }
+
+ if (type === "(color)" || type === "(range)") {
+ t = {type: type};
+ } else if (type === "(punctuator)" ||
+ (type === "(identifier)" && is_own(syntax, value))) {
+ t = syntax[value] || syntax["(error)"];
+ } else {
+ t = syntax[type];
+ }
+
+ t = Object.create(t);
+
+ if (type === "(string)" || type === "(range)") {
+ if (!option.scripturl && jx.test(value)) {
+ warningAt("Script URL.", line, from);
+ }
+ }
+
+ if (type === "(identifier)") {
+ t.identifier = true;
+ checkName(value);
+ }
+
+ t.value = value;
+ t.line = line;
+ t.character = character;
+ t.from = from;
+ i = t.id;
+ if (i !== "(endline)") {
+ prereg = i &&
+ (("(,=:[!&|?{};".indexOf(i.charAt(i.length - 1)) >= 0) ||
+ i === "return" ||
+ i === "case");
+ }
+ return t;
+ }
+
+ // Public lex methods
+ return {
+ init: function (source) {
+ if (typeof source === "string") {
+ lines = source
+ .replace(/\r\n/g, "\n")
+ .replace(/\r/g, "\n")
+ .split("\n");
+ } else {
+ lines = source;
+ }
+
+ // If the first line is a shebang (#!), make it a blank and move on.
+ // Shebangs are used by Node scripts.
+ if (lines[0] && lines[0].substr(0, 2) === "#!")
+ lines[0] = "";
+
+ line = 0;
+ nextLine();
+ from = 1;
+ },
+
+ range: function (begin, end) {
+ var c, value = "";
+ from = character;
+ if (s.charAt(0) !== begin) {
+ errorAt("Expected '{a}' and instead saw '{b}'.",
+ line, character, begin, s.charAt(0));
+ }
+ for (;;) {
+ s = s.slice(1);
+ character += 1;
+ c = s.charAt(0);
+ switch (c) {
+ case "":
+ errorAt("Missing '{a}'.", line, character, c);
+ break;
+ case end:
+ s = s.slice(1);
+ character += 1;
+ return it("(range)", value);
+ case "\\":
+ warningAt("Unexpected '{a}'.", line, character, c);
+ }
+ value += c;
+ }
+
+ },
+
+
+ // token -- this is called by advance to get the next token
+ token: function () {
+ var b, c, captures, d, depth, high, i, l, low, q, t, isLiteral, isInRange, n;
+
+ function match(x) {
+ var r = x.exec(s), r1;
+
+ if (r) {
+ l = r[0].length;
+ r1 = r[1];
+ c = r1.charAt(0);
+ s = s.substr(l);
+ from = character + l - r1.length;
+ character += l;
+ return r1;
+ }
+ }
+
+ function string(x) {
+ var c, j, r = "", allowNewLine = false;
+
+ if (jsonmode && x !== "\"") {
+ warningAt("Strings must use doublequote.",
+ line, character);
+ }
+
+ if (option.quotmark) {
+ if (option.quotmark === "single" && x !== "'") {
+ warningAt("Strings must use singlequote.",
+ line, character);
+ } else if (option.quotmark === "double" && x !== "\"") {
+ warningAt("Strings must use doublequote.",
+ line, character);
+ } else if (option.quotmark === true) {
+ quotmark = quotmark || x;
+ if (quotmark !== x) {
+ warningAt("Mixed double and single quotes.",
+ line, character);
+ }
+ }
+ }
+
+ function esc(n) {
+ var i = parseInt(s.substr(j + 1, n), 16);
+ j += n;
+ if (i >= 32 && i <= 126 &&
+ i !== 34 && i !== 92 && i !== 39) {
+ warningAt("Unnecessary escapement.", line, character);
+ }
+ character += n;
+ c = String.fromCharCode(i);
+ }
+
+ j = 0;
+
+unclosedString:
+ for (;;) {
+ while (j >= s.length) {
+ j = 0;
+
+ var cl = line, cf = from;
+ if (!nextLine()) {
+ errorAt("Unclosed string.", cl, cf);
+ break unclosedString;
+ }
+
+ if (allowNewLine) {
+ allowNewLine = false;
+ } else {
+ warningAt("Unclosed string.", cl, cf);
+ }
+ }
+
+ c = s.charAt(j);
+ if (c === x) {
+ character += 1;
+ s = s.substr(j + 1);
+ return it("(string)", r, x);
+ }
+
+ if (c < " ") {
+ if (c === "\n" || c === "\r") {
+ break;
+ }
+ warningAt("Control character in string: {a}.",
+ line, character + j, s.slice(0, j));
+ } else if (c === "\\") {
+ j += 1;
+ character += 1;
+ c = s.charAt(j);
+ n = s.charAt(j + 1);
+ switch (c) {
+ case "\\":
+ case "\"":
+ case "/":
+ break;
+ case "\'":
+ if (jsonmode) {
+ warningAt("Avoid \\'.", line, character);
+ }
+ break;
+ case "b":
+ c = "\b";
+ break;
+ case "f":
+ c = "\f";
+ break;
+ case "n":
+ c = "\n";
+ break;
+ case "r":
+ c = "\r";
+ break;
+ case "t":
+ c = "\t";
+ break;
+ case "0":
+ c = "\0";
+ // Octal literals fail in strict mode
+ // check if the number is between 00 and 07
+ // where 'n' is the token next to 'c'
+ if (n >= 0 && n <= 7 && directive["use strict"]) {
+ warningAt(
+ "Octal literals are not allowed in strict mode.",
+ line, character);
+ }
+ break;
+ case "u":
+ esc(4);
+ break;
+ case "v":
+ if (jsonmode) {
+ warningAt("Avoid \\v.", line, character);
+ }
+ c = "\v";
+ break;
+ case "x":
+ if (jsonmode) {
+ warningAt("Avoid \\x-.", line, character);
+ }
+ esc(2);
+ break;
+ case "":
+ // last character is escape character
+ // always allow new line if escaped, but show
+ // warning if option is not set
+ allowNewLine = true;
+ if (option.multistr) {
+ if (jsonmode) {
+ warningAt("Avoid EOL escapement.", line, character);
+ }
+ c = "";
+ character -= 1;
+ break;
+ }
+ warningAt("Bad escapement of EOL. Use option multistr if needed.",
+ line, character);
+ break;
+ case "!":
+ if (s.charAt(j - 2) === "<")
+ break;
+ /*falls through*/
+ default:
+ warningAt("Bad escapement.", line, character);
+ }
+ }
+ r += c;
+ character += 1;
+ j += 1;
+ }
+ }
+
+ for (;;) {
+ if (!s) {
+ return it(nextLine() ? "(endline)" : "(end)", "");
+ }
+
+ t = match(tx);
+
+ if (!t) {
+ t = "";
+ c = "";
+ while (s && s < "!") {
+ s = s.substr(1);
+ }
+ if (s) {
+ errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1));
+ s = "";
+ }
+ } else {
+
+ // identifier
+
+ if (isAlpha(c) || c === "_" || c === "$") {
+ return it("(identifier)", t);
+ }
+
+ // number
+
+ if (isDigit(c)) {
+ if (!isFinite(Number(t))) {
+ warningAt("Bad number '{a}'.",
+ line, character, t);
+ }
+ if (isAlpha(s.substr(0, 1))) {
+ warningAt("Missing space after '{a}'.",
+ line, character, t);
+ }
+ if (c === "0") {
+ d = t.substr(1, 1);
+ if (isDigit(d)) {
+ if (token.id !== ".") {
+ warningAt("Don't use extra leading zeros '{a}'.",
+ line, character, t);
+ }
+ } else if (jsonmode && (d === "x" || d === "X")) {
+ warningAt("Avoid 0x-. '{a}'.",
+ line, character, t);
+ }
+ }
+ if (t.substr(t.length - 1) === ".") {
+ warningAt(
+"A trailing decimal point can be confused with a dot '{a}'.", line, character, t);
+ }
+ return it("(number)", t);
+ }
+ switch (t) {
+
+ // string
+
+ case "\"":
+ case "'":
+ return string(t);
+
+ // // comment
+
+ case "//":
+ s = "";
+ token.comment = true;
+ break;
+
+ // /* comment
+
+ case "/*":
+ for (;;) {
+ i = s.search(lx);
+ if (i >= 0) {
+ break;
+ }
+ if (!nextLine()) {
+ errorAt("Unclosed comment.", line, character);
+ }
+ }
+ s = s.substr(i + 2);
+ token.comment = true;
+ break;
+
+ // /*members /*jshint /*global
+
+ case "/*members":
+ case "/*member":
+ case "/*jshint":
+ case "/*jslint":
+ case "/*global":
+ case "*/":
+ return {
+ value: t,
+ type: "special",
+ line: line,
+ character: character,
+ from: from
+ };
+
+ case "":
+ break;
+ // /
+ case "/":
+ if (s.charAt(0) === "=") {
+ errorAt("A regular expression literal can be confused with '/='.",
+ line, from);
+ }
+
+ if (prereg) {
+ depth = 0;
+ captures = 0;
+ l = 0;
+ for (;;) {
+ b = true;
+ c = s.charAt(l);
+ l += 1;
+ switch (c) {
+ case "":
+ errorAt("Unclosed regular expression.", line, from);
+ return quit("Stopping.", line, from);
+ case "/":
+ if (depth > 0) {
+ warningAt("{a} unterminated regular expression " +
+ "group(s).", line, from + l, depth);
+ }
+ c = s.substr(0, l - 1);
+ q = {
+ g: true,
+ i: true,
+ m: true
+ };
+ while (q[s.charAt(l)] === true) {
+ q[s.charAt(l)] = false;
+ l += 1;
+ }
+ character += l;
+ s = s.substr(l);
+ q = s.charAt(0);
+ if (q === "/" || q === "*") {
+ errorAt("Confusing regular expression.",
+ line, from);
+ }
+ return it("(regexp)", c);
+ case "\\":
+ c = s.charAt(l);
+ if (c < " ") {
+ warningAt(
+"Unexpected control character in regular expression.", line, from + l);
+ } else if (c === "<") {
+ warningAt(
+"Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
+ }
+ l += 1;
+ break;
+ case "(":
+ depth += 1;
+ b = false;
+ if (s.charAt(l) === "?") {
+ l += 1;
+ switch (s.charAt(l)) {
+ case ":":
+ case "=":
+ case "!":
+ l += 1;
+ break;
+ default:
+ warningAt(
+"Expected '{a}' and instead saw '{b}'.", line, from + l, ":", s.charAt(l));
+ }
+ } else {
+ captures += 1;
+ }
+ break;
+ case "|":
+ b = false;
+ break;
+ case ")":
+ if (depth === 0) {
+ warningAt("Unescaped '{a}'.",
+ line, from + l, ")");
+ } else {
+ depth -= 1;
+ }
+ break;
+ case " ":
+ q = 1;
+ while (s.charAt(l) === " ") {
+ l += 1;
+ q += 1;
+ }
+ if (q > 1) {
+ warningAt(
+"Spaces are hard to count. Use {{a}}.", line, from + l, q);
+ }
+ break;
+ case "[":
+ c = s.charAt(l);
+ if (c === "^") {
+ l += 1;
+ if (s.charAt(l) === "]") {
+ errorAt("Unescaped '{a}'.",
+ line, from + l, "^");
+ }
+ }
+ if (c === "]") {
+ warningAt("Empty class.", line,
+ from + l - 1);
+ }
+ isLiteral = false;
+ isInRange = false;
+klass:
+ do {
+ c = s.charAt(l);
+ l += 1;
+ switch (c) {
+ case "[":
+ case "^":
+ warningAt("Unescaped '{a}'.",
+ line, from + l, c);
+ if (isInRange) {
+ isInRange = false;
+ } else {
+ isLiteral = true;
+ }
+ break;
+ case "-":
+ if (isLiteral && !isInRange) {
+ isLiteral = false;
+ isInRange = true;
+ } else if (isInRange) {
+ isInRange = false;
+ } else if (s.charAt(l) === "]") {
+ isInRange = true;
+ } else {
+ if (option.regexdash !== (l === 2 || (l === 3 &&
+ s.charAt(1) === "^"))) {
+ warningAt("Unescaped '{a}'.",
+ line, from + l - 1, "-");
+ }
+ isLiteral = true;
+ }
+ break;
+ case "]":
+ if (isInRange && !option.regexdash) {
+ warningAt("Unescaped '{a}'.",
+ line, from + l - 1, "-");
+ }
+ break klass;
+ case "\\":
+ c = s.charAt(l);
+ if (c < " ") {
+ warningAt(
+"Unexpected control character in regular expression.", line, from + l);
+ } else if (c === "<") {
+ warningAt(
+"Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
+ }
+ l += 1;
+
+ // \w, \s and \d are never part of a character range
+ if (/[wsd]/i.test(c)) {
+ if (isInRange) {
+ warningAt("Unescaped '{a}'.",
+ line, from + l, "-");
+ isInRange = false;
+ }
+ isLiteral = false;
+ } else if (isInRange) {
+ isInRange = false;
+ } else {
+ isLiteral = true;
+ }
+ break;
+ case "/":
+ warningAt("Unescaped '{a}'.",
+ line, from + l - 1, "/");
+
+ if (isInRange) {
+ isInRange = false;
+ } else {
+ isLiteral = true;
+ }
+ break;
+ case "<":
+ if (isInRange) {
+ isInRange = false;
+ } else {
+ isLiteral = true;
+ }
+ break;
+ default:
+ if (isInRange) {
+ isInRange = false;
+ } else {
+ isLiteral = true;
+ }
+ }
+ } while (c);
+ break;
+ case ".":
+ if (option.regexp) {
+ warningAt("Insecure '{a}'.", line,
+ from + l, c);
+ }
+ break;
+ case "]":
+ case "?":
+ case "{":
+ case "}":
+ case "+":
+ case "*":
+ warningAt("Unescaped '{a}'.", line,
+ from + l, c);
+ }
+ if (b) {
+ switch (s.charAt(l)) {
+ case "?":
+ case "+":
+ case "*":
+ l += 1;
+ if (s.charAt(l) === "?") {
+ l += 1;
+ }
+ break;
+ case "{":
+ l += 1;
+ c = s.charAt(l);
+ if (c < "0" || c > "9") {
+ warningAt(
+"Expected a number and instead saw '{a}'.", line, from + l, c);
+ break; // No reason to continue checking numbers.
+ }
+ l += 1;
+ low = +c;
+ for (;;) {
+ c = s.charAt(l);
+ if (c < "0" || c > "9") {
+ break;
+ }
+ l += 1;
+ low = +c + (low * 10);
+ }
+ high = low;
+ if (c === ",") {
+ l += 1;
+ high = Infinity;
+ c = s.charAt(l);
+ if (c >= "0" && c <= "9") {
+ l += 1;
+ high = +c;
+ for (;;) {
+ c = s.charAt(l);
+ if (c < "0" || c > "9") {
+ break;
+ }
+ l += 1;
+ high = +c + (high * 10);
+ }
+ }
+ }
+ if (s.charAt(l) !== "}") {
+ warningAt(
+"Expected '{a}' and instead saw '{b}'.", line, from + l, "}", c);
+ } else {
+ l += 1;
+ }
+ if (s.charAt(l) === "?") {
+ l += 1;
+ }
+ if (low > high) {
+ warningAt(
+"'{a}' should not be greater than '{b}'.", line, from + l, low, high);
+ }
+ }
+ }
+ }
+ c = s.substr(0, l - 1);
+ character += l;
+ s = s.substr(l);
+ return it("(regexp)", c);
+ }
+ return it("(punctuator)", t);
+
+ // punctuator
+
+ case "#":
+ return it("(punctuator)", t);
+ default:
+ return it("(punctuator)", t);
+ }
+ }
+ }
+ }
+ };
+ }());
+
+
+ function addlabel(t, type, token) {
+ if (t === "hasOwnProperty") {
+ warning("'hasOwnProperty' is a really bad name.");
+ }
+
+ // Define t in the current function in the current scope.
+ if (type === "exception") {
+ if (is_own(funct["(context)"], t)) {
+ if (funct[t] !== true && !option.node) {
+ warning("Value of '{a}' may be overwritten in IE.", nexttoken, t);
+ }
+ }
+ }
+
+ if (is_own(funct, t) && !funct["(global)"]) {
+ if (funct[t] === true) {
+ if (option.latedef)
+ warning("'{a}' was used before it was defined.", nexttoken, t);
+ } else {
+ if (!option.shadow && type !== "exception") {
+ warning("'{a}' is already defined.", nexttoken, t);
+ }
+ }
+ }
+
+ funct[t] = type;
+
+ if (token) {
+ funct["(tokens)"][t] = token;
+ }
+
+ if (funct["(global)"]) {
+ global[t] = funct;
+ if (is_own(implied, t)) {
+ if (option.latedef)
+ warning("'{a}' was used before it was defined.", nexttoken, t);
+ delete implied[t];
+ }
+ } else {
+ scope[t] = funct;
+ }
+ }
+
+
+ function doOption() {
+ var nt = nexttoken;
+ var o = nt.value;
+ var quotmarkValue = option.quotmark;
+ var predef = {};
+ var b, obj, filter, t, tn, v, minus;
+
+ switch (o) {
+ case "*/":
+ error("Unbegun comment.");
+ break;
+ case "/*members":
+ case "/*member":
+ o = "/*members";
+ if (!membersOnly) {
+ membersOnly = {};
+ }
+ obj = membersOnly;
+ option.quotmark = false;
+ break;
+ case "/*jshint":
+ case "/*jslint":
+ obj = option;
+ filter = boolOptions;
+ break;
+ case "/*global":
+ obj = predef;
+ break;
+ default:
+ error("What?");
+ }
+
+ t = lex.token();
+
+loop:
+ for (;;) {
+ minus = false;
+ for (;;) {
+ if (t.type === "special" && t.value === "*/") {
+ break loop;
+ }
+ if (t.id !== "(endline)" && t.id !== ",") {
+ break;
+ }
+ t = lex.token();
+ }
+
+ if (o === "/*global" && t.value === "-") {
+ minus = true;
+ t = lex.token();
+ }
+
+ if (t.type !== "(string)" && t.type !== "(identifier)" && o !== "/*members") {
+ error("Bad option.", t);
+ }
+
+ v = lex.token();
+ if (v.id === ":") {
+ v = lex.token();
+
+ if (obj === membersOnly) {
+ error("Expected '{a}' and instead saw '{b}'.", t, "*/", ":");
+ }
+
+ if (o === "/*jshint") {
+ checkOption(t.value, t);
+ }
+
+ var numericVals = [
+ "maxstatements",
+ "maxparams",
+ "maxdepth",
+ "maxcomplexity",
+ "maxerr",
+ "maxlen",
+ "indent"
+ ];
+
+ if (numericVals.indexOf(t.value) > -1 && (o === "/*jshint" || o === "/*jslint")) {
+ b = +v.value;
+
+ if (typeof b !== "number" || !isFinite(b) || b <= 0 || Math.floor(b) !== b) {
+ error("Expected a small integer and instead saw '{a}'.", v, v.value);
+ }
+
+ if (t.value === "indent")
+ obj.white = true;
+
+ obj[t.value] = b;
+ } else if (t.value === "validthis") {
+ if (funct["(global)"]) {
+ error("Option 'validthis' can't be used in a global scope.");
+ } else {
+ if (v.value === "true" || v.value === "false")
+ obj[t.value] = v.value === "true";
+ else
+ error("Bad option value.", v);
+ }
+ } else if (t.value === "quotmark" && (o === "/*jshint")) {
+ switch (v.value) {
+ case "true":
+ obj.quotmark = true;
+ break;
+ case "false":
+ obj.quotmark = false;
+ break;
+ case "double":
+ case "single":
+ obj.quotmark = v.value;
+ break;
+ default:
+ error("Bad option value.", v);
+ }
+ } else if (v.value === "true" || v.value === "false") {
+ if (o === "/*jslint") {
+ tn = renamedOptions[t.value] || t.value;
+ obj[tn] = v.value === "true";
+ if (invertedOptions[tn] !== undefined) {
+ obj[tn] = !obj[tn];
+ }
+ } else {
+ obj[t.value] = v.value === "true";
+ }
+
+ if (t.value === "newcap")
+ obj["(explicitNewcap)"] = true;
+ } else {
+ error("Bad option value.", v);
+ }
+ t = lex.token();
+ } else {
+ if (o === "/*jshint" || o === "/*jslint") {
+ error("Missing option value.", t);
+ }
+
+ obj[t.value] = false;
+
+ if (o === "/*global" && minus === true) {
+ JSHINT.blacklist[t.value] = t.value;
+ updatePredefined();
+ }
+
+ t = v;
+ }
+ }
+
+ if (o === "/*members") {
+ option.quotmark = quotmarkValue;
+ }
+
+ combine(predefined, predef);
+
+ for (var key in predef) {
+ if (is_own(predef, key)) {
+ declared[key] = nt;
+ }
+ }
+
+ if (filter) {
+ assume();
+ }
+ }
+
+
+// We need a peek function. If it has an argument, it peeks that much farther
+// ahead. It is used to distinguish
+// for ( var i in ...
+// from
+// for ( var i = ...
+
+ function peek(p) {
+ var i = p || 0, j = 0, t;
+
+ while (j <= i) {
+ t = lookahead[j];
+ if (!t) {
+ t = lookahead[j] = lex.token();
+ }
+ j += 1;
+ }
+ return t;
+ }
+
+
+
+// Produce the next token. It looks for programming errors.
+
+ function advance(id, t) {
+ switch (token.id) {
+ case "(number)":
+ if (nexttoken.id === ".") {
+ warning("A dot following a number can be confused with a decimal point.", token);
+ }
+ break;
+ case "-":
+ if (nexttoken.id === "-" || nexttoken.id === "--") {
+ warning("Confusing minusses.");
+ }
+ break;
+ case "+":
+ if (nexttoken.id === "+" || nexttoken.id === "++") {
+ warning("Confusing plusses.");
+ }
+ break;
+ }
+
+ if (token.type === "(string)" || token.identifier) {
+ anonname = token.value;
+ }
+
+ if (id && nexttoken.id !== id) {
+ if (t) {
+ if (nexttoken.id === "(end)") {
+ warning("Unmatched '{a}'.", t, t.id);
+ } else {
+ warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
+ nexttoken, id, t.id, t.line, nexttoken.value);
+ }
+ } else if (nexttoken.type !== "(identifier)" ||
+ nexttoken.value !== id) {
+ warning("Expected '{a}' and instead saw '{b}'.",
+ nexttoken, id, nexttoken.value);
+ }
+ }
+
+ prevtoken = token;
+ token = nexttoken;
+ for (;;) {
+ nexttoken = lookahead.shift() || lex.token();
+ if (nexttoken.id === "(end)" || nexttoken.id === "(error)") {
+ return;
+ }
+ if (nexttoken.type === "special") {
+ doOption();
+ } else {
+ if (nexttoken.id !== "(endline)") {
+ break;
+ }
+ }
+ }
+ }
+
+
+// This is the heart of JSHINT, the Pratt parser. In addition to parsing, it
+// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
+// like .nud except that it is only used on the first token of a statement.
+// Having .fud makes it much easier to define statement-oriented languages like
+// JavaScript. I retained Pratt's nomenclature.
+
+// .nud Null denotation
+// .fud First null denotation
+// .led Left denotation
+// lbp Left binding power
+// rbp Right binding power
+
+// They are elements of the parsing method called Top Down Operator Precedence.
+
+ function expression(rbp, initial) {
+ var left, isArray = false, isObject = false;
+
+ if (nexttoken.id === "(end)")
+ error("Unexpected early end of program.", token);
+
+ advance();
+ if (initial) {
+ anonname = "anonymous";
+ funct["(verb)"] = token.value;
+ }
+ if (initial === true && token.fud) {
+ left = token.fud();
+ } else {
+ if (token.nud) {
+ left = token.nud();
+ } else {
+ if (nexttoken.type === "(number)" && token.id === ".") {
+ warning("A leading decimal point can be confused with a dot: '.{a}'.",
+ token, nexttoken.value);
+ advance();
+ return token;
+ } else {
+ error("Expected an identifier and instead saw '{a}'.",
+ token, token.id);
+ }
+ }
+ while (rbp < nexttoken.lbp) {
+ isArray = token.value === "Array";
+ isObject = token.value === "Object";
+
+ // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object()
+ // Line breaks in IfStatement heads exist to satisfy the checkJSHint
+ // "Line too long." error.
+ if (left && (left.value || (left.first && left.first.value))) {
+ // If the left.value is not "new", or the left.first.value is a "."
+ // then safely assume that this is not "new Array()" and possibly
+ // not "new Object()"...
+ if (left.value !== "new" ||
+ (left.first && left.first.value && left.first.value === ".")) {
+ isArray = false;
+ // ...In the case of Object, if the left.value and token.value
+ // are not equal, then safely assume that this not "new Object()"
+ if (left.value !== token.value) {
+ isObject = false;
+ }
+ }
+ }
+
+ advance();
+ if (isArray && token.id === "(" && nexttoken.id === ")")
+ warning("Use the array literal notation [].", token);
+ if (isObject && token.id === "(" && nexttoken.id === ")")
+ warning("Use the object literal notation {}.", token);
+ if (token.led) {
+ left = token.led(left);
+ } else {
+ error("Expected an operator and instead saw '{a}'.",
+ token, token.id);
+ }
+ }
+ }
+ return left;
+ }
+
+
+// Functions for conformance of style.
+
+ function adjacent(left, right) {
+ left = left || token;
+ right = right || nexttoken;
+ if (option.white) {
+ if (left.character !== right.from && left.line === right.line) {
+ left.from += (left.character - left.from);
+ warning("Unexpected space after '{a}'.", left, left.value);
+ }
+ }
+ }
+
+ function nobreak(left, right) {
+ left = left || token;
+ right = right || nexttoken;
+ if (option.white && (left.character !== right.from || left.line !== right.line)) {
+ warning("Unexpected space before '{a}'.", right, right.value);
+ }
+ }
+
+ function nospace(left, right) {
+ left = left || token;
+ right = right || nexttoken;
+ if (option.white && !left.comment) {
+ if (left.line === right.line) {
+ adjacent(left, right);
+ }
+ }
+ }
+
+ function nonadjacent(left, right) {
+ if (option.white) {
+ left = left || token;
+ right = right || nexttoken;
+ if (left.value === ";" && right.value === ";") {
+ return;
+ }
+ if (left.line === right.line && left.character === right.from) {
+ left.from += (left.character - left.from);
+ warning("Missing space after '{a}'.",
+ left, left.value);
+ }
+ }
+ }
+
+ function nobreaknonadjacent(left, right) {
+ left = left || token;
+ right = right || nexttoken;
+ if (!option.laxbreak && left.line !== right.line) {
+ warning("Bad line breaking before '{a}'.", right, right.id);
+ } else if (option.white) {
+ left = left || token;
+ right = right || nexttoken;
+ if (left.character === right.from) {
+ left.from += (left.character - left.from);
+ warning("Missing space after '{a}'.",
+ left, left.value);
+ }
+ }
+ }
+
+ function indentation(bias) {
+ var i;
+ if (option.white && nexttoken.id !== "(end)") {
+ i = indent + (bias || 0);
+ if (nexttoken.from !== i) {
+ warning(
+"Expected '{a}' to have an indentation at {b} instead at {c}.",
+ nexttoken, nexttoken.value, i, nexttoken.from);
+ }
+ }
+ }
+
+ function nolinebreak(t) {
+ t = t || token;
+ if (t.line !== nexttoken.line) {
+ warning("Line breaking error '{a}'.", t, t.value);
+ }
+ }
+
+
+ function comma() {
+ if (token.line !== nexttoken.line) {
+ if (!option.laxcomma) {
+ if (comma.first) {
+ warning("Comma warnings can be turned off with 'laxcomma'");
+ comma.first = false;
+ }
+ warning("Bad line breaking before '{a}'.", token, nexttoken.id);
+ }
+ } else if (!token.comment && token.character !== nexttoken.from && option.white) {
+ token.from += (token.character - token.from);
+ warning("Unexpected space after '{a}'.", token, token.value);
+ }
+ advance(",");
+ nonadjacent(token, nexttoken);
+ }
+
+
+// Functional constructors for making the symbols that will be inherited by
+// tokens.
+
+ function symbol(s, p) {
+ var x = syntax[s];
+ if (!x || typeof x !== "object") {
+ syntax[s] = x = {
+ id: s,
+ lbp: p,
+ value: s
+ };
+ }
+ return x;
+ }
+
+
+ function delim(s) {
+ return symbol(s, 0);
+ }
+
+
+ function stmt(s, f) {
+ var x = delim(s);
+ x.identifier = x.reserved = true;
+ x.fud = f;
+ return x;
+ }
+
+
+ function blockstmt(s, f) {
+ var x = stmt(s, f);
+ x.block = true;
+ return x;
+ }
+
+
+ function reserveName(x) {
+ var c = x.id.charAt(0);
+ if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) {
+ x.identifier = x.reserved = true;
+ }
+ return x;
+ }
+
+
+ function prefix(s, f) {
+ var x = symbol(s, 150);
+ reserveName(x);
+ x.nud = (typeof f === "function") ? f : function () {
+ this.right = expression(150);
+ this.arity = "unary";
+ if (this.id === "++" || this.id === "--") {
+ if (option.plusplus) {
+ warning("Unexpected use of '{a}'.", this, this.id);
+ } else if ((!this.right.identifier || this.right.reserved) &&
+ this.right.id !== "." && this.right.id !== "[") {
+ warning("Bad operand.", this);
+ }
+ }
+ return this;
+ };
+ return x;
+ }
+
+
+ function type(s, f) {
+ var x = delim(s);
+ x.type = s;
+ x.nud = f;
+ return x;
+ }
+
+
+ function reserve(s, f) {
+ var x = type(s, f);
+ x.identifier = x.reserved = true;
+ return x;
+ }
+
+
+ function reservevar(s, v) {
+ return reserve(s, function () {
+ if (typeof v === "function") {
+ v(this);
+ }
+ return this;
+ });
+ }
+
+
+ function infix(s, f, p, w) {
+ var x = symbol(s, p);
+ reserveName(x);
+ x.led = function (left) {
+ if (!w) {
+ nobreaknonadjacent(prevtoken, token);
+ nonadjacent(token, nexttoken);
+ }
+ if (s === "in" && left.id === "!") {
+ warning("Confusing use of '{a}'.", left, "!");
+ }
+ if (typeof f === "function") {
+ return f(left, this);
+ } else {
+ this.left = left;
+ this.right = expression(p);
+ return this;
+ }
+ };
+ return x;
+ }
+
+
+ function relation(s, f) {
+ var x = symbol(s, 100);
+ x.led = function (left) {
+ nobreaknonadjacent(prevtoken, token);
+ nonadjacent(token, nexttoken);
+ var right = expression(100);
+
+ if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) {
+ warning("Use the isNaN function to compare with NaN.", this);
+ } else if (f) {
+ f.apply(this, [left, right]);
+ }
+ if (left.id === "!") {
+ warning("Confusing use of '{a}'.", left, "!");
+ }
+ if (right.id === "!") {
+ warning("Confusing use of '{a}'.", right, "!");
+ }
+ this.left = left;
+ this.right = right;
+ return this;
+ };
+ return x;
+ }
+
+
+ function isPoorRelation(node) {
+ return node &&
+ ((node.type === "(number)" && +node.value === 0) ||
+ (node.type === "(string)" && node.value === "") ||
+ (node.type === "null" && !option.eqnull) ||
+ node.type === "true" ||
+ node.type === "false" ||
+ node.type === "undefined");
+ }
+
+
+ function assignop(s) {
+ symbol(s, 20).exps = true;
+
+ return infix(s, function (left, that) {
+ that.left = left;
+
+ if (predefined[left.value] === false &&
+ scope[left.value]["(global)"] === true) {
+ warning("Read only.", left);
+ } else if (left["function"]) {
+ warning("'{a}' is a function.", left, left.value);
+ }
+
+ if (left) {
+ if (option.esnext && funct[left.value] === "const") {
+ warning("Attempting to override '{a}' which is a constant", left, left.value);
+ }
+
+ if (left.id === "." || left.id === "[") {
+ if (!left.left || left.left.value === "arguments") {
+ warning("Bad assignment.", that);
+ }
+ that.right = expression(19);
+ return that;
+ } else if (left.identifier && !left.reserved) {
+ if (funct[left.value] === "exception") {
+ warning("Do not assign to the exception parameter.", left);
+ }
+ that.right = expression(19);
+ return that;
+ }
+
+ if (left === syntax["function"]) {
+ warning(
+"Expected an identifier in an assignment and instead saw a function invocation.",
+ token);
+ }
+ }
+
+ error("Bad assignment.", that);
+ }, 20);
+ }
+
+
+ function bitwise(s, f, p) {
+ var x = symbol(s, p);
+ reserveName(x);
+ x.led = (typeof f === "function") ? f : function (left) {
+ if (option.bitwise) {
+ warning("Unexpected use of '{a}'.", this, this.id);
+ }
+ this.left = left;
+ this.right = expression(p);
+ return this;
+ };
+ return x;
+ }
+
+
+ function bitwiseassignop(s) {
+ symbol(s, 20).exps = true;
+ return infix(s, function (left, that) {
+ if (option.bitwise) {
+ warning("Unexpected use of '{a}'.", that, that.id);
+ }
+ nonadjacent(prevtoken, token);
+ nonadjacent(token, nexttoken);
+ if (left) {
+ if (left.id === "." || left.id === "[" ||
+ (left.identifier && !left.reserved)) {
+ expression(19);
+ return that;
+ }
+ if (left === syntax["function"]) {
+ warning(
+"Expected an identifier in an assignment, and instead saw a function invocation.",
+ token);
+ }
+ return that;
+ }
+ error("Bad assignment.", that);
+ }, 20);
+ }
+
+
+ function suffix(s) {
+ var x = symbol(s, 150);
+ x.led = function (left) {
+ if (option.plusplus) {
+ warning("Unexpected use of '{a}'.", this, this.id);
+ } else if ((!left.identifier || left.reserved) &&
+ left.id !== "." && left.id !== "[") {
+ warning("Bad operand.", this);
+ }
+ this.left = left;
+ return this;
+ };
+ return x;
+ }
+
+
+ // fnparam means that this identifier is being defined as a function
+ // argument (see identifier())
+ function optionalidentifier(fnparam) {
+ if (nexttoken.identifier) {
+ advance();
+ if (token.reserved && !option.es5) {
+ // `undefined` as a function param is a common pattern to protect
+ // against the case when somebody does `undefined = true` and
+ // help with minification. More info: https://gist.github.com/315916
+ if (!fnparam || token.value !== "undefined") {
+ warning("Expected an identifier and instead saw '{a}' (a reserved word).",
+ token, token.id);
+ }
+ }
+ return token.value;
+ }
+ }
+
+ // fnparam means that this identifier is being defined as a function
+ // argument
+ function identifier(fnparam) {
+ var i = optionalidentifier(fnparam);
+ if (i) {
+ return i;
+ }
+ if (token.id === "function" && nexttoken.id === "(") {
+ warning("Missing name in function declaration.");
+ } else {
+ error("Expected an identifier and instead saw '{a}'.",
+ nexttoken, nexttoken.value);
+ }
+ }
+
+
+ function reachable(s) {
+ var i = 0, t;
+ if (nexttoken.id !== ";" || noreach) {
+ return;
+ }
+ for (;;) {
+ t = peek(i);
+ if (t.reach) {
+ return;
+ }
+ if (t.id !== "(endline)") {
+ if (t.id === "function") {
+ if (!option.latedef) {
+ break;
+ }
+ warning(
+"Inner functions should be listed at the top of the outer function.", t);
+ break;
+ }
+ warning("Unreachable '{a}' after '{b}'.", t, t.value, s);
+ break;
+ }
+ i += 1;
+ }
+ }
+
+
+ function statement(noindent) {
+ var i = indent, r, s = scope, t = nexttoken;
+
+ if (t.id === ";") {
+ advance(";");
+ return;
+ }
+
+ // Is this a labelled statement?
+
+ if (t.identifier && !t.reserved && peek().id === ":") {
+ advance();
+ advance(":");
+ scope = Object.create(s);
+ addlabel(t.value, "label");
+
+ if (!nexttoken.labelled && nexttoken.value !== "{") {
+ warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value);
+ }
+
+ if (jx.test(t.value + ":")) {
+ warning("Label '{a}' looks like a javascript url.", t, t.value);
+ }
+
+ nexttoken.label = t.value;
+ t = nexttoken;
+ }
+
+ // Is it a lonely block?
+
+ if (t.id === "{") {
+ block(true, true);
+ return;
+ }
+
+ // Parse the statement.
+
+ if (!noindent) {
+ indentation();
+ }
+ r = expression(0, true);
+
+ // Look for the final semicolon.
+
+ if (!t.block) {
+ if (!option.expr && (!r || !r.exps)) {
+ warning("Expected an assignment or function call and instead saw an expression.",
+ token);
+ } else if (option.nonew && r.id === "(" && r.left.id === "new") {
+ warning("Do not use 'new' for side effects.", t);
+ }
+
+ if (nexttoken.id === ",") {
+ return comma();
+ }
+
+ if (nexttoken.id !== ";") {
+ if (!option.asi) {
+ // If this is the last statement in a block that ends on
+ // the same line *and* option lastsemic is on, ignore the warning.
+ // Otherwise, complain about missing semicolon.
+ if (!option.lastsemic || nexttoken.id !== "}" ||
+ nexttoken.line !== token.line) {
+ warningAt("Missing semicolon.", token.line, token.character);
+ }
+ }
+ } else {
+ adjacent(token, nexttoken);
+ advance(";");
+ nonadjacent(token, nexttoken);
+ }
+ }
+
+// Restore the indentation.
+
+ indent = i;
+ scope = s;
+ return r;
+ }
+
+
+ function statements(startLine) {
+ var a = [], p;
+
+ while (!nexttoken.reach && nexttoken.id !== "(end)") {
+ if (nexttoken.id === ";") {
+ p = peek();
+ if (!p || p.id !== "(") {
+ warning("Unnecessary semicolon.");
+ }
+ advance(";");
+ } else {
+ a.push(statement(startLine === nexttoken.line));
+ }
+ }
+ return a;
+ }
+
+
+ /*
+ * read all directives
+ * recognizes a simple form of asi, but always
+ * warns, if it is used
+ */
+ function directives() {
+ var i, p, pn;
+
+ for (;;) {
+ if (nexttoken.id === "(string)") {
+ p = peek(0);
+ if (p.id === "(endline)") {
+ i = 1;
+ do {
+ pn = peek(i);
+ i = i + 1;
+ } while (pn.id === "(endline)");
+
+ if (pn.id !== ";") {
+ if (pn.id !== "(string)" && pn.id !== "(number)" &&
+ pn.id !== "(regexp)" && pn.identifier !== true &&
+ pn.id !== "}") {
+ break;
+ }
+ warning("Missing semicolon.", nexttoken);
+ } else {
+ p = pn;
+ }
+ } else if (p.id === "}") {
+ // directive with no other statements, warn about missing semicolon
+ warning("Missing semicolon.", p);
+ } else if (p.id !== ";") {
+ break;
+ }
+
+ indentation();
+ advance();
+ if (directive[token.value]) {
+ warning("Unnecessary directive \"{a}\".", token, token.value);
+ }
+
+ if (token.value === "use strict") {
+ if (!option["(explicitNewcap)"])
+ option.newcap = true;
+ option.undef = true;
+ }
+
+ // there's no directive negation, so always set to true
+ directive[token.value] = true;
+
+ if (p.id === ";") {
+ advance(";");
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+
+ /*
+ * Parses a single block. A block is a sequence of statements wrapped in
+ * braces.
+ *
+ * ordinary - true for everything but function bodies and try blocks.
+ * stmt - true if block can be a single statement (e.g. in if/for/while).
+ * isfunc - true if block is a function body
+ */
+ function block(ordinary, stmt, isfunc) {
+ var a,
+ b = inblock,
+ old_indent = indent,
+ m,
+ s = scope,
+ t,
+ line,
+ d;
+
+ inblock = ordinary;
+
+ if (!ordinary || !option.funcscope)
+ scope = Object.create(scope);
+
+ nonadjacent(token, nexttoken);
+ t = nexttoken;
+
+ var metrics = funct["(metrics)"];
+ metrics.nestedBlockDepth += 1;
+ metrics.verifyMaxNestedBlockDepthPerFunction();
+
+ if (nexttoken.id === "{") {
+ advance("{");
+ line = token.line;
+ if (nexttoken.id !== "}") {
+ indent += option.indent;
+ while (!ordinary && nexttoken.from > indent) {
+ indent += option.indent;
+ }
+
+ if (isfunc) {
+ m = {};
+ for (d in directive) {
+ if (is_own(directive, d)) {
+ m[d] = directive[d];
+ }
+ }
+ directives();
+
+ if (option.strict && funct["(context)"]["(global)"]) {
+ if (!m["use strict"] && !directive["use strict"]) {
+ warning("Missing \"use strict\" statement.");
+ }
+ }
+ }
+
+ a = statements(line);
+
+ metrics.statementCount += a.length;
+
+ if (isfunc) {
+ directive = m;
+ }
+
+ indent -= option.indent;
+ if (line !== nexttoken.line) {
+ indentation();
+ }
+ } else if (line !== nexttoken.line) {
+ indentation();
+ }
+ advance("}", t);
+ indent = old_indent;
+ } else if (!ordinary) {
+ error("Expected '{a}' and instead saw '{b}'.",
+ nexttoken, "{", nexttoken.value);
+ } else {
+ if (!stmt || option.curly)
+ warning("Expected '{a}' and instead saw '{b}'.",
+ nexttoken, "{", nexttoken.value);
+
+ noreach = true;
+ indent += option.indent;
+ // test indentation only if statement is in new line
+ a = [statement(nexttoken.line === token.line)];
+ indent -= option.indent;
+ noreach = false;
+ }
+ funct["(verb)"] = null;
+ if (!ordinary || !option.funcscope) scope = s;
+ inblock = b;
+ if (ordinary && option.noempty && (!a || a.length === 0)) {
+ warning("Empty block.");
+ }
+ metrics.nestedBlockDepth -= 1;
+ return a;
+ }
+
+
+ function countMember(m) {
+ if (membersOnly && typeof membersOnly[m] !== "boolean") {
+ warning("Unexpected /*member '{a}'.", token, m);
+ }
+ if (typeof member[m] === "number") {
+ member[m] += 1;
+ } else {
+ member[m] = 1;
+ }
+ }
+
+
+ function note_implied(token) {
+ var name = token.value, line = token.line, a = implied[name];
+ if (typeof a === "function") {
+ a = false;
+ }
+
+ if (!a) {
+ a = [line];
+ implied[name] = a;
+ } else if (a[a.length - 1] !== line) {
+ a.push(line);
+ }
+ }
+
+
+ // Build the syntax table by declaring the syntactic elements of the language.
+
+ type("(number)", function () {
+ return this;
+ });
+
+ type("(string)", function () {
+ return this;
+ });
+
+ syntax["(identifier)"] = {
+ type: "(identifier)",
+ lbp: 0,
+ identifier: true,
+ nud: function () {
+ var v = this.value,
+ s = scope[v],
+ f;
+
+ if (typeof s === "function") {
+ // Protection against accidental inheritance.
+ s = undefined;
+ } else if (typeof s === "boolean") {
+ f = funct;
+ funct = functions[0];
+ addlabel(v, "var");
+ s = funct;
+ funct = f;
+ }
+
+ // The name is in scope and defined in the current function.
+ if (funct === s) {
+ // Change 'unused' to 'var', and reject labels.
+ switch (funct[v]) {
+ case "unused":
+ funct[v] = "var";
+ break;
+ case "unction":
+ funct[v] = "function";
+ this["function"] = true;
+ break;
+ case "function":
+ this["function"] = true;
+ break;
+ case "label":
+ warning("'{a}' is a statement label.", token, v);
+ break;
+ }
+ } else if (funct["(global)"]) {
+ // The name is not defined in the function. If we are in the global
+ // scope, then we have an undefined variable.
+ //
+ // Operators typeof and delete do not raise runtime errors even if
+ // the base object of a reference is null so no need to display warning
+ // if we're inside of typeof or delete.
+
+ if (option.undef && typeof predefined[v] !== "boolean") {
+ // Attempting to subscript a null reference will throw an
+ // error, even within the typeof and delete operators
+ if (!(anonname === "typeof" || anonname === "delete") ||
+ (nexttoken && (nexttoken.value === "." || nexttoken.value === "["))) {
+
+ isundef(funct, "'{a}' is not defined.", token, v);
+ }
+ }
+
+ note_implied(token);
+ } else {
+ // If the name is already defined in the current
+ // function, but not as outer, then there is a scope error.
+
+ switch (funct[v]) {
+ case "closure":
+ case "function":
+ case "var":
+ case "unused":
+ warning("'{a}' used out of scope.", token, v);
+ break;
+ case "label":
+ warning("'{a}' is a statement label.", token, v);
+ break;
+ case "outer":
+ case "global":
+ break;
+ default:
+ // If the name is defined in an outer function, make an outer entry,
+ // and if it was unused, make it var.
+ if (s === true) {
+ funct[v] = true;
+ } else if (s === null) {
+ warning("'{a}' is not allowed.", token, v);
+ note_implied(token);
+ } else if (typeof s !== "object") {
+ // Operators typeof and delete do not raise runtime errors even
+ // if the base object of a reference is null so no need to
+ // display warning if we're inside of typeof or delete.
+ if (option.undef) {
+ // Attempting to subscript a null reference will throw an
+ // error, even within the typeof and delete operators
+ if (!(anonname === "typeof" || anonname === "delete") ||
+ (nexttoken &&
+ (nexttoken.value === "." || nexttoken.value === "["))) {
+
+ isundef(funct, "'{a}' is not defined.", token, v);
+ }
+ }
+ funct[v] = true;
+ note_implied(token);
+ } else {
+ switch (s[v]) {
+ case "function":
+ case "unction":
+ this["function"] = true;
+ s[v] = "closure";
+ funct[v] = s["(global)"] ? "global" : "outer";
+ break;
+ case "var":
+ case "unused":
+ s[v] = "closure";
+ funct[v] = s["(global)"] ? "global" : "outer";
+ break;
+ case "closure":
+ funct[v] = s["(global)"] ? "global" : "outer";
+ break;
+ case "label":
+ warning("'{a}' is a statement label.", token, v);
+ }
+ }
+ }
+ }
+ return this;
+ },
+ led: function () {
+ error("Expected an operator and instead saw '{a}'.",
+ nexttoken, nexttoken.value);
+ }
+ };
+
+ type("(regexp)", function () {
+ return this;
+ });
+
+
+// ECMAScript parser
+
+ delim("(endline)");
+ delim("(begin)");
+ delim("(end)").reach = true;
+ delim("</").reach = true;
+ delim("<!");
+ delim("<!--");
+ delim("-->");
+ delim("(error)").reach = true;
+ delim("}").reach = true;
+ delim(")");
+ delim("]");
+ delim("\"").reach = true;
+ delim("'").reach = true;
+ delim(";");
+ delim(":").reach = true;
+ delim(",");
+ delim("#");
+ delim("@");
+ reserve("else");
+ reserve("case").reach = true;
+ reserve("catch");
+ reserve("default").reach = true;
+ reserve("finally");
+ reservevar("arguments", function (x) {
+ if (directive["use strict"] && funct["(global)"]) {
+ warning("Strict violation.", x);
+ }
+ });
+ reservevar("eval");
+ reservevar("false");
+ reservevar("Infinity");
+ reservevar("null");
+ reservevar("this", function (x) {
+ if (directive["use strict"] && !option.validthis && ((funct["(statement)"] &&
+ funct["(name)"].charAt(0) > "Z") || funct["(global)"])) {
+ warning("Possible strict violation.", x);
+ }
+ });
+ reservevar("true");
+ reservevar("undefined");
+ assignop("=", "assign", 20);
+ assignop("+=", "assignadd", 20);
+ assignop("-=", "assignsub", 20);
+ assignop("*=", "assignmult", 20);
+ assignop("/=", "assigndiv", 20).nud = function () {
+ error("A regular expression literal can be confused with '/='.");
+ };
+ assignop("%=", "assignmod", 20);
+ bitwiseassignop("&=", "assignbitand", 20);
+ bitwiseassignop("|=", "assignbitor", 20);
+ bitwiseassignop("^=", "assignbitxor", 20);
+ bitwiseassignop("<<=", "assignshiftleft", 20);
+ bitwiseassignop(">>=", "assignshiftright", 20);
+ bitwiseassignop(">>>=", "assignshiftrightunsigned", 20);
+ infix("?", function (left, that) {
+ that.left = left;
+ that.right = expression(10);
+ advance(":");
+ that["else"] = expression(10);
+ return that;
+ }, 30);
+
+ infix("||", "or", 40);
+ infix("&&", "and", 50);
+ bitwise("|", "bitor", 70);
+ bitwise("^", "bitxor", 80);
+ bitwise("&", "bitand", 90);
+ relation("==", function (left, right) {
+ var eqnull = option.eqnull && (left.value === "null" || right.value === "null");
+
+ if (!eqnull && option.eqeqeq)
+ warning("Expected '{a}' and instead saw '{b}'.", this, "===", "==");
+ else if (isPoorRelation(left))
+ warning("Use '{a}' to compare with '{b}'.", this, "===", left.value);
+ else if (isPoorRelation(right))
+ warning("Use '{a}' to compare with '{b}'.", this, "===", right.value);
+
+ return this;
+ });
+ relation("===");
+ relation("!=", function (left, right) {
+ var eqnull = option.eqnull &&
+ (left.value === "null" || right.value === "null");
+
+ if (!eqnull && option.eqeqeq) {
+ warning("Expected '{a}' and instead saw '{b}'.",
+ this, "!==", "!=");
+ } else if (isPoorRelation(left)) {
+ warning("Use '{a}' to compare with '{b}'.",
+ this, "!==", left.value);
+ } else if (isPoorRelation(right)) {
+ warning("Use '{a}' to compare with '{b}'.",
+ this, "!==", right.value);
+ }
+ return this;
+ });
+ relation("!==");
+ relation("<");
+ relation(">");
+ relation("<=");
+ relation(">=");
+ bitwise("<<", "shiftleft", 120);
+ bitwise(">>", "shiftright", 120);
+ bitwise(">>>", "shiftrightunsigned", 120);
+ infix("in", "in", 120);
+ infix("instanceof", "instanceof", 120);
+ infix("+", function (left, that) {
+ var right = expression(130);
+ if (left && right && left.id === "(string)" && right.id === "(string)") {
+ left.value += right.value;
+ left.character = right.character;
+ if (!option.scripturl && jx.test(left.value)) {
+ warning("JavaScript URL.", left);
+ }
+ return left;
+ }
+ that.left = left;
+ that.right = right;
+ return that;
+ }, 130);
+ prefix("+", "num");
+ prefix("+++", function () {
+ warning("Confusing pluses.");
+ this.right = expression(150);
+ this.arity = "unary";
+ return this;
+ });
+ infix("+++", function (left) {
+ warning("Confusing pluses.");
+ this.left = left;
+ this.right = expression(130);
+ return this;
+ }, 130);
+ infix("-", "sub", 130);
+ prefix("-", "neg");
+ prefix("---", function () {
+ warning("Confusing minuses.");
+ this.right = expression(150);
+ this.arity = "unary";
+ return this;
+ });
+ infix("---", function (left) {
+ warning("Confusing minuses.");
+ this.left = left;
+ this.right = expression(130);
+ return this;
+ }, 130);
+ infix("*", "mult", 140);
+ infix("/", "div", 140);
+ infix("%", "mod", 140);
+
+ suffix("++", "postinc");
+ prefix("++", "preinc");
+ syntax["++"].exps = true;
+
+ suffix("--", "postdec");
+ prefix("--", "predec");
+ syntax["--"].exps = true;
+ prefix("delete", function () {
+ var p = expression(0);
+ if (!p || (p.id !== "." && p.id !== "[")) {
+ warning("Variables should not be deleted.");
+ }
+ this.first = p;
+ return this;
+ }).exps = true;
+
+ prefix("~", function () {
+ if (option.bitwise) {
+ warning("Unexpected '{a}'.", this, "~");
+ }
+ expression(150);
+ return this;
+ });
+
+ prefix("!", function () {
+ this.right = expression(150);
+ this.arity = "unary";
+ if (bang[this.right.id] === true) {
+ warning("Confusing use of '{a}'.", this, "!");
+ }
+ return this;
+ });
+ prefix("typeof", "typeof");
+ prefix("new", function () {
+ var c = expression(155), i;
+ if (c && c.id !== "function") {
+ if (c.identifier) {
+ c["new"] = true;
+ switch (c.value) {
+ case "Number":
+ case "String":
+ case "Boolean":
+ case "Math":
+ case "JSON":
+ warning("Do not use {a} as a constructor.", prevtoken, c.value);
+ break;
+ case "Function":
+ if (!option.evil) {
+ warning("The Function constructor is eval.");
+ }
+ break;
+ case "Date":
+ case "RegExp":
+ break;
+ default:
+ if (c.id !== "function") {
+ i = c.value.substr(0, 1);
+ if (option.newcap && (i < "A" || i > "Z") && !is_own(global, c.value)) {
+ warning("A constructor name should start with an uppercase letter.",
+ token);
+ }
+ }
+ }
+ } else {
+ if (c.id !== "." && c.id !== "[" && c.id !== "(") {
+ warning("Bad constructor.", token);
+ }
+ }
+ } else {
+ if (!option.supernew)
+ warning("Weird construction. Delete 'new'.", this);
+ }
+ adjacent(token, nexttoken);
+ if (nexttoken.id !== "(" && !option.supernew) {
+ warning("Missing '()' invoking a constructor.",
+ token, token.value);
+ }
+ this.first = c;
+ return this;
+ });
+ syntax["new"].exps = true;
+
+ prefix("void").exps = true;
+
+ infix(".", function (left, that) {
+ adjacent(prevtoken, token);
+ nobreak();
+ var m = identifier();
+ if (typeof m === "string") {
+ countMember(m);
+ }
+ that.left = left;
+ that.right = m;
+ if (left && left.value === "arguments" && (m === "callee" || m === "caller")) {
+ if (option.noarg)
+ warning("Avoid arguments.{a}.", left, m);
+ else if (directive["use strict"])
+ error("Strict violation.");
+ } else if (!option.evil && left && left.value === "document" &&
+ (m === "write" || m === "writeln")) {
+ warning("document.write can be a form of eval.", left);
+ }
+ if (!option.evil && (m === "eval" || m === "execScript")) {
+ warning("eval is evil.");
+ }
+ return that;
+ }, 160, true);
+
+ infix("(", function (left, that) {
+ if (prevtoken.id !== "}" && prevtoken.id !== ")") {
+ nobreak(prevtoken, token);
+ }
+ nospace();
+ if (option.immed && !left.immed && left.id === "function") {
+ warning("Wrap an immediate function invocation in parentheses " +
+ "to assist the reader in understanding that the expression " +
+ "is the result of a function, and not the function itself.");
+ }
+ var n = 0,
+ p = [];
+ if (left) {
+ if (left.type === "(identifier)") {
+ if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
+ if ("Number String Boolean Date Object".indexOf(left.value) === -1) {
+ if (left.value === "Math") {
+ warning("Math is not a function.", left);
+ } else if (option.newcap) {
+ warning("Missing 'new' prefix when invoking a constructor.", left);
+ }
+ }
+ }
+ }
+ }
+ if (nexttoken.id !== ")") {
+ for (;;) {
+ p[p.length] = expression(10);
+ n += 1;
+ if (nexttoken.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ }
+ advance(")");
+ nospace(prevtoken, token);
+ if (typeof left === "object") {
+ if (left.value === "parseInt" && n === 1) {
+ warning("Missing radix parameter.", token);
+ }
+ if (!option.evil) {
+ if (left.value === "eval" || left.value === "Function" ||
+ left.value === "execScript") {
+ warning("eval is evil.", left);
+
+ if (p[0] && [0].id === "(string)") {
+ addInternalSrc(left, p[0].value);
+ }
+ } else if (p[0] && p[0].id === "(string)" &&
+ (left.value === "setTimeout" ||
+ left.value === "setInterval")) {
+ warning(
+ "Implied eval is evil. Pass a function instead of a string.", left);
+ addInternalSrc(left, p[0].value);
+
+ // window.setTimeout/setInterval
+ } else if (p[0] && p[0].id === "(string)" &&
+ left.value === "." &&
+ left.left.value === "window" &&
+ (left.right === "setTimeout" ||
+ left.right === "setInterval")) {
+ warning(
+ "Implied eval is evil. Pass a function instead of a string.", left);
+ addInternalSrc(left, p[0].value);
+ }
+ }
+ if (!left.identifier && left.id !== "." && left.id !== "[" &&
+ left.id !== "(" && left.id !== "&&" && left.id !== "||" &&
+ left.id !== "?") {
+ warning("Bad invocation.", left);
+ }
+ }
+ that.left = left;
+ return that;
+ }, 155, true).exps = true;
+
+ prefix("(", function () {
+ nospace();
+ if (nexttoken.id === "function") {
+ nexttoken.immed = true;
+ }
+ var v = expression(0);
+ advance(")", this);
+ nospace(prevtoken, token);
+ if (option.immed && v.id === "function") {
+ if (nexttoken.id !== "(" &&
+ (nexttoken.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) {
+ warning(
+"Do not wrap function literals in parens unless they are to be immediately invoked.",
+ this);
+ }
+ }
+
+ return v;
+ });
+
+ infix("[", function (left, that) {
+ nobreak(prevtoken, token);
+ nospace();
+ var e = expression(0), s;
+ if (e && e.type === "(string)") {
+ if (!option.evil && (e.value === "eval" || e.value === "execScript")) {
+ warning("eval is evil.", that);
+ }
+ countMember(e.value);
+ if (!option.sub && ix.test(e.value)) {
+ s = syntax[e.value];
+ if (!s || !s.reserved) {
+ warning("['{a}'] is better written in dot notation.",
+ prevtoken, e.value);
+ }
+ }
+ }
+ advance("]", that);
+ nospace(prevtoken, token);
+ that.left = left;
+ that.right = e;
+ return that;
+ }, 160, true);
+
+ prefix("[", function () {
+ var b = token.line !== nexttoken.line;
+ this.first = [];
+ if (b) {
+ indent += option.indent;
+ if (nexttoken.from === indent + option.indent) {
+ indent += option.indent;
+ }
+ }
+ while (nexttoken.id !== "(end)") {
+ while (nexttoken.id === ",") {
+ if (!option.es5)
+ warning("Extra comma.");
+ advance(",");
+ }
+ if (nexttoken.id === "]") {
+ break;
+ }
+ if (b && token.line !== nexttoken.line) {
+ indentation();
+ }
+ this.first.push(expression(10));
+ if (nexttoken.id === ",") {
+ comma();
+ if (nexttoken.id === "]" && !option.es5) {
+ warning("Extra comma.", token);
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (b) {
+ indent -= option.indent;
+ indentation();
+ }
+ advance("]", this);
+ return this;
+ }, 160);
+
+
+ function property_name() {
+ var id = optionalidentifier(true);
+ if (!id) {
+ if (nexttoken.id === "(string)") {
+ id = nexttoken.value;
+ advance();
+ } else if (nexttoken.id === "(number)") {
+ id = nexttoken.value.toString();
+ advance();
+ }
+ }
+ return id;
+ }
+
+
+ function functionparams() {
+ var next = nexttoken;
+ var params = [];
+ var ident;
+
+ advance("(");
+ nospace();
+
+ if (nexttoken.id === ")") {
+ advance(")");
+ return;
+ }
+
+ for (;;) {
+ ident = identifier(true);
+ params.push(ident);
+ addlabel(ident, "unused", token);
+ if (nexttoken.id === ",") {
+ comma();
+ } else {
+ advance(")", next);
+ nospace(prevtoken, token);
+ return params;
+ }
+ }
+ }
+
+
+ function doFunction(name, statement) {
+ var f;
+ var oldOption = option;
+ var oldScope = scope;
+
+ option = Object.create(option);
+ scope = Object.create(scope);
+
+ funct = {
+ "(name)" : name || "\"" + anonname + "\"",
+ "(line)" : nexttoken.line,
+ "(character)": nexttoken.character,
+ "(context)" : funct,
+ "(breakage)" : 0,
+ "(loopage)" : 0,
+ "(metrics)" : createMetrics(nexttoken),
+ "(scope)" : scope,
+ "(statement)": statement,
+ "(tokens)" : {}
+ };
+
+ f = funct;
+ token.funct = funct;
+
+ functions.push(funct);
+
+ if (name) {
+ addlabel(name, "function");
+ }
+
+ funct["(params)"] = functionparams();
+ funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]);
+
+ block(false, false, true);
+
+ funct["(metrics)"].verifyMaxStatementsPerFunction();
+ funct["(metrics)"].verifyMaxComplexityPerFunction();
+
+ scope = oldScope;
+ option = oldOption;
+ funct["(last)"] = token.line;
+ funct["(lastcharacter)"] = token.character;
+ funct = funct["(context)"];
+
+ return f;
+ }
+
+ function createMetrics(functionStartToken) {
+ return {
+ statementCount: 0,
+ nestedBlockDepth: -1,
+ ComplexityCount: 1,
+ verifyMaxStatementsPerFunction: function () {
+ if (option.maxstatements &&
+ this.statementCount > option.maxstatements) {
+ var message = "Too many statements per function (" + this.statementCount + ").";
+ warning(message, functionStartToken);
+ }
+ },
+
+ verifyMaxParametersPerFunction: function (params) {
+ params = params || [];
+
+ if (option.maxparams && params.length > option.maxparams) {
+ var message = "Too many parameters per function (" + params.length + ").";
+ warning(message, functionStartToken);
+ }
+ },
+
+ verifyMaxNestedBlockDepthPerFunction: function () {
+ if (option.maxdepth &&
+ this.nestedBlockDepth > 0 &&
+ this.nestedBlockDepth === option.maxdepth + 1) {
+ var message = "Blocks are nested too deeply (" + this.nestedBlockDepth + ").";
+ warning(message);
+ }
+ },
+
+ verifyMaxComplexityPerFunction: function () {
+ var max = option.maxcomplexity;
+ var cc = this.ComplexityCount;
+ if (max && cc > max) {
+ var message = "Cyclomatic complexity is too high per function (" + cc + ").";
+ warning(message, functionStartToken);
+ }
+ }
+ };
+ }
+
+ function increaseComplexityCount() {
+ funct["(metrics)"].ComplexityCount += 1;
+ }
+
+
+ (function (x) {
+ x.nud = function () {
+ var b, f, i, p, t;
+ var props = {}; // All properties, including accessors
+
+ function saveProperty(name, token) {
+ if (props[name] && is_own(props, name))
+ warning("Duplicate member '{a}'.", nexttoken, i);
+ else
+ props[name] = {};
+
+ props[name].basic = true;
+ props[name].basicToken = token;
+ }
+
+ function saveSetter(name, token) {
+ if (props[name] && is_own(props, name)) {
+ if (props[name].basic || props[name].setter)
+ warning("Duplicate member '{a}'.", nexttoken, i);
+ } else {
+ props[name] = {};
+ }
+
+ props[name].setter = true;
+ props[name].setterToken = token;
+ }
+
+ function saveGetter(name) {
+ if (props[name] && is_own(props, name)) {
+ if (props[name].basic || props[name].getter)
+ warning("Duplicate member '{a}'.", nexttoken, i);
+ } else {
+ props[name] = {};
+ }
+
+ props[name].getter = true;
+ props[name].getterToken = token;
+ }
+
+ b = token.line !== nexttoken.line;
+ if (b) {
+ indent += option.indent;
+ if (nexttoken.from === indent + option.indent) {
+ indent += option.indent;
+ }
+ }
+ for (;;) {
+ if (nexttoken.id === "}") {
+ break;
+ }
+ if (b) {
+ indentation();
+ }
+ if (nexttoken.value === "get" && peek().id !== ":") {
+ advance("get");
+ if (!option.es5) {
+ error("get/set are ES5 features.");
+ }
+ i = property_name();
+ if (!i) {
+ error("Missing property name.");
+ }
+ saveGetter(i);
+ t = nexttoken;
+ adjacent(token, nexttoken);
+ f = doFunction();
+ p = f["(params)"];
+ if (p) {
+ warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i);
+ }
+ adjacent(token, nexttoken);
+ } else if (nexttoken.value === "set" && peek().id !== ":") {
+ advance("set");
+ if (!option.es5) {
+ error("get/set are ES5 features.");
+ }
+ i = property_name();
+ if (!i) {
+ error("Missing property name.");
+ }
+ saveSetter(i, nexttoken);
+ t = nexttoken;
+ adjacent(token, nexttoken);
+ f = doFunction();
+ p = f["(params)"];
+ if (!p || p.length !== 1) {
+ warning("Expected a single parameter in set {a} function.", t, i);
+ }
+ } else {
+ i = property_name();
+ saveProperty(i, nexttoken);
+ if (typeof i !== "string") {
+ break;
+ }
+ advance(":");
+ nonadjacent(token, nexttoken);
+ expression(10);
+ }
+
+ countMember(i);
+ if (nexttoken.id === ",") {
+ comma();
+ if (nexttoken.id === ",") {
+ warning("Extra comma.", token);
+ } else if (nexttoken.id === "}" && !option.es5) {
+ warning("Extra comma.", token);
+ }
+ } else {
+ break;
+ }
+ }
+ if (b) {
+ indent -= option.indent;
+ indentation();
+ }
+ advance("}", this);
+
+ // Check for lonely setters if in the ES5 mode.
+ if (option.es5) {
+ for (var name in props) {
+ if (is_own(props, name) && props[name].setter && !props[name].getter) {
+ warning("Setter is defined without getter.", props[name].setterToken);
+ }
+ }
+ }
+ return this;
+ };
+ x.fud = function () {
+ error("Expected to see a statement and instead saw a block.", token);
+ };
+ }(delim("{")));
+
+// This Function is called when esnext option is set to true
+// it adds the `const` statement to JSHINT
+
+ useESNextSyntax = function () {
+ var conststatement = stmt("const", function (prefix) {
+ var id, name, value;
+
+ this.first = [];
+ for (;;) {
+ nonadjacent(token, nexttoken);
+ id = identifier();
+ if (funct[id] === "const") {
+ warning("const '" + id + "' has already been declared");
+ }
+ if (funct["(global)"] && predefined[id] === false) {
+ warning("Redefinition of '{a}'.", token, id);
+ }
+ addlabel(id, "const");
+ if (prefix) {
+ break;
+ }
+ name = token;
+ this.first.push(token);
+
+ if (nexttoken.id !== "=") {
+ warning("const " +
+ "'{a}' is initialized to 'undefined'.", token, id);
+ }
+
+ if (nexttoken.id === "=") {
+ nonadjacent(token, nexttoken);
+ advance("=");
+ nonadjacent(token, nexttoken);
+ if (nexttoken.id === "undefined") {
+ warning("It is not necessary to initialize " +
+ "'{a}' to 'undefined'.", token, id);
+ }
+ if (peek(0).id === "=" && nexttoken.identifier) {
+ error("Constant {a} was not declared correctly.",
+ nexttoken, nexttoken.value);
+ }
+ value = expression(0);
+ name.first = value;
+ }
+
+ if (nexttoken.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ return this;
+ });
+ conststatement.exps = true;
+ };
+
+ var varstatement = stmt("var", function (prefix) {
+ // JavaScript does not have block scope. It only has function scope. So,
+ // declaring a variable in a block can have unexpected consequences.
+ var id, name, value;
+
+ if (funct["(onevar)"] && option.onevar) {
+ warning("Too many var statements.");
+ } else if (!funct["(global)"]) {
+ funct["(onevar)"] = true;
+ }
+
+ this.first = [];
+
+ for (;;) {
+ nonadjacent(token, nexttoken);
+ id = identifier();
+
+ if (option.esnext && funct[id] === "const") {
+ warning("const '" + id + "' has already been declared");
+ }
+
+ if (funct["(global)"] && predefined[id] === false) {
+ warning("Redefinition of '{a}'.", token, id);
+ }
+
+ addlabel(id, "unused", token);
+
+ if (prefix) {
+ break;
+ }
+
+ name = token;
+ this.first.push(token);
+
+ if (nexttoken.id === "=") {
+ nonadjacent(token, nexttoken);
+ advance("=");
+ nonadjacent(token, nexttoken);
+ if (nexttoken.id === "undefined") {
+ warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id);
+ }
+ if (peek(0).id === "=" && nexttoken.identifier) {
+ error("Variable {a} was not declared correctly.",
+ nexttoken, nexttoken.value);
+ }
+ value = expression(0);
+ name.first = value;
+ }
+ if (nexttoken.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ return this;
+ });
+ varstatement.exps = true;
+
+ blockstmt("function", function () {
+ if (inblock) {
+ warning("Function declarations should not be placed in blocks. " +
+ "Use a function expression or move the statement to the top of " +
+ "the outer function.", token);
+
+ }
+ var i = identifier();
+ if (option.esnext && funct[i] === "const") {
+ warning("const '" + i + "' has already been declared");
+ }
+ adjacent(token, nexttoken);
+ addlabel(i, "unction", token);
+
+ doFunction(i, { statement: true });
+ if (nexttoken.id === "(" && nexttoken.line === token.line) {
+ error(
+"Function declarations are not invocable. Wrap the whole function invocation in parens.");
+ }
+ return this;
+ });
+
+ prefix("function", function () {
+ var i = optionalidentifier();
+ if (i) {
+ adjacent(token, nexttoken);
+ } else {
+ nonadjacent(token, nexttoken);
+ }
+ doFunction(i);
+ if (!option.loopfunc && funct["(loopage)"]) {
+ warning("Don't make functions within a loop.");
+ }
+ return this;
+ });
+
+ blockstmt("if", function () {
+ var t = nexttoken;
+ increaseComplexityCount();
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ expression(20);
+ if (nexttoken.id === "=") {
+ if (!option.boss)
+ warning("Expected a conditional expression and instead saw an assignment.");
+ advance("=");
+ expression(20);
+ }
+ advance(")", t);
+ nospace(prevtoken, token);
+ block(true, true);
+ if (nexttoken.id === "else") {
+ nonadjacent(token, nexttoken);
+ advance("else");
+ if (nexttoken.id === "if" || nexttoken.id === "switch") {
+ statement(true);
+ } else {
+ block(true, true);
+ }
+ }
+ return this;
+ });
+
+ blockstmt("try", function () {
+ var b;
+
+ function doCatch() {
+ var oldScope = scope;
+ var e;
+
+ advance("catch");
+ nonadjacent(token, nexttoken);
+ advance("(");
+
+ scope = Object.create(oldScope);
+
+ e = nexttoken.value;
+ if (nexttoken.type !== "(identifier)") {
+ e = null;
+ warning("Expected an identifier and instead saw '{a}'.", nexttoken, e);
+ }
+
+ advance();
+ advance(")");
+
+ funct = {
+ "(name)" : "(catch)",
+ "(line)" : nexttoken.line,
+ "(character)": nexttoken.character,
+ "(context)" : funct,
+ "(breakage)" : funct["(breakage)"],
+ "(loopage)" : funct["(loopage)"],
+ "(scope)" : scope,
+ "(statement)": false,
+ "(metrics)" : createMetrics(nexttoken),
+ "(catch)" : true,
+ "(tokens)" : {}
+ };
+
+ if (e) {
+ addlabel(e, "exception");
+ }
+
+ token.funct = funct;
+ functions.push(funct);
+
+ block(false);
+
+ scope = oldScope;
+
+ funct["(last)"] = token.line;
+ funct["(lastcharacter)"] = token.character;
+ funct = funct["(context)"];
+ }
+
+ block(false);
+
+ if (nexttoken.id === "catch") {
+ increaseComplexityCount();
+ doCatch();
+ b = true;
+ }
+
+ if (nexttoken.id === "finally") {
+ advance("finally");
+ block(false);
+ return;
+ } else if (!b) {
+ error("Expected '{a}' and instead saw '{b}'.",
+ nexttoken, "catch", nexttoken.value);
+ }
+
+ return this;
+ });
+
+ blockstmt("while", function () {
+ var t = nexttoken;
+ funct["(breakage)"] += 1;
+ funct["(loopage)"] += 1;
+ increaseComplexityCount();
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ expression(20);
+ if (nexttoken.id === "=") {
+ if (!option.boss)
+ warning("Expected a conditional expression and instead saw an assignment.");
+ advance("=");
+ expression(20);
+ }
+ advance(")", t);
+ nospace(prevtoken, token);
+ block(true, true);
+ funct["(breakage)"] -= 1;
+ funct["(loopage)"] -= 1;
+ return this;
+ }).labelled = true;
+
+ blockstmt("with", function () {
+ var t = nexttoken;
+ if (directive["use strict"]) {
+ error("'with' is not allowed in strict mode.", token);
+ } else if (!option.withstmt) {
+ warning("Don't use 'with'.", token);
+ }
+
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ expression(0);
+ advance(")", t);
+ nospace(prevtoken, token);
+ block(true, true);
+
+ return this;
+ });
+
+ blockstmt("switch", function () {
+ var t = nexttoken,
+ g = false;
+ funct["(breakage)"] += 1;
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ this.condition = expression(20);
+ advance(")", t);
+ nospace(prevtoken, token);
+ nonadjacent(token, nexttoken);
+ t = nexttoken;
+ advance("{");
+ nonadjacent(token, nexttoken);
+ indent += option.indent;
+ this.cases = [];
+ for (;;) {
+ switch (nexttoken.id) {
+ case "case":
+ switch (funct["(verb)"]) {
+ case "break":
+ case "case":
+ case "continue":
+ case "return":
+ case "switch":
+ case "throw":
+ break;
+ default:
+ // You can tell JSHint that you don't use break intentionally by
+ // adding a comment /* falls through */ on a line just before
+ // the next `case`.
+ if (!ft.test(lines[nexttoken.line - 2])) {
+ warning(
+ "Expected a 'break' statement before 'case'.",
+ token);
+ }
+ }
+ indentation(-option.indent);
+ advance("case");
+ this.cases.push(expression(20));
+ increaseComplexityCount();
+ g = true;
+ advance(":");
+ funct["(verb)"] = "case";
+ break;
+ case "default":
+ switch (funct["(verb)"]) {
+ case "break":
+ case "continue":
+ case "return":
+ case "throw":
+ break;
+ default:
+ if (!ft.test(lines[nexttoken.line - 2])) {
+ warning(
+ "Expected a 'break' statement before 'default'.",
+ token);
+ }
+ }
+ indentation(-option.indent);
+ advance("default");
+ g = true;
+ advance(":");
+ break;
+ case "}":
+ indent -= option.indent;
+ indentation();
+ advance("}", t);
+ if (this.cases.length === 1 || this.condition.id === "true" ||
+ this.condition.id === "false") {
+ if (!option.onecase)
+ warning("This 'switch' should be an 'if'.", this);
+ }
+ funct["(breakage)"] -= 1;
+ funct["(verb)"] = undefined;
+ return;
+ case "(end)":
+ error("Missing '{a}'.", nexttoken, "}");
+ return;
+ default:
+ if (g) {
+ switch (token.id) {
+ case ",":
+ error("Each value should have its own case label.");
+ return;
+ case ":":
+ g = false;
+ statements();
+ break;
+ default:
+ error("Missing ':' on a case clause.", token);
+ return;
+ }
+ } else {
+ if (token.id === ":") {
+ advance(":");
+ error("Unexpected '{a}'.", token, ":");
+ statements();
+ } else {
+ error("Expected '{a}' and instead saw '{b}'.",
+ nexttoken, "case", nexttoken.value);
+ return;
+ }
+ }
+ }
+ }
+ }).labelled = true;
+
+ stmt("debugger", function () {
+ if (!option.debug) {
+ warning("All 'debugger' statements should be removed.");
+ }
+ return this;
+ }).exps = true;
+
+ (function () {
+ var x = stmt("do", function () {
+ funct["(breakage)"] += 1;
+ funct["(loopage)"] += 1;
+ increaseComplexityCount();
+
+ this.first = block(true);
+ advance("while");
+ var t = nexttoken;
+ nonadjacent(token, t);
+ advance("(");
+ nospace();
+ expression(20);
+ if (nexttoken.id === "=") {
+ if (!option.boss)
+ warning("Expected a conditional expression and instead saw an assignment.");
+ advance("=");
+ expression(20);
+ }
+ advance(")", t);
+ nospace(prevtoken, token);
+ funct["(breakage)"] -= 1;
+ funct["(loopage)"] -= 1;
+ return this;
+ });
+ x.labelled = true;
+ x.exps = true;
+ }());
+
+ blockstmt("for", function () {
+ var s, t = nexttoken;
+ funct["(breakage)"] += 1;
+ funct["(loopage)"] += 1;
+ increaseComplexityCount();
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ if (peek(nexttoken.id === "var" ? 1 : 0).id === "in") {
+ if (nexttoken.id === "var") {
+ advance("var");
+ varstatement.fud.call(varstatement, true);
+ } else {
+ switch (funct[nexttoken.value]) {
+ case "unused":
+ funct[nexttoken.value] = "var";
+ break;
+ case "var":
+ break;
+ default:
+ warning("Bad for in variable '{a}'.",
+ nexttoken, nexttoken.value);
+ }
+ advance();
+ }
+ advance("in");
+ expression(20);
+ advance(")", t);
+ s = block(true, true);
+ if (option.forin && s && (s.length > 1 || typeof s[0] !== "object" ||
+ s[0].value !== "if")) {
+ warning("The body of a for in should be wrapped in an if statement to filter " +
+ "unwanted properties from the prototype.", this);
+ }
+ funct["(breakage)"] -= 1;
+ funct["(loopage)"] -= 1;
+ return this;
+ } else {
+ if (nexttoken.id !== ";") {
+ if (nexttoken.id === "var") {
+ advance("var");
+ varstatement.fud.call(varstatement);
+ } else {
+ for (;;) {
+ expression(0, "for");
+ if (nexttoken.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ }
+ }
+ nolinebreak(token);
+ advance(";");
+ if (nexttoken.id !== ";") {
+ expression(20);
+ if (nexttoken.id === "=") {
+ if (!option.boss)
+ warning("Expected a conditional expression and instead saw an assignment.");
+ advance("=");
+ expression(20);
+ }
+ }
+ nolinebreak(token);
+ advance(";");
+ if (nexttoken.id === ";") {
+ error("Expected '{a}' and instead saw '{b}'.",
+ nexttoken, ")", ";");
+ }
+ if (nexttoken.id !== ")") {
+ for (;;) {
+ expression(0, "for");
+ if (nexttoken.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ }
+ advance(")", t);
+ nospace(prevtoken, token);
+ block(true, true);
+ funct["(breakage)"] -= 1;
+ funct["(loopage)"] -= 1;
+ return this;
+ }
+ }).labelled = true;
+
+
+ stmt("break", function () {
+ var v = nexttoken.value;
+
+ if (funct["(breakage)"] === 0)
+ warning("Unexpected '{a}'.", nexttoken, this.value);
+
+ if (!option.asi)
+ nolinebreak(this);
+
+ if (nexttoken.id !== ";") {
+ if (token.line === nexttoken.line) {
+ if (funct[v] !== "label") {
+ warning("'{a}' is not a statement label.", nexttoken, v);
+ } else if (scope[v] !== funct) {
+ warning("'{a}' is out of scope.", nexttoken, v);
+ }
+ this.first = nexttoken;
+ advance();
+ }
+ }
+ reachable("break");
+ return this;
+ }).exps = true;
+
+
+ stmt("continue", function () {
+ var v = nexttoken.value;
+
+ if (funct["(breakage)"] === 0)
+ warning("Unexpected '{a}'.", nexttoken, this.value);
+
+ if (!option.asi)
+ nolinebreak(this);
+
+ if (nexttoken.id !== ";") {
+ if (token.line === nexttoken.line) {
+ if (funct[v] !== "label") {
+ warning("'{a}' is not a statement label.", nexttoken, v);
+ } else if (scope[v] !== funct) {
+ warning("'{a}' is out of scope.", nexttoken, v);
+ }
+ this.first = nexttoken;
+ advance();
+ }
+ } else if (!funct["(loopage)"]) {
+ warning("Unexpected '{a}'.", nexttoken, this.value);
+ }
+ reachable("continue");
+ return this;
+ }).exps = true;
+
+
+ stmt("return", function () {
+ if (this.line === nexttoken.line) {
+ if (nexttoken.id === "(regexp)")
+ warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator.");
+
+ if (nexttoken.id !== ";" && !nexttoken.reach) {
+ nonadjacent(token, nexttoken);
+ if (peek().value === "=" && !option.boss) {
+ warningAt("Did you mean to return a conditional instead of an assignment?",
+ token.line, token.character + 1);
+ }
+ this.first = expression(0);
+ }
+ } else if (!option.asi) {
+ nolinebreak(this); // always warn (Line breaking error)
+ }
+ reachable("return");
+ return this;
+ }).exps = true;
+
+
+ stmt("throw", function () {
+ nolinebreak(this);
+ nonadjacent(token, nexttoken);
+ this.first = expression(20);
+ reachable("throw");
+ return this;
+ }).exps = true;
+
+// Superfluous reserved words
+
+ reserve("class");
+ reserve("const");
+ reserve("enum");
+ reserve("export");
+ reserve("extends");
+ reserve("import");
+ reserve("super");
+
+ reserve("let");
+ reserve("yield");
+ reserve("implements");
+ reserve("interface");
+ reserve("package");
+ reserve("private");
+ reserve("protected");
+ reserve("public");
+ reserve("static");
+
+
+// Parse JSON
+
+ function jsonValue() {
+
+ function jsonObject() {
+ var o = {}, t = nexttoken;
+ advance("{");
+ if (nexttoken.id !== "}") {
+ for (;;) {
+ if (nexttoken.id === "(end)") {
+ error("Missing '}' to match '{' from line {a}.",
+ nexttoken, t.line);
+ } else if (nexttoken.id === "}") {
+ warning("Unexpected comma.", token);
+ break;
+ } else if (nexttoken.id === ",") {
+ error("Unexpected comma.", nexttoken);
+ } else if (nexttoken.id !== "(string)") {
+ warning("Expected a string and instead saw {a}.",
+ nexttoken, nexttoken.value);
+ }
+ if (o[nexttoken.value] === true) {
+ warning("Duplicate key '{a}'.",
+ nexttoken, nexttoken.value);
+ } else if ((nexttoken.value === "__proto__" &&
+ !option.proto) || (nexttoken.value === "__iterator__" &&
+ !option.iterator)) {
+ warning("The '{a}' key may produce unexpected results.",
+ nexttoken, nexttoken.value);
+ } else {
+ o[nexttoken.value] = true;
+ }
+ advance();
+ advance(":");
+ jsonValue();
+ if (nexttoken.id !== ",") {
+ break;
+ }
+ advance(",");
+ }
+ }
+ advance("}");
+ }
+
+ function jsonArray() {
+ var t = nexttoken;
+ advance("[");
+ if (nexttoken.id !== "]") {
+ for (;;) {
+ if (nexttoken.id === "(end)") {
+ error("Missing ']' to match '[' from line {a}.",
+ nexttoken, t.line);
+ } else if (nexttoken.id === "]") {
+ warning("Unexpected comma.", token);
+ break;
+ } else if (nexttoken.id === ",") {
+ error("Unexpected comma.", nexttoken);
+ }
+ jsonValue();
+ if (nexttoken.id !== ",") {
+ break;
+ }
+ advance(",");
+ }
+ }
+ advance("]");
+ }
+
+ switch (nexttoken.id) {
+ case "{":
+ jsonObject();
+ break;
+ case "[":
+ jsonArray();
+ break;
+ case "true":
+ case "false":
+ case "null":
+ case "(number)":
+ case "(string)":
+ advance();
+ break;
+ case "-":
+ advance("-");
+ if (token.character !== nexttoken.from) {
+ warning("Unexpected space after '-'.", token);
+ }
+ adjacent(token, nexttoken);
+ advance("(number)");
+ break;
+ default:
+ error("Expected a JSON value.", nexttoken);
+ }
+ }
+
+
+ // The actual JSHINT function itself.
+ var itself = function (s, o, g) {
+ var a, i, k, x;
+ var optionKeys;
+ var newOptionObj = {};
+
+ if (o && o.scope) {
+ JSHINT.scope = o.scope;
+ } else {
+ JSHINT.errors = [];
+ JSHINT.undefs = [];
+ JSHINT.internals = [];
+ JSHINT.blacklist = {};
+ JSHINT.scope = "(main)";
+ }
+
+ predefined = Object.create(standard);
+ declared = Object.create(null);
+ combine(predefined, g || {});
+
+ if (o) {
+ a = o.predef;
+ if (a) {
+ if (!Array.isArray(a) && typeof a === "object") {
+ a = Object.keys(a);
+ }
+ a.forEach(function (item) {
+ var slice;
+ if (item[0] === "-") {
+ slice = item.slice(1);
+ JSHINT.blacklist[slice] = slice;
+ } else {
+ predefined[item] = true;
+ }
+ });
+ }
+
+ optionKeys = Object.keys(o);
+ for (x = 0; x < optionKeys.length; x++) {
+ newOptionObj[optionKeys[x]] = o[optionKeys[x]];
+
+ if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false)
+ newOptionObj["(explicitNewcap)"] = true;
+
+ if (optionKeys[x] === "indent")
+ newOptionObj.white = true;
+ }
+ }
+
+ option = newOptionObj;
+
+ option.indent = option.indent || 4;
+ option.maxerr = option.maxerr || 50;
+
+ tab = "";
+ for (i = 0; i < option.indent; i += 1) {
+ tab += " ";
+ }
+ indent = 1;
+ global = Object.create(predefined);
+ scope = global;
+ funct = {
+ "(global)": true,
+ "(name)": "(global)",
+ "(scope)": scope,
+ "(breakage)": 0,
+ "(loopage)": 0,
+ "(tokens)": {},
+ "(metrics)": createMetrics(nexttoken)
+ };
+ functions = [funct];
+ urls = [];
+ stack = null;
+ member = {};
+ membersOnly = null;
+ implied = {};
+ inblock = false;
+ lookahead = [];
+ jsonmode = false;
+ warnings = 0;
+ lines = [];
+ unuseds = [];
+
+ if (!isString(s) && !Array.isArray(s)) {
+ errorAt("Input is neither a string nor an array of strings.", 0);
+ return false;
+ }
+
+ if (isString(s) && /^\s*$/g.test(s)) {
+ errorAt("Input is an empty string.", 0);
+ return false;
+ }
+
+ if (s.length === 0) {
+ errorAt("Input is an empty array.", 0);
+ return false;
+ }
+
+ lex.init(s);
+
+ prereg = true;
+ directive = {};
+
+ prevtoken = token = nexttoken = syntax["(begin)"];
+
+ // Check options
+ for (var name in o) {
+ if (is_own(o, name)) {
+ checkOption(name, token);
+ }
+ }
+
+ assume();
+
+ // combine the passed globals after we've assumed all our options
+ combine(predefined, g || {});
+
+ //reset values
+ comma.first = true;
+ quotmark = undefined;
+
+ try {
+ advance();
+ switch (nexttoken.id) {
+ case "{":
+ case "[":
+ option.laxbreak = true;
+ jsonmode = true;
+ jsonValue();
+ break;
+ default:
+ directives();
+ if (directive["use strict"] && !option.globalstrict) {
+ warning("Use the function form of \"use strict\".", prevtoken);
+ }
+
+ statements();
+ }
+ advance((nexttoken && nexttoken.value !== ".") ? "(end)" : undefined);
+
+ var markDefined = function (name, context) {
+ do {
+ if (typeof context[name] === "string") {
+ // JSHINT marks unused variables as 'unused' and
+ // unused function declaration as 'unction'. This
+ // code changes such instances back 'var' and
+ // 'closure' so that the code in JSHINT.data()
+ // doesn't think they're unused.
+
+ if (context[name] === "unused")
+ context[name] = "var";
+ else if (context[name] === "unction")
+ context[name] = "closure";
+
+ return true;
+ }
+
+ context = context["(context)"];
+ } while (context);
+
+ return false;
+ };
+
+ var clearImplied = function (name, line) {
+ if (!implied[name])
+ return;
+
+ var newImplied = [];
+ for (var i = 0; i < implied[name].length; i += 1) {
+ if (implied[name][i] !== line)
+ newImplied.push(implied[name][i]);
+ }
+
+ if (newImplied.length === 0)
+ delete implied[name];
+ else
+ implied[name] = newImplied;
+ };
+
+ var warnUnused = function (name, token) {
+ var line = token.line;
+ var chr = token.character;
+
+ if (option.unused)
+ warningAt("'{a}' is defined but never used.", line, chr, name);
+
+ unuseds.push({
+ name: name,
+ line: line,
+ character: chr
+ });
+ };
+
+ var checkUnused = function (func, key) {
+ var type = func[key];
+ var token = func["(tokens)"][key];
+
+ if (key.charAt(0) === "(")
+ return;
+
+ if (type !== "unused" && type !== "unction")
+ return;
+
+ // Params are checked separately from other variables.
+ if (func["(params)"] && func["(params)"].indexOf(key) !== -1)
+ return;
+
+ warnUnused(key, token);
+ };
+
+ // Check queued 'x is not defined' instances to see if they're still undefined.
+ for (i = 0; i < JSHINT.undefs.length; i += 1) {
+ k = JSHINT.undefs[i].slice(0);
+
+ if (markDefined(k[2].value, k[0])) {
+ clearImplied(k[2].value, k[2].line);
+ } else {
+ warning.apply(warning, k.slice(1));
+ }
+ }
+
+ functions.forEach(function (func) {
+ for (var key in func) {
+ if (is_own(func, key)) {
+ checkUnused(func, key);
+ }
+ }
+
+ if (!func["(params)"])
+ return;
+
+ var params = func["(params)"].slice();
+ var param = params.pop();
+ var type;
+
+ while (param) {
+ type = func[param];
+
+ // 'undefined' is a special case for (function (window, undefined) { ... })();
+ // patterns.
+
+ if (param === "undefined")
+ return;
+
+ if (type !== "unused" && type !== "unction")
+ return;
+
+ warnUnused(param, func["(tokens)"][param]);
+ param = params.pop();
+ }
+ });
+
+ for (var key in declared) {
+ if (is_own(declared, key) && !is_own(global, key)) {
+ warnUnused(key, declared[key]);
+ }
+ }
+ } catch (e) {
+ if (e) {
+ var nt = nexttoken || {};
+ JSHINT.errors.push({
+ raw : e.raw,
+ reason : e.message,
+ line : e.line || nt.line,
+ character : e.character || nt.from
+ }, null);
+ }
+ }
+
+ // Loop over the listed "internals", and check them as well.
+
+ if (JSHINT.scope === "(main)") {
+ o = o || {};
+
+ for (i = 0; i < JSHINT.internals.length; i += 1) {
+ k = JSHINT.internals[i];
+ o.scope = k.elem;
+ itself(k.value, o, g);
+ }
+ }
+
+ return JSHINT.errors.length === 0;
+ };
+
+ // Data summary.
+ itself.data = function () {
+ var data = {
+ functions: [],
+ options: option
+ };
+ var implieds = [];
+ var members = [];
+ var fu, f, i, j, n, globals;
+
+ if (itself.errors.length) {
+ data.errors = itself.errors;
+ }
+
+ if (jsonmode) {
+ data.json = true;
+ }
+
+ for (n in implied) {
+ if (is_own(implied, n)) {
+ implieds.push({
+ name: n,
+ line: implied[n]
+ });
+ }
+ }
+
+ if (implieds.length > 0) {
+ data.implieds = implieds;
+ }
+
+ if (urls.length > 0) {
+ data.urls = urls;
+ }
+
+ globals = Object.keys(scope);
+ if (globals.length > 0) {
+ data.globals = globals;
+ }
+
+ for (i = 1; i < functions.length; i += 1) {
+ f = functions[i];
+ fu = {};
+
+ for (j = 0; j < functionicity.length; j += 1) {
+ fu[functionicity[j]] = [];
+ }
+
+ for (j = 0; j < functionicity.length; j += 1) {
+ if (fu[functionicity[j]].length === 0) {
+ delete fu[functionicity[j]];
+ }
+ }
+
+ fu.name = f["(name)"];
+ fu.param = f["(params)"];
+ fu.line = f["(line)"];
+ fu.character = f["(character)"];
+ fu.last = f["(last)"];
+ fu.lastcharacter = f["(lastcharacter)"];
+ data.functions.push(fu);
+ }
+
+ if (unuseds.length > 0) {
+ data.unused = unuseds;
+ }
+
+ members = [];
+ for (n in member) {
+ if (typeof member[n] === "number") {
+ data.member = member;
+ break;
+ }
+ }
+
+ return data;
+ };
+
+ itself.jshint = itself;
+
+ return itself;
+}());
+
+// Make JSHINT a Node module, if possible.
+if (typeof exports === "object" && exports) {
+ exports.JSHINT = JSHINT;
+} \ No newline at end of file
diff --git a/apps/it/static/js/ui/external/qunit.css b/apps/it/static/js/ui/external/qunit.css
new file mode 100644
index 0000000..d7fc0c8
--- /dev/null
+++ b/apps/it/static/js/ui/external/qunit.css
@@ -0,0 +1,244 @@
+/**
+ * QUnit v1.11.0 - A JavaScript Unit Testing Framework
+ *
+ * http://qunitjs.com
+ *
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license.
+ * http://jquery.org/license
+ */
+
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
+ margin: 0;
+ padding: 0;
+}
+
+
+/** Header */
+
+#qunit-header {
+ padding: 0.5em 0 0.5em 1em;
+
+ color: #8699a4;
+ background-color: #0d3349;
+
+ font-size: 1.5em;
+ line-height: 1em;
+ font-weight: normal;
+
+ border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ -webkit-border-top-right-radius: 5px;
+ -webkit-border-top-left-radius: 5px;
+}
+
+#qunit-header a {
+ text-decoration: none;
+ color: #c2ccd1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+ color: #fff;
+}
+
+#qunit-testrunner-toolbar label {
+ display: inline-block;
+ padding: 0 .5em 0 .1em;
+}
+
+#qunit-banner {
+ height: 5px;
+}
+
+#qunit-testrunner-toolbar {
+ padding: 0.5em 0 0.5em 2em;
+ color: #5E740B;
+ background-color: #eee;
+ overflow: hidden;
+}
+
+#qunit-userAgent {
+ padding: 0.5em 0 0.5em 2.5em;
+ background-color: #2b81af;
+ color: #fff;
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+#qunit-modulefilter-container {
+ float: right;
+}
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+ list-style-position: inside;
+}
+
+#qunit-tests li {
+ padding: 0.4em 0.5em 0.4em 2.5em;
+ border-bottom: 1px solid #fff;
+ list-style-position: inside;
+}
+
+#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
+ display: none;
+}
+
+#qunit-tests li strong {
+ cursor: pointer;
+}
+
+#qunit-tests li a {
+ padding: 0.5em;
+ color: #c2ccd1;
+ text-decoration: none;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+ color: #000;
+}
+
+#qunit-tests li .runtime {
+ float: right;
+ font-size: smaller;
+}
+
+.qunit-assert-list {
+ margin-top: 0.5em;
+ padding: 0.5em;
+
+ background-color: #fff;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+.qunit-collapsed {
+ display: none;
+}
+
+#qunit-tests table {
+ border-collapse: collapse;
+ margin-top: .2em;
+}
+
+#qunit-tests th {
+ text-align: right;
+ vertical-align: top;
+ padding: 0 .5em 0 0;
+}
+
+#qunit-tests td {
+ vertical-align: top;
+}
+
+#qunit-tests pre {
+ margin: 0;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+#qunit-tests del {
+ background-color: #e0f2be;
+ color: #374e0c;
+ text-decoration: none;
+}
+
+#qunit-tests ins {
+ background-color: #ffcaca;
+ color: #500;
+ text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts { color: black; }
+#qunit-tests b.passed { color: #5E740B; }
+#qunit-tests b.failed { color: #710909; }
+
+#qunit-tests li li {
+ padding: 5px;
+ background-color: #fff;
+ border-bottom: none;
+ list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+ color: #3c510c;
+ background-color: #fff;
+ border-left: 10px solid #C6E746;
+}
+
+#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected { color: #999999; }
+
+#qunit-banner.qunit-pass { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+ color: #710909;
+ background-color: #fff;
+ border-left: 10px solid #EE5757;
+ white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+ border-radius: 0 0 5px 5px;
+ -moz-border-radius: 0 0 5px 5px;
+ -webkit-border-bottom-right-radius: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+}
+
+#qunit-tests .fail { color: #000000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name { color: #000000; }
+
+#qunit-tests .fail .test-actual { color: #EE5757; }
+#qunit-tests .fail .test-expected { color: green; }
+
+#qunit-banner.qunit-fail { background-color: #EE5757; }
+
+
+/** Result */
+
+#qunit-testresult {
+ padding: 0.5em 0.5em 0.5em 2.5em;
+
+ color: #2b81af;
+ background-color: #D2E0E6;
+
+ border-bottom: 1px solid white;
+}
+#qunit-testresult .module-name {
+ font-weight: bold;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+ position: absolute;
+ top: -10000px;
+ left: -10000px;
+ width: 1000px;
+ height: 1000px;
+}
diff --git a/apps/it/static/js/ui/external/qunit.js b/apps/it/static/js/ui/external/qunit.js
new file mode 100644
index 0000000..302545f
--- /dev/null
+++ b/apps/it/static/js/ui/external/qunit.js
@@ -0,0 +1,2152 @@
+/**
+ * QUnit v1.11.0 - A JavaScript Unit Testing Framework
+ *
+ * http://qunitjs.com
+ *
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license.
+ * http://jquery.org/license
+ */
+
+(function( window ) {
+
+var QUnit,
+ assert,
+ config,
+ onErrorFnPrev,
+ testId = 0,
+ fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ // Keep a local reference to Date (GH-283)
+ Date = window.Date,
+ defined = {
+ setTimeout: typeof window.setTimeout !== "undefined",
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch( e ) {
+ return false;
+ }
+ }())
+ },
+ /**
+ * Provides a normalized error string, correcting an issue
+ * with IE 7 (and prior) where Error.prototype.toString is
+ * not properly implemented
+ *
+ * Based on http://es5.github.com/#x15.11.4.4
+ *
+ * @param {String|Error} error
+ * @return {String} error message
+ */
+ errorString = function( error ) {
+ var name, message,
+ errorString = error.toString();
+ if ( errorString.substring( 0, 7 ) === "[object" ) {
+ name = error.name ? error.name.toString() : "Error";
+ message = error.message ? error.message.toString() : "";
+ if ( name && message ) {
+ return name + ": " + message;
+ } else if ( name ) {
+ return name;
+ } else if ( message ) {
+ return message;
+ } else {
+ return "Error";
+ }
+ } else {
+ return errorString;
+ }
+ },
+ /**
+ * Makes a clone of an object using only Array or Object as base,
+ * and copies over the own enumerable properties.
+ *
+ * @param {Object} obj
+ * @return {Object} New object with only the own properties (recursively).
+ */
+ objectValues = function( obj ) {
+ // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
+ /*jshint newcap: false */
+ var key, val,
+ vals = QUnit.is( "array", obj ) ? [] : {};
+ for ( key in obj ) {
+ if ( hasOwn.call( obj, key ) ) {
+ val = obj[key];
+ vals[key] = val === Object(val) ? objectValues(val) : val;
+ }
+ }
+ return vals;
+ };
+
+function Test( settings ) {
+ extend( this, settings );
+ this.assertions = [];
+ this.testNumber = ++Test.count;
+}
+
+Test.count = 0;
+
+Test.prototype = {
+ init: function() {
+ var a, b, li,
+ tests = id( "qunit-tests" );
+
+ if ( tests ) {
+ b = document.createElement( "strong" );
+ b.innerHTML = this.nameHtml;
+
+ // `a` initialized at top of scope
+ a = document.createElement( "a" );
+ a.innerHTML = "Rerun";
+ a.href = QUnit.url({ testNumber: this.testNumber });
+
+ li = document.createElement( "li" );
+ li.appendChild( b );
+ li.appendChild( a );
+ li.className = "running";
+ li.id = this.id = "qunit-test-output" + testId++;
+
+ tests.appendChild( li );
+ }
+ },
+ setup: function() {
+ if ( this.module !== config.previousModule ) {
+ if ( config.previousModule ) {
+ runLoggingCallbacks( "moduleDone", QUnit, {
+ name: config.previousModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ });
+ }
+ config.previousModule = this.module;
+ config.moduleStats = { all: 0, bad: 0 };
+ runLoggingCallbacks( "moduleStart", QUnit, {
+ name: this.module
+ });
+ } else if ( config.autorun ) {
+ runLoggingCallbacks( "moduleStart", QUnit, {
+ name: this.module
+ });
+ }
+
+ config.current = this;
+
+ this.testEnvironment = extend({
+ setup: function() {},
+ teardown: function() {}
+ }, this.moduleTestEnvironment );
+
+ this.started = +new Date();
+ runLoggingCallbacks( "testStart", QUnit, {
+ name: this.testName,
+ module: this.module
+ });
+
+ // allow utility functions to access the current test environment
+ // TODO why??
+ QUnit.current_testEnvironment = this.testEnvironment;
+
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+ if ( config.notrycatch ) {
+ this.testEnvironment.setup.call( this.testEnvironment );
+ return;
+ }
+ try {
+ this.testEnvironment.setup.call( this.testEnvironment );
+ } catch( e ) {
+ QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
+ }
+ },
+ run: function() {
+ config.current = this;
+
+ var running = id( "qunit-testresult" );
+
+ if ( running ) {
+ running.innerHTML = "Running: <br/>" + this.nameHtml;
+ }
+
+ if ( this.async ) {
+ QUnit.stop();
+ }
+
+ this.callbackStarted = +new Date();
+
+ if ( config.notrycatch ) {
+ this.callback.call( this.testEnvironment, QUnit.assert );
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+ return;
+ }
+
+ try {
+ this.callback.call( this.testEnvironment, QUnit.assert );
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+ } catch( e ) {
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+
+ QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ QUnit.start();
+ }
+ }
+ },
+ teardown: function() {
+ config.current = this;
+ if ( config.notrycatch ) {
+ if ( typeof this.callbackRuntime === "undefined" ) {
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+ }
+ this.testEnvironment.teardown.call( this.testEnvironment );
+ return;
+ } else {
+ try {
+ this.testEnvironment.teardown.call( this.testEnvironment );
+ } catch( e ) {
+ QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
+ }
+ }
+ checkPollution();
+ },
+ finish: function() {
+ config.current = this;
+ if ( config.requireExpects && this.expected === null ) {
+ QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
+ } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
+ QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
+ } else if ( this.expected === null && !this.assertions.length ) {
+ QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
+ }
+
+ var i, assertion, a, b, time, li, ol,
+ test = this,
+ good = 0,
+ bad = 0,
+ tests = id( "qunit-tests" );
+
+ this.runtime = +new Date() - this.started;
+ config.stats.all += this.assertions.length;
+ config.moduleStats.all += this.assertions.length;
+
+ if ( tests ) {
+ ol = document.createElement( "ol" );
+ ol.className = "qunit-assert-list";
+
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ assertion = this.assertions[i];
+
+ li = document.createElement( "li" );
+ li.className = assertion.result ? "pass" : "fail";
+ li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
+ ol.appendChild( li );
+
+ if ( assertion.result ) {
+ good++;
+ } else {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+
+ // store result when possible
+ if ( QUnit.config.reorder && defined.sessionStorage ) {
+ if ( bad ) {
+ sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
+ } else {
+ sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
+ }
+ }
+
+ if ( bad === 0 ) {
+ addClass( ol, "qunit-collapsed" );
+ }
+
+ // `b` initialized at top of scope
+ b = document.createElement( "strong" );
+ b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
+
+ addEvent(b, "click", function() {
+ var next = b.parentNode.lastChild,
+ collapsed = hasClass( next, "qunit-collapsed" );
+ ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
+ });
+
+ addEvent(b, "dblclick", function( e ) {
+ var target = e && e.target ? e.target : window.event.srcElement;
+ if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
+ target = target.parentNode;
+ }
+ if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
+ window.location = QUnit.url({ testNumber: test.testNumber });
+ }
+ });
+
+ // `time` initialized at top of scope
+ time = document.createElement( "span" );
+ time.className = "runtime";
+ time.innerHTML = this.runtime + " ms";
+
+ // `li` initialized at top of scope
+ li = id( this.id );
+ li.className = bad ? "fail" : "pass";
+ li.removeChild( li.firstChild );
+ a = li.firstChild;
+ li.appendChild( b );
+ li.appendChild( a );
+ li.appendChild( time );
+ li.appendChild( ol );
+
+ } else {
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ if ( !this.assertions[i].result ) {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+ }
+
+ runLoggingCallbacks( "testDone", QUnit, {
+ name: this.testName,
+ module: this.module,
+ failed: bad,
+ passed: this.assertions.length - bad,
+ total: this.assertions.length,
+ duration: this.runtime
+ });
+
+ QUnit.reset();
+
+ config.current = undefined;
+ },
+
+ queue: function() {
+ var bad,
+ test = this;
+
+ synchronize(function() {
+ test.init();
+ });
+ function run() {
+ // each of these can by async
+ synchronize(function() {
+ test.setup();
+ });
+ synchronize(function() {
+ test.run();
+ });
+ synchronize(function() {
+ test.teardown();
+ });
+ synchronize(function() {
+ test.finish();
+ });
+ }
+
+ // `bad` initialized at top of scope
+ // defer when previous test run passed, if storage is available
+ bad = QUnit.config.reorder && defined.sessionStorage &&
+ +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
+
+ if ( bad ) {
+ run();
+ } else {
+ synchronize( run, true );
+ }
+ }
+};
+
+// Root QUnit object.
+// `QUnit` initialized at top of scope
+QUnit = {
+
+ // call on start of module test to prepend name to all tests
+ module: function( name, testEnvironment ) {
+ config.currentModule = name;
+ config.currentModuleTestEnvironment = testEnvironment;
+ config.modules[name] = true;
+ },
+
+ asyncTest: function( testName, expected, callback ) {
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ QUnit.test( testName, expected, callback, true );
+ },
+
+ test: function( testName, expected, callback, async ) {
+ var test,
+ nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";
+
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ if ( config.currentModule ) {
+ nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
+ }
+
+ test = new Test({
+ nameHtml: nameHtml,
+ testName: testName,
+ expected: expected,
+ async: async,
+ callback: callback,
+ module: config.currentModule,
+ moduleTestEnvironment: config.currentModuleTestEnvironment,
+ stack: sourceFromStacktrace( 2 )
+ });
+
+ if ( !validTest( test ) ) {
+ return;
+ }
+
+ test.queue();
+ },
+
+ // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
+ expect: function( asserts ) {
+ if (arguments.length === 1) {
+ config.current.expected = asserts;
+ } else {
+ return config.current.expected;
+ }
+ },
+
+ start: function( count ) {
+ // QUnit hasn't been initialized yet.
+ // Note: RequireJS (et al) may delay onLoad
+ if ( config.semaphore === undefined ) {
+ QUnit.begin(function() {
+ // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
+ setTimeout(function() {
+ QUnit.start( count );
+ });
+ });
+ return;
+ }
+
+ config.semaphore -= count || 1;
+ // don't start until equal number of stop-calls
+ if ( config.semaphore > 0 ) {
+ return;
+ }
+ // ignore if start is called more often then stop
+ if ( config.semaphore < 0 ) {
+ config.semaphore = 0;
+ QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
+ return;
+ }
+ // A slight delay, to avoid any current callbacks
+ if ( defined.setTimeout ) {
+ window.setTimeout(function() {
+ if ( config.semaphore > 0 ) {
+ return;
+ }
+ if ( config.timeout ) {
+ clearTimeout( config.timeout );
+ }
+
+ config.blocking = false;
+ process( true );
+ }, 13);
+ } else {
+ config.blocking = false;
+ process( true );
+ }
+ },
+
+ stop: function( count ) {
+ config.semaphore += count || 1;
+ config.blocking = true;
+
+ if ( config.testTimeout && defined.setTimeout ) {
+ clearTimeout( config.timeout );
+ config.timeout = window.setTimeout(function() {
+ QUnit.ok( false, "Test timed out" );
+ config.semaphore = 1;
+ QUnit.start();
+ }, config.testTimeout );
+ }
+ }
+};
+
+// `assert` initialized at top of scope
+// Asssert helpers
+// All of these must either call QUnit.push() or manually do:
+// - runLoggingCallbacks( "log", .. );
+// - config.current.assertions.push({ .. });
+// We attach it to the QUnit object *after* we expose the public API,
+// otherwise `assert` will become a global variable in browsers (#341).
+assert = {
+ /**
+ * Asserts rough true-ish result.
+ * @name ok
+ * @function
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+ */
+ ok: function( result, msg ) {
+ if ( !config.current ) {
+ throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
+ }
+ result = !!result;
+
+ var source,
+ details = {
+ module: config.current.module,
+ name: config.current.testName,
+ result: result,
+ message: msg
+ };
+
+ msg = escapeText( msg || (result ? "okay" : "failed" ) );
+ msg = "<span class='test-message'>" + msg + "</span>";
+
+ if ( !result ) {
+ source = sourceFromStacktrace( 2 );
+ if ( source ) {
+ details.source = source;
+ msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>";
+ }
+ }
+ runLoggingCallbacks( "log", QUnit, details );
+ config.current.assertions.push({
+ result: result,
+ message: msg
+ });
+ },
+
+ /**
+ * Assert that the first two arguments are equal, with an optional message.
+ * Prints out both actual and expected values.
+ * @name equal
+ * @function
+ * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
+ */
+ equal: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
+ QUnit.push( expected == actual, actual, expected, message );
+ },
+
+ /**
+ * @name notEqual
+ * @function
+ */
+ notEqual: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
+ QUnit.push( expected != actual, actual, expected, message );
+ },
+
+ /**
+ * @name propEqual
+ * @function
+ */
+ propEqual: function( actual, expected, message ) {
+ actual = objectValues(actual);
+ expected = objectValues(expected);
+ QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name notPropEqual
+ * @function
+ */
+ notPropEqual: function( actual, expected, message ) {
+ actual = objectValues(actual);
+ expected = objectValues(expected);
+ QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name deepEqual
+ * @function
+ */
+ deepEqual: function( actual, expected, message ) {
+ QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name notDeepEqual
+ * @function
+ */
+ notDeepEqual: function( actual, expected, message ) {
+ QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name strictEqual
+ * @function
+ */
+ strictEqual: function( actual, expected, message ) {
+ QUnit.push( expected === actual, actual, expected, message );
+ },
+
+ /**
+ * @name notStrictEqual
+ * @function
+ */
+ notStrictEqual: function( actual, expected, message ) {
+ QUnit.push( expected !== actual, actual, expected, message );
+ },
+
+ "throws": function( block, expected, message ) {
+ var actual,
+ expectedOutput = expected,
+ ok = false;
+
+ // 'expected' is optional
+ if ( typeof expected === "string" ) {
+ message = expected;
+ expected = null;
+ }
+
+ config.current.ignoreGlobalErrors = true;
+ try {
+ block.call( config.current.testEnvironment );
+ } catch (e) {
+ actual = e;
+ }
+ config.current.ignoreGlobalErrors = false;
+
+ if ( actual ) {
+ // we don't want to validate thrown error
+ if ( !expected ) {
+ ok = true;
+ expectedOutput = null;
+ // expected is a regexp
+ } else if ( QUnit.objectType( expected ) === "regexp" ) {
+ ok = expected.test( errorString( actual ) );
+ // expected is a constructor
+ } else if ( actual instanceof expected ) {
+ ok = true;
+ // expected is a validation function which returns true is validation passed
+ } else if ( expected.call( {}, actual ) === true ) {
+ expectedOutput = null;
+ ok = true;
+ }
+
+ QUnit.push( ok, actual, expectedOutput, message );
+ } else {
+ QUnit.pushFailure( message, null, 'No exception was thrown.' );
+ }
+ }
+};
+
+/**
+ * @deprecate since 1.8.0
+ * Kept assertion helpers in root for backwards compatibility.
+ */
+extend( QUnit, assert );
+
+/**
+ * @deprecated since 1.9.0
+ * Kept root "raises()" for backwards compatibility.
+ * (Note that we don't introduce assert.raises).
+ */
+QUnit.raises = assert[ "throws" ];
+
+/**
+ * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
+ * Kept to avoid TypeErrors for undefined methods.
+ */
+QUnit.equals = function() {
+ QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
+};
+QUnit.same = function() {
+ QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
+};
+
+// We want access to the constructor's prototype
+(function() {
+ function F() {}
+ F.prototype = QUnit;
+ QUnit = new F();
+ // Make F QUnit's constructor so that we can add to the prototype later
+ QUnit.constructor = F;
+}());
+
+/**
+ * Config object: Maintain internal state
+ * Later exposed as QUnit.config
+ * `config` initialized at top of scope
+ */
+config = {
+ // The queue of tests to run
+ queue: [],
+
+ // block until document ready
+ blocking: true,
+
+ // when enabled, show only failing tests
+ // gets persisted through sessionStorage and can be changed in UI via checkbox
+ hidepassed: false,
+
+ // by default, run previously failed tests first
+ // very useful in combination with "Hide passed tests" checked
+ reorder: true,
+
+ // by default, modify document.title when suite is done
+ altertitle: true,
+
+ // when enabled, all tests must call expect()
+ requireExpects: false,
+
+ // add checkboxes that are persisted in the query-string
+ // when enabled, the id is set to `true` as a `QUnit.config` property
+ urlConfig: [
+ {
+ id: "noglobals",
+ label: "Check for Globals",
+ tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
+ },
+ {
+ id: "notrycatch",
+ label: "No try-catch",
+ tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
+ }
+ ],
+
+ // Set of all modules.
+ modules: {},
+
+ // logging callback queues
+ begin: [],
+ done: [],
+ log: [],
+ testStart: [],
+ testDone: [],
+ moduleStart: [],
+ moduleDone: []
+};
+
+// Export global variables, unless an 'exports' object exists,
+// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
+if ( typeof exports === "undefined" ) {
+ extend( window, QUnit );
+
+ // Expose QUnit object
+ window.QUnit = QUnit;
+}
+
+// Initialize more QUnit.config and QUnit.urlParams
+(function() {
+ var i,
+ location = window.location || { search: "", protocol: "file:" },
+ params = location.search.slice( 1 ).split( "&" ),
+ length = params.length,
+ urlParams = {},
+ current;
+
+ if ( params[ 0 ] ) {
+ for ( i = 0; i < length; i++ ) {
+ current = params[ i ].split( "=" );
+ current[ 0 ] = decodeURIComponent( current[ 0 ] );
+ // allow just a key to turn on a flag, e.g., test.html?noglobals
+ current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
+ urlParams[ current[ 0 ] ] = current[ 1 ];
+ }
+ }
+
+ QUnit.urlParams = urlParams;
+
+ // String search anywhere in moduleName+testName
+ config.filter = urlParams.filter;
+
+ // Exact match of the module name
+ config.module = urlParams.module;
+
+ config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
+
+ // Figure out if we're running the tests from a server or not
+ QUnit.isLocal = location.protocol === "file:";
+}());
+
+// Extend QUnit object,
+// these after set here because they should not be exposed as global functions
+extend( QUnit, {
+ assert: assert,
+
+ config: config,
+
+ // Initialize the configuration options
+ init: function() {
+ extend( config, {
+ stats: { all: 0, bad: 0 },
+ moduleStats: { all: 0, bad: 0 },
+ started: +new Date(),
+ updateRate: 1000,
+ blocking: false,
+ autostart: true,
+ autorun: false,
+ filter: "",
+ queue: [],
+ semaphore: 1
+ });
+
+ var tests, banner, result,
+ qunit = id( "qunit" );
+
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ tests = id( "qunit-tests" );
+ banner = id( "qunit-banner" );
+ result = id( "qunit-testresult" );
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ }
+
+ if ( banner ) {
+ banner.className = "";
+ }
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br/>&nbsp;";
+ }
+ },
+
+ // Resets the test setup. Useful for tests that modify the DOM.
+ reset: function() {
+ var fixture = id( "qunit-fixture" );
+ if ( fixture ) {
+ fixture.innerHTML = config.fixture;
+ }
+ },
+
+ // Trigger an event on an element.
+ // @example triggerEvent( document.body, "click" );
+ triggerEvent: function( elem, type, event ) {
+ if ( document.createEvent ) {
+ event = document.createEvent( "MouseEvents" );
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+
+ elem.dispatchEvent( event );
+ } else if ( elem.fireEvent ) {
+ elem.fireEvent( "on" + type );
+ }
+ },
+
+ // Safe object type checking
+ is: function( type, obj ) {
+ return QUnit.objectType( obj ) === type;
+ },
+
+ objectType: function( obj ) {
+ if ( typeof obj === "undefined" ) {
+ return "undefined";
+ // consider: typeof null === object
+ }
+ if ( obj === null ) {
+ return "null";
+ }
+
+ var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
+ type = match && match[1] || "";
+
+ switch ( type ) {
+ case "Number":
+ if ( isNaN(obj) ) {
+ return "nan";
+ }
+ return "number";
+ case "String":
+ case "Boolean":
+ case "Array":
+ case "Date":
+ case "RegExp":
+ case "Function":
+ return type.toLowerCase();
+ }
+ if ( typeof obj === "object" ) {
+ return "object";
+ }
+ return undefined;
+ },
+
+ push: function( result, actual, expected, message ) {
+ if ( !config.current ) {
+ throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
+ }
+
+ var output, source,
+ details = {
+ module: config.current.module,
+ name: config.current.testName,
+ result: result,
+ message: message,
+ actual: actual,
+ expected: expected
+ };
+
+ message = escapeText( message ) || ( result ? "okay" : "failed" );
+ message = "<span class='test-message'>" + message + "</span>";
+ output = message;
+
+ if ( !result ) {
+ expected = escapeText( QUnit.jsDump.parse(expected) );
+ actual = escapeText( QUnit.jsDump.parse(actual) );
+ output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
+
+ if ( actual !== expected ) {
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
+ output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
+ }
+
+ source = sourceFromStacktrace();
+
+ if ( source ) {
+ details.source = source;
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
+ }
+
+ output += "</table>";
+ }
+
+ runLoggingCallbacks( "log", QUnit, details );
+
+ config.current.assertions.push({
+ result: !!result,
+ message: output
+ });
+ },
+
+ pushFailure: function( message, source, actual ) {
+ if ( !config.current ) {
+ throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
+ }
+
+ var output,
+ details = {
+ module: config.current.module,
+ name: config.current.testName,
+ result: false,
+ message: message
+ };
+
+ message = escapeText( message ) || "error";
+ message = "<span class='test-message'>" + message + "</span>";
+ output = message;
+
+ output += "<table>";
+
+ if ( actual ) {
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
+ }
+
+ if ( source ) {
+ details.source = source;
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
+ }
+
+ output += "</table>";
+
+ runLoggingCallbacks( "log", QUnit, details );
+
+ config.current.assertions.push({
+ result: false,
+ message: output
+ });
+ },
+
+ url: function( params ) {
+ params = extend( extend( {}, QUnit.urlParams ), params );
+ var key,
+ querystring = "?";
+
+ for ( key in params ) {
+ if ( !hasOwn.call( params, key ) ) {
+ continue;
+ }
+ querystring += encodeURIComponent( key ) + "=" +
+ encodeURIComponent( params[ key ] ) + "&";
+ }
+ return window.location.protocol + "//" + window.location.host +
+ window.location.pathname + querystring.slice( 0, -1 );
+ },
+
+ extend: extend,
+ id: id,
+ addEvent: addEvent
+ // load, equiv, jsDump, diff: Attached later
+});
+
+/**
+ * @deprecated: Created for backwards compatibility with test runner that set the hook function
+ * into QUnit.{hook}, instead of invoking it and passing the hook function.
+ * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
+ * Doing this allows us to tell if the following methods have been overwritten on the actual
+ * QUnit object.
+ */
+extend( QUnit.constructor.prototype, {
+
+ // Logging callbacks; all receive a single argument with the listed properties
+ // run test/logs.html for any related changes
+ begin: registerLoggingCallback( "begin" ),
+
+ // done: { failed, passed, total, runtime }
+ done: registerLoggingCallback( "done" ),
+
+ // log: { result, actual, expected, message }
+ log: registerLoggingCallback( "log" ),
+
+ // testStart: { name }
+ testStart: registerLoggingCallback( "testStart" ),
+
+ // testDone: { name, failed, passed, total, duration }
+ testDone: registerLoggingCallback( "testDone" ),
+
+ // moduleStart: { name }
+ moduleStart: registerLoggingCallback( "moduleStart" ),
+
+ // moduleDone: { name, failed, passed, total }
+ moduleDone: registerLoggingCallback( "moduleDone" )
+});
+
+if ( typeof document === "undefined" || document.readyState === "complete" ) {
+ config.autorun = true;
+}
+
+QUnit.load = function() {
+ runLoggingCallbacks( "begin", QUnit, {} );
+
+ // Initialize the config, saving the execution queue
+ var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
+ urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,
+ numModules = 0,
+ moduleFilterHtml = "",
+ urlConfigHtml = "",
+ oldconfig = extend( {}, config );
+
+ QUnit.init();
+ extend(config, oldconfig);
+
+ config.blocking = false;
+
+ len = config.urlConfig.length;
+
+ for ( i = 0; i < len; i++ ) {
+ val = config.urlConfig[i];
+ if ( typeof val === "string" ) {
+ val = {
+ id: val,
+ label: val,
+ tooltip: "[no tooltip available]"
+ };
+ }
+ config[ val.id ] = QUnit.urlParams[ val.id ];
+ urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
+ "' name='" + escapeText( val.id ) +
+ "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) +
+ " title='" + escapeText( val.tooltip ) +
+ "'><label for='qunit-urlconfig-" + escapeText( val.id ) +
+ "' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
+ }
+
+ moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
+ ( config.module === undefined ? "selected='selected'" : "" ) +
+ ">< All Modules ></option>";
+
+ for ( i in config.modules ) {
+ if ( config.modules.hasOwnProperty( i ) ) {
+ numModules += 1;
+ moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(i) ) + "' " +
+ ( config.module === i ? "selected='selected'" : "" ) +
+ ">" + escapeText(i) + "</option>";
+ }
+ }
+ moduleFilterHtml += "</select>";
+
+ // `userAgent` initialized at top of scope
+ userAgent = id( "qunit-userAgent" );
+ if ( userAgent ) {
+ userAgent.innerHTML = navigator.userAgent;
+ }
+
+ // `banner` initialized at top of scope
+ banner = id( "qunit-header" );
+ if ( banner ) {
+ banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
+ }
+
+ // `toolbar` initialized at top of scope
+ toolbar = id( "qunit-testrunner-toolbar" );
+ if ( toolbar ) {
+ // `filter` initialized at top of scope
+ filter = document.createElement( "input" );
+ filter.type = "checkbox";
+ filter.id = "qunit-filter-pass";
+
+ addEvent( filter, "click", function() {
+ var tmp,
+ ol = document.getElementById( "qunit-tests" );
+
+ if ( filter.checked ) {
+ ol.className = ol.className + " hidepass";
+ } else {
+ tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
+ ol.className = tmp.replace( / hidepass /, " " );
+ }
+ if ( defined.sessionStorage ) {
+ if (filter.checked) {
+ sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
+ } else {
+ sessionStorage.removeItem( "qunit-filter-passed-tests" );
+ }
+ }
+ });
+
+ if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
+ filter.checked = true;
+ // `ol` initialized at top of scope
+ ol = document.getElementById( "qunit-tests" );
+ ol.className = ol.className + " hidepass";
+ }
+ toolbar.appendChild( filter );
+
+ // `label` initialized at top of scope
+ label = document.createElement( "label" );
+ label.setAttribute( "for", "qunit-filter-pass" );
+ label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." );
+ label.innerHTML = "Hide passed tests";
+ toolbar.appendChild( label );
+
+ urlConfigCheckboxesContainer = document.createElement("span");
+ urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;
+ urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");
+ // For oldIE support:
+ // * Add handlers to the individual elements instead of the container
+ // * Use "click" instead of "change"
+ // * Fallback from event.target to event.srcElement
+ addEvents( urlConfigCheckboxes, "click", function( event ) {
+ var params = {},
+ target = event.target || event.srcElement;
+ params[ target.name ] = target.checked ? true : undefined;
+ window.location = QUnit.url( params );
+ });
+ toolbar.appendChild( urlConfigCheckboxesContainer );
+
+ if (numModules > 1) {
+ moduleFilter = document.createElement( 'span' );
+ moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' );
+ moduleFilter.innerHTML = moduleFilterHtml;
+ addEvent( moduleFilter.lastChild, "change", function() {
+ var selectBox = moduleFilter.getElementsByTagName("select")[0],
+ selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
+
+ window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } );
+ });
+ toolbar.appendChild(moduleFilter);
+ }
+ }
+
+ // `main` initialized at top of scope
+ main = id( "qunit-fixture" );
+ if ( main ) {
+ config.fixture = main.innerHTML;
+ }
+
+ if ( config.autostart ) {
+ QUnit.start();
+ }
+};
+
+addEvent( window, "load", QUnit.load );
+
+// `onErrorFnPrev` initialized at top of scope
+// Preserve other handlers
+onErrorFnPrev = window.onerror;
+
+// Cover uncaught exceptions
+// Returning true will surpress the default browser handler,
+// returning false will let it run.
+window.onerror = function ( error, filePath, linerNr ) {
+ var ret = false;
+ if ( onErrorFnPrev ) {
+ ret = onErrorFnPrev( error, filePath, linerNr );
+ }
+
+ // Treat return value as window.onerror itself does,
+ // Only do our handling if not surpressed.
+ if ( ret !== true ) {
+ if ( QUnit.config.current ) {
+ if ( QUnit.config.current.ignoreGlobalErrors ) {
+ return true;
+ }
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ } else {
+ QUnit.test( "global failure", extend( function() {
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ }, { validTest: validTest } ) );
+ }
+ return false;
+ }
+
+ return ret;
+};
+
+function done() {
+ config.autorun = true;
+
+ // Log the last module results
+ if ( config.currentModule ) {
+ runLoggingCallbacks( "moduleDone", QUnit, {
+ name: config.currentModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ });
+ }
+
+ var i, key,
+ banner = id( "qunit-banner" ),
+ tests = id( "qunit-tests" ),
+ runtime = +new Date() - config.started,
+ passed = config.stats.all - config.stats.bad,
+ html = [
+ "Tests completed in ",
+ runtime,
+ " milliseconds.<br/>",
+ "<span class='passed'>",
+ passed,
+ "</span> assertions of <span class='total'>",
+ config.stats.all,
+ "</span> passed, <span class='failed'>",
+ config.stats.bad,
+ "</span> failed."
+ ].join( "" );
+
+ if ( banner ) {
+ banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
+ }
+
+ if ( tests ) {
+ id( "qunit-testresult" ).innerHTML = html;
+ }
+
+ if ( config.altertitle && typeof document !== "undefined" && document.title ) {
+ // show ✖ for good, ✔ for bad suite result in title
+ // use escape sequences in case file gets loaded with non-utf-8-charset
+ document.title = [
+ ( config.stats.bad ? "\u2716" : "\u2714" ),
+ document.title.replace( /^[\u2714\u2716] /i, "" )
+ ].join( " " );
+ }
+
+ // clear own sessionStorage items if all tests passed
+ if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
+ // `key` & `i` initialized at top of scope
+ for ( i = 0; i < sessionStorage.length; i++ ) {
+ key = sessionStorage.key( i++ );
+ if ( key.indexOf( "qunit-test-" ) === 0 ) {
+ sessionStorage.removeItem( key );
+ }
+ }
+ }
+
+ // scroll back to top to show results
+ if ( window.scrollTo ) {
+ window.scrollTo(0, 0);
+ }
+
+ runLoggingCallbacks( "done", QUnit, {
+ failed: config.stats.bad,
+ passed: passed,
+ total: config.stats.all,
+ runtime: runtime
+ });
+}
+
+/** @return Boolean: true if this test should be ran */
+function validTest( test ) {
+ var include,
+ filter = config.filter && config.filter.toLowerCase(),
+ module = config.module && config.module.toLowerCase(),
+ fullName = (test.module + ": " + test.testName).toLowerCase();
+
+ // Internally-generated tests are always valid
+ if ( test.callback && test.callback.validTest === validTest ) {
+ delete test.callback.validTest;
+ return true;
+ }
+
+ if ( config.testNumber ) {
+ return test.testNumber === config.testNumber;
+ }
+
+ if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
+ return false;
+ }
+
+ if ( !filter ) {
+ return true;
+ }
+
+ include = filter.charAt( 0 ) !== "!";
+ if ( !include ) {
+ filter = filter.slice( 1 );
+ }
+
+ // If the filter matches, we need to honour include
+ if ( fullName.indexOf( filter ) !== -1 ) {
+ return include;
+ }
+
+ // Otherwise, do the opposite
+ return !include;
+}
+
+// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
+// Later Safari and IE10 are supposed to support error.stack as well
+// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+function extractStacktrace( e, offset ) {
+ offset = offset === undefined ? 3 : offset;
+
+ var stack, include, i;
+
+ if ( e.stacktrace ) {
+ // Opera
+ return e.stacktrace.split( "\n" )[ offset + 3 ];
+ } else if ( e.stack ) {
+ // Firefox, Chrome
+ stack = e.stack.split( "\n" );
+ if (/^error$/i.test( stack[0] ) ) {
+ stack.shift();
+ }
+ if ( fileName ) {
+ include = [];
+ for ( i = offset; i < stack.length; i++ ) {
+ if ( stack[ i ].indexOf( fileName ) !== -1 ) {
+ break;
+ }
+ include.push( stack[ i ] );
+ }
+ if ( include.length ) {
+ return include.join( "\n" );
+ }
+ }
+ return stack[ offset ];
+ } else if ( e.sourceURL ) {
+ // Safari, PhantomJS
+ // hopefully one day Safari provides actual stacktraces
+ // exclude useless self-reference for generated Error objects
+ if ( /qunit.js$/.test( e.sourceURL ) ) {
+ return;
+ }
+ // for actual exceptions, this is useful
+ return e.sourceURL + ":" + e.line;
+ }
+}
+function sourceFromStacktrace( offset ) {
+ try {
+ throw new Error();
+ } catch ( e ) {
+ return extractStacktrace( e, offset );
+ }
+}
+
+/**
+ * Escape text for attribute or text content.
+ */
+function escapeText( s ) {
+ if ( !s ) {
+ return "";
+ }
+ s = s + "";
+ // Both single quotes and double quotes (for attributes)
+ return s.replace( /['"<>&]/g, function( s ) {
+ switch( s ) {
+ case '\'':
+ return '&#039;';
+ case '"':
+ return '&quot;';
+ case '<':
+ return '&lt;';
+ case '>':
+ return '&gt;';
+ case '&':
+ return '&amp;';
+ }
+ });
+}
+
+function synchronize( callback, last ) {
+ config.queue.push( callback );
+
+ if ( config.autorun && !config.blocking ) {
+ process( last );
+ }
+}
+
+function process( last ) {
+ function next() {
+ process( last );
+ }
+ var start = new Date().getTime();
+ config.depth = config.depth ? config.depth + 1 : 1;
+
+ while ( config.queue.length && !config.blocking ) {
+ if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
+ config.queue.shift()();
+ } else {
+ window.setTimeout( next, 13 );
+ break;
+ }
+ }
+ config.depth--;
+ if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
+ done();
+ }
+}
+
+function saveGlobal() {
+ config.pollution = [];
+
+ if ( config.noglobals ) {
+ for ( var key in window ) {
+ // in Opera sometimes DOM element ids show up here, ignore them
+ if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
+ continue;
+ }
+ config.pollution.push( key );
+ }
+ }
+}
+
+function checkPollution() {
+ var newGlobals,
+ deletedGlobals,
+ old = config.pollution;
+
+ saveGlobal();
+
+ newGlobals = diff( config.pollution, old );
+ if ( newGlobals.length > 0 ) {
+ QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
+ }
+
+ deletedGlobals = diff( old, config.pollution );
+ if ( deletedGlobals.length > 0 ) {
+ QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
+ }
+}
+
+// returns a new Array with the elements that are in a but not in b
+function diff( a, b ) {
+ var i, j,
+ result = a.slice();
+
+ for ( i = 0; i < result.length; i++ ) {
+ for ( j = 0; j < b.length; j++ ) {
+ if ( result[i] === b[j] ) {
+ result.splice( i, 1 );
+ i--;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+function extend( a, b ) {
+ for ( var prop in b ) {
+ if ( b[ prop ] === undefined ) {
+ delete a[ prop ];
+
+ // Avoid "Member not found" error in IE8 caused by setting window.constructor
+ } else if ( prop !== "constructor" || a !== window ) {
+ a[ prop ] = b[ prop ];
+ }
+ }
+
+ return a;
+}
+
+/**
+ * @param {HTMLElement} elem
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvent( elem, type, fn ) {
+ // Standards-based browsers
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, fn, false );
+ // IE
+ } else {
+ elem.attachEvent( "on" + type, fn );
+ }
+}
+
+/**
+ * @param {Array|NodeList} elems
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvents( elems, type, fn ) {
+ var i = elems.length;
+ while ( i-- ) {
+ addEvent( elems[i], type, fn );
+ }
+}
+
+function hasClass( elem, name ) {
+ return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
+}
+
+function addClass( elem, name ) {
+ if ( !hasClass( elem, name ) ) {
+ elem.className += (elem.className ? " " : "") + name;
+ }
+}
+
+function removeClass( elem, name ) {
+ var set = " " + elem.className + " ";
+ // Class name may appear multiple times
+ while ( set.indexOf(" " + name + " ") > -1 ) {
+ set = set.replace(" " + name + " " , " ");
+ }
+ // If possible, trim it for prettiness, but not neccecarily
+ elem.className = window.jQuery ? jQuery.trim( set ) : ( set.trim ? set.trim() : set );
+}
+
+function id( name ) {
+ return !!( typeof document !== "undefined" && document && document.getElementById ) &&
+ document.getElementById( name );
+}
+
+function registerLoggingCallback( key ) {
+ return function( callback ) {
+ config[key].push( callback );
+ };
+}
+
+// Supports deprecated method of completely overwriting logging callbacks
+function runLoggingCallbacks( key, scope, args ) {
+ var i, callbacks;
+ if ( QUnit.hasOwnProperty( key ) ) {
+ QUnit[ key ].call(scope, args );
+ } else {
+ callbacks = config[ key ];
+ for ( i = 0; i < callbacks.length; i++ ) {
+ callbacks[ i ].call( scope, args );
+ }
+ }
+}
+
+// Test for equality any JavaScript type.
+// Author: Philippe Rathé <prathe@gmail.com>
+QUnit.equiv = (function() {
+
+ // Call the o related callback with the given arguments.
+ function bindCallbacks( o, callbacks, args ) {
+ var prop = QUnit.objectType( o );
+ if ( prop ) {
+ if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
+ return callbacks[ prop ].apply( callbacks, args );
+ } else {
+ return callbacks[ prop ]; // or undefined
+ }
+ }
+ }
+
+ // the real equiv function
+ var innerEquiv,
+ // stack to decide between skip/abort functions
+ callers = [],
+ // stack to avoiding loops from circular referencing
+ parents = [],
+
+ getProto = Object.getPrototypeOf || function ( obj ) {
+ return obj.__proto__;
+ },
+ callbacks = (function () {
+
+ // for string, boolean, number and null
+ function useStrictEquality( b, a ) {
+ /*jshint eqeqeq:false */
+ if ( b instanceof a.constructor || a instanceof b.constructor ) {
+ // to catch short annotaion VS 'new' annotation of a
+ // declaration
+ // e.g. var i = 1;
+ // var j = new Number(1);
+ return a == b;
+ } else {
+ return a === b;
+ }
+ }
+
+ return {
+ "string": useStrictEquality,
+ "boolean": useStrictEquality,
+ "number": useStrictEquality,
+ "null": useStrictEquality,
+ "undefined": useStrictEquality,
+
+ "nan": function( b ) {
+ return isNaN( b );
+ },
+
+ "date": function( b, a ) {
+ return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+ },
+
+ "regexp": function( b, a ) {
+ return QUnit.objectType( b ) === "regexp" &&
+ // the regex itself
+ a.source === b.source &&
+ // and its modifers
+ a.global === b.global &&
+ // (gmi) ...
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline &&
+ a.sticky === b.sticky;
+ },
+
+ // - skip when the property is a method of an instance (OOP)
+ // - abort otherwise,
+ // initial === would have catch identical references anyway
+ "function": function() {
+ var caller = callers[callers.length - 1];
+ return caller !== Object && typeof caller !== "undefined";
+ },
+
+ "array": function( b, a ) {
+ var i, j, len, loop;
+
+ // b could be an object literal here
+ if ( QUnit.objectType( b ) !== "array" ) {
+ return false;
+ }
+
+ len = a.length;
+ if ( len !== b.length ) {
+ // safe and faster
+ return false;
+ }
+
+ // track reference to avoid circular references
+ parents.push( a );
+ for ( i = 0; i < len; i++ ) {
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ if ( parents[j] === a[i] ) {
+ loop = true;// dont rewalk array
+ }
+ }
+ if ( !loop && !innerEquiv(a[i], b[i]) ) {
+ parents.pop();
+ return false;
+ }
+ }
+ parents.pop();
+ return true;
+ },
+
+ "object": function( b, a ) {
+ var i, j, loop,
+ // Default to true
+ eq = true,
+ aProperties = [],
+ bProperties = [];
+
+ // comparing constructors is more strict than using
+ // instanceof
+ if ( a.constructor !== b.constructor ) {
+ // Allow objects with no prototype to be equivalent to
+ // objects with Object as their constructor.
+ if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
+ ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
+ return false;
+ }
+ }
+
+ // stack constructor before traversing properties
+ callers.push( a.constructor );
+ // track reference to avoid circular references
+ parents.push( a );
+
+ for ( i in a ) { // be strict: don't ensures hasOwnProperty
+ // and go deep
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ if ( parents[j] === a[i] ) {
+ // don't go down the same path twice
+ loop = true;
+ }
+ }
+ aProperties.push(i); // collect a's properties
+
+ if (!loop && !innerEquiv( a[i], b[i] ) ) {
+ eq = false;
+ break;
+ }
+ }
+
+ callers.pop(); // unstack, we are done
+ parents.pop();
+
+ for ( i in b ) {
+ bProperties.push( i ); // collect b's properties
+ }
+
+ // Ensures identical properties name
+ return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+ }
+ };
+ }());
+
+ innerEquiv = function() { // can take multiple arguments
+ var args = [].slice.apply( arguments );
+ if ( args.length < 2 ) {
+ return true; // end transition
+ }
+
+ return (function( a, b ) {
+ if ( a === b ) {
+ return true; // catch the most you can
+ } else if ( a === null || b === null || typeof a === "undefined" ||
+ typeof b === "undefined" ||
+ QUnit.objectType(a) !== QUnit.objectType(b) ) {
+ return false; // don't lose time with error prone cases
+ } else {
+ return bindCallbacks(a, callbacks, [ b, a ]);
+ }
+
+ // apply transition with (1..n) arguments
+ }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
+ };
+
+ return innerEquiv;
+}());
+
+/**
+ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
+ * http://flesler.blogspot.com Licensed under BSD
+ * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
+ *
+ * @projectDescription Advanced and extensible data dumping for Javascript.
+ * @version 1.0.0
+ * @author Ariel Flesler
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
+ */
+QUnit.jsDump = (function() {
+ function quote( str ) {
+ return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
+ }
+ function literal( o ) {
+ return o + "";
+ }
+ function join( pre, arr, post ) {
+ var s = jsDump.separator(),
+ base = jsDump.indent(),
+ inner = jsDump.indent(1);
+ if ( arr.join ) {
+ arr = arr.join( "," + s + inner );
+ }
+ if ( !arr ) {
+ return pre + post;
+ }
+ return [ pre, inner + arr, base + post ].join(s);
+ }
+ function array( arr, stack ) {
+ var i = arr.length, ret = new Array(i);
+ this.up();
+ while ( i-- ) {
+ ret[i] = this.parse( arr[i] , undefined , stack);
+ }
+ this.down();
+ return join( "[", ret, "]" );
+ }
+
+ var reName = /^function (\w+)/,
+ jsDump = {
+ // type is used mostly internally, you can fix a (custom)type in advance
+ parse: function( obj, type, stack ) {
+ stack = stack || [ ];
+ var inStack, res,
+ parser = this.parsers[ type || this.typeOf(obj) ];
+
+ type = typeof parser;
+ inStack = inArray( obj, stack );
+
+ if ( inStack !== -1 ) {
+ return "recursion(" + (inStack - stack.length) + ")";
+ }
+ if ( type === "function" ) {
+ stack.push( obj );
+ res = parser.call( this, obj, stack );
+ stack.pop();
+ return res;
+ }
+ return ( type === "string" ) ? parser : this.parsers.error;
+ },
+ typeOf: function( obj ) {
+ var type;
+ if ( obj === null ) {
+ type = "null";
+ } else if ( typeof obj === "undefined" ) {
+ type = "undefined";
+ } else if ( QUnit.is( "regexp", obj) ) {
+ type = "regexp";
+ } else if ( QUnit.is( "date", obj) ) {
+ type = "date";
+ } else if ( QUnit.is( "function", obj) ) {
+ type = "function";
+ } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
+ type = "window";
+ } else if ( obj.nodeType === 9 ) {
+ type = "document";
+ } else if ( obj.nodeType ) {
+ type = "node";
+ } else if (
+ // native arrays
+ toString.call( obj ) === "[object Array]" ||
+ // NodeList objects
+ ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
+ ) {
+ type = "array";
+ } else if ( obj.constructor === Error.prototype.constructor ) {
+ type = "error";
+ } else {
+ type = typeof obj;
+ }
+ return type;
+ },
+ separator: function() {
+ return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
+ },
+ // extra can be a number, shortcut for increasing-calling-decreasing
+ indent: function( extra ) {
+ if ( !this.multiline ) {
+ return "";
+ }
+ var chr = this.indentChar;
+ if ( this.HTML ) {
+ chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
+ }
+ return new Array( this._depth_ + (extra||0) ).join(chr);
+ },
+ up: function( a ) {
+ this._depth_ += a || 1;
+ },
+ down: function( a ) {
+ this._depth_ -= a || 1;
+ },
+ setParser: function( name, parser ) {
+ this.parsers[name] = parser;
+ },
+ // The next 3 are exposed so you can use them
+ quote: quote,
+ literal: literal,
+ join: join,
+ //
+ _depth_: 1,
+ // This is the list of parsers, to modify them, use jsDump.setParser
+ parsers: {
+ window: "[Window]",
+ document: "[Document]",
+ error: function(error) {
+ return "Error(\"" + error.message + "\")";
+ },
+ unknown: "[Unknown]",
+ "null": "null",
+ "undefined": "undefined",
+ "function": function( fn ) {
+ var ret = "function",
+ // functions never have name in IE
+ name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
+
+ if ( name ) {
+ ret += " " + name;
+ }
+ ret += "( ";
+
+ ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
+ return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
+ },
+ array: array,
+ nodelist: array,
+ "arguments": array,
+ object: function( map, stack ) {
+ var ret = [ ], keys, key, val, i;
+ QUnit.jsDump.up();
+ keys = [];
+ for ( key in map ) {
+ keys.push( key );
+ }
+ keys.sort();
+ for ( i = 0; i < keys.length; i++ ) {
+ key = keys[ i ];
+ val = map[ key ];
+ ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
+ }
+ QUnit.jsDump.down();
+ return join( "{", ret, "}" );
+ },
+ node: function( node ) {
+ var len, i, val,
+ open = QUnit.jsDump.HTML ? "&lt;" : "<",
+ close = QUnit.jsDump.HTML ? "&gt;" : ">",
+ tag = node.nodeName.toLowerCase(),
+ ret = open + tag,
+ attrs = node.attributes;
+
+ if ( attrs ) {
+ for ( i = 0, len = attrs.length; i < len; i++ ) {
+ val = attrs[i].nodeValue;
+ // IE6 includes all attributes in .attributes, even ones not explicitly set.
+ // Those have values like undefined, null, 0, false, "" or "inherit".
+ if ( val && val !== "inherit" ) {
+ ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
+ }
+ }
+ }
+ ret += close;
+
+ // Show content of TextNode or CDATASection
+ if ( node.nodeType === 3 || node.nodeType === 4 ) {
+ ret += node.nodeValue;
+ }
+
+ return ret + open + "/" + tag + close;
+ },
+ // function calls it internally, it's the arguments part of the function
+ functionArgs: function( fn ) {
+ var args,
+ l = fn.length;
+
+ if ( !l ) {
+ return "";
+ }
+
+ args = new Array(l);
+ while ( l-- ) {
+ // 97 is 'a'
+ args[l] = String.fromCharCode(97+l);
+ }
+ return " " + args.join( ", " ) + " ";
+ },
+ // object calls it internally, the key part of an item in a map
+ key: quote,
+ // function calls it internally, it's the content of the function
+ functionCode: "[code]",
+ // node calls it internally, it's an html attribute value
+ attribute: quote,
+ string: quote,
+ date: quote,
+ regexp: literal,
+ number: literal,
+ "boolean": literal
+ },
+ // if true, entities are escaped ( <, >, \t, space and \n )
+ HTML: false,
+ // indentation unit
+ indentChar: " ",
+ // if true, items in a collection, are separated by a \n, else just a space.
+ multiline: true
+ };
+
+ return jsDump;
+}());
+
+// from jquery.js
+function inArray( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Javascript Diff Algorithm
+ * By John Resig (http://ejohn.org/)
+ * Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ * http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
+ */
+QUnit.diff = (function() {
+ /*jshint eqeqeq:false, eqnull:true */
+ function diff( o, n ) {
+ var i,
+ ns = {},
+ os = {};
+
+ for ( i = 0; i < n.length; i++ ) {
+ if ( !hasOwn.call( ns, n[i] ) ) {
+ ns[ n[i] ] = {
+ rows: [],
+ o: null
+ };
+ }
+ ns[ n[i] ].rows.push( i );
+ }
+
+ for ( i = 0; i < o.length; i++ ) {
+ if ( !hasOwn.call( os, o[i] ) ) {
+ os[ o[i] ] = {
+ rows: [],
+ n: null
+ };
+ }
+ os[ o[i] ].rows.push( i );
+ }
+
+ for ( i in ns ) {
+ if ( !hasOwn.call( ns, i ) ) {
+ continue;
+ }
+ if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
+ n[ ns[i].rows[0] ] = {
+ text: n[ ns[i].rows[0] ],
+ row: os[i].rows[0]
+ };
+ o[ os[i].rows[0] ] = {
+ text: o[ os[i].rows[0] ],
+ row: ns[i].rows[0]
+ };
+ }
+ }
+
+ for ( i = 0; i < n.length - 1; i++ ) {
+ if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
+ n[ i + 1 ] == o[ n[i].row + 1 ] ) {
+
+ n[ i + 1 ] = {
+ text: n[ i + 1 ],
+ row: n[i].row + 1
+ };
+ o[ n[i].row + 1 ] = {
+ text: o[ n[i].row + 1 ],
+ row: i + 1
+ };
+ }
+ }
+
+ for ( i = n.length - 1; i > 0; i-- ) {
+ if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
+ n[ i - 1 ] == o[ n[i].row - 1 ]) {
+
+ n[ i - 1 ] = {
+ text: n[ i - 1 ],
+ row: n[i].row - 1
+ };
+ o[ n[i].row - 1 ] = {
+ text: o[ n[i].row - 1 ],
+ row: i - 1
+ };
+ }
+ }
+
+ return {
+ o: o,
+ n: n
+ };
+ }
+
+ return function( o, n ) {
+ o = o.replace( /\s+$/, "" );
+ n = n.replace( /\s+$/, "" );
+
+ var i, pre,
+ str = "",
+ out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
+ oSpace = o.match(/\s+/g),
+ nSpace = n.match(/\s+/g);
+
+ if ( oSpace == null ) {
+ oSpace = [ " " ];
+ }
+ else {
+ oSpace.push( " " );
+ }
+
+ if ( nSpace == null ) {
+ nSpace = [ " " ];
+ }
+ else {
+ nSpace.push( " " );
+ }
+
+ if ( out.n.length === 0 ) {
+ for ( i = 0; i < out.o.length; i++ ) {
+ str += "<del>" + out.o[i] + oSpace[i] + "</del>";
+ }
+ }
+ else {
+ if ( out.n[0].text == null ) {
+ for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
+ str += "<del>" + out.o[n] + oSpace[n] + "</del>";
+ }
+ }
+
+ for ( i = 0; i < out.n.length; i++ ) {
+ if (out.n[i].text == null) {
+ str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
+ }
+ else {
+ // `pre` initialized at top of scope
+ pre = "";
+
+ for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
+ pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
+ }
+ str += " " + out.n[i].text + nSpace[i] + pre;
+ }
+ }
+ }
+
+ return str;
+ };
+}());
+
+// for CommonJS enviroments, export everything
+if ( typeof exports !== "undefined" ) {
+ extend( exports, QUnit );
+}
+
+// get at whatever the global object is, like window in browsers
+}( (function() {return this;}.call()) ));