Socket
Socket
Sign inDemoInstall

underscore.string

Package Overview
Dependencies
Maintainers
3
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

underscore.string - npm Package Compare versions

Comparing version 2.3.0 to 2.3.1

test/test_underscore/index.html

2

dist/underscore.string.min.js

@@ -1,1 +0,1 @@

!function(e,t){"use strict";var n=t.prototype.trim,r=t.prototype.trimRight,i=t.prototype.trimLeft,s=function(e){return e*1||0},o=function(e,t){if(t<1)return"";var n="";while(t>0)t&1&&(n+=e),t>>=1,e+=e;return n},u=[].slice,a=function(e){return e==null?"\\s":e.source?e.source:"["+p.escapeRegExp(e)+"]"},f={lt:"<",gt:">",quot:'"',apos:"'",amp:"&"},l={};for(var c in f)l[f[c]]=c;var h=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}var n=o,r=function(){return r.cache.hasOwnProperty(arguments[0])||(r.cache[arguments[0]]=r.parse(arguments[0])),r.format.call(null,r.cache[arguments[0]],arguments)};return r.format=function(r,i){var s=1,o=r.length,u="",a,f=[],l,c,p,d,v,m;for(l=0;l<o;l++){u=e(r[l]);if(u==="string")f.push(r[l]);else if(u==="array"){p=r[l];if(p[2]){a=i[s];for(c=0;c<p[2].length;c++){if(!a.hasOwnProperty(p[2][c]))throw new Error(h('[_.sprintf] property "%s" does not exist',p[2][c]));a=a[p[2][c]]}}else p[1]?a=i[p[1]]:a=i[s++];if(/[^s]/.test(p[8])&&e(a)!="number")throw new Error(h("[_.sprintf] expecting number but found %s",e(a)));switch(p[8]){case"b":a=a.toString(2);break;case"c":a=t.fromCharCode(a);break;case"d":a=parseInt(a,10);break;case"e":a=p[7]?a.toExponential(p[7]):a.toExponential();break;case"f":a=p[7]?parseFloat(a).toFixed(p[7]):parseFloat(a);break;case"o":a=a.toString(8);break;case"s":a=(a=t(a))&&p[7]?a.substring(0,p[7]):a;break;case"u":a=Math.abs(a);break;case"x":a=a.toString(16);break;case"X":a=a.toString(16).toUpperCase()}a=/[def]/.test(p[8])&&p[3]&&a>=0?"+"+a:a,v=p[4]?p[4]=="0"?"0":p[4].charAt(1):" ",m=p[6]-t(a).length,d=p[6]?n(v,m):"",f.push(p[5]?a+d:d+a)}}return f.join("")},r.cache={},r.parse=function(e){var t=e,n=[],r=[],i=0;while(t){if((n=/^[^\x25]+/.exec(t))!==null)r.push(n[0]);else if((n=/^\x25{2}/.exec(t))!==null)r.push("%");else{if((n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t))===null)throw new Error("[_.sprintf] huh?");if(n[2]){i|=1;var s=[],o=n[2],u=[];if((u=/^([a-z_][a-z_\d]*)/i.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1]);while((o=o.substring(u[0].length))!=="")if((u=/^\.([a-z_][a-z_\d]*)/i.exec(o))!==null)s.push(u[1]);else{if((u=/^\[(\d+)\]/.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1])}n[2]=s}else i|=2;if(i===3)throw new Error("[_.sprintf] mixing positional and named placeholders is not (yet) supported");r.push(n)}t=t.substring(n[0].length)}return r},r}(),p={VERSION:"2.3.0",isBlank:function(e){return e==null&&(e=""),/^\s*$/.test(e)},stripTags:function(e){return e==null?"":t(e).replace(/<\/?[^>]+>/g,"")},capitalize:function(e){return e=e==null?"":t(e),e.charAt(0).toUpperCase()+e.slice(1)},chop:function(e,n){return e==null?[]:(e=t(e),n=~~n,n>0?e.match(new RegExp(".{1,"+n+"}","g")):[e])},clean:function(e){return p.strip(e).replace(/\s+/g," ")},count:function(e,n){return e==null||n==null?0:t(e).split(n).length-1},chars:function(e){return e==null?[]:t(e).split("")},swapCase:function(e){return e==null?"":t(e).replace(/\S/g,function(e){return e===e.toUpperCase()?e.toLowerCase():e.toUpperCase()})},escapeHTML:function(e){return e==null?"":t(e).replace(/[&<>"']/g,function(e){return"&"+l[e]+";"})},unescapeHTML:function(e){return e==null?"":t(e).replace(/\&([^;]+);/g,function(e,n){var r;return n in f?f[n]:(r=n.match(/^#x([\da-fA-F]+)$/))?t.fromCharCode(parseInt(r[1],16)):(r=n.match(/^#(\d+)$/))?t.fromCharCode(~~r[1]):e})},escapeRegExp:function(e){return e==null?"":t(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},splice:function(e,t,n,r){var i=p.chars(e);return i.splice(~~t,~~n,r),i.join("")},insert:function(e,t,n){return p.splice(e,t,0,n)},include:function(e,n){return n===""?!0:e==null?!1:t(e).indexOf(n)!==-1},join:function(){var e=u.call(arguments),t=e.shift();return t==null&&(t=""),e.join(t)},lines:function(e){return e==null?[]:t(e).split("\n")},reverse:function(e){return p.chars(e).reverse().join("")},startsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(0,n.length)===n)},endsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(e.length-n.length)===n)},succ:function(e){return e==null?"":(e=t(e),e.slice(0,-1)+t.fromCharCode(e.charCodeAt(e.length-1)+1))},titleize:function(e){return e==null?"":t(e).replace(/(?:^|\s)\S/g,function(e){return e.toUpperCase()})},camelize:function(e){return p.trim(e).replace(/[-_\s]+(.)?/g,function(e,t){return t.toUpperCase()})},underscored:function(e){return p.trim(e).replace(/([a-z\d])([A-Z]+)/g,"$1_$2").replace(/[-\s]+/g,"_").toLowerCase()},dasherize:function(e){return p.trim(e).replace(/([A-Z])/g,"-$1").replace(/[-_\s]+/g,"-").toLowerCase()},classify:function(e){return p.titleize(t(e).replace(/_/g," ")).replace(/\s/g,"")},humanize:function(e){return p.capitalize(p.underscored(e).replace(/_id$/,"").replace(/_/g," "))},trim:function(e,r){return e==null?"":!r&&n?n.call(e):(r=a(r),t(e).replace(new RegExp("^"+r+"+|"+r+"+$","g"),""))},ltrim:function(e,n){return e==null?"":!n&&i?i.call(e):(n=a(n),t(e).replace(new RegExp("^"+n+"+"),""))},rtrim:function(e,n){return e==null?"":!n&&r?r.call(e):(n=a(n),t(e).replace(new RegExp(n+"+$"),""))},truncate:function(e,n,r){return e==null?"":(e=t(e),r=r||"...",n=~~n,e.length>n?e.slice(0,n)+r:e)},prune:function(e,n,r){if(e==null)return"";e=t(e),n=~~n,r=r!=null?t(r):"...";if(e.length<=n)return e;var i=function(e){return e.toUpperCase()!==e.toLowerCase()?"A":" "},s=e.slice(0,n+1).replace(/.(?=\W*\w*$)/g,i);return s.slice(s.length-2).match(/\w\w/)?s=s.replace(/\s*\S+$/,""):s=p.rtrim(s.slice(0,s.length-1)),(s+r).length>e.length?e:e.slice(0,s.length)+r},words:function(e,t){return p.isBlank(e)?[]:p.trim(e,t).split(t||/\s+/)},pad:function(e,n,r,i){e=e==null?"":t(e),n=~~n;var s=0;r?r.length>1&&(r=r.charAt(0)):r=" ";switch(i){case"right":return s=n-e.length,e+o(r,s);case"both":return s=n-e.length,o(r,Math.ceil(s/2))+e+o(r,Math.floor(s/2));default:return s=n-e.length,o(r,s)+e}},lpad:function(e,t,n){return p.pad(e,t,n)},rpad:function(e,t,n){return p.pad(e,t,n,"right")},lrpad:function(e,t,n){return p.pad(e,t,n,"both")},sprintf:h,vsprintf:function(e,t){return t.unshift(e),h.apply(null,t)},toNumber:function(e,n){if(e==null||e=="")return 0;e=t(e);var r=s(s(e).toFixed(~~n));return r===0&&!e.match(/^0+$/)?Number.NaN:r},numberFormat:function(e,t,n,r){if(isNaN(e)||e==null)return"";e=e.toFixed(~~t),r=r||",";var i=e.split("."),s=i[0],o=i[1]?(n||".")+i[1]:"";return s.replace(/(\d)(?=(?:\d{3})+$)/g,"$1"+r)+o},strRight:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strRightBack:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.lastIndexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strLeft:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(0,r):e},strLeftBack:function(e,t){if(e==null)return"";e+="",t=t!=null?""+t:t;var n=e.lastIndexOf(t);return~n?e.slice(0,n):e},toSentence:function(e,t,n,r){t=t||", ",n=n||" and ";var i=e.slice(),s=i.pop();return e.length>2&&r&&(n=p.rtrim(t)+n),i.length?i.join(t)+n+s:s},toSentenceSerial:function(){var e=u.call(arguments);return e[3]=!0,p.toSentence.apply(p,e)},slugify:function(e){if(e==null)return"";var n="ąàáäâãåæćęèéëêìíïîłńòóöôõøùúüûñçżź",r="aaaaaaaaceeeeeiiiilnoooooouuuunczz",i=new RegExp(a(n),"g");return e=t(e).toLowerCase().replace(i,function(e){var t=n.indexOf(e);return r.charAt(t)||"-"}),p.dasherize(e.replace(/[^\w\s-]/g,""))},surround:function(e,t){return[t,e,t].join("")},quote:function(e){return p.surround(e,'"')},exports:function(){var e={};for(var t in this){if(!this.hasOwnProperty(t)||t.match(/^(?:include|contains|reverse)$/))continue;e[t]=this[t]}return e},repeat:function(e,n,r){if(e==null)return"";n=~~n;if(r==null)return o(t(e),n);for(var i=[];n>0;i[--n]=e);return i.join(r)},levenshtein:function(e,n){if(e==null&&n==null)return 0;if(e==null)return t(n).length;if(n==null)return t(e).length;e=t(e),n=t(n);var r=[],i,s;for(var o=0;o<=n.length;o++)for(var u=0;u<=e.length;u++)o&&u?e.charAt(u-1)===n.charAt(o-1)?s=i:s=Math.min(r[u],r[u-1],i)+1:s=o+u,i=r[u],r[u]=s;return r.pop()}};p.strip=p.trim,p.lstrip=p.ltrim,p.rstrip=p.rtrim,p.center=p.lrpad,p.rjust=p.lpad,p.ljust=p.rpad,p.contains=p.include,p.q=p.quote,typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(module.exports=p),exports._s=p):typeof define=="function"&&define.amd?define("underscore.string",[],function(){return p}):(e._=e._||{},e._.string=e._.str=p)}(this,String);
!function(e,t){"use strict";var n=t.prototype.trim,r=t.prototype.trimRight,i=t.prototype.trimLeft,s=function(e){return e*1||0},o=function(e,t){if(t<1)return"";var n="";while(t>0)t&1&&(n+=e),t>>=1,e+=e;return n},u=[].slice,a=function(e){return e==null?"\\s":e.source?e.source:"["+p.escapeRegExp(e)+"]"},f={lt:"<",gt:">",quot:'"',amp:"&",apos:"'"},l={};for(var c in f)l[f[c]]=c;l["'"]="#39";var h=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}var n=o,r=function(){return r.cache.hasOwnProperty(arguments[0])||(r.cache[arguments[0]]=r.parse(arguments[0])),r.format.call(null,r.cache[arguments[0]],arguments)};return r.format=function(r,i){var s=1,o=r.length,u="",a,f=[],l,c,p,d,v,m;for(l=0;l<o;l++){u=e(r[l]);if(u==="string")f.push(r[l]);else if(u==="array"){p=r[l];if(p[2]){a=i[s];for(c=0;c<p[2].length;c++){if(!a.hasOwnProperty(p[2][c]))throw new Error(h('[_.sprintf] property "%s" does not exist',p[2][c]));a=a[p[2][c]]}}else p[1]?a=i[p[1]]:a=i[s++];if(/[^s]/.test(p[8])&&e(a)!="number")throw new Error(h("[_.sprintf] expecting number but found %s",e(a)));switch(p[8]){case"b":a=a.toString(2);break;case"c":a=t.fromCharCode(a);break;case"d":a=parseInt(a,10);break;case"e":a=p[7]?a.toExponential(p[7]):a.toExponential();break;case"f":a=p[7]?parseFloat(a).toFixed(p[7]):parseFloat(a);break;case"o":a=a.toString(8);break;case"s":a=(a=t(a))&&p[7]?a.substring(0,p[7]):a;break;case"u":a=Math.abs(a);break;case"x":a=a.toString(16);break;case"X":a=a.toString(16).toUpperCase()}a=/[def]/.test(p[8])&&p[3]&&a>=0?"+"+a:a,v=p[4]?p[4]=="0"?"0":p[4].charAt(1):" ",m=p[6]-t(a).length,d=p[6]?n(v,m):"",f.push(p[5]?a+d:d+a)}}return f.join("")},r.cache={},r.parse=function(e){var t=e,n=[],r=[],i=0;while(t){if((n=/^[^\x25]+/.exec(t))!==null)r.push(n[0]);else if((n=/^\x25{2}/.exec(t))!==null)r.push("%");else{if((n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t))===null)throw new Error("[_.sprintf] huh?");if(n[2]){i|=1;var s=[],o=n[2],u=[];if((u=/^([a-z_][a-z_\d]*)/i.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1]);while((o=o.substring(u[0].length))!=="")if((u=/^\.([a-z_][a-z_\d]*)/i.exec(o))!==null)s.push(u[1]);else{if((u=/^\[(\d+)\]/.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1])}n[2]=s}else i|=2;if(i===3)throw new Error("[_.sprintf] mixing positional and named placeholders is not (yet) supported");r.push(n)}t=t.substring(n[0].length)}return r},r}(),p={VERSION:"2.3.0",isBlank:function(e){return e==null&&(e=""),/^\s*$/.test(e)},stripTags:function(e){return e==null?"":t(e).replace(/<\/?[^>]+>/g,"")},capitalize:function(e){return e=e==null?"":t(e),e.charAt(0).toUpperCase()+e.slice(1)},chop:function(e,n){return e==null?[]:(e=t(e),n=~~n,n>0?e.match(new RegExp(".{1,"+n+"}","g")):[e])},clean:function(e){return p.strip(e).replace(/\s+/g," ")},count:function(e,n){if(e==null||n==null)return 0;e=t(e),n=t(n);var r=0,i=0,s=n.length;for(;;){i=e.indexOf(n,i);if(i===-1)break;r++,i+=s}return r},chars:function(e){return e==null?[]:t(e).split("")},swapCase:function(e){return e==null?"":t(e).replace(/\S/g,function(e){return e===e.toUpperCase()?e.toLowerCase():e.toUpperCase()})},escapeHTML:function(e){return e==null?"":t(e).replace(/[&<>"']/g,function(e){return"&"+l[e]+";"})},unescapeHTML:function(e){return e==null?"":t(e).replace(/\&([^;]+);/g,function(e,n){var r;return n in f?f[n]:(r=n.match(/^#x([\da-fA-F]+)$/))?t.fromCharCode(parseInt(r[1],16)):(r=n.match(/^#(\d+)$/))?t.fromCharCode(~~r[1]):e})},escapeRegExp:function(e){return e==null?"":t(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},splice:function(e,t,n,r){var i=p.chars(e);return i.splice(~~t,~~n,r),i.join("")},insert:function(e,t,n){return p.splice(e,t,0,n)},include:function(e,n){return n===""?!0:e==null?!1:t(e).indexOf(n)!==-1},join:function(){var e=u.call(arguments),t=e.shift();return t==null&&(t=""),e.join(t)},lines:function(e){return e==null?[]:t(e).split("\n")},reverse:function(e){return p.chars(e).reverse().join("")},startsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(0,n.length)===n)},endsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(e.length-n.length)===n)},succ:function(e){return e==null?"":(e=t(e),e.slice(0,-1)+t.fromCharCode(e.charCodeAt(e.length-1)+1))},titleize:function(e){return e==null?"":t(e).replace(/(?:^|\s)\S/g,function(e){return e.toUpperCase()})},camelize:function(e){return p.trim(e).replace(/[-_\s]+(.)?/g,function(e,t){return t.toUpperCase()})},underscored:function(e){return p.trim(e).replace(/([a-z\d])([A-Z]+)/g,"$1_$2").replace(/[-\s]+/g,"_").toLowerCase()},dasherize:function(e){return p.trim(e).replace(/([A-Z])/g,"-$1").replace(/[-_\s]+/g,"-").toLowerCase()},classify:function(e){return p.titleize(t(e).replace(/_/g," ")).replace(/\s/g,"")},humanize:function(e){return p.capitalize(p.underscored(e).replace(/_id$/,"").replace(/_/g," "))},trim:function(e,r){return e==null?"":!r&&n?n.call(e):(r=a(r),t(e).replace(new RegExp("^"+r+"+|"+r+"+$","g"),""))},ltrim:function(e,n){return e==null?"":!n&&i?i.call(e):(n=a(n),t(e).replace(new RegExp("^"+n+"+"),""))},rtrim:function(e,n){return e==null?"":!n&&r?r.call(e):(n=a(n),t(e).replace(new RegExp(n+"+$"),""))},truncate:function(e,n,r){return e==null?"":(e=t(e),r=r||"...",n=~~n,e.length>n?e.slice(0,n)+r:e)},prune:function(e,n,r){if(e==null)return"";e=t(e),n=~~n,r=r!=null?t(r):"...";if(e.length<=n)return e;var i=function(e){return e.toUpperCase()!==e.toLowerCase()?"A":" "},s=e.slice(0,n+1).replace(/.(?=\W*\w*$)/g,i);return s.slice(s.length-2).match(/\w\w/)?s=s.replace(/\s*\S+$/,""):s=p.rtrim(s.slice(0,s.length-1)),(s+r).length>e.length?e:e.slice(0,s.length)+r},words:function(e,t){return p.isBlank(e)?[]:p.trim(e,t).split(t||/\s+/)},pad:function(e,n,r,i){e=e==null?"":t(e),n=~~n;var s=0;r?r.length>1&&(r=r.charAt(0)):r=" ";switch(i){case"right":return s=n-e.length,e+o(r,s);case"both":return s=n-e.length,o(r,Math.ceil(s/2))+e+o(r,Math.floor(s/2));default:return s=n-e.length,o(r,s)+e}},lpad:function(e,t,n){return p.pad(e,t,n)},rpad:function(e,t,n){return p.pad(e,t,n,"right")},lrpad:function(e,t,n){return p.pad(e,t,n,"both")},sprintf:h,vsprintf:function(e,t){return t.unshift(e),h.apply(null,t)},toNumber:function(e,n){if(e==null||e=="")return 0;e=t(e);var r=s(s(e).toFixed(~~n));return r===0&&!e.match(/^0+$/)?Number.NaN:r},numberFormat:function(e,t,n,r){if(isNaN(e)||e==null)return"";e=e.toFixed(~~t),r=typeof r=="string"?r:",";var i=e.split("."),s=i[0],o=i[1]?(n||".")+i[1]:"";return s.replace(/(\d)(?=(?:\d{3})+$)/g,"$1"+r)+o},strRight:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strRightBack:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.lastIndexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strLeft:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(0,r):e},strLeftBack:function(e,t){if(e==null)return"";e+="",t=t!=null?""+t:t;var n=e.lastIndexOf(t);return~n?e.slice(0,n):e},toSentence:function(e,t,n,r){t=t||", ",n=n||" and ";var i=e.slice(),s=i.pop();return e.length>2&&r&&(n=p.rtrim(t)+n),i.length?i.join(t)+n+s:s},toSentenceSerial:function(){var e=u.call(arguments);return e[3]=!0,p.toSentence.apply(p,e)},slugify:function(e){if(e==null)return"";var n="ąàáäâãåæćęèéëêìíïîłńòóöôõøùúüûñçżź",r="aaaaaaaaceeeeeiiiilnoooooouuuunczz",i=new RegExp(a(n),"g");return e=t(e).toLowerCase().replace(i,function(e){var t=n.indexOf(e);return r.charAt(t)||"-"}),p.dasherize(e.replace(/[^\w\s-]/g,""))},surround:function(e,t){return[t,e,t].join("")},quote:function(e){return p.surround(e,'"')},exports:function(){var e={};for(var t in this){if(!this.hasOwnProperty(t)||t.match(/^(?:include|contains|reverse)$/))continue;e[t]=this[t]}return e},repeat:function(e,n,r){if(e==null)return"";n=~~n;if(r==null)return o(t(e),n);for(var i=[];n>0;i[--n]=e);return i.join(r)},levenshtein:function(e,n){if(e==null&&n==null)return 0;if(e==null)return t(n).length;if(n==null)return t(e).length;e=t(e),n=t(n);var r=[],i,s;for(var o=0;o<=n.length;o++)for(var u=0;u<=e.length;u++)o&&u?e.charAt(u-1)===n.charAt(o-1)?s=i:s=Math.min(r[u],r[u-1],i)+1:s=o+u,i=r[u],r[u]=s;return r.pop()}};p.strip=p.trim,p.lstrip=p.ltrim,p.rstrip=p.rtrim,p.center=p.lrpad,p.rjust=p.lpad,p.ljust=p.rpad,p.contains=p.include,p.q=p.quote,typeof exports!="undefined"&&(typeof module!="undefined"&&module.exports&&(module.exports=p),exports._s=p),typeof define=="function"&&define.amd&&define("underscore.string",[],function(){return p}),e._=e._||{},e._.string=e._.str=p}(this,String);

@@ -6,3 +6,3 @@ // Underscore.string

// Some code is borrowed from MooTools and Alexandru Marasteanu.
// Version '2.3.0'
// Version '2.3.1'

@@ -45,8 +45,9 @@ !function(root, String){

quot: '"',
apos: "'",
amp: '&'
amp: '&',
apos: "'"
};
var reversedEscapeChars = {};
for(var key in escapeChars){ reversedEscapeChars[escapeChars[key]] = key; }
for(var key in escapeChars) reversedEscapeChars[escapeChars[key]] = key;
reversedEscapeChars["'"] = '#39';

@@ -181,3 +182,3 @@ // sprintf() for JavaScript 0.7-beta1

VERSION: '2.3.0',
VERSION: '2.3.1',

@@ -212,3 +213,18 @@ isBlank: function(str){

if (str == null || substr == null) return 0;
return String(str).split(substr).length - 1;
str = String(str);
substr = String(substr);
var count = 0,
pos = 0,
length = substr.length;
while (true) {
pos = str.indexOf(substr, pos);
if (pos === -1) break;
count++;
pos += length;
}
return count;
},

@@ -327,3 +343,3 @@

classify: function(str){
return _s.titleize(String(str).replace(/_/g, ' ')).replace(/\s/g, '');
return _s.titleize(String(str).replace(/[\W_]/g, ' ')).replace(/\s/g, '');
},

@@ -437,6 +453,6 @@

toNumber: function(str, decimals) {
if (str == null || str == '') return 0;
str = String(str);
var num = parseNumber(parseNumber(str).toFixed(~~decimals));
return num === 0 && !str.match(/^0+$/) ? Number.NaN : num;
if (!str) return 0;
str = _s.trim(str);
if (!str.match(/^-?\d+(?:\.\d+)?$/)) return NaN;
return parseNumber(parseNumber(str).toFixed(~~decimals));
},

@@ -448,3 +464,3 @@

number = number.toFixed(~~dec);
tsep = tsep || ',';
tsep = typeof tsep == 'string' ? tsep : ',';

@@ -586,23 +602,21 @@ var parts = number.split('.'), fnums = parts[0],

// Exporting
// CommonJS module is defined
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
// Export module
if (typeof module !== 'undefined' && module.exports)
module.exports = _s;
}
exports._s = _s;
}
} else if (typeof define === 'function' && define.amd) {
// Register as a named module with AMD.
define('underscore.string', [], function() {
return _s;
});
// Register as a named module with AMD.
if (typeof define === 'function' && define.amd)
define('underscore.string', [], function(){ return _s; });
} else {
// Integrate with Underscore.js if defined
// or create our own underscore object.
root._ = root._ || {};
root._.string = root._.str = _s;
}
// Integrate with Underscore.js if defined
// or create our own underscore object.
root._ = root._ || {};
root._.string = root._.str = _s;
}(this, String);
{
"name": "underscore.string",
"version": "2.3.0",
"version": "2.3.1",
"description": "String manipulation extensions for Underscore.js javascript library.",

@@ -5,0 +5,0 @@ "homepage": "http://epeli.github.com/underscore.string/",

@@ -9,118 +9,118 @@ $(document).ready(function() {

test('Strings: trim', function() {
equals(_.trim(123), '123', 'Non string');
equals(_(' foo').trim(), 'foo');
equals(_('foo ').trim(), 'foo');
equals(_(' foo ').trim(), 'foo');
equals(_(' foo ').trim(), 'foo');
equals(_(' foo ').trim(' '), 'foo', 'Manually set whitespace');
equals(_('\t foo \t ').trim(/\s/), 'foo', 'Manually set RegExp /\\s+/');
equal(_.trim(123), '123', 'Non string');
equal(_(' foo').trim(), 'foo');
equal(_('foo ').trim(), 'foo');
equal(_(' foo ').trim(), 'foo');
equal(_(' foo ').trim(), 'foo');
equal(_(' foo ').trim(' '), 'foo', 'Manually set whitespace');
equal(_('\t foo \t ').trim(/\s/), 'foo', 'Manually set RegExp /\\s+/');
equals(_('ffoo').trim('f'), 'oo');
equals(_('ooff').trim('f'), 'oo');
equals(_('ffooff').trim('f'), 'oo');
equal(_('ffoo').trim('f'), 'oo');
equal(_('ooff').trim('f'), 'oo');
equal(_('ffooff').trim('f'), 'oo');
equals(_('_-foobar-_').trim('_-'), 'foobar');
equal(_('_-foobar-_').trim('_-'), 'foobar');
equals(_('http://foo/').trim('/'), 'http://foo');
equals(_('c:\\').trim('\\'), 'c:');
equal(_('http://foo/').trim('/'), 'http://foo');
equal(_('c:\\').trim('\\'), 'c:');
equals(_(123).trim(), '123');
equals(_(123).trim(3), '12');
equals(_('').trim(), '', 'Trim empty string should return empty string');
equals(_(null).trim(), '', 'Trim null should return empty string');
equals(_(undefined).trim(), '', 'Trim undefined should return empty string');
equal(_(123).trim(), '123');
equal(_(123).trim(3), '12');
equal(_('').trim(), '', 'Trim empty string should return empty string');
equal(_(null).trim(), '', 'Trim null should return empty string');
equal(_(undefined).trim(), '', 'Trim undefined should return empty string');
});
test('String: levenshtein', function() {
equals(_.levenshtein('Godfather', 'Godfather'), 0);
equals(_.levenshtein('Godfather', 'Godfathe'), 1);
equals(_.levenshtein('Godfather', 'odfather'), 1);
equals(_.levenshtein('Godfather', 'Gdfthr'), 3);
equals(_.levenshtein('seven', 'eight'), 5);
equals(_.levenshtein('123', 123), 0);
equals(_.levenshtein(321, '321'), 0);
equals(_.levenshtein('lol', null), 3);
equals(_.levenshtein('lol'), 3);
equals(_.levenshtein(null, 'lol'), 3);
equals(_.levenshtein(undefined, 'lol'), 3);
equals(_.levenshtein(), 0);
equal(_.levenshtein('Godfather', 'Godfather'), 0);
equal(_.levenshtein('Godfather', 'Godfathe'), 1);
equal(_.levenshtein('Godfather', 'odfather'), 1);
equal(_.levenshtein('Godfather', 'Gdfthr'), 3);
equal(_.levenshtein('seven', 'eight'), 5);
equal(_.levenshtein('123', 123), 0);
equal(_.levenshtein(321, '321'), 0);
equal(_.levenshtein('lol', null), 3);
equal(_.levenshtein('lol'), 3);
equal(_.levenshtein(null, 'lol'), 3);
equal(_.levenshtein(undefined, 'lol'), 3);
equal(_.levenshtein(), 0);
});
test('Strings: ltrim', function() {
equals(_(' foo').ltrim(), 'foo');
equals(_(' foo').ltrim(), 'foo');
equals(_('foo ').ltrim(), 'foo ');
equals(_(' foo ').ltrim(), 'foo ');
equals(_('').ltrim(), '', 'ltrim empty string should return empty string');
equals(_(null).ltrim(), '', 'ltrim null should return empty string');
equals(_(undefined).ltrim(), '', 'ltrim undefined should return empty string');
equal(_(' foo').ltrim(), 'foo');
equal(_(' foo').ltrim(), 'foo');
equal(_('foo ').ltrim(), 'foo ');
equal(_(' foo ').ltrim(), 'foo ');
equal(_('').ltrim(), '', 'ltrim empty string should return empty string');
equal(_(null).ltrim(), '', 'ltrim null should return empty string');
equal(_(undefined).ltrim(), '', 'ltrim undefined should return empty string');
equals(_('ffoo').ltrim('f'), 'oo');
equals(_('ooff').ltrim('f'), 'ooff');
equals(_('ffooff').ltrim('f'), 'ooff');
equal(_('ffoo').ltrim('f'), 'oo');
equal(_('ooff').ltrim('f'), 'ooff');
equal(_('ffooff').ltrim('f'), 'ooff');
equals(_('_-foobar-_').ltrim('_-'), 'foobar-_');
equal(_('_-foobar-_').ltrim('_-'), 'foobar-_');
equals(_(123).ltrim(1), '23');
equal(_(123).ltrim(1), '23');
});
test('Strings: rtrim', function() {
equals(_('http://foo/').rtrim('/'), 'http://foo', 'clean trailing slash');
equals(_(' foo').rtrim(), ' foo');
equals(_('foo ').rtrim(), 'foo');
equals(_('foo ').rtrim(), 'foo');
equals(_('foo bar ').rtrim(), 'foo bar');
equals(_(' foo ').rtrim(), ' foo');
equal(_('http://foo/').rtrim('/'), 'http://foo', 'clean trailing slash');
equal(_(' foo').rtrim(), ' foo');
equal(_('foo ').rtrim(), 'foo');
equal(_('foo ').rtrim(), 'foo');
equal(_('foo bar ').rtrim(), 'foo bar');
equal(_(' foo ').rtrim(), ' foo');
equals(_('ffoo').rtrim('f'), 'ffoo');
equals(_('ooff').rtrim('f'), 'oo');
equals(_('ffooff').rtrim('f'), 'ffoo');
equal(_('ffoo').rtrim('f'), 'ffoo');
equal(_('ooff').rtrim('f'), 'oo');
equal(_('ffooff').rtrim('f'), 'ffoo');
equals(_('_-foobar-_').rtrim('_-'), '_-foobar');
equal(_('_-foobar-_').rtrim('_-'), '_-foobar');
equals(_(123).rtrim(3), '12');
equals(_('').rtrim(), '', 'rtrim empty string should return empty string');
equals(_(null).rtrim(), '', 'rtrim null should return empty string');
equal(_(123).rtrim(3), '12');
equal(_('').rtrim(), '', 'rtrim empty string should return empty string');
equal(_(null).rtrim(), '', 'rtrim null should return empty string');
});
test('Strings: capitalize', function() {
equals(_('fabio').capitalize(), 'Fabio', 'First letter is upper case');
equals(_.capitalize('fabio'), 'Fabio', 'First letter is upper case');
equals(_.capitalize('FOO'), 'FOO', 'Other letters unchanged');
equals(_(123).capitalize(), '123', 'Non string');
equals(_.capitalize(''), '', 'Capitalizing empty string returns empty string');
equals(_.capitalize(null), '', 'Capitalizing null returns empty string');
equals(_.capitalize(undefined), '', 'Capitalizing undefined returns empty string');
equal(_('fabio').capitalize(), 'Fabio', 'First letter is upper case');
equal(_.capitalize('fabio'), 'Fabio', 'First letter is upper case');
equal(_.capitalize('FOO'), 'FOO', 'Other letters unchanged');
equal(_(123).capitalize(), '123', 'Non string');
equal(_.capitalize(''), '', 'Capitalizing empty string returns empty string');
equal(_.capitalize(null), '', 'Capitalizing null returns empty string');
equal(_.capitalize(undefined), '', 'Capitalizing undefined returns empty string');
});
test('Strings: join', function() {
equals(_.join('', 'foo', 'bar'), 'foobar', 'basic join');
equals(_.join('', 1, 'foo', 2), '1foo2', 'join numbers and strings');
equals(_.join(' ','foo', 'bar'), 'foo bar', 'join with spaces');
equals(_.join('1', '2', '2'), '212', 'join number strings');
equals(_.join(1, 2, 2), '212', 'join numbers');
equals(_.join('','foo', null), 'foo', 'join null with string returns string');
equals(_.join(null,'foo', 'bar'), 'foobar', 'join strings with null returns string');
equals(_(' ').join('foo', 'bar'), 'foo bar', 'join object oriented');
equal(_.join('', 'foo', 'bar'), 'foobar', 'basic join');
equal(_.join('', 1, 'foo', 2), '1foo2', 'join numbers and strings');
equal(_.join(' ','foo', 'bar'), 'foo bar', 'join with spaces');
equal(_.join('1', '2', '2'), '212', 'join number strings');
equal(_.join(1, 2, 2), '212', 'join numbers');
equal(_.join('','foo', null), 'foo', 'join null with string returns string');
equal(_.join(null,'foo', 'bar'), 'foobar', 'join strings with null returns string');
equal(_(' ').join('foo', 'bar'), 'foo bar', 'join object oriented');
});
test('Strings: reverse', function() {
equals(_.str.reverse('foo'), 'oof' );
equals(_.str.reverse('foobar'), 'raboof' );
equals(_.str.reverse('foo bar'), 'rab oof' );
equals(_.str.reverse('saippuakauppias'), 'saippuakauppias' );
equals(_.str.reverse(123), '321', 'Non string');
equals(_.str.reverse(123.45), '54.321', 'Non string');
equals(_.str.reverse(''), '', 'reversing empty string returns empty string' );
equals(_.str.reverse(null), '', 'reversing null returns empty string' );
equals(_.str.reverse(undefined), '', 'reversing undefined returns empty string' );
equal(_.str.reverse('foo'), 'oof' );
equal(_.str.reverse('foobar'), 'raboof' );
equal(_.str.reverse('foo bar'), 'rab oof' );
equal(_.str.reverse('saippuakauppias'), 'saippuakauppias' );
equal(_.str.reverse(123), '321', 'Non string');
equal(_.str.reverse(123.45), '54.321', 'Non string');
equal(_.str.reverse(''), '', 'reversing empty string returns empty string' );
equal(_.str.reverse(null), '', 'reversing null returns empty string' );
equal(_.str.reverse(undefined), '', 'reversing undefined returns empty string' );
});
test('Strings: clean', function() {
equals(_(' foo bar ').clean(), 'foo bar');
equals(_(123).clean(), '123');
equals(_('').clean(), '', 'claning empty string returns empty string');
equals(_(null).clean(), '', 'claning null returns empty string');
equals(_(undefined).clean(), '', 'claning undefined returns empty string');
equal(_(' foo bar ').clean(), 'foo bar');
equal(_(123).clean(), '123');
equal(_('').clean(), '', 'claning empty string returns empty string');
equal(_(null).clean(), '', 'claning null returns empty string');
equal(_(undefined).clean(), '', 'claning undefined returns empty string');
});

@@ -131,9 +131,9 @@

// http://www.diveintojavascript.com/projects/sprintf-for-javascript
equals(_.sprintf('Hello %s', 'me'), 'Hello me', 'basic');
equals(_('Hello %s').sprintf('me'), 'Hello me', 'object');
equals(_('hello %s').chain().sprintf('me').capitalize().value(), 'Hello me', 'Chaining works');
equals(_.sprintf('%.1f', 1.22222), '1.2', 'round');
equals(_.sprintf('%.1f', 1.17), '1.2', 'round 2');
equals(_.sprintf('%(id)d - %(name)s', {id: 824, name: 'Hello World'}), '824 - Hello World', 'Named replacements work');
equals(_.sprintf('%(args[0].id)d - %(args[1].name)s', {args: [{id: 824}, {name: 'Hello World'}]}), '824 - Hello World', 'Named replacements with arrays work');
equal(_.sprintf('Hello %s', 'me'), 'Hello me', 'basic');
equal(_('Hello %s').sprintf('me'), 'Hello me', 'object');
equal(_('hello %s').chain().sprintf('me').capitalize().value(), 'Hello me', 'Chaining works');
equal(_.sprintf('%.1f', 1.22222), '1.2', 'round');
equal(_.sprintf('%.1f', 1.17), '1.2', 'round 2');
equal(_.sprintf('%(id)d - %(name)s', {id: 824, name: 'Hello World'}), '824 - Hello World', 'Named replacements work');
equal(_.sprintf('%(args[0].id)d - %(args[1].name)s', {args: [{id: 824}, {name: 'Hello World'}]}), '824 - Hello World', 'Named replacements with arrays work');
});

@@ -143,9 +143,9 @@

test('Strings: vsprintf', function() {
equals(_.vsprintf('Hello %s', ['me']), 'Hello me', 'basic');
equals(_('Hello %s').vsprintf(['me']), 'Hello me', 'object');
equals(_('hello %s').chain().vsprintf(['me']).capitalize().value(), 'Hello me', 'Chaining works');
equals(_.vsprintf('%.1f', [1.22222]), '1.2', 'round');
equals(_.vsprintf('%.1f', [1.17]), '1.2', 'round 2');
equals(_.vsprintf('%(id)d - %(name)s', [{id: 824, name: 'Hello World'}]), '824 - Hello World', 'Named replacement works');
equals(_.vsprintf('%(args[0].id)d - %(args[1].name)s', [{args: [{id: 824}, {name: 'Hello World'}]}]), '824 - Hello World', 'Named replacement with arrays works');
equal(_.vsprintf('Hello %s', ['me']), 'Hello me', 'basic');
equal(_('Hello %s').vsprintf(['me']), 'Hello me', 'object');
equal(_('hello %s').chain().vsprintf(['me']).capitalize().value(), 'Hello me', 'Chaining works');
equal(_.vsprintf('%.1f', [1.22222]), '1.2', 'round');
equal(_.vsprintf('%.1f', [1.17]), '1.2', 'round 2');
equal(_.vsprintf('%(id)d - %(name)s', [{id: 824, name: 'Hello World'}]), '824 - Hello World', 'Named replacement works');
equal(_.vsprintf('%(args[0].id)d - %(args[1].name)s', [{args: [{id: 824}, {name: 'Hello World'}]}]), '824 - Hello World', 'Named replacement with arrays works');
});

@@ -193,156 +193,158 @@

test('String: clean', function(){
equals(_.clean(' foo bar '), 'foo bar');
equals(_.clean(''), '');
equals(_.clean(null), '');
equals(_.clean(1), '1');
equal(_.clean(' foo bar '), 'foo bar');
equal(_.clean(''), '');
equal(_.clean(null), '');
equal(_.clean(1), '1');
});
test('String: count', function(){
equals(_('Hello world').count('l'), 3);
equals(_('Hello world').count('Hello'), 1);
equals(_('Hello world').count('foo'), 0);
equals(_('x.xx....x.x').count('x'), 5);
equals(_('').count('x'), 0);
equals(_(null).count('x'), 0);
equals(_(undefined).count('x'), 0);
equals(_(12345).count(1), 1);
equals(_(11345).count(1), 2);
equal(_('Hello world').count('l'), 3);
equal(_('Hello world').count('Hello'), 1);
equal(_('Hello world').count('foo'), 0);
equal(_('x.xx....x.x').count('x'), 5);
equal(_('').count('x'), 0);
equal(_(null).count('x'), 0);
equal(_(undefined).count('x'), 0);
equal(_(12345).count(1), 1);
equal(_(11345).count(1), 2);
});
test('String: insert', function(){
equals(_('Hello ').insert(6, 'Jessy'), 'Hello Jessy');
equals(_('Hello ').insert(100, 'Jessy'), 'Hello Jessy');
equals(_('').insert(100, 'Jessy'), 'Jessy');
equals(_(null).insert(100, 'Jessy'), 'Jessy');
equals(_(undefined).insert(100, 'Jessy'), 'Jessy');
equals(_(12345).insert(6, 'Jessy'), '12345Jessy');
equal(_('Hello ').insert(6, 'Jessy'), 'Hello Jessy');
equal(_('Hello ').insert(100, 'Jessy'), 'Hello Jessy');
equal(_('').insert(100, 'Jessy'), 'Jessy');
equal(_(null).insert(100, 'Jessy'), 'Jessy');
equal(_(undefined).insert(100, 'Jessy'), 'Jessy');
equal(_(12345).insert(6, 'Jessy'), '12345Jessy');
});
test('String: splice', function(){
equals(_('https://edtsech@bitbucket.org/edtsech/underscore.strings').splice(30, 7, 'epeli'),
equal(_('https://edtsech@bitbucket.org/edtsech/underscore.strings').splice(30, 7, 'epeli'),
'https://edtsech@bitbucket.org/epeli/underscore.strings');
equals(_.splice(12345, 1, 2, 321), '132145', 'Non strings');
equal(_.splice(12345, 1, 2, 321), '132145', 'Non strings');
});
test('String: succ', function(){
equals(_('a').succ(), 'b');
equals(_('A').succ(), 'B');
equals(_('+').succ(), ',');
equals(_(1).succ(), '2');
equal(_('a').succ(), 'b');
equal(_('A').succ(), 'B');
equal(_('+').succ(), ',');
equal(_(1).succ(), '2');
});
test('String: titleize', function(){
equals(_('the titleize string method').titleize(), 'The Titleize String Method');
equals(_('the titleize string method').titleize(), 'The Titleize String Method');
equals(_('').titleize(), '', 'Titleize empty string returns empty string');
equals(_(null).titleize(), '', 'Titleize null returns empty string');
equals(_(undefined).titleize(), '', 'Titleize undefined returns empty string');
equals(_('let\'s have some fun').titleize(), 'Let\'s Have Some Fun');
equals(_(123).titleize(), '123');
equal(_('the titleize string method').titleize(), 'The Titleize String Method');
equal(_('the titleize string method').titleize(), 'The Titleize String Method');
equal(_('').titleize(), '', 'Titleize empty string returns empty string');
equal(_(null).titleize(), '', 'Titleize null returns empty string');
equal(_(undefined).titleize(), '', 'Titleize undefined returns empty string');
equal(_('let\'s have some fun').titleize(), 'Let\'s Have Some Fun');
equal(_(123).titleize(), '123');
});
test('String: camelize', function(){
equals(_('the_camelize_string_method').camelize(), 'theCamelizeStringMethod');
equals(_('-the-camelize-string-method').camelize(), 'TheCamelizeStringMethod');
equals(_('the camelize string method').camelize(), 'theCamelizeStringMethod');
equals(_(' the camelize string method').camelize(), 'theCamelizeStringMethod');
equals(_('the camelize string method').camelize(), 'theCamelizeStringMethod');
equals(_('').camelize(), '', 'Camelize empty string returns empty string');
equals(_(null).camelize(), '', 'Camelize null returns empty string');
equals(_(undefined).camelize(), '', 'Camelize undefined returns empty string');
equals(_(123).camelize(), '123');
equal(_('the_camelize_string_method').camelize(), 'theCamelizeStringMethod');
equal(_('-the-camelize-string-method').camelize(), 'TheCamelizeStringMethod');
equal(_('the camelize string method').camelize(), 'theCamelizeStringMethod');
equal(_(' the camelize string method').camelize(), 'theCamelizeStringMethod');
equal(_('the camelize string method').camelize(), 'theCamelizeStringMethod');
equal(_('').camelize(), '', 'Camelize empty string returns empty string');
equal(_(null).camelize(), '', 'Camelize null returns empty string');
equal(_(undefined).camelize(), '', 'Camelize undefined returns empty string');
equal(_(123).camelize(), '123');
});
test('String: underscored', function(){
equals(_('the-underscored-string-method').underscored(), 'the_underscored_string_method');
equals(_('theUnderscoredStringMethod').underscored(), 'the_underscored_string_method');
equals(_('TheUnderscoredStringMethod').underscored(), 'the_underscored_string_method');
equals(_(' the underscored string method').underscored(), 'the_underscored_string_method');
equals(_('').underscored(), '');
equals(_(null).underscored(), '');
equals(_(undefined).underscored(), '');
equals(_(123).underscored(), '123');
equal(_('the-underscored-string-method').underscored(), 'the_underscored_string_method');
equal(_('theUnderscoredStringMethod').underscored(), 'the_underscored_string_method');
equal(_('TheUnderscoredStringMethod').underscored(), 'the_underscored_string_method');
equal(_(' the underscored string method').underscored(), 'the_underscored_string_method');
equal(_('').underscored(), '');
equal(_(null).underscored(), '');
equal(_(undefined).underscored(), '');
equal(_(123).underscored(), '123');
});
test('String: dasherize', function(){
equals(_('the_dasherize_string_method').dasherize(), 'the-dasherize-string-method');
equals(_('TheDasherizeStringMethod').dasherize(), '-the-dasherize-string-method');
equals(_('thisIsATest').dasherize(), 'this-is-a-test');
equals(_('this Is A Test').dasherize(), 'this-is-a-test');
equals(_('thisIsATest123').dasherize(), 'this-is-a-test123');
equals(_('123thisIsATest').dasherize(), '123this-is-a-test');
equals(_('the dasherize string method').dasherize(), 'the-dasherize-string-method');
equals(_('the dasherize string method ').dasherize(), 'the-dasherize-string-method');
equals(_('téléphone').dasherize(), 'téléphone');
equals(_('foo$bar').dasherize(), 'foo$bar');
equals(_('').dasherize(), '');
equals(_(null).dasherize(), '');
equals(_(undefined).dasherize(), '');
equals(_(123).dasherize(), '123');
equal(_('the_dasherize_string_method').dasherize(), 'the-dasherize-string-method');
equal(_('TheDasherizeStringMethod').dasherize(), '-the-dasherize-string-method');
equal(_('thisIsATest').dasherize(), 'this-is-a-test');
equal(_('this Is A Test').dasherize(), 'this-is-a-test');
equal(_('thisIsATest123').dasherize(), 'this-is-a-test123');
equal(_('123thisIsATest').dasherize(), '123this-is-a-test');
equal(_('the dasherize string method').dasherize(), 'the-dasherize-string-method');
equal(_('the dasherize string method ').dasherize(), 'the-dasherize-string-method');
equal(_('téléphone').dasherize(), 'téléphone');
equal(_('foo$bar').dasherize(), 'foo$bar');
equal(_('').dasherize(), '');
equal(_(null).dasherize(), '');
equal(_(undefined).dasherize(), '');
equal(_(123).dasherize(), '123');
});
test('String: camelize', function(){
equals(_.camelize('-moz-transform'), 'MozTransform');
equals(_.camelize('webkit-transform'), 'webkitTransform');
equals(_.camelize('under_scored'), 'underScored');
equals(_.camelize(' with spaces'), 'withSpaces');
equals(_('').camelize(), '');
equals(_(null).camelize(), '');
equals(_(undefined).camelize(), '');
equal(_.camelize('-moz-transform'), 'MozTransform');
equal(_.camelize('webkit-transform'), 'webkitTransform');
equal(_.camelize('under_scored'), 'underScored');
equal(_.camelize(' with spaces'), 'withSpaces');
equal(_('').camelize(), '');
equal(_(null).camelize(), '');
equal(_(undefined).camelize(), '');
});
test('String: join', function(){
equals(_.join(1, 2, 3, 4), '21314');
equals(_.join('|', 'foo', 'bar', 'baz'), 'foo|bar|baz');
equals(_.join('',2,3,null), '23');
equals(_.join(null,2,3), '23');
equal(_.join(1, 2, 3, 4), '21314');
equal(_.join('|', 'foo', 'bar', 'baz'), 'foo|bar|baz');
equal(_.join('',2,3,null), '23');
equal(_.join(null,2,3), '23');
});
test('String: classify', function(){
equals(_.classify(1), '1');
equals(_('some_class_name').classify(), 'SomeClassName');
equal(_.classify(1), '1');
equal(_('some_class_name').classify(), 'SomeClassName');
equal(_('my wonderfull class_name').classify(), 'MyWonderfullClassName');
equal(_('my wonderfull.class.name').classify(), 'MyWonderfullClassName');
});
test('String: humanize', function(){
equals(_('the_humanize_string_method').humanize(), 'The humanize string method');
equals(_('ThehumanizeStringMethod').humanize(), 'Thehumanize string method');
equals(_('the humanize string method').humanize(), 'The humanize string method');
equals(_('the humanize_id string method_id').humanize(), 'The humanize id string method');
equals(_('the humanize string method ').humanize(), 'The humanize string method');
equals(_(' capitalize dash-CamelCase_underscore trim ').humanize(), 'Capitalize dash camel case underscore trim');
equals(_(123).humanize(), '123');
equals(_('').humanize(), '');
equals(_(null).humanize(), '');
equals(_(undefined).humanize(), '');
equal(_('the_humanize_string_method').humanize(), 'The humanize string method');
equal(_('ThehumanizeStringMethod').humanize(), 'Thehumanize string method');
equal(_('the humanize string method').humanize(), 'The humanize string method');
equal(_('the humanize_id string method_id').humanize(), 'The humanize id string method');
equal(_('the humanize string method ').humanize(), 'The humanize string method');
equal(_(' capitalize dash-CamelCase_underscore trim ').humanize(), 'Capitalize dash camel case underscore trim');
equal(_(123).humanize(), '123');
equal(_('').humanize(), '');
equal(_(null).humanize(), '');
equal(_(undefined).humanize(), '');
});
test('String: truncate', function(){
equals(_('Hello world').truncate(6, 'read more'), 'Hello read more');
equals(_('Hello world').truncate(5), 'Hello...');
equals(_('Hello').truncate(10), 'Hello');
equals(_('').truncate(10), '');
equals(_(null).truncate(10), '');
equals(_(undefined).truncate(10), '');
equals(_(1234567890).truncate(5), '12345...');
equal(_('Hello world').truncate(6, 'read more'), 'Hello read more');
equal(_('Hello world').truncate(5), 'Hello...');
equal(_('Hello').truncate(10), 'Hello');
equal(_('').truncate(10), '');
equal(_(null).truncate(10), '');
equal(_(undefined).truncate(10), '');
equal(_(1234567890).truncate(5), '12345...');
});
test('String: prune', function(){
equals(_('Hello, cruel world').prune(6, ' read more'), 'Hello read more');
equals(_('Hello, world').prune(5, 'read a lot more'), 'Hello, world');
equals(_('Hello, world').prune(5), 'Hello...');
equals(_('Hello, world').prune(8), 'Hello...');
equals(_('Hello, cruel world').prune(15), 'Hello, cruel...');
equals(_('Hello world').prune(22), 'Hello world');
equals(_('Привет, жестокий мир').prune(6, ' read more'), 'Привет read more');
equals(_('Привет, мир').prune(6, 'read a lot more'), 'Привет, мир');
equals(_('Привет, мир').prune(6), 'Привет...');
equals(_('Привет, мир').prune(8), 'Привет...');
equals(_('Привет, жестокий мир').prune(16), 'Привет, жестокий...');
equals(_('Привет, мир').prune(22), 'Привет, мир');
equals(_('alksjd!!!!!!....').prune(100, ''), 'alksjd!!!!!!....');
equals(_(123).prune(10), '123');
equals(_(123).prune(1, 321), '321');
equals(_('').prune(5), '');
equals(_(null).prune(5), '');
equals(_(undefined).prune(5), '');
equal(_('Hello, cruel world').prune(6, ' read more'), 'Hello read more');
equal(_('Hello, world').prune(5, 'read a lot more'), 'Hello, world');
equal(_('Hello, world').prune(5), 'Hello...');
equal(_('Hello, world').prune(8), 'Hello...');
equal(_('Hello, cruel world').prune(15), 'Hello, cruel...');
equal(_('Hello world').prune(22), 'Hello world');
equal(_('Привет, жестокий мир').prune(6, ' read more'), 'Привет read more');
equal(_('Привет, мир').prune(6, 'read a lot more'), 'Привет, мир');
equal(_('Привет, мир').prune(6), 'Привет...');
equal(_('Привет, мир').prune(8), 'Привет...');
equal(_('Привет, жестокий мир').prune(16), 'Привет, жестокий...');
equal(_('Привет, мир').prune(22), 'Привет, мир');
equal(_('alksjd!!!!!!....').prune(100, ''), 'alksjd!!!!!!....');
equal(_(123).prune(10), '123');
equal(_(123).prune(1, 321), '321');
equal(_('').prune(5), '');
equal(_(null).prune(5), '');
equal(_(undefined).prune(5), '');
});

@@ -363,34 +365,35 @@

test('String: escapeRegExp', function(){
equals(_.escapeRegExp(/hello(?=\sworld)/.source), 'hello\\(\\?\\=\\\\sworld\\)', 'with lookahead');
equals(_.escapeRegExp(/hello(?!\shell)/.source), 'hello\\(\\?\\!\\\\shell\\)', 'with negative lookahead');
equal(_.escapeRegExp(/hello(?=\sworld)/.source), 'hello\\(\\?\\=\\\\sworld\\)', 'with lookahead');
equal(_.escapeRegExp(/hello(?!\shell)/.source), 'hello\\(\\?\\!\\\\shell\\)', 'with negative lookahead');
});
test('String: escapeHTML', function(){
equals(_('<div>Blah & "blah" & \'blah\'</div>').escapeHTML(),
'&lt;div&gt;Blah &amp; &quot;blah&quot; &amp; &apos;blah&apos;&lt;/div&gt;');
equals(_('&lt;').escapeHTML(), '&amp;lt;');
equals(_(5).escapeHTML(), '5');
equals(_('').escapeHTML(), '');
equals(_(null).escapeHTML(), '');
equals(_(undefined).escapeHTML(), '');
equal(_('<div>Blah & "blah" & \'blah\'</div>').escapeHTML(),
'&lt;div&gt;Blah &amp; &quot;blah&quot; &amp; &#39;blah&#39;&lt;/div&gt;');
equal(_('&lt;').escapeHTML(), '&amp;lt;');
equal(_(5).escapeHTML(), '5');
equal(_('').escapeHTML(), '');
equal(_(null).escapeHTML(), '');
equal(_(undefined).escapeHTML(), '');
});
test('String: unescapeHTML', function(){
equals(_('&lt;div&gt;Blah &amp; &quot;blah&quot; &amp; &apos;blah&apos;&lt;/div&gt;').unescapeHTML(),
equal(_('&lt;div&gt;Blah &amp; &quot;blah&quot; &amp; &apos;blah&#39;&lt;/div&gt;').unescapeHTML(),
'<div>Blah & "blah" & \'blah\'</div>');
equals(_('&amp;lt;').unescapeHTML(), '&lt;');
equals(_('&#39;').unescapeHTML(), '\'');
equals(_('&#0039;').unescapeHTML(), '\'');
equals(_('&#x4a;').unescapeHTML(), 'J');
equals(_('&#x04A;').unescapeHTML(), 'J');
equals(_('&#X4A;').unescapeHTML(), '&#X4A;');
equals(_('&_#39;').unescapeHTML(), '&_#39;');
equals(_('&#39_;').unescapeHTML(), '&#39_;');
equals(_('&amp;#38;').unescapeHTML(), '&#38;');
equals(_('&#38;amp;').unescapeHTML(), '&amp;');
equals(_('').unescapeHTML(), '');
equals(_(null).unescapeHTML(), '');
equals(_(undefined).unescapeHTML(), '');
equals(_(5).unescapeHTML(), '5');
// equals(_(undefined).unescapeHTML(), '');
equal(_('&amp;lt;').unescapeHTML(), '&lt;');
equal(_('&apos;').unescapeHTML(), '\'');
equal(_('&#39;').unescapeHTML(), '\'');
equal(_('&#0039;').unescapeHTML(), '\'');
equal(_('&#x4a;').unescapeHTML(), 'J');
equal(_('&#x04A;').unescapeHTML(), 'J');
equal(_('&#X4A;').unescapeHTML(), '&#X4A;');
equal(_('&_#39;').unescapeHTML(), '&_#39;');
equal(_('&#39_;').unescapeHTML(), '&#39_;');
equal(_('&amp;#38;').unescapeHTML(), '&#38;');
equal(_('&#38;amp;').unescapeHTML(), '&amp;');
equal(_('').unescapeHTML(), '');
equal(_(null).unescapeHTML(), '');
equal(_(undefined).unescapeHTML(), '');
equal(_(5).unescapeHTML(), '5');
// equal(_(undefined).unescapeHTML(), '');
});

@@ -412,213 +415,218 @@

test('String: chars', function() {
equals(_('Hello').chars().length, 5);
equals(_(123).chars().length, 3);
equals(_('').chars().length, 0);
equals(_(null).chars().length, 0);
equals(_(undefined).chars().length, 0);
equal(_('Hello').chars().length, 5);
equal(_(123).chars().length, 3);
equal(_('').chars().length, 0);
equal(_(null).chars().length, 0);
equal(_(undefined).chars().length, 0);
});
test('String: swapCase', function(){
equals(_('AaBbCcDdEe').swapCase(), 'aAbBcCdDeE');
equals(_('Hello World').swapCase(), 'hELLO wORLD');
equals(_('').swapCase(), '');
equals(_(null).swapCase(), '');
equals(_(undefined).swapCase(), '');
equal(_('AaBbCcDdEe').swapCase(), 'aAbBcCdDeE');
equal(_('Hello World').swapCase(), 'hELLO wORLD');
equal(_('').swapCase(), '');
equal(_(null).swapCase(), '');
equal(_(undefined).swapCase(), '');
});
test('String: lines', function() {
equals(_('Hello\nWorld').lines().length, 2);
equals(_('Hello World').lines().length, 1);
equals(_(123).lines().length, 1);
equals(_('').lines().length, 1);
equals(_(null).lines().length, 0);
equals(_(undefined).lines().length, 0);
equal(_('Hello\nWorld').lines().length, 2);
equal(_('Hello World').lines().length, 1);
equal(_(123).lines().length, 1);
equal(_('').lines().length, 1);
equal(_(null).lines().length, 0);
equal(_(undefined).lines().length, 0);
});
test('String: pad', function() {
equals(_('1').pad(8), ' 1');
equals(_(1).pad(8), ' 1');
equals(_('1').pad(8, '0'), '00000001');
equals(_('1').pad(8, '0', 'left'), '00000001');
equals(_('1').pad(8, '0', 'right'), '10000000');
equals(_('1').pad(8, '0', 'both'), '00001000');
equals(_('foo').pad(8, '0', 'both'), '000foo00');
equals(_('foo').pad(7, '0', 'both'), '00foo00');
equals(_('foo').pad(7, '!@$%dofjrofj', 'both'), '!!foo!!');
equals(_('').pad(2), ' ');
equals(_(null).pad(2), ' ');
equals(_(undefined).pad(2), ' ');
equal(_('1').pad(8), ' 1');
equal(_(1).pad(8), ' 1');
equal(_('1').pad(8, '0'), '00000001');
equal(_('1').pad(8, '0', 'left'), '00000001');
equal(_('1').pad(8, '0', 'right'), '10000000');
equal(_('1').pad(8, '0', 'both'), '00001000');
equal(_('foo').pad(8, '0', 'both'), '000foo00');
equal(_('foo').pad(7, '0', 'both'), '00foo00');
equal(_('foo').pad(7, '!@$%dofjrofj', 'both'), '!!foo!!');
equal(_('').pad(2), ' ');
equal(_(null).pad(2), ' ');
equal(_(undefined).pad(2), ' ');
});
test('String: lpad', function() {
equals(_('1').lpad(8), ' 1');
equals(_(1).lpad(8), ' 1');
equals(_('1').lpad(8, '0'), '00000001');
equals(_('1').lpad(8, '0', 'left'), '00000001');
equals(_('').lpad(2), ' ');
equals(_(null).lpad(2), ' ');
equals(_(undefined).lpad(2), ' ');
equal(_('1').lpad(8), ' 1');
equal(_(1).lpad(8), ' 1');
equal(_('1').lpad(8, '0'), '00000001');
equal(_('1').lpad(8, '0', 'left'), '00000001');
equal(_('').lpad(2), ' ');
equal(_(null).lpad(2), ' ');
equal(_(undefined).lpad(2), ' ');
});
test('String: rpad', function() {
equals(_('1').rpad(8), '1 ');
equals(_(1).lpad(8), ' 1');
equals(_('1').rpad(8, '0'), '10000000');
equals(_('foo').rpad(8, '0'), 'foo00000');
equals(_('foo').rpad(7, '0'), 'foo0000');
equals(_('').rpad(2), ' ');
equals(_(null).rpad(2), ' ');
equals(_(undefined).rpad(2), ' ');
equal(_('1').rpad(8), '1 ');
equal(_(1).lpad(8), ' 1');
equal(_('1').rpad(8, '0'), '10000000');
equal(_('foo').rpad(8, '0'), 'foo00000');
equal(_('foo').rpad(7, '0'), 'foo0000');
equal(_('').rpad(2), ' ');
equal(_(null).rpad(2), ' ');
equal(_(undefined).rpad(2), ' ');
});
test('String: lrpad', function() {
equals(_('1').lrpad(8), ' 1 ');
equals(_(1).lrpad(8), ' 1 ');
equals(_('1').lrpad(8, '0'), '00001000');
equals(_('foo').lrpad(8, '0'), '000foo00');
equals(_('foo').lrpad(7, '0'), '00foo00');
equals(_('foo').lrpad(7, '!@$%dofjrofj'), '!!foo!!');
equals(_('').lrpad(2), ' ');
equals(_(null).lrpad(2), ' ');
equals(_(undefined).lrpad(2), ' ');
equal(_('1').lrpad(8), ' 1 ');
equal(_(1).lrpad(8), ' 1 ');
equal(_('1').lrpad(8, '0'), '00001000');
equal(_('foo').lrpad(8, '0'), '000foo00');
equal(_('foo').lrpad(7, '0'), '00foo00');
equal(_('foo').lrpad(7, '!@$%dofjrofj'), '!!foo!!');
equal(_('').lrpad(2), ' ');
equal(_(null).lrpad(2), ' ');
equal(_(undefined).lrpad(2), ' ');
});
test('String: toNumber', function() {
deepEqual(_('not a number').toNumber(), Number.NaN);
equals(_(0).toNumber(), 0);
equals(_('0').toNumber(), 0);
equals(_('0000').toNumber(), 0);
equals(_('2.345').toNumber(), 2);
equals(_('2.345').toNumber(NaN), 2);
equals(_('2.345').toNumber(2), 2.35);
equals(_('2.344').toNumber(2), 2.34);
equals(_('2').toNumber(2), 2.00);
equals(_(2).toNumber(2), 2.00);
equals(_(-2).toNumber(), -2);
equals(_('-2').toNumber(), -2);
equals(_('').toNumber(), 0);
equals(_(null).toNumber(), 0);
equals(_(undefined).toNumber(), 0);
deepEqual(_('not a number').toNumber(), NaN);
equal(_(0).toNumber(), 0);
equal(_('0').toNumber(), 0);
equal(_('0.0').toNumber(), 0);
equal(_('0.1').toNumber(), 0);
equal(_('0.1').toNumber(1), 0.1);
equal(_(' 0.1 ').toNumber(1), 0.1);
equal(_('0000').toNumber(), 0);
equal(_('2.345').toNumber(), 2);
equal(_('2.345').toNumber(NaN), 2);
equal(_('2.345').toNumber(2), 2.35);
equal(_('2.344').toNumber(2), 2.34);
equal(_('2').toNumber(2), 2.00);
equal(_(2).toNumber(2), 2.00);
equal(_(-2).toNumber(), -2);
equal(_('-2').toNumber(), -2);
equal(_('').toNumber(), 0);
equal(_(null).toNumber(), 0);
equal(_(undefined).toNumber(), 0);
});
test('String: numberFormat', function() {
equals(_.numberFormat(9000), '9,000');
equals(_.numberFormat(9000, 0), '9,000');
equals(_.numberFormat(90000, 2), '90,000.00');
equals(_.numberFormat(1000.754), '1,001');
equals(_.numberFormat(1000.754, 2), '1,000.75');
equals(_.numberFormat(1000.754, 0, ',', '.'), '1.001');
equals(_.numberFormat(1000.754, 2, ',', '.'), '1.000,75');
equals(_.numberFormat(1000000.754, 2, ',', '.'), '1.000.000,75');
equals(_.numberFormat(1000000000), '1,000,000,000');
equals(_.numberFormat(100000000), '100,000,000');
equals(_.numberFormat('not number'), '');
equals(_.numberFormat(), '');
equals(_.numberFormat(null, '.', ','), '');
equals(_.numberFormat(undefined, '.', ','), '');
equals(_.numberFormat(new Number(5000)), '5,000');
equal(_.numberFormat(9000), '9,000');
equal(_.numberFormat(9000, 0), '9,000');
equal(_.numberFormat(9000, 0, '', ''), '9000');
equal(_.numberFormat(90000, 2), '90,000.00');
equal(_.numberFormat(1000.754), '1,001');
equal(_.numberFormat(1000.754, 2), '1,000.75');
equal(_.numberFormat(1000.754, 0, ',', '.'), '1.001');
equal(_.numberFormat(1000.754, 2, ',', '.'), '1.000,75');
equal(_.numberFormat(1000000.754, 2, ',', '.'), '1.000.000,75');
equal(_.numberFormat(1000000000), '1,000,000,000');
equal(_.numberFormat(100000000), '100,000,000');
equal(_.numberFormat('not number'), '');
equal(_.numberFormat(), '');
equal(_.numberFormat(null, '.', ','), '');
equal(_.numberFormat(undefined, '.', ','), '');
equal(_.numberFormat(new Number(5000)), '5,000');
});
test('String: strRight', function() {
equals(_('This_is_a_test_string').strRight('_'), 'is_a_test_string');
equals(_('This_is_a_test_string').strRight('string'), '');
equals(_('This_is_a_test_string').strRight(), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strRight(''), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strRight('-'), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strRight(''), 'This_is_a_test_string');
equals(_('').strRight('foo'), '');
equals(_(null).strRight('foo'), '');
equals(_(undefined).strRight('foo'), '');
equals(_(12345).strRight(2), '345');
equal(_('This_is_a_test_string').strRight('_'), 'is_a_test_string');
equal(_('This_is_a_test_string').strRight('string'), '');
equal(_('This_is_a_test_string').strRight(), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strRight(''), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strRight('-'), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strRight(''), 'This_is_a_test_string');
equal(_('').strRight('foo'), '');
equal(_(null).strRight('foo'), '');
equal(_(undefined).strRight('foo'), '');
equal(_(12345).strRight(2), '345');
});
test('String: strRightBack', function() {
equals(_('This_is_a_test_string').strRightBack('_'), 'string');
equals(_('This_is_a_test_string').strRightBack('string'), '');
equals(_('This_is_a_test_string').strRightBack(), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strRightBack(''), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strRightBack('-'), 'This_is_a_test_string');
equals(_('').strRightBack('foo'), '');
equals(_(null).strRightBack('foo'), '');
equals(_(undefined).strRightBack('foo'), '');
equals(_(12345).strRightBack(2), '345');
equal(_('This_is_a_test_string').strRightBack('_'), 'string');
equal(_('This_is_a_test_string').strRightBack('string'), '');
equal(_('This_is_a_test_string').strRightBack(), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strRightBack(''), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strRightBack('-'), 'This_is_a_test_string');
equal(_('').strRightBack('foo'), '');
equal(_(null).strRightBack('foo'), '');
equal(_(undefined).strRightBack('foo'), '');
equal(_(12345).strRightBack(2), '345');
});
test('String: strLeft', function() {
equals(_('This_is_a_test_string').strLeft('_'), 'This');
equals(_('This_is_a_test_string').strLeft('This'), '');
equals(_('This_is_a_test_string').strLeft(), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strLeft(''), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strLeft('-'), 'This_is_a_test_string');
equals(_('').strLeft('foo'), '');
equals(_(null).strLeft('foo'), '');
equals(_(undefined).strLeft('foo'), '');
equals(_(123454321).strLeft(3), '12');
equal(_('This_is_a_test_string').strLeft('_'), 'This');
equal(_('This_is_a_test_string').strLeft('This'), '');
equal(_('This_is_a_test_string').strLeft(), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strLeft(''), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strLeft('-'), 'This_is_a_test_string');
equal(_('').strLeft('foo'), '');
equal(_(null).strLeft('foo'), '');
equal(_(undefined).strLeft('foo'), '');
equal(_(123454321).strLeft(3), '12');
});
test('String: strLeftBack', function() {
equals(_('This_is_a_test_string').strLeftBack('_'), 'This_is_a_test');
equals(_('This_is_a_test_string').strLeftBack('This'), '');
equals(_('This_is_a_test_string').strLeftBack(), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strLeftBack(''), 'This_is_a_test_string');
equals(_('This_is_a_test_string').strLeftBack('-'), 'This_is_a_test_string');
equals(_('').strLeftBack('foo'), '');
equals(_(null).strLeftBack('foo'), '');
equals(_(undefined).strLeftBack('foo'), '');
equals(_(123454321).strLeftBack(3), '123454');
equal(_('This_is_a_test_string').strLeftBack('_'), 'This_is_a_test');
equal(_('This_is_a_test_string').strLeftBack('This'), '');
equal(_('This_is_a_test_string').strLeftBack(), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strLeftBack(''), 'This_is_a_test_string');
equal(_('This_is_a_test_string').strLeftBack('-'), 'This_is_a_test_string');
equal(_('').strLeftBack('foo'), '');
equal(_(null).strLeftBack('foo'), '');
equal(_(undefined).strLeftBack('foo'), '');
equal(_(123454321).strLeftBack(3), '123454');
});
test('Strings: stripTags', function() {
equals(_('a <a href="#">link</a>').stripTags(), 'a link');
equals(_('a <a href="#">link</a><script>alert("hello world!")</scr'+'ipt>').stripTags(), 'a linkalert("hello world!")');
equals(_('<html><body>hello world</body></html>').stripTags(), 'hello world');
equals(_(123).stripTags(), '123');
equals(_('').stripTags(), '');
equals(_(null).stripTags(), '');
equals(_(undefined).stripTags(), '');
equal(_('a <a href="#">link</a>').stripTags(), 'a link');
equal(_('a <a href="#">link</a><script>alert("hello world!")</scr'+'ipt>').stripTags(), 'a linkalert("hello world!")');
equal(_('<html><body>hello world</body></html>').stripTags(), 'hello world');
equal(_(123).stripTags(), '123');
equal(_('').stripTags(), '');
equal(_(null).stripTags(), '');
equal(_(undefined).stripTags(), '');
});
test('Strings: toSentence', function() {
equals(_.toSentence(['jQuery']), 'jQuery', 'array with a single element');
equals(_.toSentence(['jQuery', 'MooTools']), 'jQuery and MooTools', 'array with two elements');
equals(_.toSentence(['jQuery', 'MooTools', 'Prototype']), 'jQuery, MooTools and Prototype', 'array with three elements');
equals(_.toSentence(['jQuery', 'MooTools', 'Prototype', 'YUI']), 'jQuery, MooTools, Prototype and YUI', 'array with multiple elements');
equals(_.toSentence(['jQuery', 'MooTools', 'Prototype'], ',', ' or '), 'jQuery,MooTools or Prototype', 'handles custom separators');
equal(_.toSentence(['jQuery']), 'jQuery', 'array with a single element');
equal(_.toSentence(['jQuery', 'MooTools']), 'jQuery and MooTools', 'array with two elements');
equal(_.toSentence(['jQuery', 'MooTools', 'Prototype']), 'jQuery, MooTools and Prototype', 'array with three elements');
equal(_.toSentence(['jQuery', 'MooTools', 'Prototype', 'YUI']), 'jQuery, MooTools, Prototype and YUI', 'array with multiple elements');
equal(_.toSentence(['jQuery', 'MooTools', 'Prototype'], ',', ' or '), 'jQuery,MooTools or Prototype', 'handles custom separators');
});
test('Strings: toSentenceSerial', function (){
equals(_.toSentenceSerial(['jQuery']), 'jQuery');
equals(_.toSentenceSerial(['jQuery', 'MooTools']), 'jQuery and MooTools');
equals(_.toSentenceSerial(['jQuery', 'MooTools', 'Prototype']), 'jQuery, MooTools, and Prototype');
equal(_.toSentenceSerial(['jQuery']), 'jQuery');
equal(_.toSentenceSerial(['jQuery', 'MooTools']), 'jQuery and MooTools');
equal(_.toSentenceSerial(['jQuery', 'MooTools', 'Prototype']), 'jQuery, MooTools, and Prototype');
});
test('Strings: slugify', function() {
equals(_('Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/').slugify(), 'jack-jill-like-numbers-123-and-4-and-silly-characters');
equals(_('Un éléphant à l\'orée du bois').slugify(), 'un-elephant-a-loree-du-bois');
equals(_('I know latin characters: á í ó ú ç ã õ ñ ü').slugify(), 'i-know-latin-characters-a-i-o-u-c-a-o-n-u');
equals(_('I am a word too, even though I am but a single letter: i!').slugify(), 'i-am-a-word-too-even-though-i-am-but-a-single-letter-i');
equals(_('').slugify(), '');
equals(_(null).slugify(), '');
equals(_(undefined).slugify(), '');
equal(_('Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/').slugify(), 'jack-jill-like-numbers-123-and-4-and-silly-characters');
equal(_('Un éléphant à l\'orée du bois').slugify(), 'un-elephant-a-loree-du-bois');
equal(_('I know latin characters: á í ó ú ç ã õ ñ ü').slugify(), 'i-know-latin-characters-a-i-o-u-c-a-o-n-u');
equal(_('I am a word too, even though I am but a single letter: i!').slugify(), 'i-am-a-word-too-even-though-i-am-but-a-single-letter-i');
equal(_('').slugify(), '');
equal(_(null).slugify(), '');
equal(_(undefined).slugify(), '');
});
test('Strings: quote', function(){
equals(_.quote('foo'), '"foo"');
equals(_.quote('"foo"'), '""foo""');
equals(_.quote(1), '"1"');
equal(_.quote('foo'), '"foo"');
equal(_.quote('"foo"'), '""foo""');
equal(_.quote(1), '"1"');
// alias
equals(_.q('foo'), '"foo"');
equals(_.q(''), '""');
equals(_.q(null), '""');
equals(_.q(undefined), '""');
equal(_.q('foo'), '"foo"');
equal(_.q(''), '""');
equal(_.q(null), '""');
equal(_.q(undefined), '""');
});
test('Strings: surround', function(){
equals(_.surround('foo', 'ab'), 'abfooab');
equals(_.surround(1, 'ab'), 'ab1ab');
equals(_.surround(1, 2), '212');
equals(_.surround('foo', 1), '1foo1');
equals(_.surround('', 1), '11');
equals(_.surround(null, 1), '11');
equals(_.surround('foo', ''), 'foo');
equals(_.surround('foo', null), 'foo');
equal(_.surround('foo', 'ab'), 'abfooab');
equal(_.surround(1, 'ab'), 'ab1ab');
equal(_.surround(1, 2), '212');
equal(_.surround('foo', 1), '1foo1');
equal(_.surround('', 1), '11');
equal(_.surround(null, 1), '11');
equal(_.surround('foo', ''), 'foo');
equal(_.surround('foo', null), 'foo');
});

@@ -628,13 +636,13 @@

test('Strings: repeat', function() {
equals(_.repeat('foo'), '');
equals(_.repeat('foo', 3), 'foofoofoo');
equals(_.repeat('foo', '3'), 'foofoofoo');
equals(_.repeat(123, 2), '123123');
equals(_.repeat(1234, 2, '*'), '1234*1234');
equals(_.repeat(1234, 2, 5), '123451234');
equals(_.repeat('', 2), '');
equals(_.repeat(null, 2), '');
equals(_.repeat(undefined, 2), '');
equal(_.repeat('foo'), '');
equal(_.repeat('foo', 3), 'foofoofoo');
equal(_.repeat('foo', '3'), 'foofoofoo');
equal(_.repeat(123, 2), '123123');
equal(_.repeat(1234, 2, '*'), '1234*1234');
equal(_.repeat(1234, 2, 5), '123451234');
equal(_.repeat('', 2), '');
equal(_.repeat(null, 2), '');
equal(_.repeat(undefined, 2), '');
});
});

@@ -5,3 +5,3 @@ $(document).ready(function() {

test("arrays: first", function() {
test("first", function() {
equal(_.first([1,2,3]), 1, 'can pull out the first element of an array');

@@ -18,5 +18,7 @@ equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');

equal(result.join(','), '1,2', 'aliased as take');
equal(_.first(null), undefined, 'handles nulls');
});
test("arrays: rest", function() {
test("rest", function() {
var numbers = [1, 2, 3, 4];

@@ -30,5 +32,7 @@ equal(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()');

equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map');
result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4);
equal(result.join(', '), '2, 3, 4', 'aliased as drop and works on arguments object');
});
test("arrays: initial", function() {
test("initial", function() {
equal(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()');

@@ -42,3 +46,3 @@ equal(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index');

test("arrays: last", function() {
test("last", function() {
equal(_.last([1,2,3]), 3, 'can pull out the last element of an array');

@@ -52,5 +56,7 @@ equal(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last');

equal(result.join(','), '3,3', 'works well with _.map');
equal(_.last(null), undefined, 'handles nulls');
});
test("arrays: compact", function() {
test("compact", function() {
equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values');

@@ -61,3 +67,3 @@ var result = (function(){ return _(arguments).compact().length; })(0, 1, false, 2, false, 3);

test("arrays: flatten", function() {
test("flatten", function() {
if (window.JSON) {

@@ -72,3 +78,3 @@ var list = [1, [2], [3, [[[4]]]]];

test("arrays: without", function() {
test("without", function() {
var list = [1, 2, 1, 0, 3, 1, 4];

@@ -84,3 +90,3 @@ equal(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object');

test("arrays: uniq", function() {
test("uniq", function() {
var list = [1, 2, 1, 3, 1, 4];

@@ -102,27 +108,5 @@ equal(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array');

equal(result.join(', '), '1, 2, 3, 4', 'works on an arguments object');
var list = [];
list[2] = list[3] = null;
list[8] = 2;
list[10] = 2;
list[11] = 5;
list[14] = 5;
list[16] = 8;
list[19] = 8;
list[26] = list[29] = undefined;
list[33] = "hi";
var result = _.uniq(list, true);
if (0 in [undefined]) {
// According to the JScript ES 3 spec, section 2.1.26, JScript 5.x (IE <=
// 8) treats `undefined` elements in arrays as elisions.
deepEqual(result, [null, 2, 5, 8, undefined, "hi"], "Works with sorted sparse arrays");
equal(result.length, 6, "The resulting array should not be sparse");
} else {
deepEqual(result, [null, 2, 5, 8, "hi"], "Works with sorted sparse arrays where `undefined` elements are elided");
equal(result.length, 5, "The resulting array should not be sparse");
}
});
test("arrays: intersection", function() {
test("intersection", function() {
var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho'];

@@ -135,3 +119,3 @@ equal(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays');

test("arrays: union", function() {
test("union", function() {
var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]);

@@ -144,3 +128,3 @@ equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays');

test("arrays: difference", function() {
test("difference", function() {
var result = _.difference([1, 2, 3], [2, 30, 40]);

@@ -153,3 +137,3 @@ equal(result.join(' '), '1 3', 'takes the difference of two arrays');

test('arrays: zip', function() {
test('zip', function() {
var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true];

@@ -160,3 +144,18 @@ var stooges = _.zip(names, ages, leaders);

test("arrays: indexOf", function() {
test('object', function() {
var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
var shouldBe = {moe: 30, larry: 40, curly: 50};
ok(_.isEqual(result, shouldBe), 'two arrays zipped together into an object');
result = _.object([['one', 1], ['two', 2], ['three', 3]]);
shouldBe = {one: 1, two: 2, three: 3};
ok(_.isEqual(result, shouldBe), 'an array of pairs zipped together into an object');
var stooges = {moe: 30, larry: 40, curly: 50};
ok(_.isEqual(_.object(_.pairs(stooges)), stooges), 'an object converted to pairs and back to an object');
ok(_.isEqual(_.object(null), {}), 'handles nulls');
});
test("indexOf", function() {
var numbers = [1, 2, 3];

@@ -180,6 +179,13 @@ numbers.indexOf = null;

equal(index, 1, '40 is in the list');
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
index = _.indexOf(numbers, 2, 5);
equal(index, 7, 'supports the fromIndex argument');
});
test("arrays: lastIndexOf", function() {
var numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0];
test("lastIndexOf", function() {
var numbers = [1, 0, 1];
equal(_.lastIndexOf(numbers, 1), 2);
numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0];
numbers.lastIndexOf = null;

@@ -191,5 +197,9 @@ equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');

equal(_.indexOf(null, 2), -1, 'handles nulls properly');
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
index = _.lastIndexOf(numbers, 2, 2);
equal(index, 1, 'supports the fromIndex argument');
});
test("arrays: range", function() {
test("range", function() {
equal(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array');

@@ -196,0 +206,0 @@ equal(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1');

@@ -5,3 +5,3 @@ $(document).ready(function() {

test("chaining: map/flatten/reduce", function() {
test("map/flatten/reduce", function() {
var lyrics = [

@@ -24,3 +24,3 @@ "I'm a lumberjack and I'm okay",

test("chaining: select/reject/sortBy", function() {
test("select/reject/sortBy", function() {
var numbers = [1,2,3,4,5,6,7,8,9,10];

@@ -37,3 +37,3 @@ numbers = _(numbers).chain().select(function(n) {

test("chaining: select/reject/sortBy in functional style", function() {
test("select/reject/sortBy in functional style", function() {
var numbers = [1,2,3,4,5,6,7,8,9,10];

@@ -50,3 +50,3 @@ numbers = _.chain(numbers).select(function(n) {

test("chaining: reverse/concat/unshift/pop/map", function() {
test("reverse/concat/unshift/pop/map", function() {
var numbers = [1,2,3,4,5];

@@ -53,0 +53,0 @@ numbers = _(numbers).chain()

@@ -5,3 +5,3 @@ $(document).ready(function() {

test("collections: each", function() {
test("each", function() {
_.each([1, 2, 3], function(num, i) {

@@ -35,3 +35,3 @@ equal(num, i + 1, 'each iterators provide value and iteration count');

test('collections: map', function() {
test('map', function() {
var doubled = _.map([1, 2, 3], function(num){ return num * 2; });

@@ -49,5 +49,10 @@ equal(doubled.join(', '), '2, 4, 6', 'doubled numbers');

var ids = _.map($('div.underscore-test').children(), function(n){ return n.id; });
ok(_.include(ids, 'qunit-header'), 'can use collection methods on NodeLists');
if (document.querySelectorAll) {
var ids = _.map(document.querySelectorAll('#map-test *'), function(n){ return n.id; });
deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on NodeLists.');
}
var ids = _.map($('#map-test').children(), function(n){ return n.id; });
deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on jQuery Array-likes.');
var ids = _.map(document.images, function(n){ return n.id; });

@@ -58,8 +63,5 @@ ok(ids[0] == 'chart_image', 'can use collection methods on HTMLCollections');

ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly');
var length = _.map(Array(2), function(v) { return v; }).length;
equal(length, 2, "can preserve a sparse array's length");
});
test('collections: reduce', function() {
test('reduce', function() {
var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0);

@@ -92,10 +94,5 @@ equal(sum, 6, 'can sum up an array');

raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value');
var sparseArray = [];
sparseArray[0] = 20;
sparseArray[2] = -5;
equal(_.reduce(sparseArray, function(a, b){ return a - b; }), 25, 'initially-sparse arrays with no memo');
});
test('collections: reduceRight', function() {
test('reduceRight', function() {
var list = _.reduceRight(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, '');

@@ -118,2 +115,5 @@ equal(list, 'bazbarfoo', 'can perform right folds');

var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; });
equal(sum, 6, 'default initial value on object');
ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly');

@@ -124,9 +124,43 @@

var sparseArray = [];
sparseArray[0] = 20;
sparseArray[2] = -5;
equal(_.reduceRight(sparseArray, function(a, b){ return a - b; }), -25, 'initially-sparse arrays with no memo');
// Assert that the correct arguments are being passed.
var args,
memo = {},
object = {a: 1, b: 2},
lastKey = _.keys(object).pop();
var expected = lastKey == 'a'
? [memo, 1, 'a', object]
: [memo, 2, 'b', object];
_.reduceRight(object, function() {
args || (args = _.toArray(arguments));
}, memo);
deepEqual(args, expected);
// And again, with numeric keys.
object = {'2': 'a', '1': 'b'};
lastKey = _.keys(object).pop();
args = null;
expected = lastKey == '2'
? [memo, 'a', '2', object]
: [memo, 'b', '1', object];
_.reduceRight(object, function() {
args || (args = _.toArray(arguments));
}, memo);
deepEqual(args, expected);
});
test('collections: detect', function() {
test('find', function() {
var array = [1, 2, 3, 4];
strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`');
strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found');
});
test('detect', function() {
var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; });

@@ -136,3 +170,3 @@ equal(result, 2, 'found the first "2" and broke the loop');

test('collections: select', function() {
test('select', function() {
var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });

@@ -145,8 +179,16 @@ equal(evens.join(', '), '2, 4, 6', 'selected each even number');

test('collections: reject', function() {
test('reject', function() {
var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
equal(odds.join(', '), '1, 3, 5', 'rejected each even number');
var context = "obj";
var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){
equal(context, "obj");
return num % 2 != 0;
}, context);
equal(evens.join(', '), '2, 4, 6', 'rejected each odd number');
});
test('collections: all', function() {
test('all', function() {
ok(_.all([], _.identity), 'the empty set');

@@ -160,5 +202,6 @@ ok(_.all([true, true, true], _.identity), 'all true values');

ok(_.every([true, true, true], _.identity), 'aliased as "every"');
ok(!_.all([undefined, undefined, undefined], _.identity), 'works with arrays of undefined');
});
test('collections: any', function() {
test('any', function() {
var nativeSome = Array.prototype.some;

@@ -179,3 +222,3 @@ Array.prototype.some = null;

test('collections: include', function() {
test('include', function() {
ok(_.include([1,2,3], 2), 'two is in the array');

@@ -187,3 +230,3 @@ ok(!_.include([1,3,9], 2), 'two is not in the array');

test('collections: invoke', function() {
test('invoke', function() {
var list = [[5, 1, 7], [3, 2, 1]];

@@ -195,3 +238,3 @@ var result = _.invoke(list, 'sort');

test('collections: invoke w/ function reference', function() {
test('invoke w/ function reference', function() {
var list = [[5, 1, 7], [3, 2, 1]];

@@ -204,3 +247,3 @@ var result = _.invoke(list, Array.prototype.sort);

// Relevant when using ClojureScript
test('collections: invoke when strings have a call method', function() {
test('invoke when strings have a call method', function() {
String.prototype.call = function() {

@@ -219,3 +262,3 @@ return 42;

test('collections: pluck', function() {
test('pluck', function() {
var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}];

@@ -225,3 +268,13 @@ equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects');

test('collections: max', function() {
test('where', function() {
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
var result = _.where(list, {a: 1});
equal(result.length, 3);
equal(result[result.length - 1].b, 4);
result = _.where(list, {b: 2});
equal(result.length, 2);
equal(result[0].a, 1);
});
test('max', function() {
equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max');

@@ -234,5 +287,7 @@

equal(-Infinity, _.max([]), 'Maximum value of an empty array');
equal(299999, _.max(_.range(1,300000)), "Maximum value of a too-big array");
});
test('collections: min', function() {
test('min', function() {
equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min');

@@ -249,5 +304,7 @@

equal(_.min([now, then]), then);
equal(1, _.min(_.range(1,300000)), "Minimum value of a too-big array");
});
test('collections: sortBy', function() {
test('sortBy', function() {
var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}];

@@ -263,5 +320,28 @@ people = _.sortBy(people, function(person){ return person.age; });

equal(sorted.join(' '), 'one two four five three', 'sorted by length');
function Pair(x, y) {
this.x = x;
this.y = y;
}
var collection = [
new Pair(1, 1), new Pair(1, 2),
new Pair(1, 3), new Pair(1, 4),
new Pair(1, 5), new Pair(1, 6),
new Pair(2, 1), new Pair(2, 2),
new Pair(2, 3), new Pair(2, 4),
new Pair(2, 5), new Pair(2, 6),
new Pair(undefined, 1), new Pair(undefined, 2),
new Pair(undefined, 3), new Pair(undefined, 4),
new Pair(undefined, 5), new Pair(undefined, 6)
];
var actual = _.sortBy(collection, function(pair) {
return pair.x;
});
deepEqual(actual, collection, 'sortBy should be stable');
});
test('collections: groupBy', function() {
test('groupBy', function() {
var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; });

@@ -276,11 +356,59 @@ ok('0' in parity && '1' in parity, 'created a group for each value');

equal(grouped['5'].join(' '), 'three seven eight');
var context = {};
_.groupBy([{}], function(){ ok(this === context); }, context);
grouped = _.groupBy([4.2, 6.1, 6.4], function(num) {
return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
});
equal(grouped.constructor.length, 1);
equal(grouped.hasOwnProperty.length, 2);
var array = [{}];
_.groupBy(array, function(value, index, obj){ ok(obj === array); });
});
test('collections: sortedIndex', function() {
test('countBy', function() {
var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; });
equal(parity['true'], 2);
equal(parity['false'], 3);
var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"];
var grouped = _.countBy(list, 'length');
equal(grouped['3'], 4);
equal(grouped['4'], 3);
equal(grouped['5'], 3);
var context = {};
_.countBy([{}], function(){ ok(this === context); }, context);
grouped = _.countBy([4.2, 6.1, 6.4], function(num) {
return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
});
equal(grouped.constructor, 1);
equal(grouped.hasOwnProperty, 2);
var array = [{}];
_.countBy(array, function(value, index, obj){ ok(obj === array); });
});
test('sortedIndex', function() {
var numbers = [10, 20, 30, 40, 50], num = 35;
var index = _.sortedIndex(numbers, num);
equal(index, 3, '35 should be inserted at index 3');
var indexForNum = _.sortedIndex(numbers, num);
equal(indexForNum, 3, '35 should be inserted at index 3');
var indexFor30 = _.sortedIndex(numbers, 30);
equal(indexFor30, 2, '30 should be inserted at index 2');
var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}];
var iterator = function(obj){ return obj.x; };
strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2);
strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3);
var context = {1: 2, 2: 3, 3: 4};
iterator = function(obj){ return this[obj]; };
strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1);
});
test('collections: shuffle', function() {
test('shuffle', function() {
var numbers = _.range(10);

@@ -292,3 +420,3 @@ var shuffled = _.shuffle(numbers).sort();

test('collections: toArray', function() {
test('toArray', function() {
ok(!_.isArray(arguments), 'arguments object is not an array');

@@ -302,17 +430,19 @@ ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array');

equal(numbers.join(', '), '1, 2, 3', 'object flattened into array');
var objectWithToArrayFunction = {toArray: function() {
return [1, 2, 3];
}};
equal(_.toArray(objectWithToArrayFunction).join(', '), '1, 2, 3', 'toArray method used if present');
var objectWithToArrayValue = {toArray: 1};
equal(_.toArray(objectWithToArrayValue).join(', '), '1', 'toArray property ignored if not a function');
});
test('collections: size', function() {
test('size', function() {
equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object');
equal(_.size([1, 2, 3]), 3, 'can compute the size of an array');
var func = function() {
return _.size(arguments);
};
equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object');
equal(_.size('hello'), 5, 'can compute the size of a string');
equal(_.size(null), 0, 'handles nulls');
});
});

@@ -5,3 +5,3 @@ $(document).ready(function() {

test("functions: bind", function() {
test("bind", function() {
var context = {name : 'moe'};

@@ -42,3 +42,3 @@ var func = function(arg) { return "name: " + (this.name || arg); };

test("functions: bindAll", function() {
test("bindAll", function() {
var curly = {name : 'curly'}, moe = {

@@ -66,3 +66,3 @@ name : 'moe',

test("functions: memoize", function() {
test("memoize", function() {
var fib = function(n) {

@@ -83,3 +83,3 @@ return n < 2 ? n : fib(n - 1) + fib(n - 2);

asyncTest("functions: delay", 2, function() {
asyncTest("delay", 2, function() {
var delayed = false;

@@ -91,3 +91,3 @@ _.delay(function(){ delayed = true; }, 100);

asyncTest("functions: defer", 1, function() {
asyncTest("defer", 1, function() {
var deferred = false;

@@ -98,3 +98,3 @@ _.defer(function(bool){ deferred = bool; }, true);

asyncTest("functions: throttle", 2, function() {
asyncTest("throttle", 2, function() {
var counter = 0;

@@ -114,3 +114,3 @@ var incr = function(){ counter++; };

asyncTest("functions: throttle arguments", 2, function() {
asyncTest("throttle arguments", 2, function() {
var value = 0;

@@ -127,3 +127,3 @@ var update = function(val){ value = val; };

asyncTest("functions: throttle once", 2, function() {
asyncTest("throttle once", 2, function() {
var counter = 0;

@@ -139,3 +139,3 @@ var incr = function(){ return ++counter; };

asyncTest("functions: throttle twice", 1, function() {
asyncTest("throttle twice", 1, function() {
var counter = 0;

@@ -148,4 +148,31 @@ var incr = function(){ counter++; };

asyncTest("functions: debounce", 1, function() {
asyncTest("throttle repeatedly with results", 9, function() {
var counter = 0;
var incr = function(){ return ++counter; };
var throttledIncr = _.throttle(incr, 100);
var results = [];
var saveResult = function() { results.push(throttledIncr()); };
saveResult(); saveResult(); saveResult();
setTimeout(saveResult, 70);
setTimeout(saveResult, 120);
setTimeout(saveResult, 140);
setTimeout(saveResult, 190);
setTimeout(saveResult, 240);
setTimeout(saveResult, 260);
_.delay(function() {
equal(results[0], 1, "incr was called once");
equal(results[1], 1, "incr was throttled");
equal(results[2], 1, "incr was throttled");
equal(results[3], 1, "incr was throttled");
equal(results[4], 2, "incr was called twice");
equal(results[5], 2, "incr was throttled");
equal(results[6], 2, "incr was throttled");
equal(results[7], 3, "incr was called thrice");
equal(results[8], 3, "incr was throttled");
start();
}, 400);
});
asyncTest("debounce", 1, function() {
var counter = 0;
var incr = function(){ counter++; };

@@ -162,7 +189,13 @@ var debouncedIncr = _.debounce(incr, 50);

asyncTest("functions: debounce asap", 2, function() {
asyncTest("debounce asap", 5, function() {
var a, b, c;
var counter = 0;
var incr = function(){ counter++; };
var incr = function(){ return ++counter; };
var debouncedIncr = _.debounce(incr, 50, true);
debouncedIncr(); debouncedIncr(); debouncedIncr();
a = debouncedIncr();
b = debouncedIncr();
c = debouncedIncr();
equal(a, 1);
equal(b, 1);
equal(c, 1);
equal(counter, 1, 'incr was called immediately');

@@ -177,3 +210,14 @@ setTimeout(debouncedIncr, 30);

test("functions: once", function() {
asyncTest("debounce asap recursively", 2, function() {
var counter = 0;
var debouncedIncr = _.debounce(function(){
counter++;
if (counter < 5) debouncedIncr();
}, 50, true);
debouncedIncr();
equal(counter, 1, 'incr was called immediately');
_.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 70);
});
test("once", function() {
var num = 0;

@@ -186,3 +230,3 @@ var increment = _.once(function(){ num++; });

test("functions: wrap", function() {
test("wrap", function() {
var greet = function(name){ return "hi: " + name; };

@@ -200,6 +244,6 @@ var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); });

var ret = wrapped(['whats', 'your'], 'vector', 'victor');
same(ret, [noop, ['whats', 'your'], 'vector', 'victor']);
deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']);
});
test("functions: compose", function() {
test("compose", function() {
var greet = function(name){ return "hi: " + name; };

@@ -214,3 +258,3 @@ var exclaim = function(sentence){ return sentence + '!'; };

test("functions: after", function() {
test("after", function() {
var testAfter = function(afterAmount, timesCalled) {

@@ -217,0 +261,0 @@ var afterCalled = 0;

@@ -5,3 +5,3 @@ $(document).ready(function() {

test("objects: keys", function() {
test("keys", function() {
equal(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object');

@@ -18,7 +18,22 @@ // the test above is not safe because it relies on for-in enumeration order

test("objects: values", function() {
equal(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object');
test("values", function() {
equal(_.values({one: 1, two: 2}).join(', '), '1, 2', 'can extract the values from an object');
equal(_.values({one: 1, two: 2, length: 3}).join(', '), '1, 2, 3', '... even when one of them is "length"');
});
test("objects: functions", function() {
test("pairs", function() {
deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs');
deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"');
});
test("invert", function() {
var obj = {first: 'Moe', second: 'Larry', third: 'Curly'};
equal(_.keys(_.invert(obj)).join(' '), 'Moe Larry Curly', 'can invert an object');
ok(_.isEqual(_.invert(_.invert(obj)), obj), 'two inverts gets you back where you started');
var obj = {length: 3};
ok(_.invert(obj)['3'] == 'length', 'can invert an object with "length"')
});
test("functions", function() {
var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce};

@@ -32,3 +47,3 @@ ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object');

test("objects: extend", function() {
test("extend", function() {
var result;

@@ -46,3 +61,3 @@ equal(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another');

test("objects: pick", function() {
test("pick", function() {
var result;

@@ -55,6 +70,24 @@ result = _.pick({a:1, b:2, c:3}, 'a', 'c');

ok(_.isEqual(result, {a:1, b:2}), 'can restrict properties to those named in mixed args');
var Obj = function(){};
Obj.prototype = {a: 1, b: 2, c: 3};
ok(_.isEqual(_.pick(new Obj, 'a', 'c'), {a:1, c: 3}), 'include prototype props');
});
test("objects: defaults", function() {
test("omit", function() {
var result;
result = _.omit({a:1, b:2, c:3}, 'b');
ok(_.isEqual(result, {a:1, c:3}), 'can omit a single named property');
result = _.omit({a:1, b:2, c:3}, 'a', 'c');
ok(_.isEqual(result, {b:2}), 'can omit several named properties');
result = _.omit({a:1, b:2, c:3}, ['b', 'c']);
ok(_.isEqual(result, {a:1}), 'can omit properties named in an array');
var Obj = function(){};
Obj.prototype = {a: 1, b: 2, c: 3};
ok(_.isEqual(_.omit(new Obj, 'b'), {a:1, c: 3}), 'include prototype props');
});
test("defaults", function() {
var result;
var options = {zero: 0, one: 1, empty: "", nan: NaN, string: "string"};

@@ -73,3 +106,3 @@

test("objects: clone", function() {
test("clone", function() {
var moe = {name : 'moe', lucky : [13, 27, 34]};

@@ -90,3 +123,3 @@ var clone = _.clone(moe);

test("objects: isEqual", function() {
test("isEqual", function() {
function First() {

@@ -210,10 +243,2 @@ this.value = 1;

// According to the Microsoft deviations spec, section 2.1.26, JScript 5.x treats `undefined`
// elements in arrays as elisions. Thus, sparse arrays and dense arrays containing `undefined`
// values are equivalent.
if (0 in [undefined]) {
ok(!_.isEqual(Array(3), [undefined, undefined, undefined]), "Sparse and dense arrays are not equal");
ok(!_.isEqual([undefined, undefined, undefined], Array(3)), "Commutative equality is implemented for sparse and dense arrays");
}
// Simple objects.

@@ -276,2 +301,8 @@ ok(_.isEqual({a: "Curly", b: 1, c: true}, {a: "Curly", b: 1, c: true}), "Objects containing identical primitives are equal");

// More circular arrays #767.
a = ["everything is checked but", "this", "is not"];
a[1] = a;
b = ["everything is checked but", ["this", "array"], "is not"];
ok(!_.isEqual(a, b), "Comparison of circular references with non-circular references are not equal");
// Circular Objects.

@@ -290,2 +321,8 @@ a = {abc: null};

// More circular objects #767.
a = {everything: "is checked", but: "this", is: "not"};
a.but = a;
b = {everything: "is checked", but: {that:"object"}, is: "not"};
ok(!_.isEqual(a, b), "Comparison of circular references with non-circular object references are not equal");
// Cyclic Structures.

@@ -324,56 +361,7 @@ a = [{abc: null}];

// Custom `isEqual` methods - comparing different types
LocalizedString = (function() {
function LocalizedString(id) { this.id = id; this.string = (this.id===10)? 'Bonjour': ''; }
LocalizedString.prototype.isEqual = function(that) {
if (_.isString(that)) return this.string == that;
else if (that instanceof LocalizedString) return this.id == that.id;
return false;
};
return LocalizedString;
})();
var localized_string1 = new LocalizedString(10), localized_string2 = new LocalizedString(10), localized_string3 = new LocalizedString(11);
ok(_.isEqual(localized_string1, localized_string2), 'comparing same typed instances with same ids');
ok(!_.isEqual(localized_string1, localized_string3), 'comparing same typed instances with different ids');
ok(_.isEqual(localized_string1, 'Bonjour'), 'comparing different typed instances with same values');
ok(_.isEqual('Bonjour', localized_string1), 'comparing different typed instances with same values');
ok(!_.isEqual('Bonjour', localized_string3), 'comparing two localized strings with different ids');
ok(!_.isEqual(localized_string1, 'Au revoir'), 'comparing different typed instances with different values');
ok(!_.isEqual('Au revoir', localized_string1), 'comparing different typed instances with different values');
// Custom `isEqual` methods - comparing with serialized data
Date.prototype.toJSON = function() {
return {
_type:'Date',
year:this.getUTCFullYear(),
month:this.getUTCMonth(),
day:this.getUTCDate(),
hours:this.getUTCHours(),
minutes:this.getUTCMinutes(),
seconds:this.getUTCSeconds()
};
};
Date.prototype.isEqual = function(that) {
var this_date_components = this.toJSON();
var that_date_components = (that instanceof Date) ? that.toJSON() : that;
delete this_date_components['_type']; delete that_date_components['_type'];
return _.isEqual(this_date_components, that_date_components);
};
var date = new Date();
var date_json = {
_type:'Date',
year:date.getUTCFullYear(),
month:date.getUTCMonth(),
day:date.getUTCDate(),
hours:date.getUTCHours(),
minutes:date.getUTCMinutes(),
seconds:date.getUTCSeconds()
};
ok(_.isEqual(date_json, date), 'serialized date matches date');
ok(_.isEqual(date, date_json), 'date matches serialized date');
// Objects from another frame.
ok(_.isEqual({}, iObject));
});
test("objects: isEmpty", function() {
test("isEmpty", function() {
ok(!_([1]).isEmpty(), '[1] is not empty');

@@ -412,2 +400,3 @@ ok(_.isEmpty([]), '[] is empty');

parent.iUndefined = undefined;\
parent.iObject = {};\
</script>"

@@ -417,3 +406,3 @@ );

test("objects: isElement", function() {
test("isElement", function() {
ok(!_.isElement('div'), 'strings are not dom elements');

@@ -424,3 +413,3 @@ ok(_.isElement($('html')[0]), 'the html tag is a DOM element');

test("objects: isArguments", function() {
test("isArguments", function() {
var args = (function(){ return arguments; })(1, 2, 3);

@@ -435,3 +424,3 @@ ok(!_.isArguments('string'), 'a string is not an arguments object');

test("objects: isObject", function() {
test("isObject", function() {
ok(_.isObject(arguments), 'the arguments object is object');

@@ -451,3 +440,3 @@ ok(_.isObject([1, 2, 3]), 'and arrays');

test("objects: isArray", function() {
test("isArray", function() {
ok(!_.isArray(arguments), 'the arguments object is not an array');

@@ -458,3 +447,3 @@ ok(_.isArray([1, 2, 3]), 'but arrays are');

test("objects: isString", function() {
test("isString", function() {
ok(!_.isString(document.body), 'the document body is not a string');

@@ -465,3 +454,3 @@ ok(_.isString([1, 2, 3].join(', ')), 'but strings are');

test("objects: isNumber", function() {
test("isNumber", function() {
ok(!_.isNumber('string'), 'a string is not a number');

@@ -477,3 +466,3 @@ ok(!_.isNumber(arguments), 'the arguments object is not a number');

test("objects: isBoolean", function() {
test("isBoolean", function() {
ok(!_.isBoolean(2), 'a number is not a boolean');

@@ -492,3 +481,3 @@ ok(!_.isBoolean("string"), 'a string is not a boolean');

test("objects: isFunction", function() {
test("isFunction", function() {
ok(!_.isFunction([1, 2, 3]), 'arrays are not functions');

@@ -500,3 +489,3 @@ ok(!_.isFunction('moe'), 'strings are not functions');

test("objects: isDate", function() {
test("isDate", function() {
ok(!_.isDate(100), 'numbers are not dates');

@@ -508,3 +497,3 @@ ok(!_.isDate({}), 'objects are not dates');

test("objects: isRegExp", function() {
test("isRegExp", function() {
ok(!_.isRegExp(_.identity), 'functions are not RegExps');

@@ -515,3 +504,3 @@ ok(_.isRegExp(/identity/), 'but RegExps are');

test("objects: isFinite", function() {
test("isFinite", function() {
ok(!_.isFinite(undefined), 'undefined is not Finite');

@@ -530,3 +519,3 @@ ok(!_.isFinite(null), 'null is not Finite');

test("objects: isNaN", function() {
test("isNaN", function() {
ok(!_.isNaN(undefined), 'undefined is not NaN');

@@ -537,5 +526,6 @@ ok(!_.isNaN(null), 'null is not NaN');

ok(_.isNaN(iNaN), 'even from another frame');
ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN');
});
test("objects: isNull", function() {
test("isNull", function() {
ok(!_.isNull(undefined), 'undefined is not null');

@@ -547,3 +537,3 @@ ok(!_.isNull(NaN), 'NaN is not null');

test("objects: isUndefined", function() {
test("isUndefined", function() {
ok(!_.isUndefined(1), 'numbers are defined');

@@ -559,3 +549,3 @@ ok(!_.isUndefined(null), 'null is defined');

if (window.ActiveXObject) {
test("objects: IE host objects", function() {
test("IE host objects", function() {
var xml = new ActiveXObject("Msxml2.DOMDocument.3.0");

@@ -571,3 +561,3 @@ ok(!_.isNumber(xml));

test("objects: tap", function() {
test("tap", function() {
var intercepted = null;

@@ -574,0 +564,0 @@ var interceptor = function(obj) { intercepted = obj; };

@@ -7,2 +7,3 @@ (function() {

var randomized = _.sortBy(numbers, function(){ return Math.random(); });
var deep = _.map(_.range(100), function() { return _.range(1000); });

@@ -63,4 +64,4 @@ JSLitmus.test('_.each()', function() {

JSLitmus.test('_.intersect()', function() {
return _.intersect(numbers, randomized);
JSLitmus.test('_.intersection()', function() {
return _.intersection(numbers, randomized);
});

@@ -72,2 +73,6 @@

})();
JSLitmus.test('_.flatten()', function() {
return _.flatten(deep);
});
})();

@@ -17,11 +17,9 @@ $(document).ready(function() {

test("utility: noConflict", function() {
var underscore = _.noConflict();
ok(underscore.isUndefined(_), "The '_' variable has been returned to its previous state.");
var intersection = underscore.intersect([-1, 0, 1, 2], [1, 2, 3, 4]);
equal(intersection.join(', '), '1, 2', 'but the intersection function still works');
window._ = underscore;
test("#750 - Return _ instance.", 2, function() {
var instance = _([]);
ok(_(instance) === instance);
ok(new _(instance) === instance);
});
test("utility: identity", function() {
test("identity", function() {
var moe = {name : 'moe'};

@@ -31,3 +29,3 @@ equal(_.identity(moe), moe, 'moe is the same as his identity');

test("utility: uniqueId", function() {
test("uniqueId", function() {
var ids = [], i = 0;

@@ -38,3 +36,3 @@ while(i++ < 100) ids.push(_.uniqueId());

test("utility: times", function() {
test("times", function() {
var vals = [];

@@ -49,3 +47,3 @@ _.times(3, function (i) { vals.push(i); });

test("utility: mixin", function() {
test("mixin", function() {
_.mixin({

@@ -60,8 +58,17 @@ myReverse: function(string) {

test("utility: _.escape", function() {
test("_.escape", function() {
equal(_.escape("Curly & Moe"), "Curly &amp; Moe");
equal(_.escape("Curly &amp; Moe"), "Curly &amp;amp; Moe");
equal(_.escape(null), '');
});
test("utility: template", function() {
test("_.unescape", function() {
var string = "Curly & Moe";
equal(_.unescape("Curly &amp; Moe"), string);
equal(_.unescape("Curly &amp;amp; Moe"), "Curly &amp; Moe");
equal(_.unescape(null), '');
equal(_.unescape(_.escape(string)), string);
});
test("template", function() {
var basicTemplate = _.template("<%= thing %> is gettin' on my noives!");

@@ -172,2 +179,10 @@ var result = basicTemplate({thing : 'This'});

test('_.template provides the generated function source, when a SyntaxError occurs', function() {
try {
_.template('<b><%= if %></b>');
} catch (e) {
ok(e.source.indexOf('( if )') > 0);
}
});
test('_.template handles \\u2028 & \\u2029', function() {

@@ -190,5 +205,5 @@ var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>');

var data = {x: 'x'};
strictEqual(_.template(s, data, {variable: 'data'}), 'x')
strictEqual(_.template(s, data, {variable: 'data'}), 'x');
_.templateSettings.variable = 'data';
strictEqual(_.template(s)(data), 'x')
strictEqual(_.template(s)(data), 'x');
});

@@ -202,2 +217,41 @@

test('#556 - undefined template variables.', function() {
var template = _.template('<%=x%>');
strictEqual(template({x: null}), '');
strictEqual(template({x: undefined}), '');
var templateEscaped = _.template('<%-x%>');
strictEqual(templateEscaped({x: null}), '');
strictEqual(templateEscaped({x: undefined}), '');
var templateWithProperty = _.template('<%=x.foo%>');
strictEqual(templateWithProperty({x: {} }), '');
strictEqual(templateWithProperty({x: {} }), '');
var templateWithPropertyEscaped = _.template('<%-x.foo%>');
strictEqual(templateWithPropertyEscaped({x: {} }), '');
strictEqual(templateWithPropertyEscaped({x: {} }), '');
});
test('interpolate evaluates code only once.', 2, function() {
var count = 0;
var template = _.template('<%= f() %>');
template({f: function(){ ok(!(count++)); }});
var countEscaped = 0;
var templateEscaped = _.template('<%- f() %>');
templateEscaped({f: function(){ ok(!(countEscaped++)); }});
});
test('#746 - _.template settings are not modified.', 1, function() {
var settings = {};
_.template('', null, settings);
deepEqual(settings, {});
});
test('#779 - delimeters are applied to unescaped text.', 1, function() {
var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g});
strictEqual(template(), '<<\nx\n>>');
});
});
/**
* QUnit v1.2.0 - A JavaScript Unit Testing Framework
* QUnit v1.10.0 - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
* http://qunitjs.com
*
* Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
(function(window) {
(function( window ) {
var defined = {
var QUnit,
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 {
return !!sessionStorage.getItem;
} catch(e) {
sessionStorage.setItem( x, x );
sessionStorage.removeItem( x );
return true;
} catch( e ) {
return false;
}
})()
}())
};
var testId = 0,
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty;
function Test( settings ) {
extend( this, settings );
this.assertions = [];
this.testNumber = ++Test.count;
}
var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
this.name = name;
this.testName = testName;
this.expected = expected;
this.testEnvironmentArg = testEnvironmentArg;
this.async = async;
this.callback = callback;
this.assertions = [];
};
Test.count = 0;
Test.prototype = {
init: function() {
var tests = id("qunit-tests");
if (tests) {
var b = document.createElement("strong");
b.innerHTML = "Running " + this.name;
var li = document.createElement("li");
li.appendChild( b );
li.className = "running";
li.id = this.id = "test-output" + testId++;
var a, b, li,
tests = id( "qunit-tests" );
if ( tests ) {
b = document.createElement( "strong" );
b.innerHTML = this.name;
// `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 );

@@ -51,5 +68,5 @@ }

setup: function() {
if (this.module != config.previousModule) {
if ( this.module !== config.previousModule ) {
if ( config.previousModule ) {
runLoggingCallbacks('moduleDone', QUnit, {
runLoggingCallbacks( "moduleDone", QUnit, {
name: config.previousModule,

@@ -59,21 +76,23 @@ failed: config.moduleStats.bad,

total: config.moduleStats.all
} );
});
}
config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 };
runLoggingCallbacks( 'moduleStart', QUnit, {
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);
if (this.testEnvironmentArg) {
extend(this.testEnvironment, this.testEnvironmentArg);
}
}, this.moduleTestEnvironment );
runLoggingCallbacks( 'testStart', QUnit, {
runLoggingCallbacks( "testStart", QUnit, {
name: this.testName,

@@ -87,10 +106,13 @@ module: this.module

if ( !config.pollution ) {
saveGlobal();
}
if ( config.notrycatch ) {
this.testEnvironment.setup.call( this.testEnvironment );
return;
}
try {
if ( !config.pollution ) {
saveGlobal();
}
this.testEnvironment.setup.call(this.testEnvironment);
} catch(e) {
QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
this.testEnvironment.setup.call( this.testEnvironment );
} catch( e ) {
QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
}

@@ -100,2 +122,9 @@ },

config.current = this;
var running = id( "qunit-testresult" );
if ( running ) {
running.innerHTML = "Running: <br/>" + this.name;
}
if ( this.async ) {

@@ -106,10 +135,10 @@ QUnit.stop();

if ( config.notrycatch ) {
this.callback.call(this.testEnvironment);
this.callback.call( this.testEnvironment, QUnit.assert );
return;
}
try {
this.callback.call(this.testEnvironment);
} catch(e) {
fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
this.callback.call( this.testEnvironment, QUnit.assert );
} catch( e ) {
QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
// else next test will carry the responsibility

@@ -126,17 +155,29 @@ saveGlobal();

config.current = this;
try {
this.testEnvironment.teardown.call(this.testEnvironment);
checkPollution();
} catch(e) {
QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
if ( config.notrycatch ) {
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, extractStacktrace( e, 1 ) );
}
}
checkPollution();
},
finish: function() {
config.current = this;
if ( this.expected != null && this.expected != this.assertions.length ) {
QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
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 good = 0, bad = 0,
tests = id("qunit-tests");
var assertion, a, b, i, li, ol,
test = this,
good = 0,
bad = 0,
tests = id( "qunit-tests" );

@@ -147,10 +188,10 @@ config.stats.all += this.assertions.length;

if ( tests ) {
var ol = document.createElement("ol");
ol = document.createElement( "ol" );
for ( var i = 0; i < this.assertions.length; i++ ) {
var assertion = this.assertions[i];
for ( i = 0; i < this.assertions.length; i++ ) {
assertion = this.assertions[i];
var li = document.createElement("li");
li = document.createElement( "li" );
li.className = assertion.result ? "pass" : "fail";
li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
ol.appendChild( li );

@@ -169,20 +210,17 @@

if ( QUnit.config.reorder && defined.sessionStorage ) {
if (bad) {
sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
if ( bad ) {
sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
} else {
sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
}
}
if (bad == 0) {
if ( bad === 0 ) {
ol.style.display = "none";
}
var b = document.createElement("strong");
// `b` initialized at top of scope
b = document.createElement( "strong" );
b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
var a = document.createElement("a");
a.innerHTML = "Rerun";
a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
addEvent(b, "click", function() {

@@ -194,3 +232,3 @@ var next = b.nextSibling.nextSibling,

addEvent(b, "dblclick", function(e) {
addEvent(b, "dblclick", function( e ) {
var target = e && e.target ? e.target : window.event.srcElement;

@@ -201,15 +239,17 @@ if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {

if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
window.location = QUnit.url({ testNumber: test.testNumber });
}
});
var li = id(this.id);
// `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 ( a );
li.appendChild( ol );
} else {
for ( var i = 0; i < this.assertions.length; i++ ) {
for ( i = 0; i < this.assertions.length; i++ ) {
if ( !this.assertions[i].result ) {

@@ -223,9 +263,3 @@ bad++;

try {
QUnit.reset();
} catch(e) {
fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
}
runLoggingCallbacks( 'testDone', QUnit, {
runLoggingCallbacks( "testDone", QUnit, {
name: this.testName,

@@ -236,7 +270,13 @@ module: this.module,

total: this.assertions.length
} );
});
QUnit.reset();
config.current = undefined;
},
queue: function() {
var test = this;
var bad,
test = this;
synchronize(function() {

@@ -260,22 +300,28 @@ test.init();

}
// `bad` initialized at top of scope
// defer when previous test run passed, if storage is available
var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
if (bad) {
bad = QUnit.config.reorder && defined.sessionStorage &&
+sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
if ( bad ) {
run();
} else {
synchronize(run, true);
};
synchronize( run, true );
}
}
};
var QUnit = {
// 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) {
module: function( name, testEnvironment ) {
config.currentModule = name;
config.currentModuleTestEnviroment = testEnvironment;
config.currentModuleTestEnvironment = testEnvironment;
config.modules[name] = true;
},
asyncTest: function(testName, expected, callback) {
asyncTest: function( testName, expected, callback ) {
if ( arguments.length === 2 ) {

@@ -286,7 +332,8 @@ callback = expected;

QUnit.test(testName, expected, callback, true);
QUnit.test( testName, expected, callback, true );
},
test: function(testName, expected, callback, async) {
var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
test: function( testName, expected, callback, async ) {
var test,
name = "<span class='test-name'>" + escapeInnerText( testName ) + "</span>";

@@ -297,43 +344,116 @@ if ( arguments.length === 2 ) {

}
// is 2nd argument a testEnvironment?
if ( expected && typeof expected === 'object') {
testEnvironmentArg = expected;
expected = null;
}
if ( config.currentModule ) {
name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
name = "<span class='module-name'>" + config.currentModule + "</span>: " + name;
}
if ( !validTest(config.currentModule + ": " + testName) ) {
test = new Test({
name: name,
testName: testName,
expected: expected,
async: async,
callback: callback,
module: config.currentModule,
moduleTestEnvironment: config.currentModuleTestEnvironment,
stack: sourceFromStacktrace( 2 )
});
if ( !validTest( test ) ) {
return;
}
var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
test.module = config.currentModule;
test.moduleTestEnvironment = config.currentModuleTestEnviroment;
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) {
config.current.expected = asserts;
// 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 ) {
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;
}
// 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 );
}
}
};
// Asssert helpers
// All of these must call either QUnit.push() or manually do:
// - runLoggingCallbacks( "log", .. );
// - config.current.assertions.push({ .. });
QUnit.assert = {
/**
* Asserts true.
* Asserts rough true-ish result.
* @name ok
* @function
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function(a, msg) {
a = !!a;
var details = {
result: a,
message: msg
};
msg = escapeInnerText(msg);
runLoggingCallbacks( 'log', QUnit, details );
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 = escapeInnerText( 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>" + escapeInnerText( source ) + "</pre></td></tr></table>";
}
}
runLoggingCallbacks( "log", QUnit, details );
config.current.assertions.push({
result: a,
result: result,
message: msg

@@ -344,41 +464,58 @@ });

/**
* Checks that the first two arguments are equal, with an optional message.
* Assert that the first two arguments are equal, with an optional message.
* Prints out both actual and expected values.
*
* Prefered to ok( actual == expected, message )
*
* @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
*
* @param Object actual
* @param Object expected
* @param String message (optional)
* @name equal
* @function
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
*/
equal: function(actual, expected, message) {
QUnit.push(expected == actual, actual, expected, message);
equal: function( actual, expected, message ) {
QUnit.push( expected == actual, actual, expected, message );
},
notEqual: function(actual, expected, message) {
QUnit.push(expected != actual, actual, expected, message);
/**
* @name notEqual
* @function
*/
notEqual: function( actual, expected, message ) {
QUnit.push( expected != actual, actual, expected, message );
},
deepEqual: function(actual, expected, message) {
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 );
},
notDeepEqual: 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 );
},
strictEqual: function(actual, expected, message) {
QUnit.push(expected === actual, actual, expected, message);
/**
* @name strictEqual
* @function
*/
strictEqual: function( actual, expected, message ) {
QUnit.push( expected === actual, actual, expected, message );
},
notStrictEqual: 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 );
},
raises: function(block, expected, message) {
var actual, ok = false;
throws: function( block, expected, message ) {
var actual,
ok = false;
if (typeof expected === 'string') {
// 'expected' is optional
if ( typeof expected === "string" ) {
message = expected;

@@ -388,86 +525,70 @@ expected = null;

config.current.ignoreGlobalErrors = true;
try {
block();
block.call( config.current.testEnvironment );
} catch (e) {
actual = e;
}
config.current.ignoreGlobalErrors = false;
if (actual) {
if ( actual ) {
// we don't want to validate thrown error
if (!expected) {
if ( !expected ) {
ok = true;
// expected is a regexp
} else if (QUnit.objectType(expected) === "regexp") {
ok = expected.test(actual);
} else if ( QUnit.objectType( expected ) === "regexp" ) {
ok = expected.test( actual );
// expected is a constructor
} else if (actual instanceof expected) {
} else if ( actual instanceof expected ) {
ok = true;
// expected is a validation function which returns true is validation passed
} else if (expected.call({}, actual) === true) {
} else if ( expected.call( {}, actual ) === true ) {
ok = true;
}
}
QUnit.ok(ok, message);
},
start: function(count) {
config.semaphore -= count || 1;
if (config.semaphore > 0) {
// don't start until equal number of stop-calls
return;
}
if (config.semaphore < 0) {
// ignore if start is called more often then stop
config.semaphore = 0;
}
// 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);
QUnit.push( ok, actual, null, message );
} else {
config.blocking = false;
process(true);
QUnit.pushFailure( message, null, 'No exception was thrown.' );
}
},
}
};
stop: function(count) {
config.semaphore += count || 1;
config.blocking = true;
/**
* @deprecate since 1.8.0
* Kept assertion helpers in root for backwards compatibility
*/
extend( QUnit, QUnit.assert );
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);
}
}
/**
* @deprecated since 1.9.0
* Kept global "raises()" for backwards compatibility
*/
QUnit.raises = QUnit.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
// We want access to the constructor's prototype
(function() {
function F(){};
function F() {}
F.prototype = QUnit;
QUnit = new F();
//Make F QUnit's constructor so that we can add to the prototype later
// Make F QUnit's constructor so that we can add to the prototype later
QUnit.constructor = F;
})();
}());
// Backwards compatibility, deprecated
QUnit.equals = QUnit.equal;
QUnit.same = QUnit.deepEqual;
// Maintain internal state
var config = {
/**
* Config object: Maintain internal state
* Later exposed as QUnit.config
* `config` initialized at top of scope
*/
config = {
// The queue of tests to run

@@ -490,5 +611,24 @@ queue: [],

urlConfig: ['noglobals', 'notrycatch'],
// when enabled, all tests must call expect()
requireExpects: false,
//logging callback queues
// 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: [],

@@ -503,5 +643,6 @@ done: [],

// Load paramaters
// Initialize more QUnit.config and QUnit.urlParams
(function() {
var location = window.location || { search: "", protocol: "file:" },
var i,
location = window.location || { search: "", protocol: "file:" },
params = location.search.slice( 1 ).split( "&" ),

@@ -513,3 +654,3 @@ length = params.length,

if ( params[ 0 ] ) {
for ( var i = 0; i < length; i++ ) {
for ( i = 0; i < length; i++ ) {
current = params[ i ].split( "=" );

@@ -524,20 +665,27 @@ current[ 0 ] = decodeURIComponent( current[ 0 ] );

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:');
})();
QUnit.isLocal = location.protocol === "file:";
}());
// Expose the API as global variables, unless an 'exports'
// object exists, in that case we assume we're in CommonJS
if ( typeof exports === "undefined" || typeof require === "undefined" ) {
extend(window, QUnit);
// 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;
} else {
extend(exports, QUnit);
exports.QUnit = QUnit;
}
// define these after exposing globals to keep them in these QUnit namespace only
extend(QUnit, {
// Extend QUnit object,
// these after set here because they should not be exposed as global functions
extend( QUnit, {
config: config,

@@ -547,6 +695,6 @@

init: function() {
extend(config, {
extend( config, {
stats: { all: 0, bad: 0 },
moduleStats: { all: 0, bad: 0 },
started: +new Date,
started: +new Date(),
updateRate: 1000,

@@ -561,6 +709,18 @@ blocking: false,

var tests = id( "qunit-tests" ),
banner = id( "qunit-banner" ),
result = id( "qunit-testresult" );
var tests, banner, result,
qunit = id( "qunit" );
if ( qunit ) {
qunit.innerHTML =
"<h1 id='qunit-header'>" + escapeInnerText( 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 ) {

@@ -583,39 +743,25 @@ tests.innerHTML = "";

tests.parentNode.insertBefore( result, tests );
result.innerHTML = 'Running...<br/>&nbsp;';
result.innerHTML = "Running...<br/>&nbsp;";
}
},
/**
* Resets the test setup. Useful for tests that modify the DOM.
*
* If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
*/
// Resets the test setup. Useful for tests that modify the DOM.
reset: function() {
if ( window.jQuery ) {
jQuery( "#qunit-fixture" ).html( config.fixture );
} else {
var main = id( 'qunit-fixture' );
if ( main ) {
main.innerHTML = config.fixture;
}
var fixture = id( "qunit-fixture" );
if ( fixture ) {
fixture.innerHTML = config.fixture;
}
},
/**
* Trigger an event on an element.
*
* @example triggerEvent( document.body, "click" );
*
* @param DOMElement elem
* @param String type
*/
// Trigger an event on an element.
// @example triggerEvent( document.body, "click" );
triggerEvent: function( elem, type, event ) {
if ( document.createEvent ) {
event = document.createEvent("MouseEvents");
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);
elem.fireEvent( "on" + type );
}

@@ -630,30 +776,28 @@ },

objectType: function( obj ) {
if (typeof obj === "undefined") {
if ( typeof obj === "undefined" ) {
return "undefined";
// consider: typeof null === object
}
if (obj === null) {
if ( obj === null ) {
return "null";
}
var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || '';
var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
switch (type) {
case 'Number':
if (isNaN(obj)) {
return "nan";
} else {
return "number";
}
case 'String':
case 'Boolean':
case 'Array':
case 'Date':
case 'RegExp':
case 'Function':
return type.toLowerCase();
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";
if ( typeof obj === "object" ) {
return "object";
}

@@ -663,29 +807,42 @@ return undefined;

push: function(result, actual, expected, message) {
var details = {
result: result,
message: message,
actual: actual,
expected: expected
};
push: function( result, actual, expected, message ) {
if ( !config.current ) {
throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
}
message = escapeInnerText(message) || (result ? "okay" : "failed");
message = '<span class="test-message">' + message + "</span>";
expected = escapeInnerText(QUnit.jsDump.parse(expected));
actual = escapeInnerText(QUnit.jsDump.parse(actual));
var output = message + '<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>';
}
if (!result) {
var source = sourceFromStacktrace();
if (source) {
var output, source,
details = {
module: config.current.module,
name: config.current.testName,
result: result,
message: message,
actual: actual,
expected: expected
};
message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
message = "<span class='test-message'>" + message + "</span>";
output = message;
if ( !result ) {
expected = escapeInnerText( QUnit.jsDump.parse(expected) );
actual = escapeInnerText( 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>' + escapeInnerText(source) + '</pre></td></tr>';
output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
}
output += "</table>";
}
output += "</table>";
runLoggingCallbacks( 'log', QUnit, details );
runLoggingCallbacks( "log", QUnit, details );

@@ -698,6 +855,45 @@ config.current.assertions.push({

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 = escapeInnerText( 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>" + escapeInnerText( actual ) + "</pre></td></tr>";
}
if ( source ) {
details.source = source;
output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( 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 querystring = "?",
key;
var key,
querystring = "?";
for ( key in params ) {

@@ -716,23 +912,35 @@ if ( !hasOwn.call( params, key ) ) {

addEvent: addEvent
// load, equiv, jsDump, diff: Attached later
});
//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later
//Doing this allows us to tell if the following methods have been overwritten on the actual
//QUnit object, which is a deprecated way of using the callbacks.
extend(QUnit.constructor.prototype, {
/**
* @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'),
begin: registerLoggingCallback( "begin" ),
// done: { failed, passed, total, runtime }
done: registerLoggingCallback('done'),
done: registerLoggingCallback( "done" ),
// log: { result, actual, expected, message }
log: registerLoggingCallback('log'),
log: registerLoggingCallback( "log" ),
// testStart: { name }
testStart: registerLoggingCallback('testStart'),
testStart: registerLoggingCallback( "testStart" ),
// testDone: { name, failed, passed, total }
testDone: registerLoggingCallback('testDone'),
testDone: registerLoggingCallback( "testDone" ),
// moduleStart: { name }
moduleStart: registerLoggingCallback('moduleStart'),
moduleStart: registerLoggingCallback( "moduleStart" ),
// moduleDone: { name, failed, passed, total }
moduleDone: registerLoggingCallback('moduleDone')
moduleDone: registerLoggingCallback( "moduleDone" )
});

@@ -745,6 +953,11 @@

QUnit.load = function() {
runLoggingCallbacks( 'begin', QUnit, {} );
runLoggingCallbacks( "begin", QUnit, {} );
// Initialize the config, saving the execution queue
var oldconfig = extend({}, config);
var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes, moduleFilter,
numModules = 0,
moduleFilterHtml = "",
urlConfigHtml = "",
oldconfig = extend( {}, config );
QUnit.init();

@@ -755,46 +968,69 @@ extend(config, oldconfig);

var urlConfigHtml = '', len = config.urlConfig.length;
for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) {
config[val] = QUnit.urlParams[val];
urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>';
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-" + val.id + "' name='" + val.id + "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) + " title='" + val.tooltip + "'><label for='qunit-urlconfig-" + val.id + "' title='" + val.tooltip + "'>" + val.label + "</label>";
}
var userAgent = id("qunit-userAgent");
moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " + ( config.module === undefined ? "selected" : "" ) + ">< All Modules ></option>";
for ( i in config.modules ) {
if ( config.modules.hasOwnProperty( i ) ) {
numModules += 1;
moduleFilterHtml += "<option value='" + encodeURIComponent(i) + "' " + ( config.module === i ? "selected" : "" ) + ">" + i + "</option>";
}
}
moduleFilterHtml += "</select>";
// `userAgent` initialized at top of scope
userAgent = id( "qunit-userAgent" );
if ( userAgent ) {
userAgent.innerHTML = navigator.userAgent;
}
var banner = id("qunit-header");
// `banner` initialized at top of scope
banner = id( "qunit-header" );
if ( banner ) {
banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;
addEvent( banner, "change", function( event ) {
var params = {};
params[ event.target.name ] = event.target.checked ? true : undefined;
window.location = QUnit.url( params );
});
banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
}
var toolbar = id("qunit-testrunner-toolbar");
// `toolbar` initialized at top of scope
toolbar = id( "qunit-testrunner-toolbar" );
if ( toolbar ) {
var filter = document.createElement("input");
// `filter` initialized at top of scope
filter = document.createElement( "input" );
filter.type = "checkbox";
filter.id = "qunit-filter-pass";
addEvent( filter, "click", function() {
var ol = document.getElementById("qunit-tests");
var tmp,
ol = document.getElementById( "qunit-tests" );
if ( filter.checked ) {
ol.className = ol.className + " hidepass";
} else {
var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
ol.className = tmp.replace(/ hidepass /, " ");
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");
sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
} else {
sessionStorage.removeItem("qunit-filter-passed-tests");
sessionStorage.removeItem( "qunit-filter-passed-tests" );
}
}
});
if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
filter.checked = true;
var ol = document.getElementById("qunit-tests");
// `ol` initialized at top of scope
ol = document.getElementById( "qunit-tests" );
ol.className = ol.className + " hidepass";

@@ -804,9 +1040,34 @@ }

var label = document.createElement("label");
label.setAttribute("for", "qunit-filter-pass");
// `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 );
urlConfigCheckboxes = document.createElement( 'span' );
urlConfigCheckboxes.innerHTML = urlConfigHtml;
addEvent( urlConfigCheckboxes, "change", function( event ) {
var params = {};
params[ event.target.name ] = event.target.checked ? true : undefined;
window.location = QUnit.url( params );
});
toolbar.appendChild( urlConfigCheckboxes );
if (numModules > 1) {
moduleFilter = document.createElement( 'span' );
moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' );
moduleFilter.innerHTML = moduleFilterHtml;
addEvent( moduleFilter, "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);
}
}
var main = id('qunit-fixture');
// `main` initialized at top of scope
main = id( "qunit-fixture" );
if ( main ) {

@@ -816,3 +1077,3 @@ config.fixture = main.innerHTML;

if (config.autostart) {
if ( config.autostart ) {
QUnit.start();

@@ -822,13 +1083,34 @@ }

addEvent(window, "load", QUnit.load);
addEvent( window, "load", QUnit.load );
// addEvent(window, "error") gives us a useless event object
window.onerror = function( message, file, line ) {
if ( QUnit.config.current ) {
ok( false, message + ", " + file + ":" + line );
} else {
test( "global failure", function() {
ok( false, message + ", " + file + ":" + line );
});
// `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;
};

@@ -841,3 +1123,3 @@

if ( config.currentModule ) {
runLoggingCallbacks( 'moduleDone', QUnit, {
runLoggingCallbacks( "moduleDone", QUnit, {
name: config.currentModule,

@@ -847,24 +1129,25 @@ failed: config.moduleStats.bad,

total: config.moduleStats.all
} );
});
}
var banner = id("qunit-banner"),
tests = id("qunit-tests"),
runtime = +new Date - config.started,
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 ',
"Tests completed in ",
runtime,
' milliseconds.<br/>',
'<span class="passed">',
" milliseconds.<br/>",
"<span class='passed'>",
passed,
'</span> tests of <span class="total">',
"</span> tests of <span class='total'>",
config.stats.all,
'</span> passed, <span class="failed">',
"</span> passed, <span class='failed'>",
config.stats.bad,
'</span> failed.'
].join('');
"</span> failed."
].join( "" );
if ( banner ) {
banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
}

@@ -880,8 +1163,24 @@

document.title = [
(config.stats.bad ? "\u2716" : "\u2714"),
document.title.replace(/^[\u2714\u2716] /i, "")
].join(" ");
( config.stats.bad ? "\u2716" : "\u2714" ),
document.title.replace( /^[\u2714\u2716] /i, "" )
].join( " " );
}
runLoggingCallbacks( 'done', QUnit, {
// 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,

@@ -891,9 +1190,26 @@ passed: passed,

runtime: runtime
} );
});
}
function validTest( name ) {
var filter = config.filter,
run = false;
/** @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 ) {

@@ -903,45 +1219,72 @@ return true;

var not = filter.charAt( 0 ) === "!";
if ( not ) {
include = filter.charAt( 0 ) !== "!";
if ( !include ) {
filter = filter.slice( 1 );
}
if ( name.indexOf( filter ) !== -1 ) {
return !not;
// If the filter matches, we need to honour include
if ( fullName.indexOf( filter ) !== -1 ) {
return include;
}
if ( not ) {
run = true;
// 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, regex;
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;
}
return run;
}
// so far supports only Firefox, Chrome and Opera (buggy)
// could be extended in the future to use something like https://github.com/csnover/TraceKit
function sourceFromStacktrace() {
function sourceFromStacktrace( offset ) {
try {
throw new Error();
} catch ( e ) {
if (e.stacktrace) {
// Opera
return e.stacktrace.split("\n")[6];
} else if (e.stack) {
// Firefox, Chrome
return e.stack.split("\n")[4];
} else if (e.sourceURL) {
// Safari, PhantomJS
// TODO sourceURL points at the 'throw new Error' line above, useless
//return e.sourceURL + ":" + e.line;
}
return extractStacktrace( e, offset );
}
}
function escapeInnerText(s) {
if (!s) {
function escapeInnerText( s ) {
if ( !s ) {
return "";
}
s = s + "";
return s.replace(/[\&<>]/g, function(s) {
switch(s) {
return s.replace( /[\&<>]/g, function( s ) {
switch( s ) {
case "&": return "&amp;";

@@ -959,3 +1302,3 @@ case "<": return "&lt;";

if ( config.autorun && !config.blocking ) {
process(last);
process( last );
}

@@ -965,2 +1308,5 @@ }

function process( last ) {
function next() {
process( last );
}
var start = new Date().getTime();

@@ -973,5 +1319,3 @@ config.depth = config.depth ? config.depth + 1 : 1;

} else {
window.setTimeout( function(){
process( last );
}, 13 );
window.setTimeout( next, 13 );
break;

@@ -991,3 +1335,4 @@ }

for ( var key in window ) {
if ( !hasOwn.call( window, key ) ) {
// in Opera sometimes DOM element ids show up here, ignore them
if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
continue;

@@ -1001,13 +1346,16 @@ }

function checkPollution( name ) {
var old = config.pollution;
var newGlobals,
deletedGlobals,
old = config.pollution;
saveGlobal();
var newGlobals = diff( config.pollution, old );
newGlobals = diff( config.pollution, old );
if ( newGlobals.length > 0 ) {
ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
}
var deletedGlobals = diff( old, config.pollution );
deletedGlobals = diff( old, config.pollution );
if ( deletedGlobals.length > 0 ) {
ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
}

@@ -1018,7 +1366,9 @@ }

function diff( a, b ) {
var result = a.slice();
for ( var i = 0; i < result.length; i++ ) {
for ( var j = 0; j < b.length; j++ ) {
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);
result.splice( i, 1 );
i--;

@@ -1032,21 +1382,10 @@ break;

function fail(message, exception, callback) {
if ( typeof console !== "undefined" && console.error && console.warn ) {
console.error(message);
console.error(exception);
console.warn(callback.toString());
} else if ( window.opera && opera.postError ) {
opera.postError(message, exception, callback.toString);
}
}
function extend(a, b) {
function extend( a, b ) {
for ( var prop in b ) {
if ( b[prop] === undefined ) {
delete a[prop];
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];
a[ prop ] = b[ prop ];
}

@@ -1058,3 +1397,3 @@ }

function addEvent(elem, type, fn) {
function addEvent( elem, type, fn ) {
if ( elem.addEventListener ) {

@@ -1069,9 +1408,9 @@ elem.addEventListener( type, fn, false );

function id(name) {
return !!(typeof document !== "undefined" && document && document.getElementById) &&
function id( name ) {
return !!( typeof document !== "undefined" && document && document.getElementById ) &&
document.getElementById( name );
}
function registerLoggingCallback(key){
return function(callback){
function registerLoggingCallback( key ) {
return function( callback ) {
config[key].push( callback );

@@ -1082,11 +1421,11 @@ };

// Supports deprecated method of completely overwriting logging callbacks
function runLoggingCallbacks(key, scope, args) {
function runLoggingCallbacks( key, scope, args ) {
//debugger;
var callbacks;
if ( QUnit.hasOwnProperty(key) ) {
QUnit[key].call(scope, args);
var i, callbacks;
if ( QUnit.hasOwnProperty( key ) ) {
QUnit[ key ].call(scope, args );
} else {
callbacks = config[key];
for( var i = 0; i < callbacks.length; i++ ) {
callbacks[i].call( scope, args );
callbacks = config[ key ];
for ( i = 0; i < callbacks.length; i++ ) {
callbacks[ i ].call( scope, args );
}

@@ -1098,16 +1437,12 @@ }

// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {
QUnit.equiv = (function() {
var innerEquiv; // the real equiv function
var callers = []; // stack to decide between skip/abort functions
var parents = []; // stack to avoiding loops from circular referencing
// 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);
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
return callbacks[ prop ]; // or undefined
}

@@ -1117,152 +1452,159 @@ }

var getProto = Object.getPrototypeOf || function (obj) {
return obj.__proto__;
};
// the real equiv function
var innerEquiv,
// stack to decide between skip/abort functions
callers = [],
// stack to avoiding loops from circular referencing
parents = [],
var callbacks = function () {
getProto = Object.getPrototypeOf || function ( obj ) {
return obj.__proto__;
},
callbacks = (function () {
// for string, boolean, number and null
function useStrictEquality(b, a) {
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;
// for string, boolean, number and null
function useStrictEquality( b, a ) {
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,
return {
"string": useStrictEquality,
"boolean": useStrictEquality,
"number": useStrictEquality,
"null": useStrictEquality,
"undefined": useStrictEquality,
"nan" : function(b) {
return isNaN(b);
},
"nan": function( b ) {
return isNaN( b );
},
"date" : function(b, a) {
return QUnit.objectType(b) === "date"
&& a.valueOf() === b.valueOf();
},
"date": function( b, a ) {
return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
},
"regexp" : function(b, a) {
return QUnit.objectType(b) === "regexp"
&& a.source === b.source && // the regex itself
a.global === b.global && // and its modifers
// (gmi) ...
a.ignoreCase === b.ignoreCase
&& a.multiline === b.multiline;
},
"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";
},
// - 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, loop;
var len;
"array": function( b, a ) {
var i, j, len, loop;
// b could be an object literal here
if (!(QUnit.objectType(b) === "array")) {
return false;
}
// 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;
}
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
// 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;
}
}
if (!loop && !innerEquiv(a[i], b[i])) {
parents.pop();
return false;
}
}
parents.pop();
return true;
},
parents.pop();
return true;
},
"object" : function(b, a) {
var i, j, loop;
var eq = true; // unless we can proove it
var aProperties = [], bProperties = []; // collection of
// strings
"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;
// 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);
// 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])
loop = true; // don't go down the same path
// twice
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;
}
}
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
}
}
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() );
}
};
}());
// Ensures identical properties name
return eq
&& innerEquiv(aProperties.sort(), bProperties
.sort());
}
};
}();
innerEquiv = function() { // can take multiple arguments
var args = Array.prototype.slice.apply(arguments);
if (args.length < 2) {
var args = [].slice.apply( arguments );
if ( args.length < 2 ) {
return true; // end transition
}
return (function(a, b) {
if (a === b) {
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)) {
} 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

@@ -1274,11 +1616,8 @@ } else {

// apply transition with (1..n) arguments
})(args[0], args[1])
&& arguments.callee.apply(this, args.splice(1,
args.length - 1));
}( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
};
return innerEquiv;
}());
}();
/**

@@ -1296,7 +1635,7 @@ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |

function quote( str ) {
return '"' + str.toString().replace(/"/g, '\\"') + '"';
};
return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
}
function literal( o ) {
return o + '';
};
return o + "";
}
function join( pre, arr, post ) {

@@ -1306,175 +1645,202 @@ var s = jsDump.separator(),

inner = jsDump.indent(1);
if ( arr.join )
arr = arr.join( ',' + s + inner );
if ( !arr )
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 = Array(i);
var i = arr.length, ret = new Array(i);
this.up();
while ( i-- )
while ( i-- ) {
ret[i] = this.parse( arr[i] , undefined , stack);
}
this.down();
return join( '[', ret, ']' );
};
return join( "[", ret, "]" );
}
var reName = /^function (\w+)/;
var reName = /^function (\w+)/,
jsDump = {
parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
stack = stack || [ ];
var inStack, res,
parser = this.parsers[ type || this.typeOf(obj) ];
var jsDump = {
parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
stack = stack || [ ];
var parser = this.parsers[ type || this.typeOf(obj) ];
type = typeof parser;
var inStack = inArray(obj, stack);
if (inStack != -1) {
return 'recursion('+(inStack - stack.length)+')';
}
//else
if (type == 'function') {
stack.push(obj);
var res = parser.call( this, obj, stack );
type = typeof parser;
inStack = inArray( obj, stack );
if ( inStack != -1 ) {
return "recursion(" + (inStack - stack.length) + ")";
}
//else
if ( type == "function" ) {
stack.push( obj );
res = parser.call( this, obj, stack );
stack.pop();
return res;
}
// else
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 {
type = typeof obj;
}
return type;
},
separator:function() {
return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
},
indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
if ( !this.multiline )
return '';
var chr = this.indentChar;
if ( this.HTML )
chr = chr.replace(/\t/g,' ').replace(/ /g,'&nbsp;');
return 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:'[ERROR]', //when no parser is found, shouldn't happen
unknown: '[Unknown]',
'null':'null',
'undefined':'undefined',
'function':function( fn ) {
var ret = 'function',
name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
if ( name )
ret += ' ' + name;
ret += '(';
ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
}
// else
return ( type == "string" ) ? parser : this.parsers.error;
},
array: array,
nodelist: array,
arguments: array,
object:function( map, stack ) {
var ret = [ ];
QUnit.jsDump.up();
for ( var key in map ) {
var val = map[key];
ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));
}
QUnit.jsDump.down();
return join( '{', ret, '}' );
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 {
type = typeof obj;
}
return type;
},
node:function( node ) {
var open = QUnit.jsDump.HTML ? '&lt;' : '<',
close = QUnit.jsDump.HTML ? '&gt;' : '>';
var tag = node.nodeName.toLowerCase(),
ret = open + tag;
for ( var a in QUnit.jsDump.DOMAttrs ) {
var val = node[QUnit.jsDump.DOMAttrs[a]];
if ( val )
ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
separator: function() {
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
},
indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
if ( !this.multiline ) {
return "";
}
return ret + close + open + '/' + tag + close;
var chr = this.indentChar;
if ( this.HTML ) {
chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
}
return new Array( this._depth_ + (extra||0) ).join(chr);
},
functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
var l = fn.length;
if ( !l ) return '';
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: "[ERROR]", //when no parser is found, shouldn"t happen
unknown: "[Unknown]",
"null": "null",
"undefined": "undefined",
"function": function( fn ) {
var ret = "function",
name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE
var args = Array(l);
while ( l-- )
args[l] = String.fromCharCode(97+l);//97 is 'a'
return ' ' + args.join(', ') + ' ';
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();
if ( Object.keys ) {
keys = Object.keys( map );
} else {
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 a, val,
open = QUnit.jsDump.HTML ? "&lt;" : "<",
close = QUnit.jsDump.HTML ? "&gt;" : ">",
tag = node.nodeName.toLowerCase(),
ret = open + tag;
for ( a in QUnit.jsDump.DOMAttrs ) {
val = node[ QUnit.jsDump.DOMAttrs[a] ];
if ( val ) {
ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" );
}
}
return ret + close + open + "/" + tag + close;
},
functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function
var args,
l = fn.length;
if ( !l ) {
return "";
}
args = new Array(l);
while ( l-- ) {
args[l] = String.fromCharCode(97+l);//97 is 'a'
}
return " " + args.join( ", " ) + " ";
},
key: quote, //object calls it internally, the key part of an item in a map
functionCode: "[code]", //function calls it internally, it's the content of the function
attribute: quote, //node calls it internally, it's an html attribute value
string: quote,
date: quote,
regexp: literal, //regex
number: literal,
"boolean": literal
},
key:quote, //object calls it internally, the key part of an item in a map
functionCode:'[code]', //function calls it internally, it's the content of the function
attribute:quote, //node calls it internally, it's an html attribute value
string:quote,
date:quote,
regexp:literal, //regex
number:literal,
'boolean':literal
},
DOMAttrs:{//attributes to dump from nodes, name=>realName
id:'id',
name:'name',
'class':'className'
},
HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
indentChar:' ',//indentation unit
multiline:true //if true, items in a collection, are separated by a \n, else just a space.
};
DOMAttrs: {
//attributes to dump from nodes, name=>realName
id: "id",
name: "name",
"class": "className"
},
HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
indentChar: " ",//indentation unit
multiline: true //if true, items in a collection, are separated by a \n, else just a space.
};
return jsDump;
})();
}());
// from Sizzle.js
function getText( elems ) {
var ret = "", elem;
var i, elem,
ret = "";
for ( var i = 0; elems[i]; i++ ) {
for ( i = 0; elems[i]; i++ ) {
elem = elems[i];

@@ -1493,5 +1859,5 @@

return ret;
};
}
//from jquery.js
// from jquery.js
function inArray( elem, array ) {

@@ -1523,38 +1889,41 @@ if ( array.indexOf ) {

*
* 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( "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() {
function diff(o, n) {
var ns = {};
var os = {};
function diff( o, n ) {
var i,
ns = {},
os = {};
for (var i = 0; i < n.length; i++) {
if (ns[n[i]] == null)
ns[n[i]] = {
for ( i = 0; i < n.length; i++ ) {
if ( ns[ n[i] ] == null ) {
ns[ n[i] ] = {
rows: [],
o: null
};
ns[n[i]].rows.push(i);
}
ns[ n[i] ].rows.push( i );
}
for (var i = 0; i < o.length; i++) {
if (os[o[i]] == null)
os[o[i]] = {
for ( i = 0; i < o.length; i++ ) {
if ( os[ o[i] ] == null ) {
os[ o[i] ] = {
rows: [],
n: null
};
os[o[i]].rows.push(i);
}
os[ o[i] ].rows.push( i );
}
for (var i in ns) {
for ( i in ns ) {
if ( !hasOwn.call( ns, i ) ) {
continue;
}
if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
n[ns[i].rows[0]] = {
text: n[ns[i].rows[0]],
if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && 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]],
o[ os[i].rows[0] ] = {
text: o[ os[i].rows[0] ],
row: ns[i].rows[0]

@@ -1565,11 +1934,12 @@ };

for (var 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],
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],
o[ n[i].row + 1 ] = {
text: o[ n[i].row + 1 ],
row: i + 1

@@ -1580,11 +1950,12 @@ };

for (var 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],
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],
o[ n[i].row - 1 ] = {
text: o[ n[i].row - 1 ],
row: i - 1

@@ -1601,45 +1972,48 @@ };

return function(o, n) {
o = o.replace(/\s+$/, '');
n = n.replace(/\s+$/, '');
var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
return function( o, n ) {
o = o.replace( /\s+$/, "" );
n = n.replace( /\s+$/, "" );
var str = "";
var i, pre,
str = "",
out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
oSpace = o.match(/\s+/g),
nSpace = n.match(/\s+/g);
var oSpace = o.match(/\s+/g);
if (oSpace == null) {
oSpace = [" "];
if ( oSpace == null ) {
oSpace = [ " " ];
}
else {
oSpace.push(" ");
oSpace.push( " " );
}
var nSpace = n.match(/\s+/g);
if (nSpace == null) {
nSpace = [" "];
if ( nSpace == null ) {
nSpace = [ " " ];
}
else {
nSpace.push(" ");
nSpace.push( " " );
}
if (out.n.length == 0) {
for (var i = 0; i < out.o.length; i++) {
str += '<del>' + out.o[i] + oSpace[i] + "</del>";
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>";
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 (var i = 0; i < out.n.length; i++) {
for ( i = 0; i < out.n.length; i++ ) {
if (out.n[i].text == null) {
str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
}
else {
var pre = "";
// `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>";
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>";
}

@@ -1653,4 +2027,10 @@ str += " " + out.n[i].text + nSpace[i] + pre;

};
})();
}());
})(this);
// 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()) ));

@@ -1,8 +0,5 @@

// Underscore.js 1.3.3
// Underscore.js 1.4.2
// http://underscorejs.org
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
// Underscore may be freely distributed under the MIT license.

@@ -27,3 +24,5 @@ (function() {

// Create quick reference variables for speed access to core prototypes.
var slice = ArrayProto.slice,
var push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
unshift = ArrayProto.unshift,

@@ -50,3 +49,7 @@ toString = ObjProto.toString,

// Create a safe reference to the Underscore object for use below.
var _ = function(obj) { return new wrapper(obj); };
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};

@@ -67,3 +70,3 @@ // Export the Underscore object for **Node.js**, with

// Current version.
_.VERSION = '1.3.3';
_.VERSION = '1.4.2';

@@ -82,3 +85,3 @@ // Collection Functions

for (var i = 0, l = obj.length; i < l; i++) {
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}

@@ -103,3 +106,2 @@ } else {

});
if (obj.length === +obj.length) results.length = obj.length;
return results;

@@ -136,7 +138,20 @@ };

if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var reversed = _.toArray(obj).reverse();
if (context && !initial) iterator = _.bind(iterator, context);
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
var length = obj.length;
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
return memo;
};

@@ -171,8 +186,5 @@

_.reject = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
each(obj, function(value, index, list) {
if (!iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
return _.filter(obj, function(value, index, list) {
return !iterator.call(context, value, index, list);
}, context);
};

@@ -184,2 +196,3 @@

_.every = _.all = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true;

@@ -208,5 +221,5 @@ if (obj == null) return result;

// Determine if a given value is included in the array or object using `===`.
// Aliased as `contains`.
_.include = _.contains = function(obj, target) {
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
var found = false;

@@ -225,3 +238,3 @@ if (obj == null) return found;

return _.map(obj, function(value) {
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
return (_.isFunction(method) ? method : value[method]).apply(value, args);
});

@@ -235,5 +248,21 @@ };

// Convenience version of a common use case of `filter`: selecting only objects
// with specific `key:value` pairs.
_.where = function(obj, attrs) {
if (_.isEmpty(attrs)) return [];
return _.filter(obj, function(value) {
for (var key in attrs) {
if (attrs[key] !== value[key]) return false;
}
return true;
});
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See: https://bugs.webkit.org/show_bug.cgi?id=80797
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return -Infinity;

@@ -250,3 +279,5 @@ var result = {computed : -Infinity};

_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj);
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return Infinity;

@@ -263,6 +294,8 @@ var result = {computed : Infinity};

_.shuffle = function(obj) {
var shuffled = [], rand;
each(obj, function(value, index, list) {
rand = Math.floor(Math.random() * (index + 1));
shuffled[index] = shuffled[rand];
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;

@@ -273,26 +306,34 @@ });

// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
return _.isFunction(value) ? value : function(obj){ return obj[value]; };
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, val, context) {
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
_.sortBy = function(obj, value, context) {
var iterator = lookupIterator(value);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value : value,
index : index,
criteria : iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
if (a === void 0) return 1;
if (b === void 0) return -1;
return a < b ? -1 : a > b ? 1 : 0;
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index < right.index ? -1 : 1;
}), 'value');
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = function(obj, val) {
// An internal function used for aggregate "group by" operations.
var group = function(obj, value, context, behavior) {
var result = {};
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
var iterator = lookupIterator(value);
each(obj, function(value, index) {
var key = iterator(value, index);
(result[key] || (result[key] = [])).push(value);
var key = iterator.call(context, value, index, obj);
behavior(result, key, value);
});

@@ -302,10 +343,29 @@ return result;

// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator) {
iterator || (iterator = _.identity);
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = function(obj, value, context) {
return group(obj, value, context, function(result, key, value) {
(_.has(result, key) ? result[key] : (result[key] = [])).push(value);
});
};
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = function(obj, value, context) {
return group(obj, value, context, function(result, key, value) {
if (!_.has(result, key)) result[key] = 0;
result[key]++;
});
};
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) {
iterator = iterator == null ? _.identity : lookupIterator(iterator);
var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
var mid = (low + high) >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}

@@ -317,6 +377,4 @@ return low;

_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (_.isArguments(obj)) return slice.call(obj);
if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
if (!obj) return [];
if (obj.length === +obj.length) return slice.call(obj);
return _.values(obj);

@@ -327,3 +385,4 @@ };

_.size = function(obj) {
return _.isArray(obj) ? obj.length : _.keys(obj).length;
if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};

@@ -338,6 +397,7 @@

_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};
// Returns everything but the last entry of the array. Especcialy useful on
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in

@@ -353,2 +413,3 @@ // the array, excluding the last N. The **guard** check allows it to work with

_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n != null) && !guard) {

@@ -361,8 +422,8 @@ return slice.call(array, Math.max(array.length - n, 0));

// Returns everything but the first entry of the array. Aliased as `tail`.
// Especially useful on the arguments object. Passing an **index** will return
// the rest of the values in the array from that index onward. The **guard**
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = function(array, index, guard) {
return slice.call(array, (index == null) || guard ? 1 : index);
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n);
};

@@ -375,9 +436,17 @@

// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
each(input, function(value) {
if (_.isArray(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
}
});
return output;
};
// Return a completely flattened version of an array.
_.flatten = function(array, shallow) {
return _.reduce(array, function(memo, value) {
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
memo[memo.length] = value;
return memo;
}, []);
return flatten(array, shallow, []);
};

@@ -393,14 +462,12 @@

// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator) {
var initial = iterator ? _.map(array, iterator) : array;
_.uniq = _.unique = function(array, isSorted, iterator, context) {
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
// The `isSorted` flag is irrelevant if the array only contains two elements.
if (array.length < 3) isSorted = true;
_.reduce(initial, function (memo, value, index) {
if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
memo.push(value);
var seen = [];
each(initial, function(value, index) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
}
return memo;
}, []);
});
return results;

@@ -412,8 +479,8 @@ };

_.union = function() {
return _.uniq(_.flatten(arguments, true));
return _.uniq(concat.apply(ArrayProto, arguments));
};
// Produce an array that contains every item shared between all the
// passed-in arrays. (Aliased as "intersect" for back-compat.)
_.intersection = _.intersect = function(array) {
// passed-in arrays.
_.intersection = function(array) {
var rest = slice.call(arguments, 1);

@@ -430,4 +497,4 @@ return _.filter(_.uniq(array), function(item) {

_.difference = function(array) {
var rest = _.flatten(slice.call(arguments, 1), true);
return _.filter(array, function(value){ return !_.include(rest, value); });
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};

@@ -441,6 +508,24 @@

var results = new Array(length);
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
for (var i = 0; i < length; i++) {
results[i] = _.pluck(args, "" + i);
}
return results;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, l = list.length; i < l; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),

@@ -454,9 +539,13 @@ // we need this function. Return the position of the first occurrence of an

if (array == null) return -1;
var i, l;
var i = 0, l = array.length;
if (isSorted) {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < l; i++) if (array[i] === item) return i;
return -1;

@@ -466,7 +555,10 @@ };

// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item) {
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
var i = array.length;
while (i--) if (i in array && array[i] === item) return i;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
while (i--) if (array[i] === item) return i;
return -1;

@@ -557,19 +649,21 @@ };

_.throttle = function(func, wait) {
var context, args, timeout, throttling, more, result;
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
var context, args, timeout, result;
var previous = 0;
var later = function() {
previous = new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
context = this; args = arguments;
var later = function() {
timeout = null;
if (more) func.apply(context, args);
whenDone();
};
if (!timeout) timeout = setTimeout(later, wait);
if (throttling) {
more = true;
} else {
var now = new Date;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
whenDone();
throttling = true;
return result;

@@ -584,3 +678,3 @@ };

_.debounce = function(func, wait, immediate) {
var timeout;
var timeout, result;
return function() {

@@ -590,7 +684,9 @@ var context = this, args = arguments;

timeout = null;
if (!immediate) func.apply(context, args);
if (!immediate) result = func.apply(context, args);
};
if (immediate && !timeout) func.apply(context, args);
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(context, args);
return result;
};

@@ -606,3 +702,5 @@ };

ran = true;
return memo = func.apply(this, arguments);
memo = func.apply(this, arguments);
func = null;
return memo;
};

@@ -616,3 +714,4 @@ };

return function() {
var args = [func].concat(slice.call(arguments, 0));
var args = [func];
push.apply(args, arguments);
return wrapper.apply(this, args);

@@ -639,3 +738,5 @@ };

return function() {
if (--times < 1) { return func.apply(this, arguments); }
if (--times < 1) {
return func.apply(this, arguments);
}
};

@@ -658,5 +759,21 @@ };

_.values = function(obj) {
return _.map(obj, _.identity);
var values = [];
for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
return values;
};
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
var pairs = [];
for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
return result;
};
// Return a sorted list of the function names available on the object.

@@ -684,9 +801,20 @@ // Aliased as `methods`

_.pick = function(obj) {
var result = {};
each(_.flatten(slice.call(arguments, 1)), function(key) {
if (key in obj) result[key] = obj[key];
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
each(keys, function(key) {
if (key in obj) copy[key] = obj[key];
});
return result;
return copy;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
for (var key in obj) {
if (!_.contains(keys, key)) copy[key] = obj[key];
}
return copy;
};
// Fill in a given object with default properties.

@@ -716,4 +844,4 @@ _.defaults = function(obj) {

// Internal recursive comparison function.
function eq(a, b, stack) {
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.

@@ -725,7 +853,4 @@ // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.

// Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
if (b._chain) b = b._wrapped;
// Invoke a custom `isEqual` method if one is provided.
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.

@@ -760,10 +885,11 @@ var className = toString.call(a);

// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = stack.length;
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (stack[length] == a) return true;
if (aStack[length] == a) return bStack[length] == b;
}
// Add the first object to the stack of traversed objects.
stack.push(a);
aStack.push(a);
bStack.push(b);
var size = 0, result = true;

@@ -778,9 +904,13 @@ // Recursively compare objects and arrays.

while (size--) {
// Ensure commutative equality for sparse arrays.
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
}
}
} else {
// Objects with different constructors are not equivalent.
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
_.isFunction(bCtor) && (bCtor instanceof bCtor))) {
return false;
}
// Deep compare objects.

@@ -792,3 +922,3 @@ for (var key in a) {

// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}

@@ -805,9 +935,10 @@ }

// Remove the first object from the stack of traversed objects.
stack.pop();
aStack.pop();
bStack.pop();
return result;
}
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, []);
return eq(a, b, [], []);
};

@@ -826,3 +957,3 @@

_.isElement = function(obj) {
return !!(obj && obj.nodeType == 1);
return !!(obj && obj.nodeType === 1);
};

@@ -841,6 +972,11 @@

// Is a given variable an arguments object?
_.isArguments = function(obj) {
return toString.call(obj) == '[object Arguments]';
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {

@@ -852,17 +988,9 @@ _.isArguments = function(obj) {

// Is a given value a function?
_.isFunction = function(obj) {
return toString.call(obj) == '[object Function]';
};
// Optimize `isFunction` if appropriate.
if (typeof (/./) !== 'function') {
_.isFunction = function(obj) {
return typeof obj === 'function';
};
}
// Is a given value a string?
_.isString = function(obj) {
return toString.call(obj) == '[object String]';
};
// Is a given value a number?
_.isNumber = function(obj) {
return toString.call(obj) == '[object Number]';
};
// Is a given object a finite number?

@@ -873,6 +1001,5 @@ _.isFinite = function(obj) {

// Is the given value `NaN`?
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
// `NaN` is the only value for which `===` is not reflexive.
return obj !== obj;
return _.isNumber(obj) && obj != +obj;
};

@@ -885,12 +1012,2 @@

// Is a given value a date?
_.isDate = function(obj) {
return toString.call(obj) == '[object Date]';
};
// Is the given value a regular expression?
_.isRegExp = function(obj) {
return toString.call(obj) == '[object RegExp]';
};
// Is a given value equal to null?

@@ -906,3 +1023,4 @@ _.isNull = function(obj) {

// Has own property?
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {

@@ -928,11 +1046,44 @@ return hasOwnProperty.call(obj, key);

// Run a function **n** times.
_.times = function (n, iterator, context) {
_.times = function(n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
};
// Escape a string for HTML interpolation.
_.escape = function(string) {
return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + (0 | Math.random() * (max - min + 1));
};
// List of HTML entities for escaping.
var entityMap = {
escape: {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'/': '&#x2F;'
}
};
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
};
// Functions for escaping and unescaping strings to/from HTML interpolation.
_.each(['escape', 'unescape'], function(method) {
_[method] = function(string) {
if (string == null) return '';
return ('' + string).replace(entityRegexes[method], function(match) {
return entityMap[method][match];
});
};
});
// If the value of the named property is a function then invoke it;

@@ -946,7 +1097,11 @@ // otherwise, return it.

// Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well.
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name){
addToWrapper(name, _[name] = obj[name]);
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});

@@ -974,3 +1129,3 @@ };

// guaranteed not to match.
var noMatch = /.^/;
var noMatch = /(.)^/;

@@ -980,23 +1135,13 @@ // Certain characters need to be escaped so that they can be put into a

var escapes = {
'\\': '\\',
"'": "'",
'r': '\r',
'n': '\n',
't': '\t',
'u2028': '\u2028',
'u2029': '\u2029'
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
for (var p in escapes) escapes[escapes[p]] = p;
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
// Within an interpolation, evaluation, or escaping, remove HTML escaping
// that had been previously added.
var unescape = function(code) {
return code.replace(unescaper, function(match, escape) {
return escapes[escape];
});
};
// JavaScript micro-templating, similar to John Resig's implementation.

@@ -1006,29 +1151,39 @@ // Underscore templating handles arbitrary delimiters, preserves whitespace,

_.template = function(text, data, settings) {
settings = _.defaults(settings || {}, _.templateSettings);
settings = _.defaults({}, settings, _.templateSettings);
// Compile the template source, taking care to escape characters that
// cannot be included in a string literal and then unescape them in code
// blocks.
var source = "__p+='" + text
.replace(escaper, function(match) {
return '\\' + escapes[match];
})
.replace(settings.escape || noMatch, function(match, code) {
return "'+\n_.escape(" + unescape(code) + ")+\n'";
})
.replace(settings.interpolate || noMatch, function(match, code) {
return "'+\n(" + unescape(code) + ")+\n'";
})
.replace(settings.evaluate || noMatch, function(match, code) {
return "';\n" + unescape(code) + "\n;__p+='";
}) + "';\n";
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
source +=
escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" :
interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" :
evaluate ? "';\n" + evaluate + "\n__p+='" : '';
index = offset + match.length;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __p='';" +
"var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
var render = new Function(settings.variable || 'obj', '_', source);
try {
var render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);

@@ -1039,6 +1194,4 @@ var template = function(data) {

// Provide the compiled function source as a convenience for build time
// precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
source + '}';
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';

@@ -1053,27 +1206,13 @@ return template;

// The OOP Wrapper
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
var wrapper = function(obj) { this._wrapped = obj; };
// Expose `wrapper.prototype` as `_.prototype`
_.prototype = wrapper.prototype;
// Helper function to continue chaining intermediate results.
var result = function(obj, chain) {
return chain ? _(obj).chain() : obj;
var result = function(obj) {
return this._chain ? _(obj).chain() : obj;
};
// A method to easily add functions to the OOP wrapper.
var addToWrapper = function(name, func) {
wrapper.prototype[name] = function() {
var args = slice.call(arguments);
unshift.call(args, this._wrapped);
return result(func.apply(_, args), this._chain);
};
};
// Add all of the Underscore functions to the wrapper object.

@@ -1085,8 +1224,7 @@ _.mixin(_);

var method = ArrayProto[name];
wrapper.prototype[name] = function() {
var wrapped = this._wrapped;
method.apply(wrapped, arguments);
var length = wrapped.length;
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
return result(wrapped, this._chain);
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};

@@ -1098,18 +1236,22 @@ });

var method = ArrayProto[name];
wrapper.prototype[name] = function() {
return result(method.apply(this._wrapped, arguments), this._chain);
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
// Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function() {
this._chain = true;
return this;
};
_.extend(_.prototype, {
// Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function() {
return this._wrapped;
};
// Start chaining a wrapped Underscore object.
chain: function() {
this._chain = true;
return this;
},
// Extracts the result from a wrapped and chained object.
value: function() {
return this._wrapped;
}
});
}).call(this);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc