Socket
Socket
Sign inDemoInstall

underscore.string

Package Overview
Dependencies
0
Maintainers
3
Versions
32
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.1.1 to 2.3.0

2

dist/underscore.string.min.js

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

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

@@ -1,10 +0,9 @@

// Underscore.string
// (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
// Underscore.strings is freely distributable under the terms of the MIT license.
// Documentation: https://github.com/epeli/underscore.string
// Some code is borrowed from MooTools and Alexandru Marasteanu.
// Underscore.string
// (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
// Underscore.string is freely distributable under the terms of the MIT license.
// Documentation: https://github.com/epeli/underscore.string
// Some code is borrowed from MooTools and Alexandru Marasteanu.
// Version '2.3.0'
// Version 2.1.1
(function(root){
!function(root, String){
'use strict';

@@ -19,22 +18,35 @@

var parseNumber = function(source) { return source * 1 || 0; };
var strRepeat = function(str, qty, separator){
// ~~var — is the fastest available way to convert anything to Integer in javascript.
// We'll use it extensively in this lib.
str += ''; qty = ~~qty;
for (var repeat = []; qty > 0; repeat[--qty] = str) {}
return repeat.join(separator == null ? '' : separator);
var strRepeat = function(str, qty){
if (qty < 1) return '';
var result = '';
while (qty > 0) {
if (qty & 1) result += str;
qty >>= 1, str += str;
}
return result;
};
var slice = function(a){
return Array.prototype.slice.call(a);
var slice = [].slice;
var defaultToWhiteSpace = function(characters) {
if (characters == null)
return '\\s';
else if (characters.source)
return characters.source;
else
return '[' + _s.escapeRegExp(characters) + ']';
};
var defaultToWhiteSpace = function(characters){
if (characters != null) {
return '[' + _s.escapeRegExp(''+characters) + ']';
}
return '\\s';
var escapeChars = {
lt: '<',
gt: '>',
quot: '"',
apos: "'",
amp: '&'
};
var reversedEscapeChars = {};
for(var key in escapeChars){ reversedEscapeChars[escapeChars[key]] = key; }
// sprintf() for JavaScript 0.7-beta1

@@ -168,5 +180,6 @@ // http://www.diveintojavascript.com/projects/javascript-sprintf

VERSION: '2.1.1',
VERSION: '2.3.0',
isBlank: function(str){
if (str == null) str = '';
return (/^\s*$/).test(str);

@@ -176,103 +189,127 @@ },

stripTags: function(str){
return (''+str).replace(/<\/?[^>]+>/ig, '');
if (str == null) return '';
return String(str).replace(/<\/?[^>]+>/g, '');
},
capitalize : function(str) {
str += '';
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
capitalize : function(str){
str = str == null ? '' : String(str);
return str.charAt(0).toUpperCase() + str.slice(1);
},
chop: function(str, step){
str = str+'';
step = ~~step || str.length;
var arr = [];
for (var i = 0; i < str.length;) {
arr.push(str.slice(i,i + step));
i = i + step;
}
return arr;
if (str == null) return [];
str = String(str);
step = ~~step;
return step > 0 ? str.match(new RegExp('.{1,' + step + '}', 'g')) : [str];
},
clean: function(str){
return _s.strip((''+str).replace(/\s+/g, ' '));
return _s.strip(str).replace(/\s+/g, ' ');
},
count: function(str, substr){
str += ''; substr += '';
return str.split(substr).length - 1;
if (str == null || substr == null) return 0;
return String(str).split(substr).length - 1;
},
chars: function(str) {
return (''+str).split('');
if (str == null) return [];
return String(str).split('');
},
swapCase: function(str) {
if (str == null) return '';
return String(str).replace(/\S/g, function(c){
return c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase();
});
},
escapeHTML: function(str) {
return (''+str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/"/g, '&quot;').replace(/'/g, "&apos;");
if (str == null) return '';
return String(str).replace(/[&<>"']/g, function(m){ return '&' + reversedEscapeChars[m] + ';'; });
},
unescapeHTML: function(str) {
return (''+str).replace(/&lt;/g, '<').replace(/&gt;/g, '>')
.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, '&');
if (str == null) return '';
return String(str).replace(/\&([^;]+);/g, function(entity, entityCode){
var match;
if (entityCode in escapeChars) {
return escapeChars[entityCode];
} else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) {
return String.fromCharCode(parseInt(match[1], 16));
} else if (match = entityCode.match(/^#(\d+)$/)) {
return String.fromCharCode(~~match[1]);
} else {
return entity;
}
});
},
escapeRegExp: function(str){
// From MooTools core 1.2.4
return str.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
if (str == null) return '';
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
},
insert: function(str, i, substr){
var arr = (''+str).split('');
arr.splice(~~i, 0, ''+substr);
splice: function(str, i, howmany, substr){
var arr = _s.chars(str);
arr.splice(~~i, ~~howmany, substr);
return arr.join('');
},
insert: function(str, i, substr){
return _s.splice(str, i, 0, substr);
},
include: function(str, needle){
return (''+str).indexOf(needle) !== -1;
if (needle === '') return true;
if (str == null) return false;
return String(str).indexOf(needle) !== -1;
},
join: function(sep) {
var args = slice(arguments);
return args.join(args.shift());
join: function() {
var args = slice.call(arguments),
separator = args.shift();
if (separator == null) separator = '';
return args.join(separator);
},
lines: function(str) {
return (''+str).split("\n");
if (str == null) return [];
return String(str).split("\n");
},
reverse: function(str){
return Array.prototype.reverse.apply(String(str).split('')).join('');
return _s.chars(str).reverse().join('');
},
splice: function(str, i, howmany, substr){
var arr = (''+str).split('');
arr.splice(~~i, ~~howmany, substr);
return arr.join('');
},
startsWith: function(str, starts){
str += ''; starts += '';
return str.length >= starts.length && str.substring(0, starts.length) === starts;
if (starts === '') return true;
if (str == null || starts == null) return false;
str = String(str); starts = String(starts);
return str.length >= starts.length && str.slice(0, starts.length) === starts;
},
endsWith: function(str, ends){
str += ''; ends += '';
return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
if (ends === '') return true;
if (str == null || ends == null) return false;
str = String(str); ends = String(ends);
return str.length >= ends.length && str.slice(str.length - ends.length) === ends;
},
succ: function(str){
str += '';
var arr = str.split('');
arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1));
return arr.join('');
if (str == null) return '';
str = String(str);
return str.slice(0, -1) + String.fromCharCode(str.charCodeAt(str.length-1) + 1);
},
titleize: function(str){
return (''+str).replace(/\b./g, function(ch){ return ch.toUpperCase(); });
if (str == null) return '';
return String(str).replace(/(?:^|\s)\S/g, function(c){ return c.toUpperCase(); });
},
camelize: function(str){
return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
return chr ? chr.toUpperCase() : '';
});
return _s.trim(str).replace(/[-_\s]+(.)?/g, function(match, c){ return c.toUpperCase(); });
},

@@ -285,40 +322,37 @@

dasherize: function(str){
return _s.trim(str).replace(/[_\s]+/g, '-').replace(/([A-Z])/g, '-$1').replace(/-+/g, '-').toLowerCase();
return _s.trim(str).replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase();
},
classify: function(str){
return _s.titleize(str.replace(/_/g, ' ')).replace(/\s/g, '')
return _s.titleize(String(str).replace(/_/g, ' ')).replace(/\s/g, '');
},
humanize: function(str){
return _s.capitalize(this.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
return _s.capitalize(_s.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
},
trim: function(str, characters){
str += '';
if (!characters && nativeTrim) {
return nativeTrim.call(str);
}
if (str == null) return '';
if (!characters && nativeTrim) return nativeTrim.call(str);
characters = defaultToWhiteSpace(characters);
return str.replace(new RegExp('\^' + characters + '+|' + characters + '+$', 'g'), '');
return String(str).replace(new RegExp('\^' + characters + '+|' + characters + '+$', 'g'), '');
},
ltrim: function(str, characters){
if (!characters && nativeTrimLeft) {
return nativeTrimLeft.call(str);
}
if (str == null) return '';
if (!characters && nativeTrimLeft) return nativeTrimLeft.call(str);
characters = defaultToWhiteSpace(characters);
return (''+str).replace(new RegExp('\^' + characters + '+', 'g'), '');
return String(str).replace(new RegExp('^' + characters + '+'), '');
},
rtrim: function(str, characters){
if (!characters && nativeTrimRight) {
return nativeTrimRight.call(str);
}
if (str == null) return '';
if (!characters && nativeTrimRight) return nativeTrimRight.call(str);
characters = defaultToWhiteSpace(characters);
return (''+str).replace(new RegExp(characters + '+$', 'g'), '');
return String(str).replace(new RegExp(characters + '+$'), '');
},
truncate: function(str, length, truncateStr){
str += ''; truncateStr = truncateStr || '...';
if (str == null) return '';
str = String(str); truncateStr = truncateStr || '...';
length = ~~length;

@@ -331,26 +365,25 @@ return str.length > length ? str.slice(0, length) + truncateStr : str;

* prune extra chars, never leaving a half-chopped word.
* @author github.com/sergiokas
* @author github.com/rwz
*/
prune: function(str, length, pruneStr){
str += ''; length = ~~length;
pruneStr = pruneStr != null ? ''+pruneStr : '...';
var pruned, borderChar, template = str.replace(/\W/g, function(ch){
return (ch.toUpperCase() !== ch.toLowerCase()) ? 'A' : ' ';
});
borderChar = template.charAt(length);
pruned = template.slice(0, length);
// Check if we're in the middle of a word
if (borderChar && borderChar.match(/\S/))
pruned = pruned.replace(/\s\S+$/, '');
pruned = _s.rtrim(pruned);
return (pruned+pruneStr).length > str.length ? str : str.substring(0, pruned.length)+pruneStr;
if (str == null) return '';
str = String(str); length = ~~length;
pruneStr = pruneStr != null ? String(pruneStr) : '...';
if (str.length <= length) return str;
var tmpl = function(c){ return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; },
template = str.slice(0, length+1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA'
if (template.slice(template.length-2).match(/\w\w/))
template = template.replace(/\s*\S+$/, '');
else
template = _s.rtrim(template.slice(0, template.length-1));
return (template+pruneStr).length > str.length ? str : str.slice(0, template.length)+pruneStr;
},
words: function(str, delimiter) {
if (_s.isBlank(str)) return [];
return _s.trim(str, delimiter).split(delimiter || /\s+/);

@@ -360,34 +393,24 @@ },

pad: function(str, length, padStr, type) {
str += '';
var padding = '', padlen = 0;
str = str == null ? '' : String(str);
length = ~~length;
length = ~~length;
if (!padStr) {
var padlen = 0;
if (!padStr)
padStr = ' ';
} else if (padStr.length > 1) {
else if (padStr.length > 1)
padStr = padStr.charAt(0);
}
switch(type) {
case 'right':
padlen = (length - str.length);
padding = strRepeat(padStr, padlen);
str = str+padding;
break;
padlen = length - str.length;
return str + strRepeat(padStr, padlen);
case 'both':
padlen = (length - str.length);
padding = {
'left' : strRepeat(padStr, Math.ceil(padlen/2)),
'right': strRepeat(padStr, Math.floor(padlen/2))
};
str = padding.left+str+padding.right;
break;
padlen = length - str.length;
return strRepeat(padStr, Math.ceil(padlen/2)) + str
+ strRepeat(padStr, Math.floor(padlen/2));
default: // 'left'
padlen = (length - str.length);
padding = strRepeat(padStr, padlen);;
str = padding+str;
padlen = length - str.length;
return strRepeat(padStr, padlen) + str;
}
return str;
},

@@ -415,59 +438,87 @@

toNumber: function(str, decimals) {
if (str == null || str == '') return 0;
str = String(str);
var num = parseNumber(parseNumber(str).toFixed(~~decimals));
return num === 0 && ''+str !== '0' ? Number.NaN : num;
return num === 0 && !str.match(/^0+$/) ? Number.NaN : num;
},
numberFormat : function(number, dec, dsep, tsep) {
if (isNaN(number) || number == null) return '';
number = number.toFixed(~~dec);
tsep = tsep || ',';
var parts = number.split('.'), fnums = parts[0],
decimals = parts[1] ? (dsep || '.') + parts[1] : '';
return fnums.replace(/(\d)(?=(?:\d{3})+$)/g, '$1' + tsep) + decimals;
},
strRight: function(str, sep){
str += ''; sep = sep != null ? ''+sep : sep;
var pos = (!sep) ? -1 : str.indexOf(sep);
return (pos != -1) ? str.slice(pos+sep.length, str.length) : str;
if (str == null) return '';
str = String(str); sep = sep != null ? String(sep) : sep;
var pos = !sep ? -1 : str.indexOf(sep);
return ~pos ? str.slice(pos+sep.length, str.length) : str;
},
strRightBack: function(str, sep){
str += ''; sep = sep != null ? ''+sep : sep;
var pos = (!sep) ? -1 : str.lastIndexOf(sep);
return (pos != -1) ? str.slice(pos+sep.length, str.length) : str;
if (str == null) return '';
str = String(str); sep = sep != null ? String(sep) : sep;
var pos = !sep ? -1 : str.lastIndexOf(sep);
return ~pos ? str.slice(pos+sep.length, str.length) : str;
},
strLeft: function(str, sep){
str += ''; sep = sep != null ? ''+sep : sep;
var pos = (!sep) ? -1 : str.indexOf(sep);
return (pos != -1) ? str.slice(0, pos) : str;
if (str == null) return '';
str = String(str); sep = sep != null ? String(sep) : sep;
var pos = !sep ? -1 : str.indexOf(sep);
return ~pos ? str.slice(0, pos) : str;
},
strLeftBack: function(str, sep){
if (str == null) return '';
str += ''; sep = sep != null ? ''+sep : sep;
var pos = str.lastIndexOf(sep);
return (pos != -1) ? str.slice(0, pos) : str;
return ~pos ? str.slice(0, pos) : str;
},
toSentence: function(array, separator, lastSeparator) {
separator || (separator = ', ');
lastSeparator || (lastSeparator = ' and ');
var length = array.length, str = '';
toSentence: function(array, separator, lastSeparator, serial) {
separator = separator || ', '
lastSeparator = lastSeparator || ' and '
var a = array.slice(), lastMember = a.pop();
for (var i = 0; i < length; i++) {
str += array[i];
if (i === (length - 2)) { str += lastSeparator; }
else if (i < (length - 1)) { str += separator; }
}
if (array.length > 2 && serial) lastSeparator = _s.rtrim(separator) + lastSeparator;
return str;
return a.length ? a.join(separator) + lastSeparator + lastMember : lastMember;
},
toSentenceSerial: function() {
var args = slice.call(arguments);
args[3] = true;
return _s.toSentence.apply(_s, args);
},
slugify: function(str) {
var from = "ąàáäâãćęèéëêìíïîłńòóöôõùúüûñçżź·/_:;",
to = "aaaaaaceeeeeiiiilnooooouuuunczz",
if (str == null) return '';
var from = "ąàáäâãåæćęèéëêìíïîłńòóöôõøùúüûñçżź",
to = "aaaaaaaaceeeeeiiiilnoooooouuuunczz",
regex = new RegExp(defaultToWhiteSpace(from), 'g');
str = (''+str).toLowerCase();
str = str.replace(regex, function(ch){
var index = from.indexOf(ch);
str = String(str).toLowerCase().replace(regex, function(c){
var index = from.indexOf(c);
return to.charAt(index) || '-';
});
return _s.trim(str.replace(/[^\w\s-]/g, '').replace(/[-\s]+/g, '-'), '-');
return _s.dasherize(str.replace(/[^\w\s-]/g, ''));
},
surround: function(str, wrapper) {
return [wrapper, str, wrapper].join('');
},
quote: function(str) {
return _s.surround(str, '"');
},
exports: function() {

@@ -477,3 +528,3 @@ var result = {};

for (var prop in this) {
if (!this.hasOwnProperty(prop) || prop == 'include' || prop == 'contains' || prop == 'reverse') continue;
if (!this.hasOwnProperty(prop) || prop.match(/^(?:include|contains|reverse)$/)) continue;
result[prop] = this[prop];

@@ -484,5 +535,41 @@ }

},
repeat: strRepeat
repeat: function(str, qty, separator){
if (str == null) return '';
qty = ~~qty;
// using faster implementation if separator is not needed;
if (separator == null) return strRepeat(String(str), qty);
// this one is about 300x slower in Google Chrome
for (var repeat = []; qty > 0; repeat[--qty] = str) {}
return repeat.join(separator);
},
levenshtein: function(str1, str2) {
if (str1 == null && str2 == null) return 0;
if (str1 == null) return String(str2).length;
if (str2 == null) return String(str1).length;
str1 = String(str1); str2 = String(str2);
var current = [], prev, value;
for (var i = 0; i <= str2.length; i++)
for (var j = 0; j <= str1.length; j++) {
if (i && j)
if (str1.charAt(j - 1) === str2.charAt(i - 1))
value = prev;
else
value = Math.min(current[j], current[j - 1], prev) + 1;
else
value = i + j;
prev = current[j];
current[j] = value;
}
return current.pop();
}
};

@@ -499,2 +586,3 @@

_s.contains = _s.include;
_s.q = _s.quote;

@@ -511,20 +599,13 @@ // CommonJS module is defined

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

@@ -9,2 +9,3 @@ "homepage": "http://epeli.github.com/underscore.string/",

"Edward Tsech <edtsech@gmail.com>",
"Pavel Pravosud <pavel@pravosud.com> (<https://github.com/rwz>)",
"Sasha Koss <kossnocorp@gmail.com> (http://koss.nocorp.me/)",

@@ -14,4 +15,3 @@ "Vladimir Dronnikov <dronnikov@gmail.com>",

"Paul Chavard <paul@chavard.net> (<http://tchak.net>)",
"Ed Finkler <coj@funkatron.com> (<http://funkatron.com>)",
"Pavel Pravosud <rwz@duckroll.ru>"
"Ed Finkler <coj@funkatron.com> (<http://funkatron.com>)"
],

@@ -18,0 +18,0 @@ "keywords": [

@@ -18,3 +18,4 @@ function waitFor(test, complete, timeout) {

var page = new WebPage()
var fs = require('fs'), page = require('webpage').create();
var url = 'file://localhost' + fs.workingDirectory + '/' + phantom.args[0];

@@ -25,3 +26,3 @@ page.onConsoleMessage = function(msg) {

page.open(phantom.args[0], function(status) {
page.open(url, function(status) {
waitFor(function() {

@@ -28,0 +29,0 @@ return page.evaluate(function(){

(function() {
JSLitmus.test('levenshtein', function() {
return [
_.levenshtein('pineapple', 'potato'),
_.levenshtein('seven', 'eight'),
_.levenshtein('the very same string', 'the very same string'),
_.levenshtein('very very very long string', 'something completely different')
];
});
JSLitmus.test('trimNoNative', function() {

@@ -4,0 +14,0 @@ return _.trim(" foobar ", " ");

@@ -6,141 +6,192 @@ $(document).ready(function() {

module("String extensions");
module('String extensions');
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");
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+/');
equals(_("ffoo").trim("f"), "oo");
equals(_("ooff").trim("f"), "oo");
equals(_("ffooff").trim("f"), "oo");
equals(_('ffoo').trim('f'), 'oo');
equals(_('ooff').trim('f'), 'oo');
equals(_('ffooff').trim('f'), 'oo');
equals(_("_-foobar-_").trim("_-"), "foobar");
equals(_('_-foobar-_').trim('_-'), 'foobar');
equals(_("http://foo/").trim("/"), "http://foo");
equals(_("c:\\").trim('\\'), "c:");
equals(_('http://foo/').trim('/'), 'http://foo');
equals(_('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');
});
test("Strings: ltrim", function() {
equals(_(" foo").ltrim(), "foo");
equals(_(" foo").ltrim(), "foo");
equals(_("foo ").ltrim(), "foo ");
equals(_(" foo ").ltrim(), "foo ");
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);
});
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');
equals(_("ffoo").ltrim("f"), "oo");
equals(_("ooff").ltrim("f"), "ooff");
equals(_("ffooff").ltrim("f"), "ooff");
equals(_('ffoo').ltrim('f'), 'oo');
equals(_('ooff').ltrim('f'), 'ooff');
equals(_('ffooff').ltrim('f'), 'ooff');
equals(_("_-foobar-_").ltrim("_-"), "foobar-_");
equals(_('_-foobar-_').ltrim('_-'), 'foobar-_');
equals(_(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");
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');
equals(_("ffoo").rtrim("f"), "ffoo");
equals(_("ooff").rtrim("f"), "oo");
equals(_("ffooff").rtrim("f"), "ffoo");
equals(_('ffoo').rtrim('f'), 'ffoo');
equals(_('ooff').rtrim('f'), 'oo');
equals(_('ffooff').rtrim('f'), 'ffoo');
equals(_("_-foobar-_").rtrim("_-"), "_-foobar");
equals(_('_-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');
});
test("Strings: capitalize", function() {
equals(_("fabio").capitalize(), "Fabio", 'First letter is upper case');
equals(_.capitalize("fabio"), "Fabio", 'First letter is upper case');
equals(_(123).capitalize(), "123", "Non 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');
});
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", "bar"), "foo bar", 'join object oriented');
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');
});
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");
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' );
});
test("Strings: clean", function() {
equals(_(" foo bar ").clean(), "foo bar");
equals(_(123).clean(), "123");
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');
});
test("Strings: sprintf", function() {
test('Strings: sprintf', function() {
// Should be very tested function already. Thanks to
// 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');
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');
});
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');
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');
});
test("Strings: startsWith", function() {
ok(_("foobar").startsWith("foo"), 'foobar starts with foo');
ok(!_("oobar").startsWith("foo"), 'oobar does not start with foo');
test('Strings: startsWith', function() {
ok(_('foobar').startsWith('foo'), 'foobar starts with foo');
ok(!_('oobar').startsWith('foo'), 'oobar does not start with foo');
ok(_(12345).startsWith(123), '12345 starts with 123');
ok(!_(2345).startsWith(123), '2345 does not start with 123');
ok(_('').startsWith(''), 'empty string starts with empty string');
ok(_(null).startsWith(''), 'null starts with empty string');
ok(!_(null).startsWith('foo'), 'null starts with foo');
});
test("Strings: endsWith", function() {
ok(_("foobar").endsWith("bar"), 'foobar ends with bar');
ok(_.endsWith("foobar", "bar"), 'foobar ends with bar');
ok(_.endsWith("00018-0000062.Plone.sdh264.1a7264e6912a91aa4a81b64dc5517df7b8875994.mp4", "mp4"), 'endsWith .mp4');
ok(!_("fooba").endsWith("bar"), 'fooba does not end with bar');
test('Strings: endsWith', function() {
ok(_('foobar').endsWith('bar'), 'foobar ends with bar');
ok(_.endsWith('foobar', 'bar'), 'foobar ends with bar');
ok(_.endsWith('00018-0000062.Plone.sdh264.1a7264e6912a91aa4a81b64dc5517df7b8875994.mp4', 'mp4'), 'endsWith .mp4');
ok(!_('fooba').endsWith('bar'), 'fooba does not end with bar');
ok(_.endsWith(12345, 45), '12345 ends with 45');
ok(!_.endsWith(12345, 6), '12345 does not end with 6');
ok(_('').endsWith(''), 'empty string ends with empty string');
ok(_(null).endsWith(''), 'null ends with empty string');
ok(!_(null).endsWith('foo'), 'null ends with foo');
});
test("Strings: include", function() {
ok(_.str.include("foobar", "bar"), 'foobar includes bar');
ok(!_.str.include("foobar", "buzz"), 'foobar does not includes buzz');
test('Strings: include', function() {
ok(_.str.include('foobar', 'bar'), 'foobar includes bar');
ok(!_.str.include('foobar', 'buzz'), 'foobar does not includes buzz');
ok(_.str.include(12345, 34), '12345 includes 34');
ok(!_.str.contains(12345, 6), '12345 does not includes 6');
ok(!_.str.include('', 34), 'empty string includes 34');
ok(!_.str.include(null, 34), 'null includes 34');
ok(_.str.include(null, ''), 'null includes empty string');
});
test('String: chop', function(){
ok(_('whitespace').chop(2).length === 5, "output ['wh','it','es','pa','ce']");
ok(_('whitespace').chop(3).length === 4, "output ['whi','tes','pac','e']");
ok(_('whitespace').chop()[0].length === 10, "output ['whitespace']");
ok(_(12345).chop(1).length === 5, "output ['1','2','3','4','5']");
ok(_('whitespace').chop(2).length === 5, 'output [wh, it, es, pa, ce]');
ok(_('whitespace').chop(3).length === 4, 'output [whi, tes, pac, e]');
ok(_('whitespace').chop()[0].length === 10, 'output [whitespace]');
ok(_(12345).chop(1).length === 5, 'output [1, 2, 3, 4, 5]');
});
test('String: clean', function(){
equals(_.clean(' foo bar '), 'foo bar');
equals(_.clean(''), '');
equals(_.clean(null), '');
equals(_.clean(1), '1');
});
test('String: count', function(){

@@ -151,2 +202,5 @@ equals(_('Hello world').count('l'), 3);

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);

@@ -159,2 +213,5 @@ equals(_(11345).count(1), 2);

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');

@@ -179,2 +236,6 @@ });

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');

@@ -189,2 +250,5 @@ });

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');

@@ -198,2 +262,5 @@ });

equals(_(' the underscored string method').underscored(), 'the_underscored_string_method');
equals(_('').underscored(), '');
equals(_(null).underscored(), '');
equals(_(undefined).underscored(), '');
equals(_(123).underscored(), '123');

@@ -213,6 +280,27 @@ });

equals(_('foo$bar').dasherize(), 'foo$bar');
equals(_('').dasherize(), '');
equals(_(null).dasherize(), '');
equals(_(undefined).dasherize(), '');
equals(_(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(), '');
});
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');
});
test('String: classify', function(){
equals(_.classify(1), '1');
equals(_('some_class_name').classify(), 'SomeClassName');

@@ -229,2 +317,5 @@ });

equals(_(123).humanize(), '123');
equals(_('').humanize(), '');
equals(_(null).humanize(), '');
equals(_(undefined).humanize(), '');
});

@@ -236,2 +327,5 @@

equals(_('Hello').truncate(10), 'Hello');
equals(_('').truncate(10), '');
equals(_(null).truncate(10), '');
equals(_(undefined).truncate(10), '');
equals(_(1234567890).truncate(5), '12345...');

@@ -253,4 +347,8 @@ });

equals(_('Привет, мир').prune(22), 'Привет, мир');
equals(_('alksjd!!!!!!....').prune(100, ''), 'alksjd!!!!!!....');
equals(_(123).prune(10), '123');
equals(_(123).prune(1,1), '11');
equals(_(123).prune(1, 321), '321');
equals(_('').prune(5), '');
equals(_(null).prune(5), '');
equals(_(undefined).prune(5), '');
});

@@ -265,4 +363,12 @@

ok(!_(0).isBlank());
ok(_('').isBlank());
ok(_(null).isBlank());
ok(_(undefined).isBlank());
});
test('String: escapeRegExp', function(){
equals(_.escapeRegExp(/hello(?=\sworld)/.source), 'hello\\(\\?\\=\\\\sworld\\)', 'with lookahead');
equals(_.escapeRegExp(/hello(?!\shell)/.source), 'hello\\(\\?\\!\\\\shell\\)', 'with negative lookahead');
});
test('String: escapeHTML', function(){

@@ -273,3 +379,5 @@ equals(_('<div>Blah & "blah" & \'blah\'</div>').escapeHTML(),

equals(_(5).escapeHTML(), '5');
// equals(_(undefined).escapeHTML(), '');
equals(_('').escapeHTML(), '');
equals(_(null).escapeHTML(), '');
equals(_(undefined).escapeHTML(), '');
});

@@ -281,2 +389,14 @@

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');

@@ -287,104 +407,170 @@ // equals(_(undefined).unescapeHTML(), '');

test('String: words', function() {
equals(_("I love you!").words().length, 3);
equals(_(" I love you! ").words().length, 3);
equals(_("I_love_you!").words('_').length, 3);
equals(_("I-love-you!").words(/-/).length, 3);
equals(_(123).words().length, 1);
deepEqual(_('I love you!').words(), ['I', 'love', 'you!']);
deepEqual(_(' I love you! ').words(), ['I', 'love', 'you!']);
deepEqual(_('I_love_you!').words('_'), ['I', 'love', 'you!']);
deepEqual(_('I-love-you!').words(/-/), ['I', 'love', 'you!']);
deepEqual(_(123).words(), ['123'], '123 number has one word "123".');
deepEqual(_(0).words(), ['0'], 'Zero number has one word "0".');
deepEqual(_('').words(), [], 'Empty strings has no words.');
deepEqual(_(' ').words(), [], 'Blank strings has no words.');
deepEqual(_(null).words(), [], 'null has no words.');
deepEqual(_(undefined).words(), [], 'undefined has no words.');
});
test('String: chars', function() {
equals(_("Hello").chars().length, 5);
equals(_('Hello').chars().length, 5);
equals(_(123).chars().length, 3);
equals(_('').chars().length, 0);
equals(_(null).chars().length, 0);
equals(_(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(), '');
});
test('String: lines', function() {
equals(_("Hello\nWorld").lines().length, 2);
equals(_("Hello World").lines().length, 1);
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);
});
test('String: pad', function() {
equals(_("1").pad(8), ' 1');
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(_('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), ' ');
});
test('String: lpad', function() {
equals(_("1").lpad(8), ' 1');
equals(_('1').lpad(8), ' 1');
equals(_(1).lpad(8), ' 1');
equals(_("1").lpad(8, '0'), '00000001');
equals(_("1").lpad(8, '0', 'left'), '00000001');
equals(_('1').lpad(8, '0'), '00000001');
equals(_('1').lpad(8, '0', 'left'), '00000001');
equals(_('').lpad(2), ' ');
equals(_(null).lpad(2), ' ');
equals(_(undefined).lpad(2), ' ');
});
test('String: rpad', function() {
equals(_("1").rpad(8), '1 ');
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(_('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), ' ');
});
test('String: lrpad', function() {
equals(_("1").lrpad(8), ' 1 ');
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(_('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), ' ');
});
test('String: toNumber', function() {
deepEqual(_("not a number").toNumber(), Number.NaN);
equals(_(0).toNumber(), 0);
equals(_("0").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);
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(_(-2).toNumber(), -2);
equals(_('-2').toNumber(), -2);
equals(_('').toNumber(), 0);
equals(_(null).toNumber(), 0);
equals(_(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');
});
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(_(12345).strRight(2), "345");
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');
});
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(_(12345).strRightBack(2), "345");
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');
});
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(_(123454321).strLeft(3), "12");
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');
});
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(_(123454321).strLeftBack(3), "123454");
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');
});

@@ -397,28 +583,66 @@

equals(_(123).stripTags(), '123');
equals(_('').stripTags(), '');
equals(_(null).stripTags(), '');
equals(_(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');
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');
});
test('Strings: toSentenceSerial', function (){
equals(_.toSentenceSerial(['jQuery']), 'jQuery');
equals(_.toSentenceSerial(['jQuery', 'MooTools']), 'jQuery and MooTools');
equals(_.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(_('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(), '');
});
test('Strings: quote', function(){
equals(_.quote('foo'), '"foo"');
equals(_.quote('"foo"'), '""foo""');
equals(_.quote(1), '"1"');
// alias
equals(_.q('foo'), '"foo"');
equals(_.q(''), '""');
equals(_.q(null), '""');
equals(_.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');
});
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('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), '');
});
});

@@ -6,11 +6,13 @@ $(document).ready(function() {

test("arrays: first", function() {
equals(_.first([1,2,3]), 1, 'can pull out the first element of an array');
equals(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');
equals(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first');
equals(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first');
equals(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first');
equal(_.first([1,2,3]), 1, 'can pull out the first element of an array');
equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');
equal(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first');
equal(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first');
equal(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first');
var result = (function(){ return _.first(arguments); })(4, 3, 2, 1);
equals(result, 4, 'works on an arguments object.');
equal(result, 4, 'works on an arguments object.');
result = _.map([[1,2,3],[1,2,3]], _.first);
equals(result.join(','), '1,1', 'works well with _.map');
equal(result.join(','), '1,1', 'works well with _.map');
result = (function() { return _.take([1,2,3], 2); })();
equal(result.join(','), '1,2', 'aliased as take');
});

@@ -20,35 +22,35 @@

var numbers = [1, 2, 3, 4];
equals(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()');
equals(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)');
equals(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index');
equal(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()');
equal(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)');
equal(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index');
var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4);
equals(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object');
equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object');
result = _.map([[1,2,3],[1,2,3]], _.rest);
equals(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map');
equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map');
});
test("arrays: initial", function() {
equals(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()');
equals(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index');
equal(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()');
equal(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index');
var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4);
equals(result.join(", "), "1, 2, 3", 'initial works on arguments object');
equal(result.join(", "), "1, 2, 3", 'initial works on arguments object');
result = _.map([[1,2,3],[1,2,3]], _.initial);
equals(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map');
equal(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map');
});
test("arrays: last", function() {
equals(_.last([1,2,3]), 3, 'can pull out the last element of an array');
equals(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last');
equals(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last');
equals(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last');
equal(_.last([1,2,3]), 3, 'can pull out the last element of an array');
equal(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last');
equal(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last');
equal(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last');
var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4);
equals(result, 4, 'works on an arguments object');
equal(result, 4, 'works on an arguments object');
result = _.map([[1,2,3],[1,2,3]], _.last);
equals(result.join(','), '3,3', 'works well with _.map');
equal(result.join(','), '3,3', 'works well with _.map');
});
test("arrays: compact", function() {
equals(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values');
equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values');
var result = (function(){ return _(arguments).compact().length; })(0, 1, false, 2, false, 3);
equals(result, 3, 'works on an arguments object');
equal(result, 3, 'works on an arguments object');
});

@@ -59,6 +61,6 @@

var list = [1, [2], [3, [[[4]]]]];
equals(JSON.stringify(_.flatten(list)), '[1,2,3,4]', 'can flatten nested arrays');
equals(JSON.stringify(_.flatten(list, true)), '[1,2,3,[[[4]]]]', 'can shallowly flatten nested arrays');
equal(JSON.stringify(_.flatten(list)), '[1,2,3,4]', 'can flatten nested arrays');
equal(JSON.stringify(_.flatten(list, true)), '[1,2,3,[[[4]]]]', 'can shallowly flatten nested arrays');
var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]);
equals(JSON.stringify(result), '[1,2,3,4]', 'works on an arguments object');
equal(JSON.stringify(result), '[1,2,3,4]', 'works on an arguments object');
}

@@ -69,5 +71,5 @@ });

var list = [1, 2, 1, 0, 3, 1, 4];
equals(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object');
equal(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object');
var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4);
equals(result.join(', '), '2, 3, 4', 'works on an arguments object');
equal(result.join(', '), '2, 3, 4', 'works on an arguments object');

@@ -81,17 +83,39 @@ var list = [{one : 1}, {two : 2}];

var list = [1, 2, 1, 3, 1, 4];
equals(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array');
equal(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array');
var list = [1, 1, 1, 2, 2, 3];
equals(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster');
equal(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster');
var list = [{name:'moe'}, {name:'curly'}, {name:'larry'}, {name:'curly'}];
var iterator = function(value) { return value.name; };
equals(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator');
equal(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator');
var iterator = function(value) { return value +1; };
var list = [1, 2, 2, 3, 4, 4];
equals(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array');
equal(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array');
var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4);
equals(result.join(', '), '1, 2, 3, 4', 'works on an arguments object');
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");
}
});

@@ -101,6 +125,6 @@

var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho'];
equals(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays');
equals(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection');
equal(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays');
equal(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection');
var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry');
equals(result.join(''), 'moe', 'works on an arguments object');
equal(result.join(''), 'moe', 'works on an arguments object');
});

@@ -110,6 +134,6 @@

var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]);
equals(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays');
equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays');
var result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]);
equals(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays');
equal(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays');
});

@@ -119,6 +143,6 @@

var result = _.difference([1, 2, 3], [2, 30, 40]);
equals(result.join(' '), '1 3', 'takes the difference of two arrays');
equal(result.join(' '), '1 3', 'takes the difference of two arrays');
var result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]);
equals(result.join(' '), '3 4', 'takes the difference of three arrays');
equal(result.join(' '), '3 4', 'takes the difference of three arrays');
});

@@ -129,3 +153,3 @@

var stooges = _.zip(names, ages, leaders);
equals(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths');
equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths');
});

@@ -136,18 +160,18 @@

numbers.indexOf = null;
equals(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function');
equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function');
var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3);
equals(result, 1, 'works on an arguments object');
equals(_.indexOf(null, 2), -1, 'handles nulls properly');
equal(result, 1, 'works on an arguments object');
equal(_.indexOf(null, 2), -1, 'handles nulls properly');
var numbers = [10, 20, 30, 40, 50], num = 35;
var index = _.indexOf(numbers, num, true);
equals(index, -1, '35 is not in the list');
equal(index, -1, '35 is not in the list');
numbers = [10, 20, 30, 40, 50]; num = 40;
index = _.indexOf(numbers, num, true);
equals(index, 3, '40 is in the list');
equal(index, 3, '40 is in the list');
numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40;
index = _.indexOf(numbers, num, true);
equals(index, 1, '40 is in the list');
equal(index, 1, '40 is in the list');
});

@@ -158,20 +182,20 @@

numbers.lastIndexOf = null;
equals(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
equals(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0);
equals(result, 5, 'works on an arguments object');
equals(_.indexOf(null, 2), -1, 'handles nulls properly');
equal(result, 5, 'works on an arguments object');
equal(_.indexOf(null, 2), -1, 'handles nulls properly');
});
test("arrays: range", function() {
equals(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array');
equals(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1');
equals(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a &amp; b, a&lt;b generates an array of elements a,a+1,a+2,...,b-2,b-1');
equals(_.range(8, 5).join(''), '', 'range with two arguments a &amp; b, b&lt;a generates an empty array');
equals(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a &amp; b &amp; c, c &lt; b-a, a &lt; b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) &lt; c');
equals(_.range(3, 10, 15).join(''), '3', 'range with three arguments a &amp; b &amp; c, c &gt; b-a, a &lt; b generates an array with a single element, equal to a');
equals(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a &amp; b &amp; c, a &gt; b, c &lt; 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b');
equals(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs');
equal(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array');
equal(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1');
equal(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a &amp; b, a&lt;b generates an array of elements a,a+1,a+2,...,b-2,b-1');
equal(_.range(8, 5).join(''), '', 'range with two arguments a &amp; b, b&lt;a generates an empty array');
equal(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a &amp; b &amp; c, c &lt; b-a, a &lt; b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) &lt; c');
equal(_.range(3, 10, 15).join(''), '3', 'range with three arguments a &amp; b &amp; c, c &gt; b-a, a &lt; b generates an array with a single element, equal to a');
equal(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a &amp; b &amp; c, a &gt; b, c &lt; 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b');
equal(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs');
});
});

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

}).value();
equals(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers");
equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers");
});

@@ -45,3 +45,3 @@

}).value();
equals(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers");
equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers");
});

@@ -58,5 +58,5 @@

.value();
equals(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.');
equal(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.');
});
});

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

_.each([1, 2, 3], function(num, i) {
equals(num, i + 1, 'each iterators provide value and iteration count');
equal(num, i + 1, 'each iterators provide value and iteration count');
});

@@ -13,7 +13,7 @@

_.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5});
equals(answers.join(', '), '5, 10, 15', 'context object property accessed');
equal(answers.join(', '), '5, 10, 15', 'context object property accessed');
answers = [];
_.forEach([1, 2, 3], function(num){ answers.push(num); });
equals(answers.join(', '), '1, 2, 3', 'aliased as "forEach"');
equal(answers.join(', '), '1, 2, 3', 'aliased as "forEach"');

@@ -24,3 +24,3 @@ answers = [];

_.each(obj, function(value, key){ answers.push(key); });
equals(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.');
equal(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.');
delete obj.constructor.prototype.four;

@@ -34,3 +34,3 @@

_.each(null, function(){ ++answers; });
equals(answers, 0, 'handles a null properly');
equal(answers, 0, 'handles a null properly');
});

@@ -40,12 +40,12 @@

var doubled = _.map([1, 2, 3], function(num){ return num * 2; });
equals(doubled.join(', '), '2, 4, 6', 'doubled numbers');
equal(doubled.join(', '), '2, 4, 6', 'doubled numbers');
doubled = _.collect([1, 2, 3], function(num){ return num * 2; });
equals(doubled.join(', '), '2, 4, 6', 'aliased as "collect"');
equal(doubled.join(', '), '2, 4, 6', 'aliased as "collect"');
var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3});
equals(tripled.join(', '), '3, 6, 9', 'tripled numbers with context');
equal(tripled.join(', '), '3, 6, 9', 'tripled numbers with context');
var doubled = _([1, 2, 3]).map(function(num){ return num * 2; });
equals(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers');
equal(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers');

@@ -62,3 +62,3 @@ var ids = _.map($('div.underscore-test').children(), function(n){ return n.id; });

var length = _.map(Array(2), function(v) { return v; }).length;
equals(length, 2, "can preserve a sparse array's length");
equal(length, 2, "can preserve a sparse array's length");
});

@@ -68,16 +68,16 @@

var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0);
equals(sum, 6, 'can sum up an array');
equal(sum, 6, 'can sum up an array');
var context = {multiplier : 3};
sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context);
equals(sum, 18, 'can reduce with a context object');
equal(sum, 18, 'can reduce with a context object');
sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0);
equals(sum, 6, 'aliased as "inject"');
equal(sum, 6, 'aliased as "inject"');
sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0);
equals(sum, 6, 'OO-style reduce');
equal(sum, 6, 'OO-style reduce');
var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; });
equals(sum, 6, 'default initial value');
equal(sum, 6, 'default initial value');

@@ -93,3 +93,3 @@ var ifnull;

ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly');
equals(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case');
equal(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case');
raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value');

@@ -100,3 +100,3 @@

sparseArray[2] = -5;
equals(_.reduce(sparseArray, function(a, b){ return a - b; }), 25, 'initially-sparse arrays with no memo');
equal(_.reduce(sparseArray, function(a, b){ return a - b; }), 25, 'initially-sparse arrays with no memo');
});

@@ -106,9 +106,9 @@

var list = _.reduceRight(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, '');
equals(list, 'bazbarfoo', 'can perform right folds');
equal(list, 'bazbarfoo', 'can perform right folds');
var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, '');
equals(list, 'bazbarfoo', 'aliased as "foldr"');
equal(list, 'bazbarfoo', 'aliased as "foldr"');
var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; });
equals(list, 'bazbarfoo', 'default initial value');
equal(list, 'bazbarfoo', 'default initial value');

@@ -125,3 +125,3 @@ var ifnull;

equals(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case');
equal(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case');
raises(function() { _.reduceRight([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value');

@@ -132,3 +132,3 @@

sparseArray[2] = -5;
equals(_.reduceRight(sparseArray, function(a, b){ return a - b; }), -25, 'initially-sparse arrays with no memo');
equal(_.reduceRight(sparseArray, function(a, b){ return a - b; }), -25, 'initially-sparse arrays with no memo');
});

@@ -138,3 +138,3 @@

var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; });
equals(result, 2, 'found the first "2" and broke the loop');
equal(result, 2, 'found the first "2" and broke the loop');
});

@@ -144,6 +144,6 @@

var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
equals(evens.join(', '), '2, 4, 6', 'selected each even number');
equal(evens.join(', '), '2, 4, 6', 'selected each even number');
evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
equals(evens.join(', '), '2, 4, 6', 'aliased as "filter"');
equal(evens.join(', '), '2, 4, 6', 'aliased as "filter"');
});

@@ -153,3 +153,3 @@

var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
equals(odds.join(', '), '1, 3, 5', 'rejected each even number');
equal(odds.join(', '), '1, 3, 5', 'rejected each even number');
});

@@ -163,2 +163,4 @@

ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number');
ok(_.all([1], _.identity) === true, 'cast to boolean - true');
ok(_.all([0], _.identity) === false, 'cast to boolean - false');
ok(_.every([true, true, true], _.identity), 'aliased as "every"');

@@ -177,2 +179,4 @@ });

ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number');
ok(_.any([1], _.identity) === true, 'cast to boolean - true');
ok(_.any([0], _.identity) === false, 'cast to boolean - false');
ok(_.some([false, false, true]), 'aliased as "some"');

@@ -192,4 +196,4 @@ Array.prototype.some = nativeSome;

var result = _.invoke(list, 'sort');
equals(result[0].join(', '), '1, 5, 7', 'first array sorted');
equals(result[1].join(', '), '1, 2, 3', 'second array sorted');
equal(result[0].join(', '), '1, 5, 7', 'first array sorted');
equal(result[1].join(', '), '1, 2, 3', 'second array sorted');
});

@@ -200,4 +204,4 @@

var result = _.invoke(list, Array.prototype.sort);
equals(result[0].join(', '), '1, 5, 7', 'first array sorted');
equals(result[1].join(', '), '1, 2, 3', 'second array sorted');
equal(result[0].join(', '), '1, 5, 7', 'first array sorted');
equal(result[1].join(', '), '1, 2, 3', 'second array sorted');
});

@@ -207,11 +211,13 @@

test('collections: invoke when strings have a call method', function() {
String.prototype.call = function(){return 42;}
String.prototype.call = function() {
return 42;
};
var list = [[5, 1, 7], [3, 2, 1]];
var s = "foo";
equals(s.call(), 42, "call function exists");
equal(s.call(), 42, "call function exists");
var result = _.invoke(list, 'sort');
equals(result[0].join(', '), '1, 5, 7', 'first array sorted');
equals(result[1].join(', '), '1, 2, 3', 'second array sorted');
equal(result[0].join(', '), '1, 5, 7', 'first array sorted');
equal(result[1].join(', '), '1, 2, 3', 'second array sorted');
delete String.prototype.call;
equals(s.call, undefined, "call function removed");
equal(s.call, undefined, "call function removed");
});

@@ -221,23 +227,27 @@

var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}];
equals(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects');
equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects');
});
test('collections: max', function() {
equals(3, _.max([1, 2, 3]), 'can perform a regular Math.max');
equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max');
var neg = _.max([1, 2, 3], function(num){ return -num; });
equals(neg, 1, 'can perform a computation-based max');
equal(neg, 1, 'can perform a computation-based max');
equals(-Infinity, _.max({}), 'Maximum value of an empty object');
equals(-Infinity, _.max([]), 'Maximum value of an empty array');
equal(-Infinity, _.max({}), 'Maximum value of an empty object');
equal(-Infinity, _.max([]), 'Maximum value of an empty array');
});
test('collections: min', function() {
equals(1, _.min([1, 2, 3]), 'can perform a regular Math.min');
equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min');
var neg = _.min([1, 2, 3], function(num){ return -num; });
equals(neg, 3, 'can perform a computation-based min');
equal(neg, 3, 'can perform a computation-based min');
equals(Infinity, _.min({}), 'Minimum value of an empty object');
equals(Infinity, _.min([]), 'Minimum value of an empty array');
equal(Infinity, _.min({}), 'Minimum value of an empty object');
equal(Infinity, _.min([]), 'Minimum value of an empty array');
var now = new Date(9999999999);
var then = new Date(0);
equal(_.min([now, then]), then);
});

@@ -248,3 +258,10 @@

people = _.sortBy(people, function(person){ return person.age; });
equals(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age');
equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age');
var list = [undefined, 4, 1, undefined, 3, 2];
equal(_.sortBy(list, _.identity).join(','), '1,2,3,4,,', 'sortBy with undefined values');
var list = ["one", "two", "three", "four", "five"];
var sorted = _.sortBy(list, 'length');
equal(sorted.join(' '), 'one two four five three', 'sorted by length');
});

@@ -255,9 +272,9 @@

ok('0' in parity && '1' in parity, 'created a group for each value');
equals(parity[0].join(', '), '2, 4, 6', 'put each even number in the right group');
equal(parity[0].join(', '), '2, 4, 6', 'put each even number in the right group');
var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"];
var grouped = _.groupBy(list, 'length');
equals(grouped['3'].join(' '), 'one two six ten');
equals(grouped['4'].join(' '), 'four five nine');
equals(grouped['5'].join(' '), 'three seven eight');
equal(grouped['3'].join(' '), 'one two six ten');
equal(grouped['4'].join(' '), 'four five nine');
equal(grouped['5'].join(' '), 'three seven eight');
});

