ultimate-base 0.5.0.0 → 0.6.0
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/app/assets/javascripts/ultimate/backbone/app.js.coffee +47 -5
- data/app/assets/javascripts/ultimate/backbone/base.js.coffee +9 -10
- data/app/assets/javascripts/ultimate/backbone/collection.js.coffee +0 -1
- data/app/assets/javascripts/ultimate/backbone/lib/backbone.js +765 -673
- data/app/assets/javascripts/ultimate/backbone/model.js.coffee +0 -1
- data/app/assets/javascripts/ultimate/backbone/router.js.coffee +0 -4
- data/app/assets/javascripts/ultimate/backbone/view-mixins/nodes.js.coffee +51 -0
- data/app/assets/javascripts/ultimate/backbone/view.js.coffee +18 -104
- data/app/assets/javascripts/ultimate/base.js.coffee +1 -11
- data/app/assets/javascripts/ultimate/jquery-plugin-adapter.js.coffee +0 -1
- data/app/assets/javascripts/ultimate/jquery-plugin-class.js.coffee +3 -25
- data/app/assets/javascripts/ultimate/jquery.base.js.coffee +0 -2
- data/app/assets/javascripts/ultimate/underscore/underscore.inflection.js +4 -3
- data/app/assets/javascripts/ultimate/underscore/underscore.js +103 -80
- data/app/assets/javascripts/ultimate/underscore/underscore.string.js +71 -27
- data/lib/ultimate/base.rb +0 -1
- data/lib/ultimate/base/version.rb +1 -1
- metadata +3 -8
- data/app/assets/javascripts/ultimate/backbone/extra/jquery-ext.js.coffee +0 -96
- data/app/assets/javascripts/ultimate/improves/datepicker.js.coffee +0 -34
- data/app/assets/javascripts/ultimate/improves/devise.js.coffee +0 -18
- data/app/assets/javascripts/ultimate/improves/form-errors.js.coffee +0 -146
- data/app/assets/javascripts/ultimate/improves/tablesorter.js +0 -59
- data/lib/ultimate/extensions/directive_processor.rb +0 -64
@@ -10,7 +10,6 @@ class Ultimate.Backbone.Model extends Backbone.Model
|
|
10
10
|
expireTime: Infinity
|
11
11
|
|
12
12
|
constructor: (attributes, options = {}) ->
|
13
|
-
Ultimate.Backbone.debug ".Model.constructor()", @
|
14
13
|
@expireTime = options.expireTime if options.expireTime?
|
15
14
|
@on 'sync', =>
|
16
15
|
@loadedTimeStamp = new Date()
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Cached regex to split keys for `delegate`, from backbone.js.
|
2
|
+
delegateEventSplitter = /^(\S+)\s*(.*)$/
|
3
|
+
|
4
|
+
Ultimate.Backbone.ViewMixins.Nodes =
|
5
|
+
|
6
|
+
nodes: null
|
7
|
+
|
8
|
+
# Overwritten parent method Backbone.View.setElement() as hook for findNodes()
|
9
|
+
setElement: (element, delegate) ->
|
10
|
+
@undelegateEvents() if @$el
|
11
|
+
@$el = if element instanceof Backbone.$ then element else Backbone.$(element)
|
12
|
+
@el = @$el[0]
|
13
|
+
@findNodes() # inserted hook
|
14
|
+
@delegateEvents() if delegate isnt false
|
15
|
+
@
|
16
|
+
|
17
|
+
findNodes: (jRoot = @$el, nodes = @nodes) ->
|
18
|
+
jNodes = {}
|
19
|
+
nodes = @nodes.call(@) if _.isFunction(nodes)
|
20
|
+
if _.isObject(nodes)
|
21
|
+
for nodeName, selector of nodes when nodeName isnt 'selector'
|
22
|
+
_isObject = _.isObject(selector)
|
23
|
+
if _isObject
|
24
|
+
nestedNodes = selector
|
25
|
+
selector = nestedNodes['selector']
|
26
|
+
jNodes[nodeName] = @[nodeName] = jRoot.find(selector)
|
27
|
+
if _isObject
|
28
|
+
_.extend jNodes, @findNodes(jNodes[nodeName], nestedNodes)
|
29
|
+
jNodes
|
30
|
+
|
31
|
+
# Overload and proxy parent method Backbone.View.delegateEvents() as hook for normalizeEvents().
|
32
|
+
delegateEvents: (events) ->
|
33
|
+
args = _.toArray(arguments)
|
34
|
+
args[0] = @normalizeEvents(events)
|
35
|
+
@__super 'delegateEvents', args
|
36
|
+
|
37
|
+
# TODO docs
|
38
|
+
normalizeEvents: (events) ->
|
39
|
+
events = _.result(@, "events") unless events
|
40
|
+
if events
|
41
|
+
normalizedEvents = {}
|
42
|
+
for key, method of events
|
43
|
+
[[], eventName, selector] = key.match(delegateEventSplitter)
|
44
|
+
selector = _.result(@, selector)
|
45
|
+
selector = selector.selector if selector instanceof Backbone.$
|
46
|
+
if _.isString(selector)
|
47
|
+
selector = selector.replace(@$el.selector, '') if _.string.startsWith(selector, @$el.selector)
|
48
|
+
key = "#{eventName} #{selector}"
|
49
|
+
normalizedEvents[key] = method
|
50
|
+
events = normalizedEvents
|
51
|
+
events
|
@@ -1,113 +1,27 @@
|
|
1
|
-
# * Require ./../underscore/underscore.outcasts
|
2
|
-
# * Require ./../underscore/underscore.string
|
3
1
|
#= require ./base
|
4
2
|
|
5
3
|
class Ultimate.Backbone.View extends Backbone.View
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
if @$el?.length and not sideCall
|
24
|
-
views = @$el.data("views")
|
25
|
-
for view in views when view isnt @
|
26
|
-
view.setElement element, delegate, true
|
27
|
-
@$el.data("views", [])
|
28
|
-
super
|
29
|
-
if @$el.length
|
30
|
-
if views = @$el.data("views")
|
31
|
-
views.push @
|
32
|
-
else
|
33
|
-
@$el.data "views", [@]
|
34
|
-
@findNodes()
|
35
|
-
@
|
36
|
-
|
37
|
-
findNodes: (jRoot = @$el, nodes = @nodes) ->
|
38
|
-
jNodes = {}
|
39
|
-
nodes = if _.isFunction(nodes) then @nodes.call(@) else _.clone(nodes)
|
40
|
-
if _.isObject(nodes)
|
41
|
-
for nodeName, selector of nodes
|
42
|
-
_isObject = _.isObject(selector)
|
43
|
-
if _isObject
|
44
|
-
nestedNodes = selector
|
45
|
-
selector = _.outcasts.delete(nestedNodes, "selector")
|
46
|
-
jNodes[nodeName] = @[nodeName] = jRoot.find(selector)
|
47
|
-
if _isObject
|
48
|
-
_.extend jNodes, @findNodes(jNodes[nodeName], nestedNodes)
|
49
|
-
jNodes
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# Overload and proxy parent method Backbone.View.delegateEvents() as hook for normalizeEvents().
|
54
|
-
delegateEvents: (events) ->
|
55
|
-
args = _.toArray(arguments)
|
56
|
-
args[0] = @normalizeEvents(events)
|
57
|
-
super args...
|
58
|
-
|
59
|
-
# Cached regex to split keys for `delegate`, from backbone.js.
|
60
|
-
delegateEventSplitter = /^(\S+)\s*(.*)$/
|
61
|
-
|
62
|
-
normalizeEvents: (events) ->
|
63
|
-
events = _.result(@, "events") unless events
|
64
|
-
if events
|
65
|
-
normalizedEvents = {}
|
66
|
-
for key, method of events
|
67
|
-
[[], eventName, selector] = key.match(delegateEventSplitter)
|
68
|
-
selector = _.result(@, selector)
|
69
|
-
selector = selector.selector if selector instanceof jQuery
|
70
|
-
if _.isString(selector)
|
71
|
-
selector = selector.replace(@$el.selector, '') if _.string.startsWith(selector, @$el.selector)
|
72
|
-
key = "#{eventName} #{selector}"
|
73
|
-
normalizedEvents[key] = method
|
74
|
-
events = normalizedEvents
|
75
|
-
events
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
# Overload parent method Backbone.View._configure() as hook for reflectOptions().
|
5
|
+
# Mixins support
|
6
|
+
@include: (mixin) ->
|
7
|
+
unless mixin?
|
8
|
+
throw new Error('Mixin is undefined')
|
9
|
+
_.extend @::, mixin
|
10
|
+
|
11
|
+
__super: (methodName, args) ->
|
12
|
+
obj = @
|
13
|
+
calledMethod = @[methodName]
|
14
|
+
obj = obj.constructor.__super__ while obj[methodName] is calledMethod
|
15
|
+
superMethod = obj[methodName]
|
16
|
+
unless superMethod?
|
17
|
+
throw new Error("__super can't find super method '#{methodName}'")
|
18
|
+
superMethod.apply @, args
|
19
|
+
|
20
|
+
# Overload parent method Backbone.View._configure() as hook for reflectOptions()
|
80
21
|
_configure: (options) ->
|
81
22
|
super
|
82
23
|
@reflectOptions()
|
83
24
|
|
84
|
-
reflectOptions: (
|
85
|
-
@[attr] =
|
86
|
-
@[attr] = value for attr, value of options when typeof @[attr] isnt "undefined"
|
25
|
+
reflectOptions: (options = @options) ->
|
26
|
+
@[attr] = value for attr, value of options when not _.isUndefined(@[attr])
|
87
27
|
@
|
88
|
-
|
89
|
-
# Overloadable getter for jQuery-container that will be blocked.
|
90
|
-
getJLoadingContainer: -> @$el
|
91
|
-
|
92
|
-
loading: (state, text = "", circle = false) ->
|
93
|
-
jLoadingContainer = @getJLoadingContainer()
|
94
|
-
if jLoadingContainer?.length
|
95
|
-
jLoadingContainer.removeClass @loadingStateClass
|
96
|
-
jLoadingContainer.children(".#{@loadingOverlayClass}").remove()
|
97
|
-
if @loadingState = state
|
98
|
-
jLoadingContainer.addClass @loadingStateClass
|
99
|
-
style = []
|
100
|
-
if @loadingWidthMethodName
|
101
|
-
style.push "width: #{jLoadingContainer[@loadingWidthMethodName]()}px;"
|
102
|
-
if @loadingHeightMethodName
|
103
|
-
height = jLoadingContainer[@loadingHeightMethodName]()
|
104
|
-
style.push "height: #{height}px; line-height: #{height}px;"
|
105
|
-
text = if text then "<span class=\"text\">#{text}</span>" else ''
|
106
|
-
circle = if circle then "<span class=\"circle\"></span>" else ''
|
107
|
-
jLoadingContainer.append "<div class=\"#{@loadingOverlayClass}\" style=\"#{style.join(' ')}\">#{circle}#{text}</div>"
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
# Improve templating.
|
112
|
-
jst: (name, context = @) ->
|
113
|
-
JST["backbone/templates/#{name}"] context
|
@@ -21,7 +21,6 @@
|
|
21
21
|
###
|
22
22
|
|
23
23
|
Ultimate.createJQueryPlugin = (pluginName, pluginClass) ->
|
24
|
-
Ultimate.debug(".createJQueryPlugin()", pluginName, pluginClass) if $.isFunction(Ultimate.debug)
|
25
24
|
pluginClass.pluginName ||= pluginName
|
26
25
|
jQuery.fn[pluginName] = -> @ultimatePluginAdapter pluginName, pluginClass, arguments
|
27
26
|
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# TODO simlify translations infrastructure
|
2
1
|
# `pluginClass` must store propery `$el` as jQuery object wrapped on the target DOM-object in his instance.
|
3
2
|
|
4
3
|
#= require ./base
|
@@ -14,7 +13,6 @@
|
|
14
13
|
# 1 _.bind
|
15
14
|
# 1 _.clone
|
16
15
|
# 1 _.outcasts.delete
|
17
|
-
# 1 _.string.underscored
|
18
16
|
# 1 _.string.startsWith
|
19
17
|
|
20
18
|
class Ultimate.Plugin
|
@@ -26,10 +24,6 @@ class Ultimate.Plugin
|
|
26
24
|
|
27
25
|
options: null
|
28
26
|
|
29
|
-
# @defaultLocales: { en: {} }
|
30
|
-
locale: 'en'
|
31
|
-
translations: null
|
32
|
-
|
33
27
|
constructor: (options) ->
|
34
28
|
@cid = _.uniqueId('ultimatePlugin_')
|
35
29
|
@_configure options
|
@@ -71,7 +65,7 @@ class Ultimate.Plugin
|
|
71
65
|
@_delegateEvents args...
|
72
66
|
|
73
67
|
# delegateEvents() from backbone.js
|
74
|
-
_delegateEvents: (events = _.result(@,
|
68
|
+
_delegateEvents: (events = _.result(@, 'events')) ->
|
75
69
|
return unless events
|
76
70
|
@undelegateEvents()
|
77
71
|
for key, method of events
|
@@ -86,7 +80,7 @@ class Ultimate.Plugin
|
|
86
80
|
@$el.delegate(selector, eventName, method)
|
87
81
|
|
88
82
|
_normalizeEvents: (events) ->
|
89
|
-
events = _.result(@,
|
83
|
+
events = _.result(@, 'events') unless events
|
90
84
|
if events
|
91
85
|
normalizedEvents = {}
|
92
86
|
for key, method of events
|
@@ -103,25 +97,9 @@ class Ultimate.Plugin
|
|
103
97
|
_configure: (options = {}) ->
|
104
98
|
@options ||= {}
|
105
99
|
_.extend @options, options
|
106
|
-
#cout '@options', @options
|
107
100
|
@_reflectOptions()
|
108
|
-
@_initTranslations()
|
109
101
|
|
110
|
-
_reflectOptions: (reflectableOptions = _.result(@,
|
102
|
+
_reflectOptions: (reflectableOptions = _.result(@, 'reflectableOptions'), options = @options) ->
|
111
103
|
if _.isArray(reflectableOptions)
|
112
104
|
@[attr] = options[attr] for attr in reflectableOptions when not _.isUndefined(options[attr])
|
113
105
|
@[attr] = value for attr, value of options when not _.isUndefined(@[attr])
|
114
|
-
|
115
|
-
# use I18n, and modify locale and translations
|
116
|
-
_initTranslations: ->
|
117
|
-
@translations ||= {}
|
118
|
-
if @constructor.defaultLocales?
|
119
|
-
if not @options["locale"] and I18n?.locale of @constructor.defaultLocales
|
120
|
-
@locale = I18n.locale
|
121
|
-
#cout '!!!Set LOCALE FROM I18N ', @locale
|
122
|
-
defaultTranslations = if @locale then @constructor.defaultLocales[@locale] or {} else {}
|
123
|
-
#cout '!!!defaultTranslations', @locale, defaultTranslations
|
124
|
-
_.defaults @translations, defaultTranslations
|
125
|
-
|
126
|
-
t: (key) ->
|
127
|
-
@translations[key] or _.string.humanize(key)
|
@@ -33,9 +33,10 @@
|
|
33
33
|
pluralize : function( word, count, includeNumber )
|
34
34
|
{
|
35
35
|
var result;
|
36
|
-
|
36
|
+
|
37
37
|
if( count !== undefined )
|
38
38
|
{
|
39
|
+
count = Math.round(count);
|
39
40
|
result = ( count === 1 ) ? this.singularize( word ) : this.pluralize( word );
|
40
41
|
result = ( includeNumber ) ? [ count, result ].join( ' ' ) : result;
|
41
42
|
}
|
@@ -56,7 +57,7 @@
|
|
56
57
|
},
|
57
58
|
this );
|
58
59
|
}
|
59
|
-
|
60
|
+
|
60
61
|
return result;
|
61
62
|
},
|
62
63
|
|
@@ -172,5 +173,5 @@
|
|
172
173
|
* Underscore integration
|
173
174
|
*/
|
174
175
|
_.mixin( inflector.resetInflections( ) );
|
175
|
-
|
176
|
+
|
176
177
|
} )( _ );
|
@@ -1,12 +1,13 @@
|
|
1
|
-
//
|
2
|
-
//
|
3
|
-
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
4
|
-
// Underscore may be freely distributed under the MIT license.
|
1
|
+
// Underscore.js 1.4.4
|
2
|
+
// ===================
|
5
3
|
|
6
|
-
|
4
|
+
// > http://underscorejs.org
|
5
|
+
// > (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
|
6
|
+
// > Underscore may be freely distributed under the MIT license.
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
// Baseline setup
|
9
|
+
// --------------
|
10
|
+
(function() {
|
10
11
|
|
11
12
|
// Establish the root object, `window` in the browser, or `global` on the server.
|
12
13
|
var root = this;
|
@@ -24,7 +25,6 @@
|
|
24
25
|
var push = ArrayProto.push,
|
25
26
|
slice = ArrayProto.slice,
|
26
27
|
concat = ArrayProto.concat,
|
27
|
-
unshift = ArrayProto.unshift,
|
28
28
|
toString = ObjProto.toString,
|
29
29
|
hasOwnProperty = ObjProto.hasOwnProperty;
|
30
30
|
|
@@ -61,11 +61,11 @@
|
|
61
61
|
}
|
62
62
|
exports._ = _;
|
63
63
|
} else {
|
64
|
-
root
|
64
|
+
root._ = _;
|
65
65
|
}
|
66
66
|
|
67
67
|
// Current version.
|
68
|
-
_.VERSION = '1.4.
|
68
|
+
_.VERSION = '1.4.4';
|
69
69
|
|
70
70
|
// Collection Functions
|
71
71
|
// --------------------
|
@@ -102,6 +102,8 @@
|
|
102
102
|
return results;
|
103
103
|
};
|
104
104
|
|
105
|
+
var reduceError = 'Reduce of empty array with no initial value';
|
106
|
+
|
105
107
|
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
106
108
|
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
107
109
|
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
@@ -119,7 +121,7 @@
|
|
119
121
|
memo = iterator.call(context, memo, value, index, list);
|
120
122
|
}
|
121
123
|
});
|
122
|
-
if (!initial) throw new TypeError(
|
124
|
+
if (!initial) throw new TypeError(reduceError);
|
123
125
|
return memo;
|
124
126
|
};
|
125
127
|
|
@@ -130,7 +132,7 @@
|
|
130
132
|
if (obj == null) obj = [];
|
131
133
|
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
132
134
|
if (context) iterator = _.bind(iterator, context);
|
133
|
-
return
|
135
|
+
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
134
136
|
}
|
135
137
|
var length = obj.length;
|
136
138
|
if (length !== +length) {
|
@@ -146,7 +148,7 @@
|
|
146
148
|
memo = iterator.call(context, memo, obj[index], index, list);
|
147
149
|
}
|
148
150
|
});
|
149
|
-
if (!initial) throw new TypeError(
|
151
|
+
if (!initial) throw new TypeError(reduceError);
|
150
152
|
return memo;
|
151
153
|
};
|
152
154
|
|
@@ -177,12 +179,9 @@
|
|
177
179
|
|
178
180
|
// Return all the elements for which a truth test fails.
|
179
181
|
_.reject = function(obj, iterator, context) {
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
184
|
-
});
|
185
|
-
return results;
|
182
|
+
return _.filter(obj, function(value, index, list) {
|
183
|
+
return !iterator.call(context, value, index, list);
|
184
|
+
}, context);
|
186
185
|
};
|
187
186
|
|
188
187
|
// Determine whether all of the elements match a truth test.
|
@@ -216,20 +215,19 @@
|
|
216
215
|
// Determine if the array or object contains a given value (using `===`).
|
217
216
|
// Aliased as `include`.
|
218
217
|
_.contains = _.include = function(obj, target) {
|
219
|
-
|
220
|
-
if (obj == null) return found;
|
218
|
+
if (obj == null) return false;
|
221
219
|
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
222
|
-
|
220
|
+
return any(obj, function(value) {
|
223
221
|
return value === target;
|
224
222
|
});
|
225
|
-
return found;
|
226
223
|
};
|
227
224
|
|
228
225
|
// Invoke a method (with arguments) on every item in a collection.
|
229
226
|
_.invoke = function(obj, method) {
|
230
227
|
var args = slice.call(arguments, 2);
|
228
|
+
var isFunc = _.isFunction(method);
|
231
229
|
return _.map(obj, function(value) {
|
232
|
-
return (
|
230
|
+
return (isFunc ? method : value[method]).apply(value, args);
|
233
231
|
});
|
234
232
|
};
|
235
233
|
|
@@ -239,10 +237,10 @@
|
|
239
237
|
};
|
240
238
|
|
241
239
|
// Convenience version of a common use case of `filter`: selecting only objects
|
242
|
-
//
|
243
|
-
_.where = function(obj, attrs) {
|
244
|
-
if (_.isEmpty(attrs)) return [];
|
245
|
-
return _
|
240
|
+
// containing specific `key:value` pairs.
|
241
|
+
_.where = function(obj, attrs, first) {
|
242
|
+
if (_.isEmpty(attrs)) return first ? null : [];
|
243
|
+
return _[first ? 'find' : 'filter'](obj, function(value) {
|
246
244
|
for (var key in attrs) {
|
247
245
|
if (attrs[key] !== value[key]) return false;
|
248
246
|
}
|
@@ -250,6 +248,12 @@
|
|
250
248
|
});
|
251
249
|
};
|
252
250
|
|
251
|
+
// Convenience version of a common use case of `find`: getting the first object
|
252
|
+
// containing specific `key:value` pairs.
|
253
|
+
_.findWhere = function(obj, attrs) {
|
254
|
+
return _.where(obj, attrs, true);
|
255
|
+
};
|
256
|
+
|
253
257
|
// Return the maximum element or (element-based computation).
|
254
258
|
// Can't optimize arrays of integers longer than 65,535 elements.
|
255
259
|
// See: https://bugs.webkit.org/show_bug.cgi?id=80797
|
@@ -258,7 +262,7 @@
|
|
258
262
|
return Math.max.apply(Math, obj);
|
259
263
|
}
|
260
264
|
if (!iterator && _.isEmpty(obj)) return -Infinity;
|
261
|
-
var result = {computed : -Infinity};
|
265
|
+
var result = {computed : -Infinity, value: -Infinity};
|
262
266
|
each(obj, function(value, index, list) {
|
263
267
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
264
268
|
computed >= result.computed && (result = {value : value, computed : computed});
|
@@ -272,7 +276,7 @@
|
|
272
276
|
return Math.min.apply(Math, obj);
|
273
277
|
}
|
274
278
|
if (!iterator && _.isEmpty(obj)) return Infinity;
|
275
|
-
var result = {computed : Infinity};
|
279
|
+
var result = {computed : Infinity, value: Infinity};
|
276
280
|
each(obj, function(value, index, list) {
|
277
281
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
278
282
|
computed < result.computed && (result = {value : value, computed : computed});
|
@@ -321,7 +325,7 @@
|
|
321
325
|
// An internal function used for aggregate "group by" operations.
|
322
326
|
var group = function(obj, value, context, behavior) {
|
323
327
|
var result = {};
|
324
|
-
var iterator = lookupIterator(value);
|
328
|
+
var iterator = lookupIterator(value || _.identity);
|
325
329
|
each(obj, function(value, index) {
|
326
330
|
var key = iterator.call(context, value, index, obj);
|
327
331
|
behavior(result, key, value);
|
@@ -341,7 +345,7 @@
|
|
341
345
|
// either a string attribute to count by, or a function that returns the
|
342
346
|
// criterion.
|
343
347
|
_.countBy = function(obj, value, context) {
|
344
|
-
return group(obj, value, context, function(result, key
|
348
|
+
return group(obj, value, context, function(result, key) {
|
345
349
|
if (!_.has(result, key)) result[key] = 0;
|
346
350
|
result[key]++;
|
347
351
|
});
|
@@ -363,7 +367,8 @@
|
|
363
367
|
// Safely convert anything iterable into a real, live array.
|
364
368
|
_.toArray = function(obj) {
|
365
369
|
if (!obj) return [];
|
366
|
-
if (
|
370
|
+
if (_.isArray(obj)) return slice.call(obj);
|
371
|
+
if (obj.length === +obj.length) return _.map(obj, _.identity);
|
367
372
|
return _.values(obj);
|
368
373
|
};
|
369
374
|
|
@@ -413,7 +418,7 @@
|
|
413
418
|
|
414
419
|
// Trim out all falsy values from an array.
|
415
420
|
_.compact = function(array) {
|
416
|
-
return _.filter(array,
|
421
|
+
return _.filter(array, _.identity);
|
417
422
|
};
|
418
423
|
|
419
424
|
// Internal implementation of a recursive `flatten` function.
|
@@ -442,6 +447,11 @@
|
|
442
447
|
// been sorted, you have the option of using a faster algorithm.
|
443
448
|
// Aliased as `unique`.
|
444
449
|
_.uniq = _.unique = function(array, isSorted, iterator, context) {
|
450
|
+
if (_.isFunction(isSorted)) {
|
451
|
+
context = iterator;
|
452
|
+
iterator = isSorted;
|
453
|
+
isSorted = false;
|
454
|
+
}
|
445
455
|
var initial = iterator ? _.map(array, iterator, context) : array;
|
446
456
|
var results = [];
|
447
457
|
var seen = [];
|
@@ -565,25 +575,23 @@
|
|
565
575
|
// Function (ahem) Functions
|
566
576
|
// ------------------
|
567
577
|
|
568
|
-
// Reusable constructor function for prototype setting.
|
569
|
-
var ctor = function(){};
|
570
|
-
|
571
578
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
572
|
-
// optionally).
|
573
|
-
//
|
574
|
-
|
575
|
-
_.bind = function bind(func, context) {
|
576
|
-
var bound, args;
|
579
|
+
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
|
580
|
+
// available.
|
581
|
+
_.bind = function(func, context) {
|
577
582
|
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
583
|
+
var args = slice.call(arguments, 2);
|
584
|
+
return function() {
|
585
|
+
return func.apply(context, args.concat(slice.call(arguments)));
|
586
|
+
};
|
587
|
+
};
|
588
|
+
|
589
|
+
// Partially apply a function by creating a version that has had some of its
|
590
|
+
// arguments pre-filled, without changing its dynamic `this` context.
|
591
|
+
_.partial = function(func) {
|
592
|
+
var args = slice.call(arguments, 1);
|
593
|
+
return function() {
|
594
|
+
return func.apply(this, args.concat(slice.call(arguments)));
|
587
595
|
};
|
588
596
|
};
|
589
597
|
|
@@ -591,7 +599,7 @@
|
|
591
599
|
// all callbacks defined on an object belong to it.
|
592
600
|
_.bindAll = function(obj) {
|
593
601
|
var funcs = slice.call(arguments, 1);
|
594
|
-
if (funcs.length
|
602
|
+
if (funcs.length === 0) funcs = _.functions(obj);
|
595
603
|
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
596
604
|
return obj;
|
597
605
|
};
|
@@ -622,25 +630,26 @@
|
|
622
630
|
// Returns a function, that, when invoked, will only be triggered at most once
|
623
631
|
// during a given window of time.
|
624
632
|
_.throttle = function(func, wait) {
|
625
|
-
var context, args, timeout,
|
626
|
-
var
|
633
|
+
var context, args, timeout, result;
|
634
|
+
var previous = 0;
|
635
|
+
var later = function() {
|
636
|
+
previous = new Date;
|
637
|
+
timeout = null;
|
638
|
+
result = func.apply(context, args);
|
639
|
+
};
|
627
640
|
return function() {
|
628
|
-
|
629
|
-
var
|
641
|
+
var now = new Date;
|
642
|
+
var remaining = wait - (now - previous);
|
643
|
+
context = this;
|
644
|
+
args = arguments;
|
645
|
+
if (remaining <= 0) {
|
646
|
+
clearTimeout(timeout);
|
630
647
|
timeout = null;
|
631
|
-
|
632
|
-
result = func.apply(context, args);
|
633
|
-
}
|
634
|
-
whenDone();
|
635
|
-
};
|
636
|
-
if (!timeout) timeout = setTimeout(later, wait);
|
637
|
-
if (throttling) {
|
638
|
-
more = true;
|
639
|
-
} else {
|
640
|
-
throttling = true;
|
648
|
+
previous = now;
|
641
649
|
result = func.apply(context, args);
|
650
|
+
} else if (!timeout) {
|
651
|
+
timeout = setTimeout(later, remaining);
|
642
652
|
}
|
643
|
-
whenDone();
|
644
653
|
return result;
|
645
654
|
};
|
646
655
|
};
|
@@ -758,8 +767,10 @@
|
|
758
767
|
// Extend a given object with all the properties in passed-in object(s).
|
759
768
|
_.extend = function(obj) {
|
760
769
|
each(slice.call(arguments, 1), function(source) {
|
761
|
-
|
762
|
-
|
770
|
+
if (source) {
|
771
|
+
for (var prop in source) {
|
772
|
+
obj[prop] = source[prop];
|
773
|
+
}
|
763
774
|
}
|
764
775
|
});
|
765
776
|
return obj;
|
@@ -788,8 +799,10 @@
|
|
788
799
|
// Fill in a given object with default properties.
|
789
800
|
_.defaults = function(obj) {
|
790
801
|
each(slice.call(arguments, 1), function(source) {
|
791
|
-
|
792
|
-
|
802
|
+
if (source) {
|
803
|
+
for (var prop in source) {
|
804
|
+
if (obj[prop] == null) obj[prop] = source[prop];
|
805
|
+
}
|
793
806
|
}
|
794
807
|
});
|
795
808
|
return obj;
|
@@ -954,7 +967,7 @@
|
|
954
967
|
|
955
968
|
// Is a given object a finite number?
|
956
969
|
_.isFinite = function(obj) {
|
957
|
-
return
|
970
|
+
return isFinite(obj) && !isNaN(parseFloat(obj));
|
958
971
|
};
|
959
972
|
|
960
973
|
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
@@ -1000,7 +1013,9 @@
|
|
1000
1013
|
|
1001
1014
|
// Run a function **n** times.
|
1002
1015
|
_.times = function(n, iterator, context) {
|
1003
|
-
|
1016
|
+
var accum = Array(n);
|
1017
|
+
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
|
1018
|
+
return accum;
|
1004
1019
|
};
|
1005
1020
|
|
1006
1021
|
// Return a random integer between min and max (inclusive).
|
@@ -1009,7 +1024,7 @@
|
|
1009
1024
|
max = min;
|
1010
1025
|
min = 0;
|
1011
1026
|
}
|
1012
|
-
return min + (
|
1027
|
+
return min + Math.floor(Math.random() * (max - min + 1));
|
1013
1028
|
};
|
1014
1029
|
|
1015
1030
|
// List of HTML entities for escaping.
|
@@ -1065,7 +1080,7 @@
|
|
1065
1080
|
// Useful for temporary DOM ids.
|
1066
1081
|
var idCounter = 0;
|
1067
1082
|
_.uniqueId = function(prefix) {
|
1068
|
-
var id = idCounter
|
1083
|
+
var id = ++idCounter + '';
|
1069
1084
|
return prefix ? prefix + id : id;
|
1070
1085
|
};
|
1071
1086
|
|
@@ -1100,6 +1115,7 @@
|
|
1100
1115
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
1101
1116
|
// and correctly escapes quotes within interpolated code.
|
1102
1117
|
_.template = function(text, data, settings) {
|
1118
|
+
var render;
|
1103
1119
|
settings = _.defaults({}, settings, _.templateSettings);
|
1104
1120
|
|
1105
1121
|
// Combine delimiters into one regular expression via alternation.
|
@@ -1115,11 +1131,18 @@
|
|
1115
1131
|
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
1116
1132
|
source += text.slice(index, offset)
|
1117
1133
|
.replace(escaper, function(match) { return '\\' + escapes[match]; });
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1134
|
+
|
1135
|
+
if (escape) {
|
1136
|
+
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
1137
|
+
}
|
1138
|
+
if (interpolate) {
|
1139
|
+
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
1140
|
+
}
|
1141
|
+
if (evaluate) {
|
1142
|
+
source += "';\n" + evaluate + "\n__p+='";
|
1143
|
+
}
|
1122
1144
|
index = offset + match.length;
|
1145
|
+
return match;
|
1123
1146
|
});
|
1124
1147
|
source += "';\n";
|
1125
1148
|
|
@@ -1131,7 +1154,7 @@
|
|
1131
1154
|
source + "return __p;\n";
|
1132
1155
|
|
1133
1156
|
try {
|
1134
|
-
|
1157
|
+
render = new Function(settings.variable || 'obj', '_', source);
|
1135
1158
|
} catch (e) {
|
1136
1159
|
e.source = source;
|
1137
1160
|
throw e;
|