brancusi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/Rakefile +13 -0
  2. data/lib/assets/javascripts/brancusi/application/application.js.coffee +114 -0
  3. data/lib/assets/javascripts/brancusi/application/application_controller.js.coffee +17 -0
  4. data/lib/assets/javascripts/brancusi/application/application_module.js.coffee +29 -0
  5. data/lib/assets/javascripts/brancusi/application/bootstrapper.js.coffee +18 -0
  6. data/lib/assets/javascripts/brancusi/application/index.js.coffee +1 -0
  7. data/lib/assets/javascripts/brancusi/application/sandbox.js.coffee +14 -0
  8. data/lib/assets/javascripts/brancusi/container/container.js.coffee +139 -0
  9. data/lib/assets/javascripts/brancusi/container/dependent_module.js.coffee +30 -0
  10. data/lib/assets/javascripts/brancusi/container/dependent_object.js.coffee +9 -0
  11. data/lib/assets/javascripts/brancusi/container/index.js.coffee +1 -0
  12. data/lib/assets/javascripts/brancusi/events/event_object.js.coffee +10 -0
  13. data/lib/assets/javascripts/brancusi/events/events_module.js.coffee +14 -0
  14. data/lib/assets/javascripts/brancusi/events/index.js.coffee +1 -0
  15. data/lib/assets/javascripts/brancusi/events/mediator.js.coffee +70 -0
  16. data/lib/assets/javascripts/brancusi/index.js.coffee +2 -0
  17. data/lib/assets/javascripts/brancusi/namespace.js.coffee +13 -0
  18. data/lib/assets/javascripts/brancusi/object_model/base_object.js.coffee +27 -0
  19. data/lib/assets/javascripts/brancusi/object_model/decorate.js.coffee +14 -0
  20. data/lib/assets/javascripts/brancusi/object_model/extend.js.coffee +10 -0
  21. data/lib/assets/javascripts/brancusi/object_model/include.js.coffee +13 -0
  22. data/lib/assets/javascripts/brancusi/object_model/index.js.coffee +1 -0
  23. data/lib/assets/javascripts/brancusi/renderer/index.js.coffee +1 -0
  24. data/lib/assets/javascripts/brancusi/renderer/region_manager.js.coffee +30 -0
  25. data/lib/assets/javascripts/brancusi/renderer/renderer.js.coffee +41 -0
  26. data/lib/assets/javascripts/brancusi/renderer/template_manager.js.coffee +0 -0
  27. data/lib/assets/javascripts/brancusi/routes/dispatcher.js.coffee +10 -0
  28. data/lib/assets/javascripts/brancusi/routes/index.js.coffee +1 -0
  29. data/lib/assets/javascripts/brancusi/routes/mapper.js.coffee +41 -0
  30. data/lib/assets/javascripts/brancusi/routes/router.js.coffee +15 -0
  31. data/lib/assets/javascripts/brancusi/support/davis_router.js.coffee +19 -0
  32. data/lib/assets/javascripts/brancusi/support/knockout_renderer.js.coffee +12 -0
  33. data/lib/assets/javascripts/davis.js +1838 -0
  34. data/lib/assets/javascripts/knockout.js +3583 -0
  35. data/lib/assets/javascripts/underscore.js +1221 -0
  36. data/lib/assets/javascripts/underscore.string.js +600 -0
  37. data/lib/brancusi.rb +4 -0
  38. data/lib/brancusi/engine.rb +4 -0
  39. data/lib/brancusi/version.rb +3 -0
  40. metadata +282 -0