@@ -268,3 +285,3 @@

var index = _.sortedIndex(numbers, num);
equals(index, 3, '35 should be inserted at index 3');
equal(index, 3, '35 should be inserted at index 3');
});

@@ -274,5 +291,5 @@

var numbers = _.range(10);
var shuffled = _.shuffle(numbers).sort();
notStrictEqual(numbers, shuffled, 'original object is unmodified');
equals(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle');
var shuffled = _.shuffle(numbers).sort();
notStrictEqual(numbers, shuffled, 'original object is unmodified');
equal(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle');
});

@@ -285,12 +302,21 @@

ok(_.toArray(a) !== a, 'array is cloned');
equals(_.toArray(a).join(', '), '1, 2, 3', 'cloned array contains same elements');
equal(_.toArray(a).join(', '), '1, 2, 3', 'cloned array contains same elements');
var numbers = _.toArray({one : 1, two : 2, three : 3});
equals(numbers.join(', '), '1, 2, 3', 'object flattened 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() {
equals(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object');
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');
});
});

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

var bound = _.bind(func, context);
equals(bound(), 'name: moe', 'can bind a function to a context');
equal(bound(), 'name: moe', 'can bind a function to a context');
bound = _(func).bind(context);
equals(bound(), 'name: moe', 'can do OO-style binding');
equal(bound(), 'name: moe', 'can do OO-style binding');
bound = _.bind(func, null, 'curly');
equals(bound(), 'name: curly', 'can bind without specifying a context');
equal(bound(), 'name: curly', 'can bind without specifying a context');
func = function(salutation, name) { return salutation + ': ' + name; };
func = _.bind(func, this, 'hello');
equals(func('moe'), 'hello: moe', 'the function was partially applied in advance');
equal(func('moe'), 'hello: moe', 'the function was partially applied in advance');
var func = _.bind(func, this, 'curly');
equals(func(), 'hello: curly', 'the function was completely applied in advance');
equal(func(), 'hello: curly', 'the function was completely applied in advance');
var func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; };
func = _.bind(func, this, 'hello', 'moe', 'curly');
equals(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
func = function(context, message) { equals(this, context, message); };
func = function(context, message) { equal(this, context, message); };
_.bind(func, 0, 0, 'can bind a function to `0`')();

@@ -51,4 +51,4 @@ _.bind(func, '', '', 'can bind a function to an empty string')();

curly.sayHi = moe.sayHi;
equals(curly.getName(), 'name: curly', 'unbound function is bound to current object');
equals(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object');
equal(curly.getName(), 'name: curly', 'unbound function is bound to current object');
equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object');

@@ -63,3 +63,3 @@ curly = {name : 'curly'};

curly.sayHi = moe.sayHi;
equals(curly.sayHi(), 'hi: moe', 'calling bindAll with no arguments binds all functions to the object');
equal(curly.sayHi(), 'hi: moe', 'calling bindAll with no arguments binds all functions to the object');
});

