ultimate-base 0.3.1.1 → 0.3.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.
- data/Gemfile.lock +15 -15
- data/app/assets/javascripts/ultimate/backbone/lib/backbone.js +70 -56
- data/app/assets/javascripts/ultimate/backbone/model.js.coffee +2 -2
- data/app/assets/javascripts/ultimate/backbone/view.js.coffee +5 -4
- data/app/assets/javascripts/ultimate/backbone/views/typed-fields.js.coffee +1 -1
- data/app/assets/javascripts/ultimate/helpers/asset_tag.js.coffee +21 -19
- data/app/assets/javascripts/ultimate/helpers/form_options.js.coffee +65 -0
- data/app/assets/javascripts/ultimate/helpers/form_tag.js.coffee +177 -0
- data/app/assets/javascripts/ultimate/helpers/javascript.js.coffee +31 -0
- data/app/assets/javascripts/ultimate/helpers/record_tag.js.coffee +29 -15
- data/app/assets/javascripts/ultimate/helpers/tag.js.coffee +7 -3
- data/app/assets/javascripts/ultimate/helpers/url.js.coffee +62 -7
- data/app/assets/javascripts/ultimate/jquery-plugin-adapter.js.coffee +1 -0
- data/app/assets/javascripts/ultimate/jquery-plugin-class.js.coffee +35 -16
- data/app/assets/javascripts/ultimate/jquery.base.js.coffee +1 -1
- data/app/assets/javascripts/ultimate/underscore/underscore.js +292 -192
- data/app/assets/javascripts/ultimate/underscore/underscore.outcasts.js.coffee +27 -2
- data/app/assets/javascripts/ultimate/underscore/underscore.string.js +4 -4
- data/app/assets/stylesheets/polyfills/PIE.htc +81 -81
- data/app/assets/stylesheets/polyfills/boxsizing.htc +255 -54
- data/app/assets/stylesheets/ultimate/mixins/_vendors.scss +9 -0
- data/app/assets/stylesheets/ultimate/mixins/css3.scss +39 -28
- data/app/assets/stylesheets/ultimate/mixins/microstructures.scss +32 -6
- data/lib/ultimate/base/version.rb +1 -1
- data/test/javascripts/tests/helpers/asset_tag_test.js.coffee +1 -1
- data/test/javascripts/tests/helpers/form_options_test.js.coffee +96 -0
- data/test/javascripts/tests/helpers/form_tag_test.js.coffee +225 -0
- data/test/javascripts/tests/helpers/javascript_test.js.coffee +25 -0
- data/test/javascripts/tests/helpers/record_tag_test.js.coffee +5 -3
- data/test/javascripts/tests/helpers/tag_test.js.coffee +22 -17
- data/test/javascripts/tests/helpers/url_test.js.coffee +50 -6
- data/test/javascripts/tests/underscore/underscore.outcasts.test.js.coffee +9 -0
- metadata +8 -4
- data/app/assets/javascripts/ultimate/helpers/translation.js.coffee +0 -97
- data/test/javascripts/tests/helpers/translation_test.js.coffee +0 -140
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
Ultimate.createJQueryPlugin = (pluginName, pluginClass) ->
|
16
16
|
Ultimate.debug(".createJQueryPlugin()", pluginName, pluginClass) if _.isFunction(Ultimate.debug)
|
17
|
+
pluginClass.pluginName ||= pluginName
|
17
18
|
jQuery.fn[pluginName] = -> @ultimatePluginAdapter pluginName, pluginClass, arguments
|
18
19
|
|
19
20
|
|
@@ -1,8 +1,24 @@
|
|
1
|
+
# TODO simlify translations infrastructure
|
1
2
|
# `pluginClass` must store propery `$el` as jQuery object wrapped on the target DOM-object in his instance.
|
2
3
|
|
3
4
|
#= require ./base
|
4
5
|
|
6
|
+
# TODO minimize requirements
|
7
|
+
# requirements stats:
|
8
|
+
# 4 _.result
|
9
|
+
# 3 _.extend
|
10
|
+
# 2 _.isFunction
|
11
|
+
# 2 _.isObject
|
12
|
+
# 1 _.isString
|
13
|
+
# 1 _.isArray
|
14
|
+
# 1 _.bind
|
15
|
+
# 1 _.clone
|
16
|
+
# 1 _.outcasts.delete
|
17
|
+
# 1 _.string.underscored
|
18
|
+
# 1 _.string.startsWith
|
19
|
+
|
5
20
|
class Ultimate.Plugin
|
21
|
+
el: null
|
6
22
|
$el: null
|
7
23
|
nodes: {}
|
8
24
|
events: {}
|
@@ -13,7 +29,8 @@ class Ultimate.Plugin
|
|
13
29
|
translations: {}
|
14
30
|
|
15
31
|
constructor: (options) ->
|
16
|
-
|
32
|
+
@_configure(options || {});
|
33
|
+
@$el = $(@el)
|
17
34
|
@findNodes()
|
18
35
|
@initialize arguments...
|
19
36
|
@delegateEvents()
|
@@ -30,7 +47,7 @@ class Ultimate.Plugin
|
|
30
47
|
|
31
48
|
findNodes: (jRoot = @$el, nodes = @nodes) ->
|
32
49
|
jNodes = {}
|
33
|
-
nodes = nodes()
|
50
|
+
nodes = if _.isFunction(nodes) then @nodes.call(@) else _.clone(nodes)
|
34
51
|
if _.isObject(nodes)
|
35
52
|
for nodeName, selector of nodes
|
36
53
|
_isObject = _.isObject(selector)
|
@@ -77,8 +94,9 @@ class Ultimate.Plugin
|
|
77
94
|
for key, method of events
|
78
95
|
[[], eventName, selector] = key.match(delegateEventSplitter)
|
79
96
|
selector = _.result(@, selector)
|
80
|
-
selector = selector.selector
|
97
|
+
selector = selector.selector if selector instanceof jQuery
|
81
98
|
if _.isString(selector)
|
99
|
+
selector = selector.replace(@$el.selector, '') if _.string.startsWith(selector, @$el.selector)
|
82
100
|
key = "#{eventName} #{selector}"
|
83
101
|
normalizedEvents[key] = method
|
84
102
|
events = normalizedEvents
|
@@ -89,8 +107,9 @@ class Ultimate.Plugin
|
|
89
107
|
@initTranslations()
|
90
108
|
@reflectOptions()
|
91
109
|
|
92
|
-
reflectOptions: (
|
93
|
-
|
110
|
+
reflectOptions: (reflectableOptions = _.result(@, "reflectableOptions"), options = @options) ->
|
111
|
+
if _.isArray(reflectableOptions)
|
112
|
+
@[attr] = options[attr] for attr in reflectableOptions when typeof options[attr] isnt "undefined"
|
94
113
|
@[attr] = value for attr, value of options when typeof @[attr] isnt "undefined"
|
95
114
|
@
|
96
115
|
|
@@ -98,17 +117,17 @@ class Ultimate.Plugin
|
|
98
117
|
# modify and return merged data
|
99
118
|
initTranslations: (options = @options) ->
|
100
119
|
# if global compatible I18n
|
101
|
-
if I18n? and I18n.locale and I18n.t
|
102
|
-
options["locale"] ||= I18n.locale
|
103
|
-
if options["locale"] is I18n.locale
|
104
|
-
# pointing to defaults locales of language specified in I18n
|
105
|
-
_defaultLocales = @constructor.defaultLocales?[I18n.locale] ||= {}
|
106
|
-
unless _defaultLocales["loaded"]
|
107
|
-
_defaultLocales["loaded"] = true
|
108
|
-
# try read localized strings
|
109
|
-
if _localesFromI18n = I18n.t(options["i18nKey"] or _.underscored(@constructor.pluginName or @constructor.name))
|
110
|
-
# fill it from I18n
|
111
|
-
_.extend _defaultLocales, _localesFromI18n
|
120
|
+
# if I18n? and I18n.locale and I18n.t
|
121
|
+
# options["locale"] ||= I18n.locale
|
122
|
+
# if options["locale"] is I18n.locale
|
123
|
+
# # pointing to defaults locales of language specified in I18n
|
124
|
+
# _defaultLocales = @constructor.defaultLocales?[I18n.locale] ||= {}
|
125
|
+
# unless _defaultLocales["loaded"]
|
126
|
+
# _defaultLocales["loaded"] = true
|
127
|
+
# # try read localized strings
|
128
|
+
# if _localesFromI18n = I18n.t(options["i18nKey"] or _.string.underscored(@constructor.pluginName or @constructor.name))
|
129
|
+
# # fill it from I18n
|
130
|
+
# _.extend _defaultLocales, _localesFromI18n
|
112
131
|
@locale = options["locale"] if options["locale"]
|
113
132
|
translations = if @locale then @constructor.defaultLocales?[@locale] or {} else {}
|
114
133
|
$.extend true, options, translations: translations, options
|
@@ -1,10 +1,7 @@
|
|
1
|
-
// Underscore.js 1.
|
1
|
+
// Underscore.js 1.4.2
|
2
|
+
// http://underscorejs.org
|
2
3
|
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
3
4
|
// Underscore may be freely distributed under the MIT license.
|
4
|
-
// Portions of Underscore are inspired or borrowed from Prototype,
|
5
|
-
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
6
|
-
// For all details and documentation:
|
7
|
-
// http://documentcloud.github.com/underscore
|
8
5
|
|
9
6
|
(function() {
|
10
7
|
|
@@ -26,6 +23,7 @@
|
|
26
23
|
// Create quick reference variables for speed access to core prototypes.
|
27
24
|
var push = ArrayProto.push,
|
28
25
|
slice = ArrayProto.slice,
|
26
|
+
concat = ArrayProto.concat,
|
29
27
|
unshift = ArrayProto.unshift,
|
30
28
|
toString = ObjProto.toString,
|
31
29
|
hasOwnProperty = ObjProto.hasOwnProperty;
|
@@ -47,7 +45,11 @@
|
|
47
45
|
nativeBind = FuncProto.bind;
|
48
46
|
|
49
47
|
// Create a safe reference to the Underscore object for use below.
|
50
|
-
var _ = function(obj) {
|
48
|
+
var _ = function(obj) {
|
49
|
+
if (obj instanceof _) return obj;
|
50
|
+
if (!(this instanceof _)) return new _(obj);
|
51
|
+
this._wrapped = obj;
|
52
|
+
};
|
51
53
|
|
52
54
|
// Export the Underscore object for **Node.js**, with
|
53
55
|
// backwards-compatibility for the old `require()` API. If we're in
|
@@ -63,7 +65,7 @@
|
|
63
65
|
}
|
64
66
|
|
65
67
|
// Current version.
|
66
|
-
_.VERSION = '1.
|
68
|
+
_.VERSION = '1.4.2';
|
67
69
|
|
68
70
|
// Collection Functions
|
69
71
|
// --------------------
|
@@ -128,11 +130,24 @@
|
|
128
130
|
if (obj == null) obj = [];
|
129
131
|
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
130
132
|
if (context) iterator = _.bind(iterator, context);
|
131
|
-
return
|
133
|
+
return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
134
|
+
}
|
135
|
+
var length = obj.length;
|
136
|
+
if (length !== +length) {
|
137
|
+
var keys = _.keys(obj);
|
138
|
+
length = keys.length;
|
132
139
|
}
|
133
|
-
|
134
|
-
|
135
|
-
|
140
|
+
each(obj, function(value, index, list) {
|
141
|
+
index = keys ? keys[--length] : --length;
|
142
|
+
if (!initial) {
|
143
|
+
memo = obj[index];
|
144
|
+
initial = true;
|
145
|
+
} else {
|
146
|
+
memo = iterator.call(context, memo, obj[index], index, list);
|
147
|
+
}
|
148
|
+
});
|
149
|
+
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
|
150
|
+
return memo;
|
136
151
|
};
|
137
152
|
|
138
153
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
@@ -174,6 +189,7 @@
|
|
174
189
|
// Delegates to **ECMAScript 5**'s native `every` if available.
|
175
190
|
// Aliased as `all`.
|
176
191
|
_.every = _.all = function(obj, iterator, context) {
|
192
|
+
iterator || (iterator = _.identity);
|
177
193
|
var result = true;
|
178
194
|
if (obj == null) return result;
|
179
195
|
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
@@ -197,9 +213,9 @@
|
|
197
213
|
return !!result;
|
198
214
|
};
|
199
215
|
|
200
|
-
// Determine if
|
201
|
-
// Aliased as `
|
202
|
-
_.
|
216
|
+
// Determine if the array or object contains a given value (using `===`).
|
217
|
+
// Aliased as `include`.
|
218
|
+
_.contains = _.include = function(obj, target) {
|
203
219
|
var found = false;
|
204
220
|
if (obj == null) return found;
|
205
221
|
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
@@ -222,6 +238,18 @@
|
|
222
238
|
return _.map(obj, function(value){ return value[key]; });
|
223
239
|
};
|
224
240
|
|
241
|
+
// Convenience version of a common use case of `filter`: selecting only objects
|
242
|
+
// with specific `key:value` pairs.
|
243
|
+
_.where = function(obj, attrs) {
|
244
|
+
if (_.isEmpty(attrs)) return [];
|
245
|
+
return _.filter(obj, function(value) {
|
246
|
+
for (var key in attrs) {
|
247
|
+
if (attrs[key] !== value[key]) return false;
|
248
|
+
}
|
249
|
+
return true;
|
250
|
+
});
|
251
|
+
};
|
252
|
+
|
225
253
|
// Return the maximum element or (element-based computation).
|
226
254
|
// Can't optimize arrays of integers longer than 65,535 elements.
|
227
255
|
// See: https://bugs.webkit.org/show_bug.cgi?id=80797
|
@@ -258,40 +286,44 @@
|
|
258
286
|
var index = 0;
|
259
287
|
var shuffled = [];
|
260
288
|
each(obj, function(value) {
|
261
|
-
rand =
|
289
|
+
rand = _.random(index++);
|
262
290
|
shuffled[index - 1] = shuffled[rand];
|
263
291
|
shuffled[rand] = value;
|
264
292
|
});
|
265
293
|
return shuffled;
|
266
294
|
};
|
267
295
|
|
296
|
+
// An internal function to generate lookup iterators.
|
297
|
+
var lookupIterator = function(value) {
|
298
|
+
return _.isFunction(value) ? value : function(obj){ return obj[value]; };
|
299
|
+
};
|
300
|
+
|
268
301
|
// Sort the object's values by a criterion produced by an iterator.
|
269
|
-
_.sortBy = function(obj,
|
270
|
-
var iterator = lookupIterator(
|
302
|
+
_.sortBy = function(obj, value, context) {
|
303
|
+
var iterator = lookupIterator(value);
|
271
304
|
return _.pluck(_.map(obj, function(value, index, list) {
|
272
305
|
return {
|
273
306
|
value : value,
|
307
|
+
index : index,
|
274
308
|
criteria : iterator.call(context, value, index, list)
|
275
309
|
};
|
276
310
|
}).sort(function(left, right) {
|
277
|
-
var a = left.criteria
|
278
|
-
|
279
|
-
if (
|
280
|
-
|
311
|
+
var a = left.criteria;
|
312
|
+
var b = right.criteria;
|
313
|
+
if (a !== b) {
|
314
|
+
if (a > b || a === void 0) return 1;
|
315
|
+
if (a < b || b === void 0) return -1;
|
316
|
+
}
|
317
|
+
return left.index < right.index ? -1 : 1;
|
281
318
|
}), 'value');
|
282
319
|
};
|
283
320
|
|
284
|
-
// An internal function to generate lookup iterators.
|
285
|
-
var lookupIterator = function(obj, val) {
|
286
|
-
return _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
287
|
-
};
|
288
|
-
|
289
321
|
// An internal function used for aggregate "group by" operations.
|
290
|
-
var group = function(obj,
|
322
|
+
var group = function(obj, value, context, behavior) {
|
291
323
|
var result = {};
|
292
|
-
var iterator = lookupIterator(
|
324
|
+
var iterator = lookupIterator(value);
|
293
325
|
each(obj, function(value, index) {
|
294
|
-
var key = iterator(value, index);
|
326
|
+
var key = iterator.call(context, value, index, obj);
|
295
327
|
behavior(result, key, value);
|
296
328
|
});
|
297
329
|
return result;
|
@@ -299,47 +331,46 @@
|
|
299
331
|
|
300
332
|
// Groups the object's values by a criterion. Pass either a string attribute
|
301
333
|
// to group by, or a function that returns the criterion.
|
302
|
-
_.groupBy = function(obj,
|
303
|
-
return group(obj,
|
304
|
-
(result[key]
|
334
|
+
_.groupBy = function(obj, value, context) {
|
335
|
+
return group(obj, value, context, function(result, key, value) {
|
336
|
+
(_.has(result, key) ? result[key] : (result[key] = [])).push(value);
|
305
337
|
});
|
306
338
|
};
|
307
339
|
|
308
340
|
// Counts instances of an object that group by a certain criterion. Pass
|
309
341
|
// either a string attribute to count by, or a function that returns the
|
310
342
|
// criterion.
|
311
|
-
_.countBy = function(obj,
|
312
|
-
return group(obj,
|
313
|
-
result
|
343
|
+
_.countBy = function(obj, value, context) {
|
344
|
+
return group(obj, value, context, function(result, key, value) {
|
345
|
+
if (!_.has(result, key)) result[key] = 0;
|
314
346
|
result[key]++;
|
315
347
|
});
|
316
348
|
};
|
317
349
|
|
318
350
|
// Use a comparator function to figure out the smallest index at which
|
319
351
|
// an object should be inserted so as to maintain order. Uses binary search.
|
320
|
-
_.sortedIndex = function(array, obj, iterator) {
|
321
|
-
iterator
|
322
|
-
var value = iterator(obj);
|
352
|
+
_.sortedIndex = function(array, obj, iterator, context) {
|
353
|
+
iterator = iterator == null ? _.identity : lookupIterator(iterator);
|
354
|
+
var value = iterator.call(context, obj);
|
323
355
|
var low = 0, high = array.length;
|
324
356
|
while (low < high) {
|
325
|
-
var mid = (low + high)
|
326
|
-
iterator(array[mid]) < value ? low = mid + 1 : high = mid;
|
357
|
+
var mid = (low + high) >>> 1;
|
358
|
+
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
|
327
359
|
}
|
328
360
|
return low;
|
329
361
|
};
|
330
362
|
|
331
363
|
// Safely convert anything iterable into a real, live array.
|
332
364
|
_.toArray = function(obj) {
|
333
|
-
if (!obj)
|
334
|
-
if (
|
335
|
-
if (_.isArguments(obj)) return slice.call(obj);
|
336
|
-
if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
|
365
|
+
if (!obj) return [];
|
366
|
+
if (obj.length === +obj.length) return slice.call(obj);
|
337
367
|
return _.values(obj);
|
338
368
|
};
|
339
369
|
|
340
370
|
// Return the number of elements in an object.
|
341
371
|
_.size = function(obj) {
|
342
|
-
|
372
|
+
if (obj == null) return 0;
|
373
|
+
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
|
343
374
|
};
|
344
375
|
|
345
376
|
// Array Functions
|
@@ -349,6 +380,7 @@
|
|
349
380
|
// values in the array. Aliased as `head` and `take`. The **guard** check
|
350
381
|
// allows it to work with `_.map`.
|
351
382
|
_.first = _.head = _.take = function(array, n, guard) {
|
383
|
+
if (array == null) return void 0;
|
352
384
|
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
353
385
|
};
|
354
386
|
|
@@ -363,6 +395,7 @@
|
|
363
395
|
// Get the last element of an array. Passing **n** will return the last N
|
364
396
|
// values in the array. The **guard** check allows it to work with `_.map`.
|
365
397
|
_.last = function(array, n, guard) {
|
398
|
+
if (array == null) return void 0;
|
366
399
|
if ((n != null) && !guard) {
|
367
400
|
return slice.call(array, Math.max(array.length - n, 0));
|
368
401
|
} else {
|
@@ -370,12 +403,12 @@
|
|
370
403
|
}
|
371
404
|
};
|
372
405
|
|
373
|
-
// Returns everything but the first entry of the array. Aliased as `tail`.
|
374
|
-
// Especially useful on the arguments object. Passing an **
|
375
|
-
// the rest
|
406
|
+
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
|
407
|
+
// Especially useful on the arguments object. Passing an **n** will return
|
408
|
+
// the rest N values in the array. The **guard**
|
376
409
|
// check allows it to work with `_.map`.
|
377
|
-
_.rest = _.tail = function(array,
|
378
|
-
return slice.call(array, (
|
410
|
+
_.rest = _.tail = _.drop = function(array, n, guard) {
|
411
|
+
return slice.call(array, (n == null) || guard ? 1 : n);
|
379
412
|
};
|
380
413
|
|
381
414
|
// Trim out all falsy values from an array.
|
@@ -408,23 +441,23 @@
|
|
408
441
|
// Produce a duplicate-free version of the array. If the array has already
|
409
442
|
// been sorted, you have the option of using a faster algorithm.
|
410
443
|
// Aliased as `unique`.
|
411
|
-
_.uniq = _.unique = function(array, isSorted, iterator) {
|
412
|
-
var initial = iterator ? _.map(array, iterator) : array;
|
444
|
+
_.uniq = _.unique = function(array, isSorted, iterator, context) {
|
445
|
+
var initial = iterator ? _.map(array, iterator, context) : array;
|
413
446
|
var results = [];
|
414
|
-
|
415
|
-
|
416
|
-
|
447
|
+
var seen = [];
|
448
|
+
each(initial, function(value, index) {
|
449
|
+
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
|
450
|
+
seen.push(value);
|
417
451
|
results.push(array[index]);
|
418
452
|
}
|
419
|
-
|
420
|
-
}, []);
|
453
|
+
});
|
421
454
|
return results;
|
422
455
|
};
|
423
456
|
|
424
457
|
// Produce an array that contains the union: each distinct element from all of
|
425
458
|
// the passed-in arrays.
|
426
459
|
_.union = function() {
|
427
|
-
return _.uniq(
|
460
|
+
return _.uniq(concat.apply(ArrayProto, arguments));
|
428
461
|
};
|
429
462
|
|
430
463
|
// Produce an array that contains every item shared between all the
|
@@ -441,8 +474,8 @@
|
|
441
474
|
// Take the difference between one array and a number of other arrays.
|
442
475
|
// Only the elements present in just the first array will remain.
|
443
476
|
_.difference = function(array) {
|
444
|
-
var rest =
|
445
|
-
return _.filter(array, function(value){ return !_.
|
477
|
+
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
|
478
|
+
return _.filter(array, function(value){ return !_.contains(rest, value); });
|
446
479
|
};
|
447
480
|
|
448
481
|
// Zip together multiple lists into a single array -- elements that share
|
@@ -457,12 +490,18 @@
|
|
457
490
|
return results;
|
458
491
|
};
|
459
492
|
|
460
|
-
//
|
461
|
-
//
|
462
|
-
|
493
|
+
// Converts lists into objects. Pass either a single array of `[key, value]`
|
494
|
+
// pairs, or two parallel arrays of the same length -- one of keys, and one of
|
495
|
+
// the corresponding values.
|
496
|
+
_.object = function(list, values) {
|
497
|
+
if (list == null) return {};
|
463
498
|
var result = {};
|
464
|
-
for (var i = 0, l =
|
465
|
-
|
499
|
+
for (var i = 0, l = list.length; i < l; i++) {
|
500
|
+
if (values) {
|
501
|
+
result[list[i]] = values[i];
|
502
|
+
} else {
|
503
|
+
result[list[i][0]] = list[i][1];
|
504
|
+
}
|
466
505
|
}
|
467
506
|
return result;
|
468
507
|
};
|
@@ -475,21 +514,28 @@
|
|
475
514
|
// for **isSorted** to use binary search.
|
476
515
|
_.indexOf = function(array, item, isSorted) {
|
477
516
|
if (array == null) return -1;
|
478
|
-
var i, l;
|
517
|
+
var i = 0, l = array.length;
|
479
518
|
if (isSorted) {
|
480
|
-
|
481
|
-
|
519
|
+
if (typeof isSorted == 'number') {
|
520
|
+
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
|
521
|
+
} else {
|
522
|
+
i = _.sortedIndex(array, item);
|
523
|
+
return array[i] === item ? i : -1;
|
524
|
+
}
|
482
525
|
}
|
483
|
-
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
484
|
-
for (
|
526
|
+
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
|
527
|
+
for (; i < l; i++) if (array[i] === item) return i;
|
485
528
|
return -1;
|
486
529
|
};
|
487
530
|
|
488
531
|
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
489
|
-
_.lastIndexOf = function(array, item) {
|
532
|
+
_.lastIndexOf = function(array, item, from) {
|
490
533
|
if (array == null) return -1;
|
491
|
-
|
492
|
-
|
534
|
+
var hasIndex = from != null;
|
535
|
+
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
|
536
|
+
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
|
537
|
+
}
|
538
|
+
var i = (hasIndex ? from : array.length);
|
493
539
|
while (i--) if (array[i] === item) return i;
|
494
540
|
return -1;
|
495
541
|
};
|
@@ -582,7 +628,9 @@
|
|
582
628
|
context = this; args = arguments;
|
583
629
|
var later = function() {
|
584
630
|
timeout = null;
|
585
|
-
if (more)
|
631
|
+
if (more) {
|
632
|
+
result = func.apply(context, args);
|
633
|
+
}
|
586
634
|
whenDone();
|
587
635
|
};
|
588
636
|
if (!timeout) timeout = setTimeout(later, wait);
|
@@ -602,17 +650,18 @@
|
|
602
650
|
// N milliseconds. If `immediate` is passed, trigger the function on the
|
603
651
|
// leading edge, instead of the trailing.
|
604
652
|
_.debounce = function(func, wait, immediate) {
|
605
|
-
var timeout;
|
653
|
+
var timeout, result;
|
606
654
|
return function() {
|
607
655
|
var context = this, args = arguments;
|
608
656
|
var later = function() {
|
609
657
|
timeout = null;
|
610
|
-
if (!immediate) func.apply(context, args);
|
658
|
+
if (!immediate) result = func.apply(context, args);
|
611
659
|
};
|
612
660
|
var callNow = immediate && !timeout;
|
613
661
|
clearTimeout(timeout);
|
614
662
|
timeout = setTimeout(later, wait);
|
615
|
-
if (callNow) func.apply(context, args);
|
663
|
+
if (callNow) result = func.apply(context, args);
|
664
|
+
return result;
|
616
665
|
};
|
617
666
|
};
|
618
667
|
|
@@ -623,7 +672,9 @@
|
|
623
672
|
return function() {
|
624
673
|
if (ran) return memo;
|
625
674
|
ran = true;
|
626
|
-
|
675
|
+
memo = func.apply(this, arguments);
|
676
|
+
func = null;
|
677
|
+
return memo;
|
627
678
|
};
|
628
679
|
};
|
629
680
|
|
@@ -632,7 +683,8 @@
|
|
632
683
|
// conditionally execute the original function.
|
633
684
|
_.wrap = function(func, wrapper) {
|
634
685
|
return function() {
|
635
|
-
var args = [func]
|
686
|
+
var args = [func];
|
687
|
+
push.apply(args, arguments);
|
636
688
|
return wrapper.apply(this, args);
|
637
689
|
};
|
638
690
|
};
|
@@ -674,7 +726,23 @@
|
|
674
726
|
|
675
727
|
// Retrieve the values of an object's properties.
|
676
728
|
_.values = function(obj) {
|
677
|
-
|
729
|
+
var values = [];
|
730
|
+
for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
|
731
|
+
return values;
|
732
|
+
};
|
733
|
+
|
734
|
+
// Convert an object into a list of `[key, value]` pairs.
|
735
|
+
_.pairs = function(obj) {
|
736
|
+
var pairs = [];
|
737
|
+
for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
|
738
|
+
return pairs;
|
739
|
+
};
|
740
|
+
|
741
|
+
// Invert the keys and values of an object. The values must be serializable.
|
742
|
+
_.invert = function(obj) {
|
743
|
+
var result = {};
|
744
|
+
for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
|
745
|
+
return result;
|
678
746
|
};
|
679
747
|
|
680
748
|
// Return a sorted list of the function names available on the object.
|
@@ -699,11 +767,22 @@
|
|
699
767
|
|
700
768
|
// Return a copy of the object only containing the whitelisted properties.
|
701
769
|
_.pick = function(obj) {
|
702
|
-
var
|
703
|
-
|
704
|
-
|
770
|
+
var copy = {};
|
771
|
+
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
|
772
|
+
each(keys, function(key) {
|
773
|
+
if (key in obj) copy[key] = obj[key];
|
705
774
|
});
|
706
|
-
return
|
775
|
+
return copy;
|
776
|
+
};
|
777
|
+
|
778
|
+
// Return a copy of the object without the blacklisted properties.
|
779
|
+
_.omit = function(obj) {
|
780
|
+
var copy = {};
|
781
|
+
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
|
782
|
+
for (var key in obj) {
|
783
|
+
if (!_.contains(keys, key)) copy[key] = obj[key];
|
784
|
+
}
|
785
|
+
return copy;
|
707
786
|
};
|
708
787
|
|
709
788
|
// Fill in a given object with default properties.
|
@@ -731,18 +810,15 @@
|
|
731
810
|
};
|
732
811
|
|
733
812
|
// Internal recursive comparison function for `isEqual`.
|
734
|
-
var eq = function(a, b,
|
813
|
+
var eq = function(a, b, aStack, bStack) {
|
735
814
|
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
736
815
|
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
|
737
816
|
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
738
817
|
// A strict comparison is necessary because `null == undefined`.
|
739
818
|
if (a == null || b == null) return a === b;
|
740
819
|
// Unwrap any wrapped objects.
|
741
|
-
if (a
|
742
|
-
if (b
|
743
|
-
// Invoke a custom `isEqual` method if one is provided.
|
744
|
-
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
|
745
|
-
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
|
820
|
+
if (a instanceof _) a = a._wrapped;
|
821
|
+
if (b instanceof _) b = b._wrapped;
|
746
822
|
// Compare `[[Class]]` names.
|
747
823
|
var className = toString.call(a);
|
748
824
|
if (className != toString.call(b)) return false;
|
@@ -772,14 +848,15 @@
|
|
772
848
|
if (typeof a != 'object' || typeof b != 'object') return false;
|
773
849
|
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
774
850
|
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
775
|
-
var length =
|
851
|
+
var length = aStack.length;
|
776
852
|
while (length--) {
|
777
853
|
// Linear search. Performance is inversely proportional to the number of
|
778
854
|
// unique nested structures.
|
779
|
-
if (
|
855
|
+
if (aStack[length] == a) return bStack[length] == b;
|
780
856
|
}
|
781
857
|
// Add the first object to the stack of traversed objects.
|
782
|
-
|
858
|
+
aStack.push(a);
|
859
|
+
bStack.push(b);
|
783
860
|
var size = 0, result = true;
|
784
861
|
// Recursively compare objects and arrays.
|
785
862
|
if (className == '[object Array]') {
|
@@ -789,20 +866,24 @@
|
|
789
866
|
if (result) {
|
790
867
|
// Deep compare the contents, ignoring non-numeric properties.
|
791
868
|
while (size--) {
|
792
|
-
|
793
|
-
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
869
|
+
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
|
794
870
|
}
|
795
871
|
}
|
796
872
|
} else {
|
797
|
-
// Objects with different constructors are not equivalent
|
798
|
-
|
873
|
+
// Objects with different constructors are not equivalent, but `Object`s
|
874
|
+
// from different frames are.
|
875
|
+
var aCtor = a.constructor, bCtor = b.constructor;
|
876
|
+
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
|
877
|
+
_.isFunction(bCtor) && (bCtor instanceof bCtor))) {
|
878
|
+
return false;
|
879
|
+
}
|
799
880
|
// Deep compare objects.
|
800
881
|
for (var key in a) {
|
801
882
|
if (_.has(a, key)) {
|
802
883
|
// Count the expected number of properties.
|
803
884
|
size++;
|
804
885
|
// Deep compare each member.
|
805
|
-
if (!(result = _.has(b, key) && eq(a[key], b[key],
|
886
|
+
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
|
806
887
|
}
|
807
888
|
}
|
808
889
|
// Ensure that both objects contain the same number of properties.
|
@@ -814,13 +895,14 @@
|
|
814
895
|
}
|
815
896
|
}
|
816
897
|
// Remove the first object from the stack of traversed objects.
|
817
|
-
|
898
|
+
aStack.pop();
|
899
|
+
bStack.pop();
|
818
900
|
return result;
|
819
901
|
};
|
820
902
|
|
821
903
|
// Perform a deep comparison to check if two objects are equal.
|
822
904
|
_.isEqual = function(a, b) {
|
823
|
-
return eq(a, b, []);
|
905
|
+
return eq(a, b, [], []);
|
824
906
|
};
|
825
907
|
|
826
908
|
// Is a given array, string, or object empty?
|
@@ -834,7 +916,7 @@
|
|
834
916
|
|
835
917
|
// Is a given value a DOM element?
|
836
918
|
_.isElement = function(obj) {
|
837
|
-
return !!(obj && obj.nodeType
|
919
|
+
return !!(obj && obj.nodeType === 1);
|
838
920
|
};
|
839
921
|
|
840
922
|
// Is a given value an array?
|
@@ -863,15 +945,21 @@
|
|
863
945
|
};
|
864
946
|
}
|
865
947
|
|
948
|
+
// Optimize `isFunction` if appropriate.
|
949
|
+
if (typeof (/./) !== 'function') {
|
950
|
+
_.isFunction = function(obj) {
|
951
|
+
return typeof obj === 'function';
|
952
|
+
};
|
953
|
+
}
|
954
|
+
|
866
955
|
// Is a given object a finite number?
|
867
956
|
_.isFinite = function(obj) {
|
868
957
|
return _.isNumber(obj) && isFinite(obj);
|
869
958
|
};
|
870
959
|
|
871
|
-
// Is the given value `NaN`?
|
960
|
+
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
872
961
|
_.isNaN = function(obj) {
|
873
|
-
|
874
|
-
return obj !== obj;
|
962
|
+
return _.isNumber(obj) && obj != +obj;
|
875
963
|
};
|
876
964
|
|
877
965
|
// Is a given value a boolean?
|
@@ -915,25 +1003,43 @@
|
|
915
1003
|
for (var i = 0; i < n; i++) iterator.call(context, i);
|
916
1004
|
};
|
917
1005
|
|
1006
|
+
// Return a random integer between min and max (inclusive).
|
1007
|
+
_.random = function(min, max) {
|
1008
|
+
if (max == null) {
|
1009
|
+
max = min;
|
1010
|
+
min = 0;
|
1011
|
+
}
|
1012
|
+
return min + (0 | Math.random() * (max - min + 1));
|
1013
|
+
};
|
1014
|
+
|
918
1015
|
// List of HTML entities for escaping.
|
919
|
-
var
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
// Regex containing the keys listed immediately above.
|
929
|
-
var htmlEscaper = /[&<>"'\/]/g;
|
930
|
-
|
931
|
-
// Escape a string for HTML interpolation.
|
932
|
-
_.escape = function(string) {
|
933
|
-
return ('' + string).replace(htmlEscaper, function(match) {
|
934
|
-
return htmlEscapes[match];
|
935
|
-
});
|
1016
|
+
var entityMap = {
|
1017
|
+
escape: {
|
1018
|
+
'&': '&',
|
1019
|
+
'<': '<',
|
1020
|
+
'>': '>',
|
1021
|
+
'"': '"',
|
1022
|
+
"'": ''',
|
1023
|
+
'/': '/'
|
1024
|
+
}
|
936
1025
|
};
|
1026
|
+
entityMap.unescape = _.invert(entityMap.escape);
|
1027
|
+
|
1028
|
+
// Regexes containing the keys and values listed immediately above.
|
1029
|
+
var entityRegexes = {
|
1030
|
+
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
|
1031
|
+
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
|
1032
|
+
};
|
1033
|
+
|
1034
|
+
// Functions for escaping and unescaping strings to/from HTML interpolation.
|
1035
|
+
_.each(['escape', 'unescape'], function(method) {
|
1036
|
+
_[method] = function(string) {
|
1037
|
+
if (string == null) return '';
|
1038
|
+
return ('' + string).replace(entityRegexes[method], function(match) {
|
1039
|
+
return entityMap[method][match];
|
1040
|
+
});
|
1041
|
+
};
|
1042
|
+
});
|
937
1043
|
|
938
1044
|
// If the value of the named property is a function then invoke it;
|
939
1045
|
// otherwise, return it.
|
@@ -943,11 +1049,15 @@
|
|
943
1049
|
return _.isFunction(value) ? value.call(object) : value;
|
944
1050
|
};
|
945
1051
|
|
946
|
-
// Add your own custom functions to the Underscore object
|
947
|
-
// they're correctly added to the OOP wrapper as well.
|
1052
|
+
// Add your own custom functions to the Underscore object.
|
948
1053
|
_.mixin = function(obj) {
|
949
1054
|
each(_.functions(obj), function(name){
|
950
|
-
|
1055
|
+
var func = _[name] = obj[name];
|
1056
|
+
_.prototype[name] = function() {
|
1057
|
+
var args = [this._wrapped];
|
1058
|
+
push.apply(args, arguments);
|
1059
|
+
return result.call(this, func.apply(_, args));
|
1060
|
+
};
|
951
1061
|
});
|
952
1062
|
};
|
953
1063
|
|
@@ -970,63 +1080,63 @@
|
|
970
1080
|
// When customizing `templateSettings`, if you don't want to define an
|
971
1081
|
// interpolation, evaluation or escaping regex, we need one that is
|
972
1082
|
// guaranteed not to match.
|
973
|
-
var noMatch =
|
1083
|
+
var noMatch = /(.)^/;
|
974
1084
|
|
975
1085
|
// Certain characters need to be escaped so that they can be put into a
|
976
1086
|
// string literal.
|
977
1087
|
var escapes = {
|
978
|
-
'
|
979
|
-
|
980
|
-
r:
|
981
|
-
n:
|
982
|
-
t:
|
983
|
-
u2028:
|
984
|
-
u2029:
|
1088
|
+
"'": "'",
|
1089
|
+
'\\': '\\',
|
1090
|
+
'\r': 'r',
|
1091
|
+
'\n': 'n',
|
1092
|
+
'\t': 't',
|
1093
|
+
'\u2028': 'u2028',
|
1094
|
+
'\u2029': 'u2029'
|
985
1095
|
};
|
986
1096
|
|
987
|
-
for (var key in escapes) escapes[escapes[key]] = key;
|
988
1097
|
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
|
989
|
-
var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
|
990
|
-
|
991
|
-
// Within an interpolation, evaluation, or escaping, remove HTML escaping
|
992
|
-
// that had been previously added.
|
993
|
-
var unescape = function(code) {
|
994
|
-
return code.replace(unescaper, function(match, escape) {
|
995
|
-
return escapes[escape];
|
996
|
-
});
|
997
|
-
};
|
998
1098
|
|
999
1099
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
1000
1100
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
1001
1101
|
// and correctly escapes quotes within interpolated code.
|
1002
1102
|
_.template = function(text, data, settings) {
|
1003
|
-
settings = _.defaults(
|
1004
|
-
|
1005
|
-
//
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
.
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1103
|
+
settings = _.defaults({}, settings, _.templateSettings);
|
1104
|
+
|
1105
|
+
// Combine delimiters into one regular expression via alternation.
|
1106
|
+
var matcher = new RegExp([
|
1107
|
+
(settings.escape || noMatch).source,
|
1108
|
+
(settings.interpolate || noMatch).source,
|
1109
|
+
(settings.evaluate || noMatch).source
|
1110
|
+
].join('|') + '|$', 'g');
|
1111
|
+
|
1112
|
+
// Compile the template source, escaping string literals appropriately.
|
1113
|
+
var index = 0;
|
1114
|
+
var source = "__p+='";
|
1115
|
+
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
1116
|
+
source += text.slice(index, offset)
|
1117
|
+
.replace(escaper, function(match) { return '\\' + escapes[match]; });
|
1118
|
+
source +=
|
1119
|
+
escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" :
|
1120
|
+
interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" :
|
1121
|
+
evaluate ? "';\n" + evaluate + "\n__p+='" : '';
|
1122
|
+
index = offset + match.length;
|
1123
|
+
});
|
1124
|
+
source += "';\n";
|
1021
1125
|
|
1022
1126
|
// If a variable is not specified, place data values in local scope.
|
1023
1127
|
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
|
1024
1128
|
|
1025
1129
|
source = "var __t,__p='',__j=Array.prototype.join," +
|
1026
|
-
"print=function(){__p+=__j.call(arguments,'')};\n" +
|
1130
|
+
"print=function(){__p+=__j.call(arguments,'');};\n" +
|
1027
1131
|
source + "return __p;\n";
|
1028
1132
|
|
1029
|
-
|
1133
|
+
try {
|
1134
|
+
var render = new Function(settings.variable || 'obj', '_', source);
|
1135
|
+
} catch (e) {
|
1136
|
+
e.source = source;
|
1137
|
+
throw e;
|
1138
|
+
}
|
1139
|
+
|
1030
1140
|
if (data) return render(data, _);
|
1031
1141
|
var template = function(data) {
|
1032
1142
|
return render.call(this, data, _);
|
@@ -1043,29 +1153,15 @@
|
|
1043
1153
|
return _(obj).chain();
|
1044
1154
|
};
|
1045
1155
|
|
1046
|
-
//
|
1156
|
+
// OOP
|
1047
1157
|
// ---------------
|
1048
|
-
|
1049
1158
|
// If Underscore is called as a function, it returns a wrapped object that
|
1050
1159
|
// can be used OO-style. This wrapper holds altered versions of all the
|
1051
1160
|
// underscore functions. Wrapped objects may be chained.
|
1052
|
-
var wrapper = function(obj) { this._wrapped = obj; };
|
1053
|
-
|
1054
|
-
// Expose `wrapper.prototype` as `_.prototype`
|
1055
|
-
_.prototype = wrapper.prototype;
|
1056
1161
|
|
1057
1162
|
// Helper function to continue chaining intermediate results.
|
1058
|
-
var result = function(obj
|
1059
|
-
return
|
1060
|
-
};
|
1061
|
-
|
1062
|
-
// A method to easily add functions to the OOP wrapper.
|
1063
|
-
var addToWrapper = function(name, func) {
|
1064
|
-
wrapper.prototype[name] = function() {
|
1065
|
-
var args = slice.call(arguments);
|
1066
|
-
unshift.call(args, this._wrapped);
|
1067
|
-
return result(func.apply(_, args), this._chain);
|
1068
|
-
};
|
1163
|
+
var result = function(obj) {
|
1164
|
+
return this._chain ? _(obj).chain() : obj;
|
1069
1165
|
};
|
1070
1166
|
|
1071
1167
|
// Add all of the Underscore functions to the wrapper object.
|
@@ -1074,31 +1170,35 @@
|
|
1074
1170
|
// Add all mutator Array functions to the wrapper.
|
1075
1171
|
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
1076
1172
|
var method = ArrayProto[name];
|
1077
|
-
|
1173
|
+
_.prototype[name] = function() {
|
1078
1174
|
var obj = this._wrapped;
|
1079
1175
|
method.apply(obj, arguments);
|
1080
1176
|
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
|
1081
|
-
return result(
|
1177
|
+
return result.call(this, obj);
|
1082
1178
|
};
|
1083
1179
|
});
|
1084
1180
|
|
1085
1181
|
// Add all accessor Array functions to the wrapper.
|
1086
1182
|
each(['concat', 'join', 'slice'], function(name) {
|
1087
1183
|
var method = ArrayProto[name];
|
1088
|
-
|
1089
|
-
return result(method.apply(this._wrapped, arguments)
|
1184
|
+
_.prototype[name] = function() {
|
1185
|
+
return result.call(this, method.apply(this._wrapped, arguments));
|
1090
1186
|
};
|
1091
1187
|
});
|
1092
1188
|
|
1093
|
-
|
1094
|
-
wrapper.prototype.chain = function() {
|
1095
|
-
this._chain = true;
|
1096
|
-
return this;
|
1097
|
-
};
|
1189
|
+
_.extend(_.prototype, {
|
1098
1190
|
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1191
|
+
// Start chaining a wrapped Underscore object.
|
1192
|
+
chain: function() {
|
1193
|
+
this._chain = true;
|
1194
|
+
return this;
|
1195
|
+
},
|
1196
|
+
|
1197
|
+
// Extracts the result from a wrapped and chained object.
|
1198
|
+
value: function() {
|
1199
|
+
return this._wrapped;
|
1200
|
+
}
|
1201
|
+
|
1202
|
+
});
|
1103
1203
|
|
1104
1204
|
}).call(this);
|