underscore-string-rails 0.0.1 → 0.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0fe03c79e0fe827f6c7997ec5b1dc377e4e09eef
4
+ data.tar.gz: bcee371bbeb18202063243fa762e0357579fa233
5
+ SHA512:
6
+ metadata.gz: c4a9e608f3ea67c5accf2511dad01fbd5aa940c533401d01a9b1e571cf1b35179a87dfc0aa917e8733a6458c1cd3bfa076fc8330e9c4fe38b8483403a378b9d3
7
+ data.tar.gz: 0ce1c57b161fa9f241ef373bd1042d477d99067a520d5e103e765a75a32abdaed1cceacd1b9e6e697e74f766cfca0a93b385417dd3246c92f1c9e9c7bfd7930e
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # underscore-string-rails
2
2
 
3
- This includes the underscore string library in your application
3
+ This includes the [underscore.string](https://github.com/epeli/underscore.string) library in your application
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,9 +8,13 @@ Add this line to your application's Gemfile:
8
8
 
9
9
  gem 'underscore-string-rails'
10
10
 
11
+ Optionally add a dependency on [underscore-rails](https://github.com/rweng/underscore-rails):
12
+
13
+ gem 'underscore-rails'
14
+
11
15
  And then execute:
12
16
 
13
- $ bundle
17
+ $ bundle install
14
18
 
15
19
  Or install it yourself as:
16
20
 
@@ -18,21 +22,11 @@ Or install it yourself as:
18
22
 
19
23
  ## Usage
20
24
 
21
- Optionally include underscore in your project:
22
-
23
- `//= require underscore`
24
-
25
- Minified version:
26
-
27
- `//= require underscore-min`
28
-
29
- In your application.js manifest:
25
+ In any JavaScript file:
30
26
 
31
27
  `//= require underscore.string`
32
28
 
33
- For minimal compressed version:
34
-
35
- `//= require underscore.string.min`
29
+ It will be minified/uglified/etc. as part of the asset pipeline.
36
30
 
37
31
  ## Contributing
38
32
 
@@ -1,7 +1,7 @@
1
1
  module Underscore
2
2
  module String
3
3
  module Rails
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
6
6
  end
7
7
  end
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
6
6
  gem.email = ["jaimie@jaimievansanten.nl"]
7
7
  gem.description = %q{This includes the underscore string library in your application}
8
8
  gem.summary = %q{asset pipline javascript underscore rails}
9
- gem.homepage = "https://github.com/jaimie-van-santen/underscore-string-rails"
9
+ gem.homepage = ""
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
12
12
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -1,45 +1,66 @@
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.3.3'
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);
29
+ var slice = [].slice;
30
+
31
+ var defaultToWhiteSpace = function(characters) {
32
+ if (characters == null)
33
+ return '\\s';
34
+ else if (characters.source)
35
+ return characters.source;
36
+ else
37
+ return '[' + _s.escapeRegExp(characters) + ']';
25
38
  };
26
39
 
27
- var defaultToWhiteSpace = function(characters){
28
- if (characters) {
29
- return _s.escapeRegExp(characters);
40
+ // Helper for toBoolean
41
+ function boolMatch(s, matchers) {
42
+ var i, matcher, down = s.toLowerCase();
43
+ matchers = [].concat(matchers);
44
+ for (i = 0; i < matchers.length; i += 1) {
45
+ matcher = matchers[i];
46
+ if (!matcher) continue;
47
+ if (matcher.test && matcher.test(s)) return true;
48
+ if (matcher.toLowerCase() === down) return true;
30
49
  }
31
- return '\\s';
32
- };
50
+ }
33
51
 
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
- };
52
+ var escapeChars = {
53
+ lt: '<',
54
+ gt: '>',
55
+ quot: '"',
56
+ amp: '&',
57
+ apos: "'"
41
58
  };
42
59
 
60
+ var reversedEscapeChars = {};
61
+ for(var key in escapeChars) reversedEscapeChars[escapeChars[key]] = key;
62
+ reversedEscapeChars["'"] = '#39';
63
+
43
64
  // sprintf() for JavaScript 0.7-beta1
44
65
  // http://www.diveintojavascript.com/projects/javascript-sprintf
45
66
  //
@@ -73,7 +94,7 @@
73
94
  arg = argv[cursor];
74
95
  for (k = 0; k < match[2].length; k++) {
75
96
  if (!arg.hasOwnProperty(match[2][k])) {
76
- throw(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
97
+ throw new Error(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
77
98
  }
78
99
  arg = arg[match[2][k]];
79
100
  }
@@ -85,7 +106,7 @@
85
106
  }
86
107
 
87
108
  if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
88
- throw(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
109
+ throw new Error(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
89
110
  }
90
111
  switch (match[8]) {
91
112
  case 'b': arg = arg.toString(2); break;
@@ -134,12 +155,12 @@
134
155
  field_list.push(field_match[1]);
135
156
  }
136
157
  else {
137
- throw('[_.sprintf] huh?');
158
+ throw new Error('[_.sprintf] huh?');
138
159
  }
139
160
  }
140
161
  }
141
162
  else {
142
- throw('[_.sprintf] huh?');
163
+ throw new Error('[_.sprintf] huh?');
143
164
  }
144
165
  match[2] = field_list;
145
166
  }
@@ -147,12 +168,12 @@
147
168
  arg_names |= 2;
148
169
  }
149
170
  if (arg_names === 3) {
150
- throw('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
171
+ throw new Error('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
151
172
  }
152
173
  parse_tree.push(match);
153
174
  }
154
175
  else {
155
- throw('[_.sprintf] huh?');
176
+ throw new Error('[_.sprintf] huh?');
156
177
  }
157
178
  _fmt = _fmt.substring(match[0].length);
158
179
  }
@@ -167,226 +188,255 @@
167
188
  // Defining underscore.string
168
189
 
169
190
  var _s = {
170
-
171
- VERSION: '2.0.0',
172
191
 
173
- isBlank: sArgs(function(str){
192
+ VERSION: '2.3.0',
193
+
194
+ isBlank: function(str){
195
+ if (str == null) str = '';
174
196
  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;
197
+ },
198
+
199
+ stripTags: function(str){
200
+ if (str == null) return '';
201
+ return String(str).replace(/<\/?[^>]+>/g, '');
202
+ },
203
+
204
+ capitalize : function(str){
205
+ str = str == null ? '' : String(str);
206
+ return str.charAt(0).toUpperCase() + str.slice(1);
207
+ },
208
+
209
+ chop: function(str, step){
210
+ if (str == null) return [];
211
+ str = String(str);
212
+ step = ~~step;
213
+ return step > 0 ? str.match(new RegExp('.{1,' + step + '}', 'g')) : [str];
214
+ },
215
+
216
+ clean: function(str){
217
+ return _s.strip(str).replace(/\s+/g, ' ');
218
+ },
219
+
220
+ count: function(str, substr){
221
+ if (str == null || substr == null) return 0;
222
+
223
+ str = String(str);
224
+ substr = String(substr);
225
+
226
+ var count = 0,
227
+ pos = 0,
228
+ length = substr.length;
229
+
230
+ while (true) {
231
+ pos = str.indexOf(substr, pos);
232
+ if (pos === -1) break;
233
+ count++;
234
+ pos += length;
205
235
  }
236
+
206
237
  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
- }),
238
+ },
233
239
 
234
- include: sArgs(function(str, needle){
235
- return str.indexOf(needle) !== -1;
236
- }),
240
+ chars: function(str) {
241
+ if (str == null) return [];
242
+ return String(str).split('');
243
+ },
237
244
 
238
- join: sArgs(function(sep) {
239
- var args = slice(arguments);
240
- return args.join(args.shift());
241
- }),
245
+ swapCase: function(str) {
246
+ if (str == null) return '';
247
+ return String(str).replace(/\S/g, function(c){
248
+ return c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase();
249
+ });
250
+ },
242
251
 
243
- lines: sArgs(function(str) {
244
- return str.split("\n");
245
- }),
252
+ escapeHTML: function(str) {
253
+ if (str == null) return '';
254
+ return String(str).replace(/[&<>"']/g, function(m){ return '&' + reversedEscapeChars[m] + ';'; });
255
+ },
246
256
 
247
- reverse: sArgs(function(str){
248
- return Array.prototype.reverse.apply(String(str).split('')).join('');
249
- }),
257
+ unescapeHTML: function(str) {
258
+ if (str == null) return '';
259
+ return String(str).replace(/\&([^;]+);/g, function(entity, entityCode){
260
+ var match;
261
+
262
+ if (entityCode in escapeChars) {
263
+ return escapeChars[entityCode];
264
+ } else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) {
265
+ return String.fromCharCode(parseInt(match[1], 16));
266
+ } else if (match = entityCode.match(/^#(\d+)$/)) {
267
+ return String.fromCharCode(~~match[1]);
268
+ } else {
269
+ return entity;
270
+ }
271
+ });
272
+ },
250
273
 
251
- splice: sArgs(function(str, i, howmany, substr){
252
- var arr = str.split('');
253
- arr.splice(parseNumber(i), parseNumber(howmany), substr);
274
+ escapeRegExp: function(str){
275
+ if (str == null) return '';
276
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
277
+ },
278
+
279
+ splice: function(str, i, howmany, substr){
280
+ var arr = _s.chars(str);
281
+ arr.splice(~~i, ~~howmany, substr);
254
282
  return arr.join('');
255
- }),
283
+ },
256
284
 
257
- startsWith: sArgs(function(str, starts){
258
- return str.length >= starts.length && str.substring(0, starts.length) === starts;
259
- }),
285
+ insert: function(str, i, substr){
286
+ return _s.splice(str, i, 0, substr);
287
+ },
260
288
 
261
- endsWith: sArgs(function(str, ends){
262
- return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
263
- }),
289
+ include: function(str, needle){
290
+ if (needle === '') return true;
291
+ if (str == null) return false;
292
+ return String(str).indexOf(needle) !== -1;
293
+ },
264
294
 
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
- }),
295
+ join: function() {
296
+ var args = slice.call(arguments),
297
+ separator = args.shift();
281
298
 
282
- camelize: sArgs(function(str){
283
- return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
284
- return chr ? chr.toUpperCase() : '';
285
- });
286
- }),
299
+ if (separator == null) separator = '';
300
+
301
+ return args.join(separator);
302
+ },
303
+
304
+ lines: function(str) {
305
+ if (str == null) return [];
306
+ return String(str).split("\n");
307
+ },
308
+
309
+ reverse: function(str){
310
+ return _s.chars(str).reverse().join('');
311
+ },
312
+
313
+ startsWith: function(str, starts){
314
+ if (starts === '') return true;
315
+ if (str == null || starts == null) return false;
316
+ str = String(str); starts = String(starts);
317
+ return str.length >= starts.length && str.slice(0, starts.length) === starts;
318
+ },
319
+
320
+ endsWith: function(str, ends){
321
+ if (ends === '') return true;
322
+ if (str == null || ends == null) return false;
323
+ str = String(str); ends = String(ends);
324
+ return str.length >= ends.length && str.slice(str.length - ends.length) === ends;
325
+ },
326
+
327
+ succ: function(str){
328
+ if (str == null) return '';
329
+ str = String(str);
330
+ return str.slice(0, -1) + String.fromCharCode(str.charCodeAt(str.length-1) + 1);
331
+ },
332
+
333
+ titleize: function(str){
334
+ if (str == null) return '';
335
+ str = String(str).toLowerCase();
336
+ return str.replace(/(?:^|\s|-)\S/g, function(c){ return c.toUpperCase(); });
337
+ },
338
+
339
+ camelize: function(str){
340
+ return _s.trim(str).replace(/[-_\s]+(.)?/g, function(match, c){ return c ? c.toUpperCase() : ""; });
341
+ },
287
342
 