@@ -72,4 +72,4 @@

var fastFib = _.memoize(fib);
equals(fib(10), 55, 'a memoized version of fibonacci produces identical results');
equals(fastFib(10), 55, 'a memoized version of fibonacci produces identical results');
equal(fib(10), 55, 'a memoized version of fibonacci produces identical results');
equal(fastFib(10), 55, 'a memoized version of fibonacci produces identical results');

@@ -80,4 +80,4 @@ var o = function(str) {

var fastO = _.memoize(o);
equals(o('toString'), 'toString', 'checks hasOwnProperty');
equals(fastO('toString'), 'toString', 'checks hasOwnProperty');
equal(o('toString'), 'toString', 'checks hasOwnProperty');
equal(fastO('toString'), 'toString', 'checks hasOwnProperty');
});

@@ -109,4 +109,4 @@

setTimeout(throttledIncr, 240);
_.delay(function(){ ok(counter == 1, "incr was called immediately"); }, 30);
_.delay(function(){ ok(counter == 4, "incr was throttled"); start(); }, 400);
_.delay(function(){ equal(counter, 1, "incr was called immediately"); }, 30);
_.delay(function(){ equal(counter, 4, "incr was throttled"); start(); }, 400);
});

@@ -122,12 +122,15 @@

setTimeout(function(){ throttledUpdate(6); }, 250);
_.delay(function(){ equals(value, 1, "updated to latest value"); }, 40);
_.delay(function(){ equals(value, 6, "updated to latest value"); start(); }, 400);
_.delay(function(){ equal(value, 1, "updated to latest value"); }, 40);
_.delay(function(){ equal(value, 6, "updated to latest value"); start(); }, 400);
});
asyncTest("functions: throttle once", 1, function() {
asyncTest("functions: throttle once", 2, function() {
var counter = 0;
var incr = function(){ counter++; };
var incr = function(){ return ++counter; };
var throttledIncr = _.throttle(incr, 100);
throttledIncr();
_.delay(function(){ ok(counter == 1, "incr was called once"); start(); }, 220);
var result = throttledIncr();
_.delay(function(){
equal(result, 1, "throttled functions return their value");
equal(counter, 1, "incr was called once"); start();
}, 220);
});

@@ -140,3 +143,3 @@

throttledIncr(); throttledIncr();
_.delay(function(){ ok(counter == 2, "incr was called twice"); start(); }, 220);
_.delay(function(){ equal(counter, 2, "incr was called twice"); start(); }, 220);
});

