underscore_extensions 0.1.0 → 0.1.1

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