288
343
  underscored: function(str){
289
- return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
344
+ return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
290
345
  },
291
346
 
292
347
  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();
348
+ return _s.trim(str).replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase();
349
+ },
350
+
351
+ classify: function(str){
352
+ return _s.titleize(String(str).replace(/[\W_]/g, ' ')).replace(/\s/g, '');
294
353
  },
295
354
 
296
355
  humanize: function(str){
297
- return _s.capitalize(this.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
356
+ return _s.capitalize(_s.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
298
357
  },
299
358
 
300
- trim: sArgs(function(str, characters){
301
- if (!characters && nativeTrim) {
302
- return nativeTrim.call(str);
303
- }
359
+ trim: function(str, characters){
360
+ if (str == null) return '';
361
+ if (!characters && nativeTrim) return nativeTrim.call(str);
304
362
  characters = defaultToWhiteSpace(characters);
305
- return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
306
- }),
363
+ return String(str).replace(new RegExp('\^' + characters + '+|' + characters + '+$', 'g'), '');
364
+ },
307
365
 
308
- ltrim: sArgs(function(str, characters){
366
+ ltrim: function(str, characters){
367
+ if (str == null) return '';
368
+ if (!characters && nativeTrimLeft) return nativeTrimLeft.call(str);
309
369
  characters = defaultToWhiteSpace(characters);
310
- return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
311
- }),
370
+ return String(str).replace(new RegExp('^' + characters + '+'), '');
371
+ },
312
372
 
313
- rtrim: sArgs(function(str, characters){
373
+ rtrim: function(str, characters){
374
+ if (str == null) return '';
375
+ if (!characters && nativeTrimRight) return nativeTrimRight.call(str);
314
376
  characters = defaultToWhiteSpace(characters);
315
- return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
316
- }),
377
+ return String(str).replace(new RegExp(characters + '+$'), '');
378
+ },
317
379
 
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
- }),
380
+ truncate: function(str, length, truncateStr){
381
+ if (str == null) return '';
382
+ str = String(str); truncateStr = truncateStr || '...';
383
+ length = ~~length;
384
+ return str.length > length ? str.slice(0, length) + truncateStr : str;
385
+ },
323
386
 
