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.
Files changed (24) hide show
  1. data/app/assets/javascripts/ultimate/backbone/app.js.coffee +47 -5
  2. data/app/assets/javascripts/ultimate/backbone/base.js.coffee +9 -10
  3. data/app/assets/javascripts/ultimate/backbone/collection.js.coffee +0 -1
  4. data/app/assets/javascripts/ultimate/backbone/lib/backbone.js +765 -673
  5. data/app/assets/javascripts/ultimate/backbone/model.js.coffee +0 -1
  6. data/app/assets/javascripts/ultimate/backbone/router.js.coffee +0 -4
  7. data/app/assets/javascripts/ultimate/backbone/view-mixins/nodes.js.coffee +51 -0
  8. data/app/assets/javascripts/ultimate/backbone/view.js.coffee +18 -104
  9. data/app/assets/javascripts/ultimate/base.js.coffee +1 -11
  10. data/app/assets/javascripts/ultimate/jquery-plugin-adapter.js.coffee +0 -1
  11. data/app/assets/javascripts/ultimate/jquery-plugin-class.js.coffee +3 -25
  12. data/app/assets/javascripts/ultimate/jquery.base.js.coffee +0 -2
  13. data/app/assets/javascripts/ultimate/underscore/underscore.inflection.js +4 -3
  14. data/app/assets/javascripts/ultimate/underscore/underscore.js +103 -80
  15. data/app/assets/javascripts/ultimate/underscore/underscore.string.js +71 -27
  16. data/lib/ultimate/base.rb +0 -1
  17. data/lib/ultimate/base/version.rb +1 -1
  18. metadata +3 -8
  19. data/app/assets/javascripts/ultimate/backbone/extra/jquery-ext.js.coffee +0 -96
  20. data/app/assets/javascripts/ultimate/improves/datepicker.js.coffee +0 -34
  21. data/app/assets/javascripts/ultimate/improves/devise.js.coffee +0 -18
  22. data/app/assets/javascripts/ultimate/improves/form-errors.js.coffee +0 -146
  23. data/app/assets/javascripts/ultimate/improves/tablesorter.js +0 -59
  24. 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()
@@ -2,10 +2,6 @@
2
2
 
3
3
  class Ultimate.Backbone.Router extends Backbone.Router
4
4
 
5
- constructor: ->
6
- Ultimate.Backbone.debug ".Router.constructor()", @
7
- super
8
-
9
5
  namedParam = /:\w+/g
10
6
  splatParam = /\*\w+/g
11
7
  escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g
@@ -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
- loadingState: null
8
- loadingWidthMethodName: "innerWidth"
9
- loadingHeightMethodName: "innerHeight"
10
- loadingStateClass: "loading"
11
- loadingOverlayClass: "loading-overlay"
12
-
13
- viewOptions: -> []
14
-
15
- constructor: ->
16
- Ultimate.Backbone.debug ".View.constructor()", @
17
- super
18
-
19
-
20
-
21
- # Overload parent method Backbone.View.setElement() as hook for findNodes() and trick for data("views").
22
- setElement: (element, delegate, sideCall = false) ->
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: (viewOptions = _.result(@, "viewOptions"), options = @options) ->
85
- @[attr] = options[attr] for attr in viewOptions when typeof options[attr] isnt "undefined"
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
@@ -1,11 +1 @@
1
- #= require ./helpers
2
-
3
- @Ultimate ||=
4
-
5
- debugMode: false
6
-
7
- debug: ->
8
- if @debugMode
9
- a = ["info", "Ultimate"]
10
- Array::push.apply a, arguments if arguments.length > 0
11
- cout.apply @, a
1
+ window.Ultimate ||= {}
@@ -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(@, "events")) ->
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(@, "events") unless events
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(@, "reflectableOptions"), options = @options) ->
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)
@@ -1,5 +1,3 @@
1
- # TODO: register components
2
-
3
1
  do ($ = jQuery) =>
4
2
 
5
3
  $.regexp ||= {}
@@ -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
- // Underscore.js 1.4.2
2
- // http://underscorejs.org
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
- (function() {
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
- // Baseline setup
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.2';
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('Reduce of empty array with no initial value');
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 arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
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('Reduce of empty array with no initial value');
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
- var results = [];
181
- if (obj == null) return results;
182
- each(obj, function(value, index, list) {
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
- var found = false;
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
- found = any(obj, function(value) {
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 (_.isFunction(method) ? method : value[method]).apply(value, args);
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
- // with specific `key:value` pairs.
243
- _.where = function(obj, attrs) {
244
- if (_.isEmpty(attrs)) return [];
245
- return _.filter(obj, function(value) {
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, value) {
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 (obj.length === +obj.length) return slice.call(obj);
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, function(value){ return !!value; });
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). Binding with arguments is also known as `curry`.
573
- // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
574
- // We check for `func.bind` first, to fail fast when `func` is undefined.
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
- if (!_.isFunction(func)) throw new TypeError;
579
- args = slice.call(arguments, 2);
580
- return bound = function() {
581
- if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
582
- ctor.prototype = func.prototype;
583
- var self = new ctor;
584
- var result = func.apply(self, args.concat(slice.call(arguments)));
585
- if (Object(result) === result) return result;
586
- return self;
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 == 0) funcs = _.functions(obj);
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, throttling, more, result;
626
- var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
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
- context = this; args = arguments;
629
- var later = function() {
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
- if (more) {
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
- for (var prop in source) {
762
- obj[prop] = source[prop];
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
- for (var prop in source) {
792
- if (obj[prop] == null) obj[prop] = source[prop];
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 _.isNumber(obj) && isFinite(obj);
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
- for (var i = 0; i < n; i++) iterator.call(context, i);
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 + (0 | Math.random() * (max - min + 1));
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
- source +=
1119
- escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" :
1120
- interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" :
1121
- evaluate ? "';\n" + evaluate + "\n__p+='" : '';
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
- var render = new Function(settings.variable || 'obj', '_', source);
1157
+ render = new Function(settings.variable || 'obj', '_', source);
1135
1158
  } catch (e) {
1136
1159
  e.source = source;
1137
1160
  throw e;