Unreviewed, rolling out r216677.
[WebKit-https.git] / PerformanceTests / Speedometer / resources / todomvc / dependency-examples / flight / flight / node_modules / flight / lib / utils.js
1 /* Copyright 2013 Twitter, Inc. Licensed under The MIT License. http://opensource.org/licenses/MIT */
2
3 define(
4
5   ['./debug'],
6
7   function(debug) {
8     'use strict';
9
10     var DEFAULT_INTERVAL = 100;
11
12     function canWriteProtect() {
13       var writeProtectSupported = debug.enabled && !Object.propertyIsEnumerable('getOwnPropertyDescriptor');
14       if (writeProtectSupported) {
15         //IE8 getOwnPropertyDescriptor is built-in but throws exeption on non DOM objects
16         try {
17           Object.getOwnPropertyDescriptor(Object, 'keys');
18         } catch (e) {
19          return false;
20         }
21       }
22
23       return writeProtectSupported;
24     }
25
26     var utils = {
27
28       isDomObj: function(obj) {
29         return !!(obj.nodeType || (obj === window));
30       },
31
32       toArray: function(obj, from) {
33         from = from || 0;
34         var len = obj.length, arr = new Array(len - from);
35         for (var i = from; i < len; i++) {
36           arr[i - from] = obj[i];
37         }
38         return arr;
39       },
40
41       // returns new object representing multiple objects merged together
42       // optional final argument is boolean which specifies if merge is recursive
43       // original objects are unmodified
44       //
45       // usage:
46       //   var base = {a:2, b:6};
47       //   var extra = {b:3, c:4};
48       //   merge(base, extra); //{a:2, b:3, c:4}
49       //   base; //{a:2, b:6}
50       //
51       //   var base = {a:2, b:6};
52       //   var extra = {b:3, c:4};
53       //   var extraExtra = {a:4, d:9};
54       //   merge(base, extra, extraExtra); //{a:4, b:3, c:4. d: 9}
55       //   base; //{a:2, b:6}
56       //
57       //   var base = {a:2, b:{bb:4, cc:5}};
58       //   var extra = {a:4, b:{cc:7, dd:1}};
59       //   merge(base, extra, true); //{a:4, b:{bb:4, cc:7, dd:1}}
60       //   base; //{a:2, b:6}
61
62       merge: function(/*obj1, obj2,....deepCopy*/) {
63         // unpacking arguments by hand benchmarked faster
64         var l = arguments.length,
65             args = new Array(l + 1);
66
67         if (l === 0) {
68           return {};
69         }
70
71         for (var i = 0; i < l; i++) {
72           args[i + 1] = arguments[i];
73         }
74
75         //start with empty object so a copy is created
76         args[0] = {};
77
78         if (args[args.length - 1] === true) {
79           //jquery extend requires deep copy as first arg
80           args.pop();
81           args.unshift(true);
82         }
83
84         return $.extend.apply(undefined, args);
85       },
86
87       // updates base in place by copying properties of extra to it
88       // optionally clobber protected
89       // usage:
90       //   var base = {a:2, b:6};
91       //   var extra = {c:4};
92       //   push(base, extra); //{a:2, b:6, c:4}
93       //   base; //{a:2, b:6, c:4}
94       //
95       //   var base = {a:2, b:6};
96       //   var extra = {b: 4 c:4};
97       //   push(base, extra, true); //Error ("utils.push attempted to overwrite 'b' while running in protected mode")
98       //   base; //{a:2, b:6}
99       //
100       // objects with the same key will merge recursively when protect is false
101       // eg:
102       // var base = {a:16, b:{bb:4, cc:10}};
103       // var extra = {b:{cc:25, dd:19}, c:5};
104       // push(base, extra); //{a:16, {bb:4, cc:25, dd:19}, c:5}
105       //
106       push: function(base, extra, protect) {
107         if (base) {
108           Object.keys(extra || {}).forEach(function(key) {
109             if (base[key] && protect) {
110               throw new Error('utils.push attempted to overwrite "' + key + '" while running in protected mode');
111             }
112
113             if (typeof base[key] == 'object' && typeof extra[key] == 'object') {
114               // recurse
115               this.push(base[key], extra[key]);
116             } else {
117               // no protect, so extra wins
118               base[key] = extra[key];
119             }
120           }, this);
121         }
122
123         return base;
124       },
125
126       // If obj.key points to an enumerable property, return its value
127       // If obj.key points to a non-enumerable property, return undefined
128       getEnumerableProperty: function(obj, key) {
129         return obj.propertyIsEnumerable(key) ? obj[key] : undefined;
130       },
131
132       // build a function from other function(s)
133       // utils.compose(a,b,c) -> a(b(c()));
134       // implementation lifted from underscore.js (c) 2009-2012 Jeremy Ashkenas
135       compose: function() {
136         var funcs = arguments;
137
138         return function() {
139           var args = arguments;
140
141           for (var i = funcs.length - 1; i >= 0; i--) {
142             args = [funcs[i].apply(this, args)];
143           }
144
145           return args[0];
146         };
147       },
148
149       // Can only unique arrays of homogeneous primitives, e.g. an array of only strings, an array of only booleans, or an array of only numerics
150       uniqueArray: function(array) {
151         var u = {}, a = [];
152
153         for (var i = 0, l = array.length; i < l; ++i) {
154           if (u.hasOwnProperty(array[i])) {
155             continue;
156           }
157
158           a.push(array[i]);
159           u[array[i]] = 1;
160         }
161
162         return a;
163       },
164
165       debounce: function(func, wait, immediate) {
166         if (typeof wait != 'number') {
167           wait = DEFAULT_INTERVAL;
168         }
169
170         var timeout, result;
171
172         return function() {
173           var context = this, args = arguments;
174           var later = function() {
175             timeout = null;
176             if (!immediate) {
177               result = func.apply(context, args);
178             }
179           };
180           var callNow = immediate && !timeout;
181
182           timeout && clearTimeout(timeout);
183           timeout = setTimeout(later, wait);
184
185           if (callNow) {
186             result = func.apply(context, args);
187           }
188
189           return result;
190         };
191       },
192
193       throttle: function(func, wait) {
194         if (typeof wait != 'number') {
195           wait = DEFAULT_INTERVAL;
196         }
197
198         var context, args, timeout, throttling, more, result;
199         var whenDone = this.debounce(function() {
200           more = throttling = false;
201         }, wait);
202
203         return function() {
204           context = this; args = arguments;
205           var later = function() {
206             timeout = null;
207             if (more) {
208               result = func.apply(context, args);
209             }
210             whenDone();
211           };
212
213           if (!timeout) {
214             timeout = setTimeout(later, wait);
215           }
216
217           if (throttling) {
218             more = true;
219           } else {
220             throttling = true;
221             result = func.apply(context, args);
222           }
223
224           whenDone();
225           return result;
226         };
227       },
228
229       countThen: function(num, base) {
230         return function() {
231           if (!--num) { return base.apply(this, arguments); }
232         };
233       },
234
235       delegate: function(rules) {
236         return function(e, data) {
237           var target = $(e.target), parent;
238
239           Object.keys(rules).forEach(function(selector) {
240             if (!e.isPropagationStopped() && (parent = target.closest(selector)).length) {
241               data = data || {};
242               data.el = parent[0];
243               return rules[selector].apply(this, [e, data]);
244             }
245           }, this);
246         };
247       },
248
249       // ensures that a function will only be called once.
250       // usage:
251       // will only create the application once
252       //   var initialize = utils.once(createApplication)
253       //     initialize();
254       //     initialize();
255       //
256       // will only delete a record once
257       //   var myHanlder = function () {
258       //     $.ajax({type: 'DELETE', url: 'someurl.com', data: {id: 1}});
259       //   };
260       //   this.on('click', utils.once(myHandler));
261       //
262       once: function(func) {
263         var ran, result;
264
265         return function() {
266           if (ran) {
267             return result;
268           }
269
270           ran = true;
271           result = func.apply(this, arguments);
272
273           return result;
274         };
275       },
276
277       propertyWritability: function(obj, prop, writable) {
278         if (canWriteProtect() && obj.hasOwnProperty(prop)) {
279           Object.defineProperty(obj, prop, { writable: writable });
280         }
281       },
282
283       // Property locking/unlocking
284       mutateProperty: function(obj, prop, op) {
285         var writable;
286
287         if (!canWriteProtect() || !obj.hasOwnProperty(prop)) {
288           op.call(obj);
289           return;
290         }
291
292         writable = Object.getOwnPropertyDescriptor(obj, prop).writable;
293
294         Object.defineProperty(obj, prop, { writable: true });
295         op.call(obj);
296         Object.defineProperty(obj, prop, { writable: writable });
297
298       }
299
300     };
301
302     return utils;
303   }
304 );