@@ -0,0 +1,600 @@
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){
9
+ 'use strict';
10
+
11
+ // Defining helper functions.
12
+
13
+ var nativeTrim = String.prototype.trim;
14
+ var nativeTrimRight = String.prototype.trimRight;
15
+ var nativeTrimLeft = String.prototype.trimLeft;
16
+
17
+ var parseNumber = function(source) { return source * 1 || 0; };
18
+
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;
27
+ };
28
+
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) + ']';
38
+ };
39
+
40
+ var escapeChars = {
41
+ lt: '<',
42
+ gt: '>',
43
+ quot: '"',
44
+ apos: "'",
45
+ amp: '&'
46
+ };
47
+
48
+ var reversedEscapeChars = {};
49
+ for(var key in escapeChars){ reversedEscapeChars[escapeChars[key]] = key; }
50
+
51
+ // sprintf() for JavaScript 0.7-beta1
52
+ // http://www.diveintojavascript.com/projects/javascript-sprintf
53
+ //
54
+ // Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
55
+ // All rights reserved.
56
+
57
+ var sprintf = (function() {
58
+ function get_type(variable) {
59
+ return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
60
+ }
61
+
62
+ var str_repeat = strRepeat;
63
+
64
+ var str_format = function() {
65
+ if (!str_format.cache.hasOwnProperty(arguments[0])) {
66
+ str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
67
+ }
68
+ return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
69
+ };
70
+
71
+ str_format.format = function(parse_tree, argv) {
72
+ var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
73
+ for (i = 0; i < tree_length; i++) {
74
+ node_type = get_type(parse_tree[i]);
75
+ if (node_type === 'string') {
76
+ output.push(parse_tree[i]);
77
+ }
78
+ else if (node_type === 'array') {
79
+ match = parse_tree[i]; // convenience purposes only
80
+ if (match[2]) { // keyword argument
81
+ arg = argv[cursor];
82
+ for (k = 0; k < match[2].length; k++) {
83
+ if (!arg.hasOwnProperty(match[2][k])) {
84
+ throw new Error(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
85
+ }
86
+ arg = arg[match[2][k]];
87
+ }
88
+ } else if (match[1]) { // positional argument (explicit)
89
+ arg = argv[match[1]];
90
+ }
91
+ else { // positional argument (implicit)
92
+ arg = argv[cursor++];
93
+ }
94
+
95
+ if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
96
+ throw new Error(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
97
+ }
98
+ switch (match[8]) {
99
+ case 'b': arg = arg.toString(2); break;
100
+ case 'c': arg = String.fromCharCode(arg); break;
101
+ case 'd': arg = parseInt(arg, 10); break;
102
+ case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
103
+ case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
104
+ case 'o': arg = arg.toString(8); break;
105
+ case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
106
+ case 'u': arg = Math.abs(arg); break;
107
+ case 'x': arg = arg.toString(16); break;
108
+ case 'X': arg = arg.toString(16).toUpperCase(); break;
109
+ }
110
+ arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
111
+ pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
112
+ pad_length = match[6] - String(arg).length;
113
+ pad = match[6] ? str_repeat(pad_character, pad_length) : '';
114
+ output.push(match[5] ? arg + pad : pad + arg);
115
+ }
116
+ }
117
+ return output.join('');
118
+ };
119
+
120
+ str_format.cache = {};
121
+
122
+ str_format.parse = function(fmt) {
123
+ var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
124
+ while (_fmt) {
125
+ if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
126
+ parse_tree.push(match[0]);
127
+ }
128
+ else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
129
+ parse_tree.push('%');
130
+ }
131
+ else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
132
+ if (match[2]) {
133
+ arg_names |= 1;
134
+ var field_list = [], replacement_field = match[2], field_match = [];
135
+ if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
136
+ field_list.push(field_match[1]);
137
+ while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
138
+ if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
139
+ field_list.push(field_match[1]);
140
+ }
141
+ else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
142
+ field_list.push(field_match[1]);
143
+ }
144
+ else {
145
+ throw new Error('[_.sprintf] huh?');
146
+ }
147
+ }
148
+ }
149
+ else {
150
+ throw new Error('[_.sprintf] huh?');
151
+ }
152
+ match[2] = field_list;
153
+ }
154
+ else {
155
+ arg_names |= 2;
156
+ }
157
+ if (arg_names === 3) {
158
+ throw new Error('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
159
+ }
160
+ parse_tree.push(match);
161
+ }
162
+ else {
163
+ throw new Error('[_.sprintf] huh?');
164
+ }
165
+ _fmt = _fmt.substring(match[0].length);
166
+ }
167
+ return parse_tree;
168
+ };
169
+
170
+ return str_format;
171
+ })();
172
+
173
+
174
+
175
+ // Defining underscore.string
176
+
177
+ var _s = {
178
+
179
+ VERSION: '2.3.0',
180
+
181
+ isBlank: function(str){
182
+ if (str == null) str = '';
183
+ return (/^\s*$/).test(str);
184
+ },
185
+
186
+ stripTags: function(str){
187
+ if (str == null) return '';
188
+ return String(str).replace(/<\/?[^>]+>/g, '');
189
+ },
190
+
191
+ capitalize : function(str){
192
+ str = str == null ? '' : String(str);
193
+ return str.charAt(0).toUpperCase() + str.slice(1);
194
+ },
195
+
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
+ },
202
+
203
+ clean: function(str){
204
+ return _s.strip(str).replace(/\s+/g, ' ');
205
+ },
206
+
207
+ count: function(str, substr){
208
+ if (str == null || substr == null) return 0;
209
+ return String(str).split(substr).length - 1;
210
+ },
211
+
212
+ chars: function(str) {
213
+ if (str == null) return [];
214
+ return String(str).split('');
215
+ },
216
+
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
+ },
223
+
224
+ escapeHTML: function(str) {
225
+ if (str == null) return '';
226
+ return String(str).replace(/[&<>"']/g, function(m){ return '&' + reversedEscapeChars[m] + ';'; });
227
+ },
228
+
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
+ }
243
+ });
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
+ },
313
+
314
+ underscored: function(str){
315
+ return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
316
+ },
317
+
318
+ dasherize: function(str){
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, '');
324
+ },
325
+
326
+ humanize: function(str){
327
+ return _s.capitalize(_s.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
328
+ },
329
+
330
+ trim: function(str, characters){
331
+ if (str == null) return '';
332
+ if (!characters && nativeTrim) return nativeTrim.call(str);
333
+ characters = defaultToWhiteSpace(characters);
334
+ return String(str).replace(new RegExp('\^' + characters + '+|' + characters + '+$', 'g'), '');
335
+ },
336
+
337
+ ltrim: function(str, characters){
338
+ if (str == null) return '';
339
+ if (!characters && nativeTrimLeft) return nativeTrimLeft.call(str);
340
+ characters = defaultToWhiteSpace(characters);
341
+ return String(str).replace(new RegExp('^' + characters + '+'), '');
342
+ },
343
+
344
+ rtrim: function(str, characters){
345
+ if (str == null) return '';
346
+ if (!characters && nativeTrimRight) return nativeTrimRight.call(str);
347
+ characters = defaultToWhiteSpace(characters);
348
+ return String(str).replace(new RegExp(characters + '+$'), '');
349
+ },
350
+
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
+ },
357
+
358
+ /**
359
+ * _s.prune: a more elegant version of truncate
360
+ * prune extra chars, never leaving a half-chopped word.
361
+ * @author github.com/rwz
362
+ */
363
+ prune: function(str, length, pruneStr){
364
+ if (str == null) return '';
365
+
366
+ str = String(str); length = ~~length;
367
+ pruneStr = pruneStr != null ? String(pruneStr) : '...';
368
+
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
+ },
381
+
382
+ words: function(str, delimiter) {
383
+ if (_s.isBlank(str)) return [];
384
+ return _s.trim(str, delimiter).split(delimiter || /\s+/);
385
+ },
386
+
387
+ pad: function(str, length, padStr, type) {
388
+ str = str == null ? '' : String(str);
389
+ length = ~~length;
390
+
391
+ var padlen = 0;
392
+
393
+ if (!padStr)
394
+ padStr = ' ';
395
+ else if (padStr.length > 1)
396
+ padStr = padStr.charAt(0);
397
+
398
+ switch(type) {
399
+ case 'right':
400
+ padlen = length - str.length;
401
+ return str + strRepeat(padStr, padlen);
402
+ case 'both':
403
+ padlen = length - str.length;
404
+ return strRepeat(padStr, Math.ceil(padlen/2)) + str
405
+ + strRepeat(padStr, Math.floor(padlen/2));
406
+ default: // 'left'
407
+ padlen = length - str.length;
408
+ return strRepeat(padStr, padlen) + str;
409
+ }
410
+ },
411
+
412
+ lpad: function(str, length, padStr) {
413
+ return _s.pad(str, length, padStr);
414
+ },
415
+
416
+ rpad: function(str, length, padStr) {
417
+ return _s.pad(str, length, padStr, 'right');
418
+ },
419
+
420
+ lrpad: function(str, length, padStr) {
421
+ return _s.pad(str, length, padStr, 'both');
422
+ },
423
+
424
+ sprintf: sprintf,
425
+
426
+ vsprintf: function(fmt, argv){
427
+ argv.unshift(fmt);
428
+ return sprintf.apply(null, argv);
429
+ },
430
+
431
+ toNumber: function(str, decimals) {
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;
476
+ },
477
+
478
+ toSentence: function(array, separator, lastSeparator, serial) {
479
+ separator = separator || ', '
480
+ lastSeparator = lastSeparator || ' and '
481
+ var a = array.slice(), lastMember = a.pop();
482
+
483
+ if (array.length > 2 && serial) lastSeparator = _s.rtrim(separator) + lastSeparator;
484
+
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
+ });
505
+
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
+ },
516
+
517
+ exports: function() {
518
+ var result = {};
519
+
520
+ for (var prop in this) {
521
+ if (!this.hasOwnProperty(prop) || prop.match(/^(?:include|contains|reverse)$/)) continue;
522
+ result[prop] = this[prop];
523
+ }
524
+
525
+ return result;
526
+ },
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
+ }
566
+ };
567
+
568
+ // Aliases
569
+
570
+ _s.strip = _s.trim;
571
+ _s.lstrip = _s.ltrim;
572
+ _s.rstrip = _s.rtrim;
573
+ _s.center = _s.lrpad;
574
+ _s.rjust = _s.lpad;
575
+ _s.ljust = _s.rpad;
576
+ _s.contains = _s.include;
577
+ _s.q = _s.quote;
578
+
579
+ // CommonJS module is defined
580
+ if (typeof exports !== 'undefined') {
581
+ if (typeof module !== 'undefined' && module.exports) {
582
+ // Export module
583
+ module.exports = _s;
584
+ }
585
+ exports._s = _s;
586
+
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
+ });
592
+
593
+ } else {
594
+ // Integrate with Underscore.js if defined
595
+ // or create our own underscore object.
596
+ root._ = root._ || {};
597
+ root._.string = root._.str = _s;
598
+ }
599
+
600
+ }(this, String);