ultimate-base 0.5.0.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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;