324
387
  /**
325
388
  * _s.prune: a more elegant version of truncate
326
389
  * prune extra chars, never leaving a half-chopped word.
327
- * @author github.com/sergiokas
390
+ * @author github.com/rwz
328
391
  */
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));
392
+ prune: function(str, length, pruneStr){
393
+ if (str == null) return '';
394
+
395
+ str = String(str); length = ~~length;
396
+ pruneStr = pruneStr != null ? String(pruneStr) : '...';
351
397
 
352
- pruned = pruned.replace(/\W+$/,'');
398
+ if (str.length <= length) return str;
353
399
 
354
- return (pruned.length+pruneStr.length>str.length) ? str : str.substring(0, pruned.length)+pruneStr;
355
- }),
400
+ var tmpl = function(c){ return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; },
401
+ template = str.slice(0, length+1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA'
402
+
403
+ if (template.slice(template.length-2).match(/\w\w/))
404
+ template = template.replace(/\s*\S+$/, '');
405
+ else
406
+ template = _s.rtrim(template.slice(0, template.length-1));
407
+
408
+ return (template+pruneStr).length > str.length ? str : str.slice(0, template.length)+pruneStr;
409
+ },
356
410
 
357
411
  words: function(str, delimiter) {
358
- return String(str).split(delimiter || " ");
412
+ if (_s.isBlank(str)) return [];
413
+ return _s.trim(str, delimiter).split(delimiter || /\s+/);
359
414
  },