@@ -154,5 +157,19 @@

setTimeout(debouncedIncr, 150);
_.delay(function(){ ok(counter == 1, "incr was debounced"); start(); }, 220);
_.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 220);
});
asyncTest("functions: debounce asap", 2, function() {
var counter = 0;
var incr = function(){ counter++; };
var debouncedIncr = _.debounce(incr, 50, true);
debouncedIncr(); debouncedIncr(); debouncedIncr();
equal(counter, 1, 'incr was called immediately');
setTimeout(debouncedIncr, 30);
setTimeout(debouncedIncr, 60);
setTimeout(debouncedIncr, 90);
setTimeout(debouncedIncr, 120);
setTimeout(debouncedIncr, 150);
_.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 220);
});
test("functions: once", function() {

@@ -163,3 +180,3 @@ var num = 0;

increment();
equals(num, 1);
equal(num, 1);
});

@@ -170,3 +187,3 @@

var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); });
equals(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function');
equal(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function');

@@ -176,3 +193,3 @@ var inner = function(){ return "Hello "; };

obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; });
equals(obj.hi(), "Hello Moe");
equal(obj.hi(), "Hello Moe");

@@ -189,6 +206,6 @@ var noop = function(){};

var composed = _.compose(exclaim, greet);
equals(composed('moe'), 'hi: moe!', 'can compose a function that takes another');
equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another');
composed = _.compose(greet, exclaim);
equals(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
});

@@ -206,7 +223,7 @@

equals(testAfter(5, 5), 1, "after(N) should fire after being called N times");
equals(testAfter(5, 4), 0, "after(N) should not fire unless called N times");
equals(testAfter(0, 0), 1, "after(0) should fire immediately");
equal(testAfter(5, 5), 1, "after(N) should fire after being called N times");
equal(testAfter(5, 4), 0, "after(N) should not fire unless called N times");
equal(testAfter(0, 0), 1, "after(0) should fire immediately");
});
});

