ultimate-base 0.2.3.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,45 +1,51 @@
1
- // Underscore.string
2
- // (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
3
- // Underscore.strings is freely distributable under the terms of the MIT license.
4
- // Documentation: https://github.com/epeli/underscore.string
5
- // Some code is borrowed from MooTools and Alexandru Marasteanu.
6
-
7
- // Version 2.0.0
8
-
9
- (function(root){
1
+ // Underscore.string
2
+ // (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
3
+ // Underscore.string is freely distributable under the terms of the MIT license.
4
+ // Documentation: https://github.com/epeli/underscore.string
5
+ // Some code is borrowed from MooTools and Alexandru Marasteanu.
6
+ // Version '2.2.0rc'
7
+
8
+ !function(root, String){
10
9
  'use strict';
11
10
 
12
11
  // Defining helper functions.
13
12
 
14
13
  var nativeTrim = String.prototype.trim;
14
+ var nativeTrimRight = String.prototype.trimRight;
15
+ var nativeTrimLeft = String.prototype.trimLeft;
15
16
 
16
17
  var parseNumber = function(source) { return source * 1 || 0; };
17
18
 
18
- var strRepeat = function(i, m) {
19
- for (var o = []; m > 0; o[--m] = i) {}
20
- return o.join('');
19
+ var strRepeat = function(str, qty){
20
+ if (qty < 1) return '';
21
+ var result = '';
22
+ while (qty > 0) {
23
+ if (qty & 1) result += str;
24
+ qty >>= 1, str += str;
25
+ }
26
+ return result;
21
27
  };
22
28
 
23
- var slice = function(a){
24
- return Array.prototype.slice.call(a);
25
- };
29
+ var slice = [].slice;
26
30
 
27
31
  var defaultToWhiteSpace = function(characters){
28
- if (characters) {
29
- return _s.escapeRegExp(characters);
32
+ if (characters != null) {
33
+ return '[' + _s.escapeRegExp(characters) + ']';
30
34
  }
31
35
  return '\\s';
32
36
  };
33
37
 
34
- var sArgs = function(method){
35
- return function(){
36
- var args = slice(arguments);
37
- for(var i=0; i<args.length; i++)
38
- args[i] = args[i] == null ? '' : '' + args[i];
39
- return method.apply(null, args);
40
- };
38
+ var escapeChars = {
39
+ lt: '<',
40
+ gt: '>',
41
+ quot: '"',
42
+ apos: "'",
43
+ amp: '&'
41
44
  };
42
45
 
46
+ var reversedEscapeChars = {};
47
+ for(var key in escapeChars){ reversedEscapeChars[escapeChars[key]] = key; }
48
+
43
49
  // sprintf() for JavaScript 0.7-beta1
44
50
  // http://www.diveintojavascript.com/projects/javascript-sprintf
45
51
  //
@@ -73,7 +79,7 @@
73
79
  arg = argv[cursor];
74
80
  for (k = 0; k < match[2].length; k++) {
75
81
  if (!arg.hasOwnProperty(match[2][k])) {
76
- throw(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
82
+ throw new Error(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
77
83
  }
78
84
  arg = arg[match[2][k]];
79
85
  }
@@ -85,7 +91,7 @@
85
91
  }
86
92
 
87
93
  if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
88
- throw(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
94
+ throw new Error(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
89
95
  }
90
96
  switch (match[8]) {
91
97
  case 'b': arg = arg.toString(2); break;
@@ -134,12 +140,12 @@
134
140
  field_list.push(field_match[1]);
135
141
  }
136
142
  else {
137
- throw('[_.sprintf] huh?');
143
+ throw new Error('[_.sprintf] huh?');
138
144
  }
139
145
  }
140
146
  }
141
147
  else {
142
- throw('[_.sprintf] huh?');
148
+ throw new Error('[_.sprintf] huh?');
143
149
  }
144
150
  match[2] = field_list;
145
151
  }
@@ -147,12 +153,12 @@
147
153
  arg_names |= 2;
148
154
  }
149
155
  if (arg_names === 3) {
150
- throw('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
156
+ throw new Error('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
151
157
  }
152
158
  parse_tree.push(match);
153
159
  }
154
160
  else {
155
- throw('[_.sprintf] huh?');
161
+ throw new Error('[_.sprintf] huh?');
156
162
  }
157
163
  _fmt = _fmt.substring(match[0].length);
158
164
  }
@@ -167,226 +173,239 @@
167
173
  // Defining underscore.string
168
174
 
169
175
  var _s = {
170
-
171
- VERSION: '2.0.0',
172
176
 
173
- isBlank: sArgs(function(str){
177
+ VERSION: '2.2.0rc',
178
+
179
+ isBlank: function(str){
180
+ if (str == null) str = '';
174
181
  return (/^\s*$/).test(str);
175
- }),
176
-
177
- stripTags: sArgs(function(str){
178
- return str.replace(/<\/?[^>]+>/ig, '');
179
- }),
180
-
181
- capitalize : sArgs(function(str) {
182
- return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
183
- }),
184
-
185
- chop: sArgs(function(str, step){
186
- step = parseNumber(step) || str.length;
187
- var arr = [];
188
- for (var i = 0; i < str.length;) {
189
- arr.push(str.slice(i,i + step));
190
- i = i + step;
191
- }
192
- return arr;
193
- }),
194
-
195
- clean: sArgs(function(str){
196
- return _s.strip(str.replace(/\s+/g, ' '));
197
- }),
198
-
199
- count: sArgs(function(str, substr){
200
- var count = 0, index;
201
- for (var i=0; i < str.length;) {
202
- index = str.indexOf(substr, i);
203
- index >= 0 && count++;
204
- i = i + (index >= 0 ? index : 0) + substr.length;
205
- }
206
- return count;
207
- }),
208
-
209
- chars: sArgs(function(str) {
210
- return str.split('');
211
- }),
212
-
213
- escapeHTML: sArgs(function(str) {
214
- return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
215
- .replace(/"/g, '&quot;').replace(/'/g, "&apos;");
216
- }),
217
-
218
- unescapeHTML: sArgs(function(str) {
219
- return str.replace(/&lt;/g, '<').replace(/&gt;/g, '>')
220
- .replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, '&');
221
- }),
222
-
223
- escapeRegExp: sArgs(function(str){
224
- // From MooTools core 1.2.4
225
- return str.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
226
- }),
227
-
228
- insert: sArgs(function(str, i, substr){
229
- var arr = str.split('');
230
- arr.splice(parseNumber(i), 0, substr);
231
- return arr.join('');
232
- }),
182
+ },
233
183
 
234
- include: sArgs(function(str, needle){
235
- return str.indexOf(needle) !== -1;
236
- }),
184
+ stripTags: function(str){
185
+ if (str == null) return '';
186
+ return String(str).replace(/<\/?[^>]+>/g, '');
187
+ },
237
188
 
238
- join: sArgs(function(sep) {
239
- var args = slice(arguments);
240
- return args.join(args.shift());
241
- }),
189
+ capitalize : function(str){
190
+ str = str == null ? '' : String(str);
191
+ return str.charAt(0).toUpperCase() + str.slice(1);
192
+ },
242
193
 
243
- lines: sArgs(function(str) {
244
- return str.split("\n");
245
- }),
194
+ chop: function(str, step){
195
+ if (str == null) return [];
196
+ str = String(str);
197
+ step = ~~step;
198
+ return step > 0 ? str.match(new RegExp('.{1,' + step + '}', 'g')) : [str];
199
+ },
246
200
 
247
- reverse: sArgs(function(str){
248
- return Array.prototype.reverse.apply(String(str).split('')).join('');
249
- }),
201
+ clean: function(str){
202
+ return _s.strip(str).replace(/\s+/g, ' ');
203
+ },
250
204
 
251
- splice: sArgs(function(str, i, howmany, substr){
252
- var arr = str.split('');
253
- arr.splice(parseNumber(i), parseNumber(howmany), substr);
254
- return arr.join('');
255
- }),
205
+ count: function(str, substr){
206
+ if (str == null || substr == null) return 0;
207
+ return String(str).split(substr).length - 1;
208
+ },
256
209
 
257
- startsWith: sArgs(function(str, starts){
258
- return str.length >= starts.length && str.substring(0, starts.length) === starts;
259
- }),
210
+ chars: function(str) {
211
+ if (str == null) return [];
212
+ return String(str).split('');
213
+ },
260
214
 
261
- endsWith: sArgs(function(str, ends){
262
- return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
263
- }),
215
+ swapCase: function(str) {
216
+ if (str == null) return '';
217
+ return String(str).replace(/\S/g, function(c){
218
+ return c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase();
219
+ });
220
+ },
264
221
 
265
- succ: sArgs(function(str){
266
- var arr = str.split('');
267
- arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1));
268
- return arr.join('');
269
- }),
270
-
271
- titleize: sArgs(function(str){
272
- var arr = str.split(' '),
273
- word;
274
- for (var i=0; i < arr.length; i++) {
275
- word = arr[i].split('');
276
- if(typeof word[0] !== 'undefined') word[0] = word[0].toUpperCase();
277
- i+1 === arr.length ? arr[i] = word.join('') : arr[i] = word.join('') + ' ';
278
- }
279
- return arr.join('');
280
- }),
222
+ escapeHTML: function(str) {
223
+ if (str == null) return '';
224
+ return String(str).replace(/[&<>"']/g, function(m){ return '&' + reversedEscapeChars[m] + ';'; });
225
+ },
281
226
 
282
- camelize: sArgs(function(str){
283
- return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
284
- return chr ? chr.toUpperCase() : '';
227
+ unescapeHTML: function(str) {
228
+ if (str == null) return '';
229
+ return String(str).replace(/\&([^;]+);/g, function(entity, entityCode){
230
+ var match;
231
+
232
+ if (entityCode in escapeChars) {
233
+ return escapeChars[entityCode];
234
+ } else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) {
235
+ return String.fromCharCode(parseInt(match[1], 16));
236
+ } else if (match = entityCode.match(/^#(\d+)$/)) {
237
+ return String.fromCharCode(~~match[1]);
238
+ } else {
239
+ return entity;
240
+ }
285
241
  });
286
- }),
242
+ },
243
+
244
+ escapeRegExp: function(str){
245
+ if (str == null) return '';
246
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
247
+ },
248
+
249
+ splice: function(str, i, howmany, substr){
250
+ var arr = _s.chars(str);
251
+ arr.splice(~~i, ~~howmany, substr);
252
+ return arr.join('');
253
+ },
254
+
255
+ insert: function(str, i, substr){
256
+ return _s.splice(str, i, 0, substr);
257
+ },
258
+
259
+ include: function(str, needle){
260
+ if (needle === '') return true;
261
+ if (str == null) return false;
262
+ return String(str).indexOf(needle) !== -1;
263
+ },
264
+
265
+ join: function() {
266
+ var args = slice.call(arguments),
267
+ separator = args.shift();
268
+
269
+ if (separator == null) separator = '';
270
+
271
+ return args.join(separator);
272
+ },
273
+
274
+ lines: function(str) {
275
+ if (str == null) return [];
276
+ return String(str).split("\n");
277
+ },
278
+
279
+ reverse: function(str){
280
+ return _s.chars(str).reverse().join('');
281
+ },
282
+
283
+ startsWith: function(str, starts){
284
+ if (starts === '') return true;
285
+ if (str == null || starts == null) return false;
286
+ str = String(str); starts = String(starts);
287
+ return str.length >= starts.length && str.slice(0, starts.length) === starts;
288
+ },
289
+
290
+ endsWith: function(str, ends){
291
+ if (ends === '') return true;
292
+ if (str == null || ends == null) return false;
293
+ str = String(str); ends = String(ends);
294
+ return str.length >= ends.length && str.slice(str.length - ends.length) === ends;
295
+ },
296
+
297
+ succ: function(str){
298
+ if (str == null) return '';
299
+ str = String(str);
300
+ return str.slice(0, -1) + String.fromCharCode(str.charCodeAt(str.length-1) + 1);
301
+ },
302
+
303
+ titleize: function(str){
304
+ if (str == null) return '';
305
+ return String(str).replace(/(?:^|\s)\S/g, function(c){ return c.toUpperCase(); });
306
+ },
307
+
308
+ camelize: function(str){
309
+ return _s.trim(str).replace(/[-_\s]+(.)?/g, function(match, c){ return c.toUpperCase(); });
310
+ },
287
311
 
288
312
  underscored: function(str){
289
- return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
313
+ return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
290
314
  },
291
315
 
292
316
  dasherize: function(str){
293
- return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/^([A-Z]+)/, '-$1').replace(/\_|\s+/g, '-').toLowerCase();
317
+ return _s.trim(str).replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase();
318
+ },
319
+
320
+ classify: function(str){
321
+ return _s.titleize(String(str).replace(/_/g, ' ')).replace(/\s/g, '');
294
322
  },
295
323
 
296
324
  humanize: function(str){
297
- return _s.capitalize(this.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
325
+ return _s.capitalize(_s.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
298
326
  },
299
327
 
300
- trim: sArgs(function(str, characters){
301
- if (!characters && nativeTrim) {
302
- return nativeTrim.call(str);
303
- }
328
+ trim: function(str, characters){
329
+ if (str == null) return '';
330
+ if (!characters && nativeTrim) return nativeTrim.call(str);
304
331
  characters = defaultToWhiteSpace(characters);
305
- return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
306
- }),
332
+ return String(str).replace(new RegExp('\^' + characters + '+|' + characters + '+$', 'g'), '');
333
+ },
307
334
 
308
- ltrim: sArgs(function(str, characters){
335
+ ltrim: function(str, characters){
336
+ if (str == null) return '';
337
+ if (!characters && nativeTrimLeft) return nativeTrimLeft.call(str);
309
338
  characters = defaultToWhiteSpace(characters);
310
- return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
311
- }),
339
+ return String(str).replace(new RegExp('^' + characters + '+'), '');
340
+ },
312
341
 
313
- rtrim: sArgs(function(str, characters){
342
+ rtrim: function(str, characters){
343
+ if (str == null) return '';
344
+ if (!characters && nativeTrimRight) return nativeTrimRight.call(str);
314
345
  characters = defaultToWhiteSpace(characters);
315
- return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
316
- }),
346
+ return String(str).replace(new RegExp(characters + '+$'), '');
347
+ },
317
348
 
318
- truncate: sArgs(function(str, length, truncateStr){
319
- truncateStr = truncateStr || '...';
320
- length = parseNumber(length);
321
- return str.length > length ? str.slice(0,length) + truncateStr : str;
322
- }),
349
+ truncate: function(str, length, truncateStr){
350
+ if (str == null) return '';
351
+ str = String(str); truncateStr = truncateStr || '...';
352
+ length = ~~length;
353
+ return str.length > length ? str.slice(0, length) + truncateStr : str;
354
+ },
323
355
 
324
356
  /**
325
357
  * _s.prune: a more elegant version of truncate
326
358
  * prune extra chars, never leaving a half-chopped word.
327
- * @author github.com/sergiokas
359
+ * @author github.com/rwz
328
360
  */
329
- prune: sArgs(function(str, length, pruneStr){
330
- // Function to check word/digit chars including non-ASCII encodings.
331
- var isWordChar = function(c) { return ((c.toUpperCase() != c.toLowerCase()) || /[-_\d]/.test(c)); }
332
-
333
- var template = '';
334
- var pruned = '';
335
- var i = 0;
336
-
337
- // Set default values
338
- pruneStr = pruneStr || '...';
339
- length = parseNumber(length);
340
-
341
- // Convert to an ASCII string to avoid problems with unicode chars.
342
- for (i in str) {
343
- template += (isWordChar(str[i]))?'A':' ';
344
- }
345
-
346
- // Check if we're in the middle of a word
347
- if( template.substring(length-1, length+1).search(/^\w\w$/) === 0 )
348
- pruned = _s.rtrim(template.slice(0,length).replace(/([\W][\w]*)$/,''));
349
- else
350
- pruned = _s.rtrim(template.slice(0,length));
361
+ prune: function(str, length, pruneStr){
362
+ if (str == null) return '';
363
+
364
+ str = String(str); length = ~~length;
365
+ pruneStr = pruneStr != null ? String(pruneStr) : '...';
366
+
367
+ if (str.length <= length) return str;
351
368
 
352
- pruned = pruned.replace(/\W+$/,'');
369
+ var tmpl = function(c){ return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; },
370
+ template = str.slice(0, length+1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA'
353
371
 
354
- return (pruned.length+pruneStr.length>str.length) ? str : str.substring(0, pruned.length)+pruneStr;
355
- }),
372
+ if (template.slice(template.length-2).match(/\w\w/))
373
+ template = template.replace(/\s*\S+$/, '');
374
+ else
375
+ template = _s.rtrim(template.slice(0, template.length-1));
376
+
377
+ return (template+pruneStr).length > str.length ? str : str.slice(0, template.length)+pruneStr;
378
+ },
356
379
 
357
380
  words: function(str, delimiter) {
358
- return String(str).split(delimiter || " ");
381
+ if (str == null || str == '') return '';
382
+ return _s.trim(str, delimiter).split(delimiter || /\s+/);
359
383
  },
360
384
 
361
- pad: sArgs(function(str, length, padStr, type) {
362
- var padding = '',
363
- padlen = 0;
385
+ pad: function(str, length, padStr, type) {
386
+ str = str == null ? '' : String(str);
387
+ length = ~~length;
364
388
 
365
- length = parseNumber(length);
389
+ var padlen = 0;
390
+
391
+ if (!padStr)
392
+ padStr = ' ';
393
+ else if (padStr.length > 1)
394
+ padStr = padStr.charAt(0);
366
395
 
367
- if (!padStr) { padStr = ' '; }
368
- else if (padStr.length > 1) { padStr = padStr.charAt(0); }
369
396
  switch(type) {
370
397
  case 'right':
371
- padlen = (length - str.length);
372
- padding = strRepeat(padStr, padlen);
373
- str = str+padding;
374
- break;
398
+ padlen = length - str.length;
399
+ return str + strRepeat(padStr, padlen);
375
400
  case 'both':
376
- padlen = (length - str.length);
377
- padding = {
378
- 'left' : strRepeat(padStr, Math.ceil(padlen/2)),
379
- 'right': strRepeat(padStr, Math.floor(padlen/2))
380
- };
381
- str = padding.left+str+padding.right;
382
- break;
401
+ padlen = length - str.length;
402
+ return strRepeat(padStr, Math.ceil(padlen/2)) + str
403
+ + strRepeat(padStr, Math.floor(padlen/2));
383
404
  default: // 'left'
384
- padlen = (length - str.length);
385
- padding = strRepeat(padStr, padlen);;
386
- str = padding+str;
405
+ padlen = length - str.length;
406
+ return strRepeat(padStr, padlen) + str;
387
407
  }
388
- return str;
389
- }),
408
+ },
390
409
 
391
410
  lpad: function(str, length, padStr) {
392
411
  return _s.pad(str, length, padStr);
@@ -408,41 +427,132 @@
408
427
  },
409
428
 
410
429
  toNumber: function(str, decimals) {
411
- var num = parseNumber(parseNumber(str).toFixed(parseNumber(decimals)));
412
- return (!(num === 0 && (str !== "0" && str !== 0))) ? num : Number.NaN;
430
+ if (str == null || str == '') return 0;
431
+ str = String(str);
432
+ var num = parseNumber(parseNumber(str).toFixed(~~decimals));
433
+ return num === 0 && !str.match(/^0+$/) ? Number.NaN : num;
434
+ },
435
+
436
+ numberFormat : function(number, dec, dsep, tsep) {
437
+ if (isNaN(number) || number == null) return '';
438
+
439
+ number = number.toFixed(~~dec);
440
+ tsep = tsep || ',';
441
+
442
+ var parts = number.split('.'), fnums = parts[0],
443
+ decimals = parts[1] ? (dsep || '.') + parts[1] : '';
444
+
445
+ return fnums.replace(/(\d)(?=(?:\d{3})+$)/g, '$1' + tsep) + decimals;
446
+ },
447
+
448
+ strRight: function(str, sep){
449
+ if (str == null) return '';
450
+ str = String(str); sep = sep != null ? String(sep) : sep;
451
+ var pos = !sep ? -1 : str.indexOf(sep);
452
+ return ~pos ? str.slice(pos+sep.length, str.length) : str;
453
+ },
454
+
455
+ strRightBack: function(str, sep){
456
+ if (str == null) return '';
457
+ str = String(str); sep = sep != null ? String(sep) : sep;
458
+ var pos = !sep ? -1 : str.lastIndexOf(sep);
459
+ return ~pos ? str.slice(pos+sep.length, str.length) : str;
460
+ },
461
+
462
+ strLeft: function(str, sep){
463
+ if (str == null) return '';
464
+ str = String(str); sep = sep != null ? String(sep) : sep;
465
+ var pos = !sep ? -1 : str.indexOf(sep);
466
+ return ~pos ? str.slice(0, pos) : str;
413
467
  },
414
468
 
415
- strRight: sArgs(function(sourceStr, sep){
416
- var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
417
- return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
418
- }),
469
+ strLeftBack: function(str, sep){
470
+ if (str == null) return '';
471
+ str += ''; sep = sep != null ? ''+sep : sep;
472
+ var pos = str.lastIndexOf(sep);
473
+ return ~pos ? str.slice(0, pos) : str;
474
+ },
475
+
476
+ toSentence: function(array, separator, lastSeparator) {
477
+ separator = separator || ', '
478
+ lastSeparator = lastSeparator || ' and '
479
+ var a = array.slice(), lastMember = a.pop();
480
+
481
+ return a.length ? a.join(separator) + lastSeparator + lastMember : lastMember;
482
+ },
419
483
 
420
- strRightBack: sArgs(function(sourceStr, sep){
421
- var pos = (!sep) ? -1 : sourceStr.lastIndexOf(sep);
422
- return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
423
- }),
484
+ slugify: function(str) {
485
+ if (str == null) return '';
424
486
 
425
- strLeft: sArgs(function(sourceStr, sep){
426
- var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
427
- return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
428
- }),
487
+ var from = "ąàáäâãćęèéëêìíïîłńòóöôõùúüûñçżź",
488
+ to = "aaaaaaceeeeeiiiilnooooouuuunczz",
489
+ regex = new RegExp(defaultToWhiteSpace(from), 'g');
429
490
 
430
- strLeftBack: sArgs(function(sourceStr, sep){
431
- var pos = sourceStr.lastIndexOf(sep);
432
- return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
433
- }),
491
+ str = String(str).toLowerCase().replace(regex, function(c){
492
+ var index = from.indexOf(c);
493
+ return to.charAt(index) || '-';
494
+ });
495
+
496
+ return _s.dasherize(str.replace(/[^\w\s-]/g, ''));
497
+ },
498
+
499
+ surround: function(str, wrapper) {
500
+ return [wrapper, str, wrapper].join('');
501
+ },
502
+
503
+ quote: function(str) {
504
+ return _s.surround(str, '"');
505
+ },
434
506
 
435
507
  exports: function() {
436
508
  var result = {};
437
509
 
438
510
  for (var prop in this) {
439
- if (!this.hasOwnProperty(prop) || prop == 'include' || prop == 'contains' || prop == 'reverse') continue;
511
+ if (!this.hasOwnProperty(prop) || prop.match(/^(?:include|contains|reverse)$/)) continue;
440
512
  result[prop] = this[prop];
441
513
  }
442
514
 
443
515
  return result;
444
- }
516
+ },
445
517
 
518
+ repeat: function(str, qty, separator){
519
+ if (str == null) return '';
520
+
521
+ qty = ~~qty;
522
+
523
+ // using faster implementation if separator is not needed;
524
+ if (separator == null) return strRepeat(String(str), qty);
525
+
526
+ // this one is about 300x slower in Google Chrome
527
+ for (var repeat = []; qty > 0; repeat[--qty] = str) {}
528
+ return repeat.join(separator);
529
+ },
530
+
531
+ levenshtein: function(str1, str2) {
532
+ if (str1 == null && str2 == null) return 0;
533
+ if (str1 == null) return String(str2).length;
534
+ if (str2 == null) return String(str1).length;
535
+
536
+ str1 = String(str1); str2 = String(str2);
537
+
538
+ var current = [], prev, value;
539
+
540
+ for (var i = 0; i <= str2.length; i++)
541
+ for (var j = 0; j <= str1.length; j++) {
542
+ if (i && j)
543
+ if (str1.charAt(j - 1) === str2.charAt(i - 1))
544
+ value = prev;
545
+ else
546
+ value = Math.min(current[j], current[j - 1], prev) + 1;
547
+ else
548
+ value = i + j;
549
+
550
+ prev = current[j];
551
+ current[j] = value;
552
+ }
553
+
554
+ return current.pop();
555
+ }
446
556
  };
447
557
 
448
558
  // Aliases
@@ -451,9 +561,10 @@
451
561
  _s.lstrip = _s.ltrim;
452
562
  _s.rstrip = _s.rtrim;
453
563
  _s.center = _s.lrpad;
454
- _s.ljust = _s.lpad;
455
- _s.rjust = _s.rpad;
564
+ _s.rjust = _s.lpad;
565
+ _s.ljust = _s.rpad;
456
566
  _s.contains = _s.include;
567
+ _s.q = _s.quote;
457
568
 
458
569
  // CommonJS module is defined
459
570
  if (typeof exports !== 'undefined') {
@@ -463,18 +574,17 @@
463
574
  }
464
575
  exports._s = _s;
465
576
 
466
- // Integrate with Underscore.js
467
- } else if (typeof root._ !== 'undefined') {
468
- // root._.mixin(_s);
469
- root._.string = _s;
470
- root._.str = root._.string;
577
+ } else if (typeof define === 'function' && define.amd) {
578
+ // Register as a named module with AMD.
579
+ define('underscore.string', [], function() {
580
+ return _s;
581
+ });
471
582
 
472
- // Or define it
473
583
  } else {
474
- root._ = {
475
- string: _s,
476
- str: _s
477
- };
584
+ // Integrate with Underscore.js if defined
585
+ // or create our own underscore object.
586
+ root._ = root._ || {};
587
+ root._.string = root._.str = _s;
478
588
  }
479
589
 
480
- }(this || window));
590
+ }(this, String);