360
415
 
361
- pad: sArgs(function(str, length, padStr, type) {
362
- var padding = '',
363
- padlen = 0;
416
+ pad: function(str, length, padStr, type) {
417
+ str = str == null ? '' : String(str);
418
+ length = ~~length;
419
+
420
+ var padlen = 0;
364
421
 
365
- length = parseNumber(length);
422
+ if (!padStr)
423
+ padStr = ' ';
424
+ else if (padStr.length > 1)
425
+ padStr = padStr.charAt(0);
366
426
 
367
- if (!padStr) { padStr = ' '; }
368
- else if (padStr.length > 1) { padStr = padStr.charAt(0); }
369
427
  switch(type) {
370
428
  case 'right':
371
- padlen = (length - str.length);
372
- padding = strRepeat(padStr, padlen);
373
- str = str+padding;
374
- break;
429
+ padlen = length - str.length;
430
+ return str + strRepeat(padStr, padlen);
375
431
  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;
432
+ padlen = length - str.length;
433
+ return strRepeat(padStr, Math.ceil(padlen/2)) + str
434
+ + strRepeat(padStr, Math.floor(padlen/2));
383
435
  default: // 'left'
384
- padlen = (length - str.length);
385
- padding = strRepeat(padStr, padlen);;
386
- str = padding+str;
436
+ padlen = length - str.length;
437
+ return strRepeat(padStr, padlen) + str;
387
438
  }
388
- return str;
389
- }),
439
+ },
390
440
 
391
441
  lpad: function(str, length, padStr) {
392
442
  return _s.pad(str, length, padStr);
@@ -408,41 +458,185 @@
408
458
  },
409
459
 
410
460
  toNumber: function(str, decimals) {
411
- var num = parseNumber(parseNumber(str).toFixed(parseNumber(decimals)));
412
- return (!(num === 0 && (str !== "0" && str !== 0))) ? num : Number.NaN;
461
+ if (!str) return 0;
462
+ str = _s.trim(str);
463
+ if (!str.match(/^-?\d+(?:\.\d+)?$/)) return NaN;
464
+ return parseNumber(parseNumber(str).toFixed(~~decimals));
413
465
  },
414
466
 
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
- }),
467
+ numberFormat : function(number, dec, dsep, tsep) {
468
+ if (isNaN(number) || number == null) return '';
419
469
 
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
- }),
470
+ number = number.toFixed(~~dec);
471
+ tsep = typeof tsep == 'string' ? tsep : ',';
424
472
 
425
- strLeft: sArgs(function(sourceStr, sep){
426
- var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
427
- return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
428
- }),
473
+ var parts = number.split('.'), fnums = parts[0],
474
+ decimals = parts[1] ? (dsep || '.') + parts[1] : '';
429
475
 