@@ -6,16 +6,15 @@ $(document).ready(function() {

test("objects: keys", function() {
var exception = /object/;
equals(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object');
equal(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object');
// the test above is not safe because it relies on for-in enumeration order
var a = []; a[1] = 0;
equals(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95');
raises(function() { _.keys(null); }, exception, 'throws an error for `null` values');
raises(function() { _.keys(void 0); }, exception, 'throws an error for `undefined` values');
raises(function() { _.keys(1); }, exception, 'throws an error for number primitives');
raises(function() { _.keys('a'); }, exception, 'throws an error for string primitives');
raises(function() { _.keys(true); }, exception, 'throws an error for boolean primitives');
equal(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95');
raises(function() { _.keys(null); }, TypeError, 'throws an error for `null` values');
raises(function() { _.keys(void 0); }, TypeError, 'throws an error for `undefined` values');
raises(function() { _.keys(1); }, TypeError, 'throws an error for number primitives');
raises(function() { _.keys('a'); }, TypeError, 'throws an error for string primitives');
raises(function() { _.keys(true); }, TypeError, 'throws an error for boolean primitives');
});
test("objects: values", function() {
equals(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object');
equal(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object');
});

@@ -29,3 +28,3 @@

Animal.prototype.run = function(){};
equals(_.functions(new Animal).join(''), 'run', 'also looks up functions on the prototype');
equal(_.functions(new Animal).join(''), 'run', 'also looks up functions on the prototype');
});

@@ -35,5 +34,5 @@

var result;
equals(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another');
equals(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination');
equals(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden');
equal(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another');
equal(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination');
equal(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden');
result = _.extend({x:'x'}, {a:'a'}, {b:'b'});

@@ -44,5 +43,15 @@ ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects');

result = _.extend({}, {a: void 0, b: null});
equals(_.keys(result).join(''), 'ab', 'extend does not copy undefined values');
equal(_.keys(result).join(''), 'ab', 'extend does not copy undefined values');
});
test("objects: pick", function() {
var result;
result = _.pick({a:1, b:2, c:3}, 'a', 'c');
ok(_.isEqual(result, {a:1, c:3}), 'can restrict properties to those named');
result = _.pick({a:1, b:2, c:3}, ['b', 'c']);
ok(_.isEqual(result, {b:2, c:3}), 'can restrict properties to those named in an array');
result = _.pick({a:1, b:2, c:3}, ['a'], 'b');
ok(_.isEqual(result, {a:1, b:2}), 'can restrict properties to those named in mixed args');
});
test("objects: defaults", function() {

@@ -53,10 +62,10 @@ var result;

_.defaults(options, {zero: 1, one: 10, twenty: 20});
equals(options.zero, 0, 'value exists');
equals(options.one, 1, 'value exists');
equals(options.twenty, 20, 'default applied');
equal(options.zero, 0, 'value exists');
equal(options.one, 1, 'value exists');
equal(options.twenty, 20, 'default applied');
_.defaults(options, {empty: "full"}, {nan: "nan"}, {word: "word"}, {word: "dog"});
equals(options.empty, "", 'value exists');
equal(options.empty, "", 'value exists');
ok(_.isNaN(options.nan), "NaN isn't overridden");
equals(options.word, "word", 'new value is added, first one wins');
equal(options.word, "word", 'new value is added, first one wins');
});

@@ -67,3 +76,3 @@

var clone = _.clone(moe);
equals(clone.name, 'moe', 'the clone as the attributes of the original');
equal(clone.name, 'moe', 'the clone as the attributes of the original');

@@ -74,7 +83,7 @@ clone.name = 'curly';

clone.lucky.push(101);
equals(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original');
equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original');
equals(_.clone(undefined), void 0, 'non objects should not be changed by clone');
equals(_.clone(1), 1, 'non objects should not be changed by clone');
equals(_.clone(null), null, 'non objects should not be changed by clone');
equal(_.clone(undefined), void 0, 'non objects should not be changed by clone');
equal(_.clone(1), 1, 'non objects should not be changed by clone');
equal(_.clone(null), null, 'non objects should not be changed by clone');
});

@@ -178,3 +187,3 @@

ok(_.isEqual([1, "Larry", true], [1, "Larry", true]), "Arrays containing identical primitives are equal");
ok(_.isEqual([/Moe/g, new Date(2009, 9, 25)], [/Moe/g, new Date(2009, 9, 25)]), "Arrays containing equivalent elements are equal");
ok(_.isEqual([(/Moe/g), new Date(2009, 9, 25)], [(/Moe/g), new Date(2009, 9, 25)]), "Arrays containing equivalent elements are equal");

@@ -302,3 +311,3 @@ // Multi-dimensional arrays.

ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal');
equals(_({x: 1, y: 2}).chain().isEqual(_({x: 1, y: 2}).chain()).value(), true, '`isEqual` can be chained');
equal(_({x: 1, y: 2}).chain().isEqual(_({x: 1, y: 2}).chain()).value(), true, '`isEqual` can be chained');

@@ -348,3 +357,3 @@ // Custom `isEqual` methods.

var that_date_components = (that instanceof Date) ? that.toJSON() : that;
delete this_date_components['_type']; delete that_date_components['_type']
delete this_date_components['_type']; delete that_date_components['_type'];
return _.isEqual(this_date_components, that_date_components);

@@ -494,2 +503,16 @@ };

test("objects: isFinite", function() {
ok(!_.isFinite(undefined), 'undefined is not Finite');
ok(!_.isFinite(null), 'null is not Finite');
ok(!_.isFinite(NaN), 'NaN is not Finite');
ok(!_.isFinite(Infinity), 'Infinity is not Finite');
ok(!_.isFinite(-Infinity), '-Infinity is not Finite');
ok(!_.isFinite('12'), 'Strings are not numbers');
var obj = new Number(5);
ok(_.isFinite(obj), 'Number instances can be finite');
ok(_.isFinite(0), '0 is Finite');
ok(_.isFinite(123), 'Ints are Finite');
ok(_.isFinite(-12.44), 'Floats are Finite');
});
test("objects: isNaN", function() {

@@ -536,4 +559,4 @@ ok(!_.isNaN(undefined), 'undefined is not NaN');

var returned = _.tap(1, interceptor);
equals(intercepted, 1, "passes tapped object to interceptor");
equals(returned, 1, "returns tapped object");
equal(intercepted, 1, "passes tapped object to interceptor");
equal(returned, 1, "returns tapped object");

@@ -540,0 +563,0 @@ returned = _([1,2,3]).chain().

$(document).ready(function() {
module("Utility");
var templateSettings;
module("Utility", {
setup: function() {
templateSettings = _.clone(_.templateSettings);
},
teardown: function() {
_.templateSettings = templateSettings;
}
});
test("utility: noConflict", function() {

@@ -9,3 +21,3 @@ var underscore = _.noConflict();

var intersection = underscore.intersect([-1, 0, 1, 2], [1, 2, 3, 4]);
equals(intersection.join(', '), '1, 2', 'but the intersection function still works');
equal(intersection.join(', '), '1, 2', 'but the intersection function still works');
window._ = underscore;

@@ -16,3 +28,3 @@ });

var moe = {name : 'moe'};
equals(_.identity(moe), moe, 'moe is the same as his identity');
equal(_.identity(moe), moe, 'moe is the same as his identity');
});

@@ -23,3 +35,3 @@

while(i++ < 100) ids.push(_.uniqueId());
equals(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
});

@@ -43,9 +55,9 @@

});
equals(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
equals(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
});
test("utility: _.escape", function() {
equals(_.escape("Curly & Moe"), "Curly &amp; Moe");
equals(_.escape("Curly &amp; Moe"), "Curly &amp;amp; Moe");
equal(_.escape("Curly & Moe"), "Curly &amp; Moe");
equal(_.escape("Curly &amp; Moe"), "Curly &amp;amp; Moe");
});

@@ -56,12 +68,12 @@

var result = basicTemplate({thing : 'This'});
equals(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');
equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');
var sansSemicolonTemplate = _.template("A <% this %> B");
equals(sansSemicolonTemplate(), "A B");
equal(sansSemicolonTemplate(), "A B");
var backslashTemplate = _.template("<%= thing %> is \\ridanculous");
equals(backslashTemplate({thing: 'This'}), "This is \\ridanculous");
equal(backslashTemplate({thing: 'This'}), "This is \\ridanculous");
var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>');
equals(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');
equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');

@@ -72,7 +84,7 @@ var fancyTemplate = _.template("<ul><% \

result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}});
equals(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
equal(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
var escapedCharsInJavascriptTemplate = _.template("<ul><% _.each(numbers.split('\\n'), function(item) { %><li><%= item %></li><% }) %></ul>");
result = escapedCharsInJavascriptTemplate({numbers: "one\ntwo\nthree\nfour"});
equals(result, "<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>", 'Can use escaped characters (e.g. \\n) in Javascript');
equal(result, "<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>", 'Can use escaped characters (e.g. \\n) in Javascript');

@@ -88,10 +100,10 @@ var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %><div class=\"thumbnail\" rel=\"<%= p %>\"></div><% }); %>");

});
equals(result, "3 p3-thumbnail.gif <div class=\"thumbnail\" rel=\"p1-thumbnail.gif\"></div><div class=\"thumbnail\" rel=\"p2-thumbnail.gif\"></div><div class=\"thumbnail\" rel=\"p3-thumbnail.gif\"></div>");
equal(result, "3 p3-thumbnail.gif <div class=\"thumbnail\" rel=\"p1-thumbnail.gif\"></div><div class=\"thumbnail\" rel=\"p2-thumbnail.gif\"></div><div class=\"thumbnail\" rel=\"p3-thumbnail.gif\"></div>");
var noInterpolateTemplate = _.template("<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>");
result = noInterpolateTemplate();
equals(result, "<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>");
equal(result, "<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>");
var quoteTemplate = _.template("It's its, not it's");
equals(quoteTemplate({}), "It's its, not it's");
equal(quoteTemplate({}), "It's its, not it's");

@@ -101,10 +113,10 @@ var quoteInStatementAndBody = _.template("<%\

%>Statement quotes and 'quotes'.<% } %>");
equals(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'.");
equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'.");
var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.');
equals(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.');
equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.');
var template = _.template("<i><%- value %></i>");
var result = template({value: "<script>"});
equals(result, '<i>&lt;script&gt;</i>');
equal(result, '<i>&lt;script&gt;</i>');

@@ -115,7 +127,7 @@ var stooge = {

};
equals(stooge.template(), "I'm Moe");
equal(stooge.template(), "I'm Moe");
if (!$.browser.msie) {
var fromHTML = _.template($('#template').html());
equals(fromHTML({data : 12345}).replace(/\s/g, ''), '<li>24690</li>');
equal(fromHTML({data : 12345}).replace(/\s/g, ''), '<li>24690</li>');
}

@@ -130,9 +142,9 @@

result = custom({people : {moe : "Moe", larry : "Larry", curly : "Curly"}});
equals(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
equal(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
var customQuote = _.template("It's its, not it's");
equals(customQuote({}), "It's its, not it's");
equal(customQuote({}), "It's its, not it's");
var quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}");
equals(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'.");
equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'.");

@@ -146,9 +158,9 @@ _.templateSettings = {

result = customWithSpecialChars({people : {moe : "Moe", larry : "Larry", curly : "Curly"}});
equals(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
equal(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
var customWithSpecialCharsQuote = _.template("It's its, not it's");
equals(customWithSpecialCharsQuote({}), "It's its, not it's");
equal(customWithSpecialCharsQuote({}), "It's its, not it's");
var quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>");
equals(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'.");
equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'.");

@@ -160,8 +172,36 @@ _.templateSettings = {

var mustache = _.template("Hello {{planet}}!");
equals(mustache({planet : "World"}), "Hello World!", "can mimic mustache.js");
equal(mustache({planet : "World"}), "Hello World!", "can mimic mustache.js");
var templateWithNull = _.template("a null undefined {{planet}}");
equals(templateWithNull({planet : "world"}), "a null undefined world", "can handle missing escape and evaluate settings");
equal(templateWithNull({planet : "world"}), "a null undefined world", "can handle missing escape and evaluate settings");
});
test('_.template handles \\u2028 & \\u2029', function() {
var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>');
strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>');
});
test('result calls functions and returns primitives', function() {
var obj = {w: '', x: 'x', y: function(){ return this.x; }};
strictEqual(_.result(obj, 'w'), '');
strictEqual(_.result(obj, 'x'), 'x');
strictEqual(_.result(obj, 'y'), 'x');
strictEqual(_.result(obj, 'z'), undefined);
strictEqual(_.result(null, 'x'), null);
});
test('_.templateSettings.variable', function() {
var s = '<%=data.x%>';
var data = {x: 'x'};
strictEqual(_.template(s, data, {variable: 'data'}), 'x')
_.templateSettings.variable = 'data';
strictEqual(_.template(s)(data), 'x')
});
test('#547 - _.templateSettings is unchanged by custom settings.', function() {
ok(!_.templateSettings.variable);
_.template('', {}, {variable: 'x'});
ok(!_.templateSettings.variable);
});
});

@@ -1,1005 +0,9 @@

/*
* QUnit - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2009 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*/
(function(window) {
var QUnit = {
// Initialize the configuration options
init: function init() {
config = {
stats: { all: 0, bad: 0 },
moduleStats: { all: 0, bad: 0 },
started: +new Date,
blocking: false,
autorun: false,
assertions: [],
filters: [],
queue: []
};
var tests = id("qunit-tests"),
banner = id("qunit-banner"),
result = id("qunit-testresult");
if ( tests ) {
tests.innerHTML = "";
}
if ( banner ) {
banner.className = "";
}
if ( result ) {
result.parentNode.removeChild( result );
}
},
// call on start of module test to prepend name to all tests
module: function module(name, testEnvironment) {
synchronize(function() {
if ( config.currentModule ) {
QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
}
config.currentModule = name;
config.moduleTestEnvironment = testEnvironment;
config.moduleStats = { all: 0, bad: 0 };
QUnit.moduleStart( name, testEnvironment );
});
},
asyncTest: function asyncTest(testName, expected, callback) {
if ( arguments.length === 2 ) {
callback = expected;
expected = 0;
}
QUnit.test(testName, expected, callback, true);
},
test: function test(testName, expected, callback, async) {
var name = testName, testEnvironment = {};
if ( arguments.length === 2 ) {
callback = expected;
expected = null;
}
if ( config.currentModule ) {
name = config.currentModule + " module: " + name;
}
if ( !validTest(name) ) {
return;
}
synchronize(function() {
QUnit.testStart( testName );
testEnvironment = extend({
setup: function() {},
teardown: function() {}
}, config.moduleTestEnvironment);
config.assertions = [];
config.expected = null;
if ( arguments.length >= 3 ) {
config.expected = callback;
callback = arguments[2];
}
try {
if ( !config.pollution ) {
saveGlobal();
}
testEnvironment.setup.call(testEnvironment);
} catch(e) {
QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
}
if ( async ) {
QUnit.stop();
}
try {
callback.call(testEnvironment);
} catch(e) {
fail("Test " + name + " died, exception and test follows", e, callback);
QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
// else next test will carry the responsibility
saveGlobal();
// Restart the tests if they're blocking
if ( config.blocking ) {
start();
}
}
});
synchronize(function() {
try {
checkPollution();
testEnvironment.teardown.call(testEnvironment);
} catch(e) {
QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
}
try {
QUnit.reset();
} catch(e) {
fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);
}
if ( config.expected && config.expected != config.assertions.length ) {
QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
}
var good = 0, bad = 0,
tests = id("qunit-tests");
config.stats.all += config.assertions.length;
config.moduleStats.all += config.assertions.length;
if ( tests ) {
var ol = document.createElement("ol");
ol.style.display = "none";
for ( var i = 0; i < config.assertions.length; i++ ) {
var assertion = config.assertions[i];
var li = document.createElement("li");
li.className = assertion.result ? "pass" : "fail";
li.innerHTML = assertion.message || "(no message)";
ol.appendChild( li );
if ( assertion.result ) {
good++;
} else {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
}
var b = document.createElement("strong");
b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";
addEvent(b, "click", function() {
var next = b.nextSibling, display = next.style.display;
next.style.display = display === "none" ? "block" : "none";
});
addEvent(b, "dblclick", function(e) {
var target = (e || window.event).target;
if ( target.nodeName.toLowerCase() === "strong" ) {
var text = "", node = target.firstChild;
while ( node.nodeType === 3 ) {
text += node.nodeValue;
node = node.nextSibling;
}
text = text.replace(/(^\s*|\s*$)/g, "");
if ( window.location ) {
window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);
}
}
});
var li = document.createElement("li");
li.className = bad ? "fail" : "pass";
li.appendChild( b );
li.appendChild( ol );
tests.appendChild( li );
if ( bad ) {
var toolbar = id("qunit-testrunner-toolbar");
if ( toolbar ) {
toolbar.style.display = "block";
id("qunit-filter-pass").disabled = null;
id("qunit-filter-missing").disabled = null;
}
}
} else {
for ( var i = 0; i < config.assertions.length; i++ ) {
if ( !config.assertions[i].result ) {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
}
}
QUnit.testDone( testName, bad, config.assertions.length );
if ( !window.setTimeout && !config.queue.length ) {
done();
}
});
if ( window.setTimeout && !config.doneTimer ) {
config.doneTimer = window.setTimeout(function(){
if ( !config.queue.length ) {
done();
} else {
synchronize( done );
}
}, 13);
}
},
/**
* Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
*/
expect: function expect(asserts) {
config.expected = asserts;
},
/**
* Asserts true.
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function ok(a, msg) {
QUnit.log(a, msg);
config.assertions.push({
result: !!a,
message: msg
});
},
/**
* Checks 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 equals( format("Received {0} bytes.", 2), "Received 2 bytes." );
*
* @param Object actual
* @param Object expected
* @param String message (optional)
*/
equals: function equals(actual, expected, message) {
push(expected == actual, actual, expected, message);
},
same: function(a, b, message) {
push(QUnit.equiv(a, b), a, b, message);
},
start: function start() {
// A slight delay, to avoid any current callbacks
if ( window.setTimeout ) {
window.setTimeout(function() {
if ( config.timeout ) {
clearTimeout(config.timeout);
}
config.blocking = false;
process();
}, 13);
} else {
config.blocking = false;
process();
}
},
stop: function stop(timeout) {
config.blocking = true;
if ( timeout && window.setTimeout ) {
config.timeout = window.setTimeout(function() {
QUnit.ok( false, "Test timed out" );
QUnit.start();
}, timeout);
}
},
/**
* Resets the test setup. Useful for tests that modify the DOM.
*/
reset: function reset() {
if ( window.jQuery ) {
jQuery("#main").html( config.fixture );
jQuery.event.global = {};
jQuery.ajaxSettings = extend({}, config.ajaxSettings);
}
},
/**
* Trigger an event on an element.
*
* @example triggerEvent( document.body, "click" );
*
* @param DOMElement elem
* @param String type
*/
triggerEvent: function triggerEvent( elem, type, event ) {
if ( document.createEvent ) {
event = document.createEvent("MouseEvents");
event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
elem.dispatchEvent( event );
} else if ( elem.fireEvent ) {
elem.fireEvent("on"+type);
}
},
// Logging callbacks
done: function done(failures, total) {},
log: function log(result, message) {},
testStart: function testStart(name) {},
testDone: function testDone(name, failures, total) {},
moduleStart: function moduleStart(name, testEnvironment) {},
moduleDone: function moduleDone(name, failures, total) {}
};
// Maintain internal state
var config = {
// The queue of tests to run
queue: [],
// block until document ready
blocking: true
};
// Load paramaters
(function() {
var location = window.location || { search: "", protocol: "file:" },
GETParams = location.search.slice(1).split('&');
for ( var i = 0; i < GETParams.length; i++ ) {
GETParams[i] = decodeURIComponent( GETParams[i] );
if ( GETParams[i] === "noglobals" ) {
GETParams.splice( i, 1 );
i--;
config.noglobals = true;
}
}
// restrict modules/tests by get parameters
config.filters = GETParams;
// Figure out if we're running the tests from a server or not
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);
window.QUnit = QUnit;
} else {
extend(exports, QUnit);
exports.QUnit = QUnit;
}
if ( typeof document === "undefined" || document.readyState === "complete" ) {
config.autorun = true;
}
addEvent(window, "load", function() {
// Initialize the config, saving the execution queue
var oldconfig = extend({}, config);
QUnit.init();
extend(config, oldconfig);
config.blocking = false;
var userAgent = id("qunit-userAgent");
if ( userAgent ) {
userAgent.innerHTML = navigator.userAgent;
}
var toolbar = id("qunit-testrunner-toolbar");
if ( toolbar ) {
toolbar.style.display = "none";
var filter = document.createElement("input");
filter.type = "checkbox";
filter.id = "qunit-filter-pass";
filter.disabled = true;
addEvent( filter, "click", function() {
var li = document.getElementsByTagName("li");
for ( var i = 0; i < li.length; i++ ) {
if ( li[i].className.indexOf("pass") > -1 ) {
li[i].style.display = filter.checked ? "none" : "block";
}
}
});
toolbar.appendChild( filter );
var label = document.createElement("label");
label.setAttribute("for", "filter-pass");
label.innerHTML = "Hide passed tests";
toolbar.appendChild( label );
var missing = document.createElement("input");
missing.type = "checkbox";
missing.id = "qunit-filter-missing";
missing.disabled = true;
addEvent( missing, "click", function() {
var li = document.getElementsByTagName("li");
for ( var i = 0; i < li.length; i++ ) {
if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {
li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";
}
}
});
toolbar.appendChild( missing );
label = document.createElement("label");
label.setAttribute("for", "filter-missing");
label.innerHTML = "Hide missing tests (untested code is broken code)";
toolbar.appendChild( label );
}
var main = id('main');
if ( main ) {
config.fixture = main.innerHTML;
}
if ( window.jQuery ) {
config.ajaxSettings = window.jQuery.ajaxSettings;
}
QUnit.start();
});
function done() {
if ( config.doneTimer && window.clearTimeout ) {
window.clearTimeout( config.doneTimer );
config.doneTimer = null;
}
if ( config.queue.length ) {
config.doneTimer = window.setTimeout(function(){
if ( !config.queue.length ) {
done();
} else {
synchronize( done );
}
}, 13);
return;
}
config.autorun = true;
// Log the last module results
if ( config.currentModule ) {
QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
}
var banner = id("qunit-banner"),
tests = id("qunit-tests"),
html = ['Tests completed in ',
+new Date - config.started, ' milliseconds.<br/>',
'<span class="bad">', config.stats.all - config.stats.bad, '</span> tests of <span class="all">', config.stats.all, '</span> passed, ', config.stats.bad,' failed.'].join('');
if ( banner ) {
banner.className += " " + (config.stats.bad ? "fail" : "pass");
}
if ( tests ) {
var result = id("qunit-testresult");
if ( !result ) {
result = document.createElement("p");
result.id = "qunit-testresult";
result.className = "result";
tests.parentNode.insertBefore( result, tests.nextSibling );
}
result.innerHTML = html;
}
QUnit.done( config.stats.bad, config.stats.all );
}
function validTest( name ) {
var i = config.filters.length,
run = false;
if ( !i ) {
return true;
}
while ( i-- ) {
var filter = config.filters[i],
not = filter.charAt(0) == '!';
if ( not ) {
filter = filter.slice(1);
}
if ( name.indexOf(filter) !== -1 ) {
return !not;
}
if ( not ) {
run = true;
}
}
return run;
}
function push(result, actual, expected, message) {
message = message || (result ? "okay" : "failed");
QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );
}
function synchronize( callback ) {
config.queue.push( callback );
if ( config.autorun && !config.blocking ) {
process();
}
}
function process() {
while ( config.queue.length && !config.blocking ) {
config.queue.shift()();
}
}
function saveGlobal() {
config.pollution = [];
if ( config.noglobals ) {
for ( var key in window ) {
config.pollution.push( key );
}
}
}
function checkPollution( name ) {
var old = config.pollution;
saveGlobal();
var newGlobals = diff( old, config.pollution );
if ( newGlobals.length > 0 ) {
ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
config.expected++;
}
var deletedGlobals = diff( config.pollution, old );
if ( deletedGlobals.length > 0 ) {
ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
config.expected++;
}
}
// returns a new Array with the elements that are in a but not in b
function diff( a, b ) {
var result = a.slice();
for ( var i = 0; i < result.length; i++ ) {
for ( var j = 0; j < b.length; j++ ) {
if ( result[i] === b[j] ) {
result.splice(i, 1);
i--;
break;
}
}
}
return result;
}
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) {
for ( var prop in b ) {
a[prop] = b[prop];
}
return a;
}
function addEvent(elem, type, fn) {
if ( elem.addEventListener ) {
elem.addEventListener( type, fn, false );
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, fn );
} else {
fn();
}
}
function id(name) {
return !!(typeof document !== "undefined" && document && document.getElementById) &&
document.getElementById( name );
}
// Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {
var innerEquiv; // the real equiv function
var callers = []; // stack to decide between skip/abort functions
// Determine what is o.
function hoozit(o) {
if (o.constructor === String) {
return "string";
} else if (o.constructor === Boolean) {
return "boolean";
} else if (o.constructor === Number) {
if (isNaN(o)) {
return "nan";
} else {
return "number";
}
} else if (typeof o === "undefined") {
return "undefined";
// consider: typeof null === object
} else if (o === null) {
return "null";
// consider: typeof [] === object
} else if (o instanceof Array) {
return "array";
// consider: typeof new Date() === object
} else if (o instanceof Date) {
return "date";
// consider: /./ instanceof Object;
// /./ instanceof RegExp;
// typeof /./ === "function"; // => false in IE and Opera,
// true in FF and Safari
} else if (o instanceof RegExp) {
return "regexp";
} else if (typeof o === "object") {
return "object";
} else if (o instanceof Function) {
return "function";
} else {
return undefined;
}
}
// Call the o related callback with the given arguments.
function handleEvents(o, callbacks, args) {
var prop = hoozit(o);
if (prop) {
if (hoozit(callbacks[prop]) === "function") {
return callbacks[prop].apply(callbacks, args);
} else {
return callbacks[prop]; // or undefined
}
}
}
var 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;
}
}
return {
"string": useStrictEquality,
"boolean": useStrictEquality,
"number": useStrictEquality,
"null": useStrictEquality,
"undefined": useStrictEquality,
"nan": function (b) {
return isNaN(b);
},
"date": function (b, a) {
return hoozit(b) === "date" && a.valueOf() === b.valueOf();
},
"regexp": function (b, a) {
return hoozit(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;
},
// - 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;
var len;
// b could be an object literal here
if ( ! (hoozit(b) === "array")) {
return false;
}
len = a.length;
if (len !== b.length) { // safe and faster
return false;
}
for (i = 0; i < len; i++) {
if ( ! innerEquiv(a[i], b[i])) {
return false;
}
}
return true;
},
"object": function (b, a) {
var i;
var eq = true; // unless we can proove it
var aProperties = [], bProperties = []; // collection of strings
// comparing constructors is more strict than using instanceof
if ( a.constructor !== b.constructor) {
return false;
}
// stack constructor before traversing properties
callers.push(a.constructor);
for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
aProperties.push(i); // collect a's properties
if ( ! innerEquiv(a[i], b[i])) {
eq = false;
}
}
callers.pop(); // unstack, we are done
for (i in b) {
bProperties.push(i); // collect b's properties
}
// Ensures identical properties name
return eq && innerEquiv(aProperties.sort(), bProperties.sort());
}
};
}();
innerEquiv = function () { // can take multiple arguments
var args = Array.prototype.slice.apply(arguments);
if (args.length < 2) {
return true; // end transition
}
return (function (a, b) {
if (a === b) {
return true; // catch the most you can
} else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
return false; // don't lose time with error prone cases
} else {
return handleEvents(a, callbacks, [b, a]);
}
// apply transition with (1..n) arguments
})(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
};
return innerEquiv;
}();
/**
* jsDump
* Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
* Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
* Date: 5/15/2008
* @projectDescription Advanced and extensible data dumping for Javascript.
* @version 1.0.0
* @author Ariel Flesler
* @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
*/
QUnit.jsDump = (function() {
function quote( str ) {
return '"' + str.toString().replace(/"/g, '\\"') + '"';
};
function literal( o ) {
return o + '';
};
function join( pre, arr, post ) {
var s = jsDump.separator(),
base = jsDump.indent(),
inner = jsDump.indent(1);
if ( arr.join )
arr = arr.join( ',' + s + inner );
if ( !arr )
return pre + post;
return [ pre, inner + arr, base + post ].join(s);
};
function array( arr ) {
var i = arr.length, ret = Array(i);
this.up();
while ( i-- )
ret[i] = this.parse( arr[i] );
this.down();
return join( '[', ret, ']' );
};
var reName = /^function (\w+)/;
var jsDump = {
parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
var parser = this.parsers[ type || this.typeOf(obj) ];
type = typeof parser;
return type == 'function' ? parser.call( this, obj ) :
type == 'string' ? parser :
this.parsers.error;
},
typeOf:function( obj ) {
var type = typeof obj,
f = 'function';//we'll use it 3 times, save it
return type != 'object' && type != f ? type :
!obj ? 'null' :
obj.exec ? 'regexp' :// some browsers (FF) consider regexps functions
obj.getHours ? 'date' :
obj.scrollBy ? 'window' :
obj.nodeName == '#document' ? 'document' :
obj.nodeName ? 'node' :
obj.item ? 'nodelist' : // Safari reports nodelists as functions
obj.callee ? 'arguments' :
obj.call || obj.constructor != Array && //an array would also fall on this hack
(obj+'').indexOf(f) != -1 ? f : //IE reports functions like alert, as objects
'length' in obj ? 'array' :
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, this.parse( fn, 'functionArgs' ), '){'].join('');
return join( ret, this.parse(fn,'functionCode'), '}' );
},
array: array,
nodelist: array,
arguments: array,
object:function( map ) {
var ret = [ ];
this.up();
for ( var key in map )
ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
this.down();
return join( '{', ret, '}' );
},
node:function( node ) {
var open = this.HTML ? '&lt;' : '<',
close = this.HTML ? '&gt;' : '>';
var tag = node.nodeName.toLowerCase(),
ret = open + tag;
for ( var a in this.DOMAttrs ) {
var val = node[this.DOMAttrs[a]];
if ( val )
ret += ' ' + a + '=' + this.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 l = fn.length;
if ( !l ) return '';
var args = 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
},
DOMAttrs:{//attributes to dump from nodes, name=>realName
id:'id',
name:'name',
'class':'className'
},
HTML:true,//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;
})();
})(this);/*
* QUnit - A JavaScript Unit Testing Framework
* QUnit v1.2.0 - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2009 John Resig, Jörn Zaefferer
* Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
* or GPL (GPL-LICENSE.txt) licenses.
*/

@@ -1014,9 +18,11 @@

return !!sessionStorage.getItem;
} catch(e){
} catch(e) {
return false;
}
})()
}
})()
};
var testId = 0;
var testId = 0,
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty;

