underscore_extensions 0.0.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.
@@ -0,0 +1,480 @@
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){
10
+ 'use strict';
11
+
12
+ // Defining helper functions.
13
+
14
+ var nativeTrim = String.prototype.trim;
15
+
16
+ var parseNumber = function(source) { return source * 1 || 0; };
17
+
18
+ var strRepeat = function(i, m) {
19
+ for (var o = []; m > 0; o[--m] = i) {}
20
+ return o.join('');
21
+ };
22
+
23
+ var slice = function(a){
24
+ return Array.prototype.slice.call(a);
25
+ };
26
+
27
+ var defaultToWhiteSpace = function(characters){
28
+ if (characters) {
29
+ return _s.escapeRegExp(characters);
30
+ }
31
+ return '\\s';
32
+ };
33
+
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
+ };
41
+ };
42
+
43
+ // sprintf() for JavaScript 0.7-beta1
44
+ // http://www.diveintojavascript.com/projects/javascript-sprintf
45
+ //
46
+ // Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
47
+ // All rights reserved.
48
+
49
+ var sprintf = (function() {
50
+ function get_type(variable) {
51
+ return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
52
+ }
53
+
54
+ var str_repeat = strRepeat;
55
+
56
+ var str_format = function() {
57
+ if (!str_format.cache.hasOwnProperty(arguments[0])) {
58
+ str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
59
+ }
60
+ return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
61
+ };
62
+
63
+ str_format.format = function(parse_tree, argv) {
64
+ var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
65
+ for (i = 0; i < tree_length; i++) {
66
+ node_type = get_type(parse_tree[i]);
67
+ if (node_type === 'string') {
68
+ output.push(parse_tree[i]);
69
+ }
70
+ else if (node_type === 'array') {
71
+ match = parse_tree[i]; // convenience purposes only
72
+ if (match[2]) { // keyword argument
73
+ arg = argv[cursor];
74
+ for (k = 0; k < match[2].length; k++) {
75
+ if (!arg.hasOwnProperty(match[2][k])) {
76
+ throw(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
77
+ }
78
+ arg = arg[match[2][k]];
79
+ }
80
+ } else if (match[1]) { // positional argument (explicit)
81
+ arg = argv[match[1]];
82
+ }
83
+ else { // positional argument (implicit)
84
+ arg = argv[cursor++];
85
+ }
86
+
87
+ if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
88
+ throw(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
89
+ }
90
+ switch (match[8]) {
91
+ case 'b': arg = arg.toString(2); break;
92
+ case 'c': arg = String.fromCharCode(arg); break;
93
+ case 'd': arg = parseInt(arg, 10); break;
94
+ case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
95
+ case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
96
+ case 'o': arg = arg.toString(8); break;
97
+ case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
98
+ case 'u': arg = Math.abs(arg); break;
99
+ case 'x': arg = arg.toString(16); break;
100
+ case 'X': arg = arg.toString(16).toUpperCase(); break;
101
+ }
102
+ arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
103
+ pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
104
+ pad_length = match[6] - String(arg).length;
105
+ pad = match[6] ? str_repeat(pad_character, pad_length) : '';
106
+ output.push(match[5] ? arg + pad : pad + arg);
107
+ }
108
+ }
109
+ return output.join('');
110
+ };
111
+
112
+ str_format.cache = {};
113
+
114
+ str_format.parse = function(fmt) {
115
+ var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
116
+ while (_fmt) {
117
+ if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
118
+ parse_tree.push(match[0]);
119
+ }
120
+ else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
121
+ parse_tree.push('%');
122
+ }
123
+ else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
124
+ if (match[2]) {
125
+ arg_names |= 1;
126
+ var field_list = [], replacement_field = match[2], field_match = [];
127
+ if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
128
+ field_list.push(field_match[1]);
129
+ while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
130
+ if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
131
+ field_list.push(field_match[1]);
132
+ }
133
+ else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
134
+ field_list.push(field_match[1]);
135
+ }
136
+ else {
137
+ throw('[_.sprintf] huh?');
138
+ }
139
+ }
140
+ }
141
+ else {
142
+ throw('[_.sprintf] huh?');
143
+ }
144
+ match[2] = field_list;
145
+ }
146
+ else {
147
+ arg_names |= 2;
148
+ }
149
+ if (arg_names === 3) {
150
+ throw('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
151
+ }
152
+ parse_tree.push(match);
153
+ }
154
+ else {
155
+ throw('[_.sprintf] huh?');
156
+ }
157
+ _fmt = _fmt.substring(match[0].length);
158
+ }
159
+ return parse_tree;
160
+ };
161
+
162
+ return str_format;
163
+ })();
164
+
165
+
166
+
167
+ // Defining underscore.string
168
+
169
+ var _s = {
170
+
171
+ VERSION: '2.0.0',
172
+
173
+ isBlank: sArgs(function(str){
174
+ 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
+ }),
233
+
234
+ include: sArgs(function(str, needle){
235
+ return str.indexOf(needle) !== -1;
236
+ }),
237
+
238
+ join: sArgs(function(sep) {
239
+ var args = slice(arguments);
240
+ return args.join(args.shift());
241
+ }),
242
+
243
+ lines: sArgs(function(str) {
244
+ return str.split("\n");
245
+ }),
246
+
247
+ reverse: sArgs(function(str){
248
+ return Array.prototype.reverse.apply(String(str).split('')).join('');
249
+ }),
250
+
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
+ }),
256
+
257
+ startsWith: sArgs(function(str, starts){
258
+ return str.length >= starts.length && str.substring(0, starts.length) === starts;
259
+ }),
260
+
261
+ endsWith: sArgs(function(str, ends){
262
+ return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
263
+ }),
264
+
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
+ }),
281
+
282
+ camelize: sArgs(function(str){
283
+ return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
284
+ return chr ? chr.toUpperCase() : '';
285
+ });
286
+ }),
287
+
288
+ underscored: function(str){
289
+ return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
290
+ },
291
+
292
+ 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();
294
+ },
295
+
296
+ humanize: function(str){
297
+ return _s.capitalize(this.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
298
+ },
299
+
300
+ trim: sArgs(function(str, characters){
301
+ if (!characters && nativeTrim) {
302
+ return nativeTrim.call(str);
303
+ }
304
+ characters = defaultToWhiteSpace(characters);
305
+ return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
306
+ }),
307
+
308
+ ltrim: sArgs(function(str, characters){
309
+ characters = defaultToWhiteSpace(characters);
310
+ return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
311
+ }),
312
+
313
+ rtrim: sArgs(function(str, characters){
314
+ characters = defaultToWhiteSpace(characters);
315
+ return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
316
+ }),
317
+
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
+ }),
323
+
324
+ /**
325
+ * _s.prune: a more elegant version of truncate
326
+ * prune extra chars, never leaving a half-chopped word.
327
+ * @author github.com/sergiokas
328
+ */
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));
351
+
352
+ pruned = pruned.replace(/\W+$/,'');
353
+
354
+ return (pruned.length+pruneStr.length>str.length) ? str : str.substring(0, pruned.length)+pruneStr;
355
+ }),
356
+
357
+ words: function(str, delimiter) {
358
+ return String(str).split(delimiter || " ");
359
+ },
360
+
361
+ pad: sArgs(function(str, length, padStr, type) {
362
+ var padding = '',
363
+ padlen = 0;
364
+
365
+ length = parseNumber(length);
366
+
367
+ if (!padStr) { padStr = ' '; }
368
+ else if (padStr.length > 1) { padStr = padStr.charAt(0); }
369
+ switch(type) {
370
+ case 'right':
371
+ padlen = (length - str.length);
372
+ padding = strRepeat(padStr, padlen);
373
+ str = str+padding;
374
+ break;
375
+ 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;
383
+ default: // 'left'
384
+ padlen = (length - str.length);
385
+ padding = strRepeat(padStr, padlen);;
386
+ str = padding+str;
387
+ }
388
+ return str;
389
+ }),
390
+
391
+ lpad: function(str, length, padStr) {
392
+ return _s.pad(str, length, padStr);
393
+ },
394
+
395
+ rpad: function(str, length, padStr) {
396
+ return _s.pad(str, length, padStr, 'right');
397
+ },
398
+
399
+ lrpad: function(str, length, padStr) {
400
+ return _s.pad(str, length, padStr, 'both');
401
+ },
402
+
403
+ sprintf: sprintf,
404
+
405
+ vsprintf: function(fmt, argv){
406
+ argv.unshift(fmt);
407
+ return sprintf.apply(null, argv);
408
+ },
409
+
410
+ toNumber: function(str, decimals) {
411
+ var num = parseNumber(parseNumber(str).toFixed(parseNumber(decimals)));
412
+ return (!(num === 0 && (str !== "0" && str !== 0))) ? num : Number.NaN;
413
+ },
414
+
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
+ }),
419
+
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
+ }),
424
+
425
+ strLeft: sArgs(function(sourceStr, sep){
426
+ var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
427
+ return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
428
+ }),
429
+
430
+ strLeftBack: sArgs(function(sourceStr, sep){
431
+ var pos = sourceStr.lastIndexOf(sep);
432
+ return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
433
+ }),
434
+
435
+ exports: function() {
436
+ var result = {};
437
+
438
+ for (var prop in this) {
439
+ if (!this.hasOwnProperty(prop) || prop == 'include' || prop == 'contains' || prop == 'reverse') continue;
440
+ result[prop] = this[prop];
441
+ }
442
+
443
+ return result;
444
+ }
445
+
446
+ };
447
+
448
+ // Aliases
449
+
450
+ _s.strip = _s.trim;
451
+ _s.lstrip = _s.ltrim;
452
+ _s.rstrip = _s.rtrim;
453
+ _s.center = _s.lrpad;
454
+ _s.ljust = _s.lpad;
455
+ _s.rjust = _s.rpad;
456
+ _s.contains = _s.include;
457
+
458
+ // CommonJS module is defined
459
+ if (typeof exports !== 'undefined') {
460
+ if (typeof module !== 'undefined' && module.exports) {
461
+ // Export module
462
+ module.exports = _s;
463
+ }
464
+ exports._s = _s;
465
+
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
+ };
478
+ }
479
+
480
+ }(this || window));