430
- strLeftBack: sArgs(function(sourceStr, sep){
431
- var pos = sourceStr.lastIndexOf(sep);
432
- return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
433
- }),
476
+ return fnums.replace(/(\d)(?=(?:\d{3})+$)/g, '$1' + tsep) + decimals;
477
+ },
478
+
479
+ strRight: function(str, sep){
480
+ if (str == null) return '';
481
+ str = String(str); sep = sep != null ? String(sep) : sep;
482
+ var pos = !sep ? -1 : str.indexOf(sep);
483
+ return ~pos ? str.slice(pos+sep.length, str.length) : str;
484
+ },
485
+
486
+ strRightBack: function(str, sep){
487
+ if (str == null) return '';
488
+ str = String(str); sep = sep != null ? String(sep) : sep;
489
+ var pos = !sep ? -1 : str.lastIndexOf(sep);
490
+ return ~pos ? str.slice(pos+sep.length, str.length) : str;
491
+ },
492
+
493
+ strLeft: function(str, sep){
494
+ if (str == null) return '';
495
+ str = String(str); sep = sep != null ? String(sep) : sep;
496
+ var pos = !sep ? -1 : str.indexOf(sep);
497
+ return ~pos ? str.slice(0, pos) : str;
498
+ },
499
+
500
+ strLeftBack: function(str, sep){
501
+ if (str == null) return '';
502
+ str += ''; sep = sep != null ? ''+sep : sep;
503
+ var pos = str.lastIndexOf(sep);
504
+ return ~pos ? str.slice(0, pos) : str;
505
+ },
506
+
507
+ toSentence: function(array, separator, lastSeparator, serial) {
508
+ separator = separator || ', ';
509
+ lastSeparator = lastSeparator || ' and ';
510
+ var a = array.slice(), lastMember = a.pop();
511
+
512
+ if (array.length > 2 && serial) lastSeparator = _s.rtrim(separator) + lastSeparator;
513
+
514
+ return a.length ? a.join(separator) + lastSeparator + lastMember : lastMember;
515
+ },
516
+
517
+ toSentenceSerial: function() {
518
+ var args = slice.call(arguments);
519
+ args[3] = true;
520
+ return _s.toSentence.apply(_s, args);
521
+ },
522
+
523
+ slugify: function(str) {
524
+ if (str == null) return '';
525
+
526
+ var from = "ąàáäâãåæăćęèéëêìíïîłńòóöôõøśșțùúüûñçżź",
527
+ to = "aaaaaaaaaceeeeeiiiilnoooooosstuuuunczz",
528
+ regex = new RegExp(defaultToWhiteSpace(from), 'g');
529
+
530
+ str = String(str).toLowerCase().replace(regex, function(c){
531
+ var index = from.indexOf(c);
532
+ return to.charAt(index) || '-';
533
+ });
534
+
535
+ return _s.dasherize(str.replace(/[^\w\s-]/g, ''));
536
+ },
537
+
538
+ surround: function(str, wrapper) {
539
+ return [wrapper, str, wrapper].join('');
540
+ },
541
+
542
+ quote: function(str, quoteChar) {
543
+ return _s.surround(str, quoteChar || '"');
544
+ },
545
+
546
+ unquote: function(str, quoteChar) {
547
+ quoteChar = quoteChar || '"';
548
+ if (str[0] === quoteChar && str[str.length-1] === quoteChar)
549
+ return str.slice(1,str.length-1);
550
+ else return str;
551
+ },
434
552
 
435
553
  exports: function() {
436
554
  var result = {};
437
555
 
438
556
  for (var prop in this) {
439
- if (!this.hasOwnProperty(prop) || prop == 'include' || prop == 'contains' || prop == 'reverse') continue;
557
+ if (!this.hasOwnProperty(prop) || prop.match(/^(?:include|contains|reverse)$/)) continue;
440
558
  result[prop] = this[prop];
441
559
  }
442
560
 
443
561
  return result;
444
- }
562
+ },
563
+
564
+ repeat: function(str, qty, separator){
565
+ if (str == null) return '';
566
+
567
+ qty = ~~qty;
568
+
569
+ // using faster implementation if separator is not needed;
570
+ if (separator == null) return strRepeat(String(str), qty);
571
+
572
+ // this one is about 300x slower in Google Chrome
573
+ for (var repeat = []; qty > 0; repeat[--qty] = str) {}
574
+ return repeat.join(separator);
575
+ },
576
+
577
+ naturalCmp: function(str1, str2){
578
+ if (str1 == str2) return 0;
579
+ if (!str1) return -1;
580
+ if (!str2) return 1;
581
+
582
+ var cmpRegex = /(\.\d+)|(\d+)|(\D+)/g,
583
+ tokens1 = String(str1).toLowerCase().match(cmpRegex),
584
+ tokens2 = String(str2).toLowerCase().match(cmpRegex),
585
+ count = Math.min(tokens1.length, tokens2.length);
586
+
587
+ for(var i = 0; i < count; i++) {
588
+ var a = tokens1[i], b = tokens2[i];
589
+
590
+ if (a !== b){
591
+ var num1 = parseInt(a, 10);
592
+ if (!isNaN(num1)){
593
+ var num2 = parseInt(b, 10);
594
+ if (!isNaN(num2) && num1 - num2)
595
+ return num1 - num2;
596
+ }
597
+ return a < b ? -1 : 1;
598
+ }
599
+ }
600
+
601
+ if (tokens1.length === tokens2.length)
602
+ return tokens1.length - tokens2.length;
603
+
604
+ return str1 < str2 ? -1 : 1;
605
+ },
445
606
 