@@ -1040,2 +46,3 @@ var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {

li.appendChild( b );
li.className = "running";
li.id = this.id = "test-output" + testId++;

@@ -1047,8 +54,15 @@ tests.appendChild( li );

if (this.module != config.previousModule) {
if ( this.previousModule ) {
QUnit.moduleDone( this.module, config.moduleStats.bad, config.moduleStats.all );
if ( config.previousModule ) {
runLoggingCallbacks('moduleDone', QUnit, {
name: config.previousModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
} );
}
config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 };
QUnit.moduleStart( this.module, this.moduleTestEnvironment );
runLoggingCallbacks( 'moduleStart', QUnit, {
name: this.module
} );
}

@@ -1065,3 +79,6 @@

QUnit.testStart( this.testName, this.testEnvironment );
runLoggingCallbacks( 'testStart', QUnit, {
name: this.testName,
module: this.module
});

@@ -1079,7 +96,7 @@ // allow utility functions to access the current test environment

} catch(e) {
// TODO use testName instead of name for no-markup message?
QUnit.ok( false, "Setup failed on " + this.name + ": " + e.message );
QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
}
},
run: function() {
config.current = this;
if ( this.async ) {

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

if ( config.notrycatch ) {
this.callback.call(this.testEnvironment);
return;
}
try {
this.callback.call(this.testEnvironment);
} catch(e) {
// TODO use testName instead of name for no-markup message?
fail("Test " + this.name + " died, exception and test follows", e, this.callback);
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) );

@@ -1101,3 +121,3 @@ // else next test will carry the responsibility

if ( config.blocking ) {
start();
QUnit.start();
}

@@ -1107,12 +127,13 @@ }