607
+ levenshtein: function(str1, str2) {
608
+ if (str1 == null && str2 == null) return 0;
609
+ if (str1 == null) return String(str2).length;
610
+ if (str2 == null) return String(str1).length;
611
+
612
+ str1 = String(str1); str2 = String(str2);
613
+
614
+ var current = [], prev, value;
615
+
616
+ for (var i = 0; i <= str2.length; i++)
617
+ for (var j = 0; j <= str1.length; j++) {
618
+ if (i && j)
619
+ if (str1.charAt(j - 1) === str2.charAt(i - 1))
620
+ value = prev;
621
+ else
622
+ value = Math.min(current[j], current[j - 1], prev) + 1;
623
+ else
624
+ value = i + j;
625
+
626
+ prev = current[j];
627
+ current[j] = value;
628
+ }
629
+
630
+ return current.pop();
631
+ },
632
+
633
+ toBoolean: function(str, trueValues, falseValues) {
634
+ if (typeof str === "number") str = "" + str;
635
+ if (typeof str !== "string") return !!str;
636
+ str = _s.trim(str);
637
+ if (boolMatch(str, trueValues || ["true", "1"])) return true;
638
+ if (boolMatch(str, falseValues || ["false", "0"])) return false;
639
+ }
446
640
  };
447
641
 
448
642
  // Aliases
@@ -451,30 +645,29 @@
451
645
  _s.lstrip = _s.ltrim;
452
646
  _s.rstrip = _s.rtrim;
453
647
  _s.center = _s.lrpad;
454
- _s.ljust = _s.lpad;
455
- _s.rjust = _s.rpad;
648
+ _s.rjust = _s.lpad;
649
+ _s.ljust = _s.rpad;
456
650
  _s.contains = _s.include;
651
+ _s.q = _s.quote;
652
+ _s.toBool = _s.toBoolean;
653
+
654
+ // Exporting
457
655
 
458
656
  // CommonJS module is defined
459
657
  if (typeof exports !== 'undefined') {
460
- if (typeof module !== 'undefined' && module.exports) {
461
- // Export module
658
+ if (typeof module !== 'undefined' && module.exports)
462
659
  module.exports = _s;
463
- }
464
- exports._s = _s;
465
660
 
466
- // Integrate with Underscore.js
467
- } else if (typeof root._ !== 'undefined') {
468
- // root._.mixin(_s);
469
- root._.string = _s;
470
- root._.str = root._.string;
471
-
472
- // Or define it
473
- } else {
474
- root._ = {
475
- string: _s,
476
- str: _s
477
- };
661
+ exports._s = _s;
478
662
  }
479
663
 
480
- }(this || window));
664
+ // Register as a named module with AMD.
665
+ if (typeof define === 'function' && define.amd)
666
+ define('underscore.string', [], function(){ return _s; });
667
+
668
+
669
+ // Integrate with Underscore.js if defined
670
+ // or create our own underscore object.
671
+ root._ = root._ || {};
672
+ root._.string = root._.str = _s;
673
+ }(this, String);