teardown: function() {
config.current = this;
try {
this.testEnvironment.teardown.call(this.testEnvironment);
checkPollution();
this.testEnvironment.teardown.call(this.testEnvironment);
} catch(e) {
// TODO use testName instead of name for no-markup message?
QUnit.ok( false, "Teardown failed on " + this.name + ": " + e.message );
QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
}
},
finish: function() {
if ( this.expected && this.expected != this.assertions.length ) {
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" );

@@ -1128,3 +149,3 @@ }

if ( tests ) {
var ol = document.createElement("ol");
var ol = document.createElement("ol");

@@ -1149,3 +170,9 @@ for ( var i = 0; i < this.assertions.length; i++ ) {

// store result when possible
defined.sessionStorage && sessionStorage.setItem("qunit-" + this.testName, bad);
if ( QUnit.config.reorder && defined.sessionStorage ) {
if (bad) {
sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
} else {
sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
}
}

@@ -1159,4 +186,9 @@ if (bad == 0) {

var a = document.createElement("a");
a.innerHTML = "Rerun";
a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
addEvent(b, "click", function() {
var next = b.nextSibling, display = next.style.display;
var next = b.nextSibling.nextSibling,
display = next.style.display;
next.style.display = display === "none" ? "block" : "none";

@@ -1171,3 +203,3 @@ });

if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, ""));
window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
}

@@ -1178,15 +210,7 @@ });

li.className = bad ? "fail" : "pass";
li.style.display = resultDisplayStyle(!bad);
li.removeChild( li.firstChild );
li.appendChild( b );
li.appendChild( a );
li.appendChild( ol );
if ( bad ) {
var toolbar = id("qunit-testrunner-toolbar");
if ( toolbar ) {
toolbar.style.display = "block";
id("qunit-filter-pass").disabled = null;
}
}
} else {

@@ -1205,7 +229,12 @@ for ( var i = 0; i < this.assertions.length; i++ ) {

} catch(e) {
// TODO use testName instead of name for no-markup message?
fail("reset() failed, following Test " + this.name + ", exception and reset fn follows", e, QUnit.reset);
fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
}
QUnit.testDone( this.testName, bad, this.assertions.length );
runLoggingCallbacks( 'testDone', QUnit, {
name: this.testName,
module: this.module,
failed: bad,
passed: this.assertions.length - bad,
total: this.assertions.length
} );
},

@@ -1234,11 +263,11 @@

// defer when previous test run passed, if storage is available
var bad = defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName);
var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
if (bad) {
run();
} else {
synchronize(run);
synchronize(run, true);
};
}
}
};

@@ -1249,3 +278,2 @@ var QUnit = {

module: function(name, testEnvironment) {
config.previousModule = config.currentModule;
config.currentModule = name;

@@ -1258,3 +286,3 @@ config.currentModuleTestEnviroment = testEnvironment;

callback = expected;
expected = 0;
expected = null;
}

@@ -1274,3 +302,3 @@

if ( expected && typeof expected === 'object') {
testEnvironmentArg = expected;
testEnvironmentArg = expected;
expected = null;

@@ -1288,3 +316,2 @@ }

var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
test.previousModule = config.previousModule;
test.module = config.currentModule;

@@ -1312,4 +339,4 @@ test.moduleTestEnvironment = config.currentModuleTestEnviroment;

};
msg = escapeHtml(msg);
QUnit.log(a, msg, details);
msg = escapeInnerText(msg);
runLoggingCallbacks( 'log', QUnit, details );
config.current.assertions.push({

@@ -1390,6 +417,18 @@ result: a,

start: function() {
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 ) {

@@ -1400,23 +439,34 @@ clearTimeout(config.timeout);

config.blocking = false;
process();
process(true);
}, 13);
} else {
config.blocking = false;
process();
process(true);
}
},
stop: function(timeout) {
stop: function(count) {
config.semaphore += count || 1;
config.blocking = true;
if ( timeout && defined.setTimeout ) {
if ( config.testTimeout && defined.setTimeout ) {
clearTimeout(config.timeout);
config.timeout = window.setTimeout(function() {
QUnit.ok( false, "Test timed out" );
config.semaphore = 1;
QUnit.start();
}, timeout);
}, config.testTimeout);
}
}
};
//We want access to the constructor's prototype
(function() {
function F(){};
F.prototype = QUnit;
QUnit = new F();
//Make F QUnit's constructor so that we can add to the prototype later
QUnit.constructor = F;
})();
// Backwards compatibility, deprecated

@@ -1432,3 +482,25 @@ QUnit.equals = QUnit.equal;

// block until document ready
blocking: true
blocking: true,
// when enabled, show only failing tests
// gets persisted through sessionStorage and can be changed in UI via checkbox
hidepassed: false,
// by default, run previously failed tests first
// very useful in combination with "Hide passed tests" checked
reorder: true,
// by default, modify document.title when suite is done
altertitle: true,
urlConfig: ['noglobals', 'notrycatch'],
//logging callback queues
begin: [],
done: [],
log: [],
testStart: [],
testDone: [],
moduleStart: [],
moduleDone: []
};

@@ -1439,18 +511,19 @@

var location = window.location || { search: "", protocol: "file:" },
GETParams = location.search.slice(1).split('&');
params = location.search.slice( 1 ).split( "&" ),
length = params.length,
urlParams = {},
current;
for ( var i = 0; i < GETParams.length; i++ ) {
GETParams[i] = decodeURIComponent( GETParams[i] );
if ( GETParams[i] === "noglobals" ) {
GETParams.splice( i, 1 );
i--;
config.noglobals = true;
} else if ( GETParams[i].search('=') > -1 ) {
GETParams.splice( i, 1 );
i--;
if ( params[ 0 ] ) {
for ( var i = 0; i < length; i++ ) {
current = params[ i ].split( "=" );
current[ 0 ] = decodeURIComponent( current[ 0 ] );
// allow just a key to turn on a flag, e.g., test.html?noglobals
current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
urlParams[ current[ 0 ] ] = current[ 1 ];
}
}
// restrict modules/tests by get parameters
config.filters = GETParams;
QUnit.urlParams = urlParams;
config.filter = urlParams.filter;

@@ -1485,9 +558,10 @@ // Figure out if we're running the tests from a server or not

autorun: false,
filters: [],
queue: []
filter: "",
queue: [],
semaphore: 0
});
var tests = id("qunit-tests"),
banner = id("qunit-banner"),
result = id("qunit-testresult");
var tests = id( "qunit-tests" ),
banner = id( "qunit-banner" ),
result = id( "qunit-testresult" );

@@ -1505,2 +579,10 @@ if ( tests ) {

}
if ( tests ) {
result = document.createElement( "p" );
result.id = "qunit-testresult";
result.className = "result";
tests.parentNode.insertBefore( result, tests );
result.innerHTML = 'Running...<br/>&nbsp;';
}
},

@@ -1515,5 +597,5 @@

if ( window.jQuery ) {
jQuery( "#main, #qunit-fixture" ).html( config.fixture );
jQuery( "#qunit-fixture" ).html( config.fixture );
} else {
var main = id( 'main' ) || id( 'qunit-fixture' );
var main = id( 'qunit-fixture' );
if ( main ) {

@@ -1560,4 +642,3 @@ main.innerHTML = config.fixture;

var type = Object.prototype.toString.call( obj )
.match(/^\[object\s(.*)\]$/)[1] || '';
var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || '';

@@ -1593,6 +674,6 @@ switch (type) {

message = escapeHtml(message) || (result ? "okay" : "failed");
message = escapeInnerText(message) || (result ? "okay" : "failed");
message = '<span class="test-message">' + message + "</span>";
expected = escapeHtml(QUnit.jsDump.parse(expected));
actual = escapeHtml(QUnit.jsDump.parse(actual));
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>';

@@ -1607,3 +688,3 @@ if (actual != expected) {

details.source = source;
output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>';
output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';
}

@@ -1613,3 +694,3 @@ }

QUnit.log(result, message, details);
runLoggingCallbacks( 'log', QUnit, details );

@@ -1622,12 +703,42 @@ config.current.assertions.push({

// Logging callbacks
begin: function() {},
done: function(failures, total) {},
log: function(result, message) {},
testStart: function(name, testEnvironment) {},
testDone: function(name, failures, total) {},
moduleStart: function(name, testEnvironment) {},
moduleDone: function(name, failures, total) {}
url: function( params ) {
params = extend( extend( {}, QUnit.urlParams ), params );
var querystring = "?",
key;
for ( key in params ) {
if ( !hasOwn.call( params, key ) ) {
continue;
}
querystring += encodeURIComponent( key ) + "=" +
encodeURIComponent( params[ key ] ) + "&";
}
return window.location.pathname + querystring.slice( 0, -1 );
},
extend: extend,
id: id,
addEvent: addEvent
});
//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, {
// Logging callbacks; all receive a single argument with the listed properties
// run test/logs.html for any related changes
begin: registerLoggingCallback('begin'),
// done: { failed, passed, total, runtime }
done: registerLoggingCallback('done'),
// log: { result, actual, expected, message }
log: registerLoggingCallback('log'),
// testStart: { name }
testStart: registerLoggingCallback('testStart'),
// testDone: { name, failed, passed, total }
testDone: registerLoggingCallback('testDone'),
// moduleStart: { name }
moduleStart: registerLoggingCallback('moduleStart'),
// moduleDone: { name, failed, passed, total }
moduleDone: registerLoggingCallback('moduleDone')
});
if ( typeof document === "undefined" || document.readyState === "complete" ) {

@@ -1637,4 +748,4 @@ config.autorun = true;

addEvent(window, "load", function() {
QUnit.begin();
QUnit.load = function() {
runLoggingCallbacks( 'begin', QUnit, {} );

@@ -1648,2 +759,8 @@ // Initialize the config, saving the execution queue

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>';
}
var userAgent = id("qunit-userAgent");

@@ -1655,12 +772,8 @@ if ( userAgent ) {

if ( banner ) {
var paramsIndex = location.href.lastIndexOf(location.search);
if ( paramsIndex > -1 ) {
var mainPageLocation = location.href.slice(0, paramsIndex);
if ( mainPageLocation == location.href ) {
banner.innerHTML = '<a href=""> ' + banner.innerHTML + '</a> ';
} else {
var testName = decodeURIComponent(location.search.slice(1));
banner.innerHTML = '<a href="' + mainPageLocation + '">' + banner.innerHTML + '</a> &#8250; <a href="">' + testName + '</a>';
}
}
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 );
});
}

@@ -1670,16 +783,26 @@

if ( toolbar ) {
toolbar.style.display = "none";
var filter = document.createElement("input");
filter.type = "checkbox";
filter.id = "qunit-filter-pass";
filter.disabled = true;
addEvent( filter, "click", function() {
var li = document.getElementsByTagName("li");
for ( var i = 0; i < li.length; i++ ) {
if ( li[i].className.indexOf("pass") > -1 ) {
li[i].style.display = filter.checked ? "none" : "";
var 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 /, " ");
}
if ( defined.sessionStorage ) {
if (filter.checked) {
sessionStorage.setItem("qunit-filter-passed-tests", "true");
} else {
sessionStorage.removeItem("qunit-filter-passed-tests");
}
}
});
if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
filter.checked = true;
var ol = document.getElementById("qunit-tests");
ol.className = ol.className + " hidepass";
}
toolbar.appendChild( filter );

@@ -1693,3 +816,3 @@

var main = id('main') || id('qunit-fixture');
var main = id('qunit-fixture');
if ( main ) {

@@ -1702,4 +825,17 @@ config.fixture = main.innerHTML;

}
});
};
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 );
});
}
};
function done() {

@@ -1710,3 +846,8 @@ config.autorun = true;

if ( config.currentModule ) {
QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
runLoggingCallbacks( 'moduleDone', QUnit, {
name: config.currentModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
} );
}

@@ -1716,5 +857,16 @@

tests = id("qunit-tests"),
html = ['Tests completed in ',
+new Date - config.started, ' milliseconds.<br/>',
'<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> failed.'].join('');
runtime = +new Date - config.started,
passed = config.stats.all - config.stats.bad,
html = [
'Tests completed in ',
runtime,
' milliseconds.<br/>',
'<span class="passed">',
passed,
'</span> tests of <span class="total">',
config.stats.all,
'</span> passed, <span class="failed">',
config.stats.bad,
'</span> failed.'
].join('');

@@ -1726,40 +878,41 @@ if ( banner ) {

if ( tests ) {
var result = id("qunit-testresult");
id( "qunit-testresult" ).innerHTML = html;
}
if ( !result ) {
result = document.createElement("p");
result.id = "qunit-testresult";
result.className = "result";
tests.parentNode.insertBefore( result, tests.nextSibling );
}
result.innerHTML = html;
if ( config.altertitle && typeof document !== "undefined" && document.title ) {
// show ✖ for good, ✔ for bad suite result in title
// use escape sequences in case file gets loaded with non-utf-8-charset
document.title = [
(config.stats.bad ? "\u2716" : "\u2714"),
document.title.replace(/^[\u2714\u2716] /i, "")
].join(" ");
}
QUnit.done( config.stats.bad, config.stats.all );
runLoggingCallbacks( 'done', QUnit, {
failed: config.stats.bad,
passed: passed,
total: config.stats.all,
runtime: runtime
} );
}
function validTest( name ) {
var i = config.filters.length,
var filter = config.filter,
run = false;
if ( !i ) {
if ( !filter ) {
return true;
}
while ( i-- ) {
var filter = config.filters[i],
not = filter.charAt(0) == '!';
var not = filter.charAt( 0 ) === "!";
if ( not ) {
filter = filter.slice( 1 );
}
if ( not ) {
filter = filter.slice(1);
}
if ( name.indexOf( filter ) !== -1 ) {
return !not;
}
if ( name.indexOf(filter) !== -1 ) {
return !not;
}
if ( not ) {
run = true;
}
if ( not ) {
run = true;
}

@@ -1782,2 +935,6 @@

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;
}

@@ -1787,7 +944,3 @@ }

function resultDisplayStyle(passed) {
return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : '';
}
function escapeHtml(s) {
function escapeInnerText(s) {
if (!s) {

@@ -1797,7 +950,5 @@ return "";

s = s + "";
return s.replace(/[\&"<>\\]/g, function(s) {
return s.replace(/[\&<>]/g, function(s) {
switch(s) {
case "&": return "&amp;";
case "\\": return "\\\\";
case '"': return '\"';
case "<": return "&lt;";

@@ -1810,24 +961,28 @@ case ">": return "&gt;";

function synchronize( callback ) {
function synchronize( callback, last ) {
config.queue.push( callback );
if ( config.autorun && !config.blocking ) {
process();
process(last);
}
}
function process() {
var start = (new Date()).getTime();
function process( last ) {
var start = new Date().getTime();
config.depth = config.depth ? config.depth + 1 : 1;
while ( config.queue.length && !config.blocking ) {
if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
config.queue.shift()();
} else {
window.setTimeout( process, 13 );
window.setTimeout( function(){
process( last );
}, 13 );
break;
}
}
if (!config.blocking && !config.queue.length) {
done();
}
config.depth--;
if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
done();
}
}

@@ -1840,2 +995,5 @@

for ( var key in window ) {
if ( !hasOwn.call( window, key ) ) {
continue;
}
config.pollution.push( key );

@@ -1850,12 +1008,10 @@ }

var newGlobals = diff( old, config.pollution );
var newGlobals = diff( config.pollution, old );
if ( newGlobals.length > 0 ) {
ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
config.current.expected++;
}
var deletedGlobals = diff( config.pollution, old );
var deletedGlobals = diff( old, config.pollution );
if ( deletedGlobals.length > 0 ) {
ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
config.current.expected++;
}

@@ -1892,3 +1048,9 @@ }

for ( var prop in b ) {
a[prop] = b[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];
}
}

@@ -1914,172 +1076,212 @@

function registerLoggingCallback(key){
return function(callback){
config[key].push( callback );
};
}
// Supports deprecated method of completely overwriting logging callbacks
function runLoggingCallbacks(key, scope, args) {
//debugger;
var 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 );
}
}
}
// Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
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
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);
} else {
return callbacks[prop]; // or undefined
}
}
}
// Call the o related callback with the given arguments.
function bindCallbacks(o, callbacks, args) {
var prop = QUnit.objectType(o);
if (prop) {
if (QUnit.objectType(callbacks[prop]) === "function") {
return callbacks[prop].apply(callbacks, args);
} else {
return callbacks[prop]; // or undefined
}
}
}
var callbacks = function () {
var getProto = Object.getPrototypeOf || function (obj) {
return obj.__proto__;
};
// 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;
}
}
var callbacks = function () {
return {
"string": useStrictEquality,
"boolean": useStrictEquality,
"number": useStrictEquality,
"null": useStrictEquality,
"undefined": useStrictEquality,
// 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;
}
}
"nan": function (b) {
return isNaN(b);
},
return {
"string" : useStrictEquality,
"boolean" : useStrictEquality,
"number" : useStrictEquality,
"null" : useStrictEquality,
"undefined" : useStrictEquality,
"date": function (b, a) {
return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
},
"nan" : function(b) {
return isNaN(b);
},
"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;
},
"date" : function(b, a) {
return QUnit.objectType(b) === "date"
&& a.valueOf() === b.valueOf();
},
// - 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";
},
"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;
},
"array": function (b, a) {
var i, j, loop;
var len;
// - 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";
},
// b could be an object literal here
if ( ! (QUnit.objectType(b) === "array")) {
return false;
}
"array" : function(b, a) {
var i, j, loop;
var len;
len = a.length;
if (len !== b.length) { // safe and faster
return false;
}
// b could be an object literal here
if (!(QUnit.objectType(b) === "array")) {
return false;
}
//track reference to avoid circular references
parents.push(a);
for (i = 0; i < len; i++) {
loop = false;
for(j=0;j<parents.length;j++){
if(parents[j] === a[i]){
loop = true;//dont rewalk array
}
}
if (!loop && ! innerEquiv(a[i], b[i])) {
parents.pop();
return false;
}
}
parents.pop();
return true;
},
len = a.length;
if (len !== b.length) { // safe and faster
return false;
}
"object": function (b, a) {
var i, j, loop;
var eq = true; // unless we can proove it
var aProperties = [], bProperties = []; // collection of strings
// track reference to avoid circular references
parents.push(a);
for (i = 0; i < len; i++) {
loop = false;
for (j = 0; j < parents.length; j++) {
if (parents[j] === a[i]) {
loop = true;// dont rewalk array
}
}
if (!loop && !innerEquiv(a[i], b[i])) {
parents.pop();
return false;
}
}
parents.pop();
return true;
},
// comparing constructors is more strict than using instanceof
if ( a.constructor !== b.constructor) {
return false;
}
"object" : function(b, a) {
var i, j, loop;
var eq = true; // unless we can proove it
var aProperties = [], bProperties = []; // collection of
// strings
// stack constructor before traversing properties
callers.push(a.constructor);
//track reference to avoid circular references
parents.push(a);
// 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;
}
}
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
}
aProperties.push(i); // collect a's properties
// stack constructor before traversing properties
callers.push(a.constructor);
// track reference to avoid circular references
parents.push(a);
if (!loop && ! innerEquiv(a[i], b[i])) {
eq = false;
break;
}
}
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
}
aProperties.push(i); // collect a's properties
callers.pop(); // unstack, we are done
parents.pop();
if (!loop && !innerEquiv(a[i], b[i])) {
eq = false;
break;
}
}
for (i in b) {
bProperties.push(i); // collect b's properties
}
callers.pop(); // unstack, we are done
parents.pop();
// Ensures identical properties name
return eq && innerEquiv(aProperties.sort(), bProperties.sort());
}
};
}();
for (i in b) {
bProperties.push(i); // collect b's properties
}
innerEquiv = function () { // can take multiple arguments
var args = Array.prototype.slice.apply(arguments);
if (args.length < 2) {
return true; // end transition
}
// Ensures identical properties name
return eq
&& innerEquiv(aProperties.sort(), bProperties
.sort());
}
};
}();
return (function (a, b) {
if (a === b) {
return true; // catch the most you can
} else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
return false; // don't lose time with error prone cases
} else {
return bindCallbacks(a, callbacks, [b, a]);
}
innerEquiv = function() { // can take multiple arguments
var args = Array.prototype.slice.apply(arguments);
if (args.length < 2) {
return true; // end transition
}
// apply transition with (1..n) arguments
})(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
};
return (function(a, b) {
if (a === b) {
return true; // catch the most you can
} else if (a === null || b === null || typeof a === "undefined"
|| typeof b === "undefined"
|| QUnit.objectType(a) !== QUnit.objectType(b)) {
return false; // don't lose time with error prone cases
} else {
return bindCallbacks(a, callbacks, [ b, a ]);
}
return innerEquiv;
// apply transition with (1..n) arguments
})(args[0], args[1])
&& arguments.callee.apply(this, args.splice(1,
args.length - 1));
};
return innerEquiv;
}();
/**
* jsDump
* Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
* Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
* Date: 5/15/2008
* jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
* http://flesler.blogspot.com Licensed under BSD
* (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
*
* @projectDescription Advanced and extensible data dumping for Javascript.

@@ -2107,7 +1309,7 @@ * @version 1.0.0

};
function array( arr ) {
var i = arr.length, ret = Array(i);
function array( arr, stack ) {
var i = arr.length, ret = Array(i);
this.up();
while ( i-- )
ret[i] = this.parse( arr[i] );
ret[i] = this.parse( arr[i] , undefined , stack);
this.down();

@@ -2120,9 +1322,19 @@ return join( '[', ret, ']' );

var jsDump = {
parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
var parser = this.parsers[ type || this.typeOf(obj) ];
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;
return type == 'function' ? parser.call( this, obj ) :
type == 'string' ? parser :
this.parsers.error;
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 );
stack.pop();
return res;
}
// else
return (type == 'string') ? parser : this.parsers.error;
},

@@ -2147,3 +1359,8 @@ typeOf:function( obj ) {

type = "node";
} else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
} 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";

@@ -2188,3 +1405,3 @@ } else {

'null':'null',
undefined:'undefined',
'undefined':'undefined',
'function':function( fn ) {

@@ -2203,7 +1420,9 @@ var ret = 'function',

arguments: array,
object:function( map ) {
object:function( map, stack ) {
var ret = [ ];
QUnit.jsDump.up();
for ( var key in map )
ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );
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();

@@ -2277,2 +1496,17 @@ return join( '{', ret, '}' );

//from jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
}
/*

@@ -2293,5 +1527,5 @@ * Javascript Diff Algorithm

QUnit.diff = (function() {
function diff(o, n){
var ns = new Object();
var os = new Object();
function diff(o, n) {
var ns = {};
var os = {};

@@ -2301,3 +1535,3 @@ for (var i = 0; i < n.length; i++) {

ns[n[i]] = {
rows: new Array(),
rows: [],
o: null

@@ -2311,3 +1545,3 @@ };

os[o[i]] = {
rows: new Array(),
rows: [],
n: null

@@ -2319,2 +1553,5 @@ };

for (var i in ns) {
if ( !hasOwn.call( ns, i ) ) {
continue;
}
if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {

@@ -2366,3 +1603,3 @@ n[ns[i].rows[0]] = {

return function(o, n){
return function(o, n) {
o = o.replace(/\s+$/, '');

@@ -2420,2 +1657,2 @@ n = n.replace(/\s+$/, '');

})(this);
})(this);

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

// Underscore.js 1.3.1
// Underscore.js 1.3.3
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.

@@ -65,3 +65,3 @@ // Underscore is freely distributable under the MIT license.

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

@@ -184,3 +184,3 @@ // Collection Functions

});
return result;
return !!result;
};

@@ -229,3 +229,3 @@

_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
if (!iterator && _.isEmpty(obj)) return -Infinity;

@@ -242,3 +242,3 @@ var result = {computed : -Infinity};

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

@@ -257,9 +257,5 @@ var result = {computed : Infinity};

each(obj, function(value, index, list) {
if (index == 0) {
shuffled[0] = value;
} else {
rand = Math.floor(Math.random() * (index + 1));
shuffled[index] = shuffled[rand];
shuffled[rand] = value;
}
rand = Math.floor(Math.random() * (index + 1));
shuffled[index] = shuffled[rand];
shuffled[rand] = value;
});

@@ -270,3 +266,4 @@ return shuffled;

// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
_.sortBy = function(obj, val, context) {
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
return _.pluck(_.map(obj, function(value, index, list) {

@@ -279,2 +276,4 @@ return {

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;

@@ -309,8 +308,8 @@ }), 'value');

// Safely convert anything iterable into a real, live array.
_.toArray = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
if (_.isArray(iterable)) return slice.call(iterable);
if (_.isArguments(iterable)) return slice.call(iterable);
return _.values(iterable);
_.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();
return _.values(obj);
};

@@ -320,3 +319,3 @@

_.size = function(obj) {
return _.toArray(obj).length;
return _.isArray(obj) ? obj.length : _.keys(obj).length;
};

@@ -328,5 +327,5 @@

// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head`. The **guard** check allows it to work
// with `_.map`.
_.first = _.head = function(array, n, guard) {
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];

@@ -385,11 +384,13 @@ };

var initial = iterator ? _.map(array, iterator) : array;
var result = [];
_.reduce(initial, function(memo, el, i) {
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
memo[memo.length] = el;
result[result.length] = array[i];
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);
results.push(array[index]);
}
return memo;
}, []);
return result;
return results;
};

@@ -417,3 +418,3 @@

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

@@ -529,3 +530,3 @@ };

var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(func, args); }, wait);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};

@@ -542,3 +543,3 @@

_.throttle = function(func, wait) {
var context, args, timeout, throttling, more;
var context, args, timeout, throttling, more, result;
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);

@@ -556,6 +557,7 @@ return function() {

} else {
func.apply(context, args);
result = func.apply(context, args);
}
whenDone();
throttling = true;
return result;
};

@@ -566,4 +568,5 @@ };

// be triggered. The function will be called after it stops being called for
// N milliseconds.
_.debounce = function(func, wait) {
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout;

@@ -574,4 +577,5 @@ return function() {

timeout = null;
func.apply(context, args);
if (!immediate) func.apply(context, args);
};
if (immediate && !timeout) func.apply(context, args);
clearTimeout(timeout);

@@ -661,2 +665,11 @@ timeout = setTimeout(later, wait);

// Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) {
var result = {};
each(_.flatten(slice.call(arguments, 1)), function(key) {
if (key in obj) result[key] = obj[key];
});
return result;
};
// Fill in a given object with default properties.

@@ -782,2 +795,3 @@ _.defaults = function(obj) {

_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;

@@ -829,2 +843,7 @@ for (var key in obj) if (_.has(obj, key)) return false;

// Is a given object a finite number?
_.isFinite = function(obj) {
return _.isNumber(obj) && isFinite(obj);
};
// Is the given value `NaN`?

@@ -891,2 +910,10 @@ _.isNaN = function(obj) {

// If the value of the named property is a function then invoke it;
// otherwise, return it.
_.result = function(object, property) {
if (object == null) return null;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};
// Add your own custom functions to the Underscore object, ensuring that

@@ -921,6 +948,24 @@ // they're correctly added to the OOP wrapper as well.

// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
'\\': '\\',
"'": "'",
'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(/\\\\/g, '\\').replace(/\\'/g, "'");
return code.replace(unescaper, function(match, escape) {
return escapes[escape];
});
};

@@ -931,26 +976,41 @@

// and correctly escapes quotes within interpolated code.
_.template = function(str, data) {
var c = _.templateSettings;
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' +
str.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(c.escape || noMatch, function(match, code) {
return "',_.escape(" + unescape(code) + "),'";
})
.replace(c.interpolate || noMatch, function(match, code) {
return "'," + unescape(code) + ",'";
})
.replace(c.evaluate || noMatch, function(match, code) {
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
})
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
+ "');}return __p.join('');";
var func = new Function('obj', '_', tmpl);
if (data) return func(data, _);
return function(data) {
return func.call(this, data, _);
_.template = function(text, data, settings) {
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";
// 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 + "return __p;\n";
var render = new Function(settings.variable || 'obj', '_', source);
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for build time
// precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
source + '}';
return template;
};

@@ -1022,2 +1082,2 @@

}).call(this);
}).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 not supported yet

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 not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc