reservation plugin - unbound request (unclean
[unfold.git] / portal / static / unbound_reservation_static / src / util.js
1 /*
2  * jsPlumb
3  * 
4  * Title:jsPlumb 1.5.5
5  * 
6  * Provides a way to visually connect elements on an HTML page, using either SVG or VML.  
7  * 
8  * This file contains the util functions
9  *
10  * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
11  * 
12  * http://jsplumb.org
13  * http://github.com/sporritt/jsplumb
14  * http://code.google.com/p/jsplumb
15  * 
16  * Dual licensed under the MIT and GPL2 licenses.
17  */
18
19 ;(function() {
20
21     var _isa = function(a) { return Object.prototype.toString.call(a) === "[object Array]"; },
22         _isnum = function(n) { return Object.prototype.toString.call(n) === "[object Number]"; },
23         _iss = function(s) { return typeof s === "string"; },
24         _isb = function(s) { return typeof s === "boolean"; },
25         _isnull = function(s) { return s == null; },  
26         _iso = function(o) { return o == null ? false : Object.prototype.toString.call(o) === "[object Object]"; },
27         _isd = function(o) { return Object.prototype.toString.call(o) === "[object Date]"; },
28         _isf = function(o) { return Object.prototype.toString.call(o) === "[object Function]"; },
29         _ise = function(o) {
30             for (var i in o) { if (o.hasOwnProperty(i)) return false; }
31             return true;
32         },
33         pointHelper = function(p1, p2, fn) {
34             p1 = _isa(p1) ? p1 : [p1.x, p1.y];
35             p2 = _isa(p2) ? p2 : [p2.x, p2.y];    
36             return fn(p1, p2);
37         };
38     
39     jsPlumbUtil = {        
40         isArray : _isa,        
41         isString : _iss,        
42         isBoolean: _isb,        
43         isNull : _isnull,        
44         isObject : _iso,
45         isDate : _isd,
46         isFunction: _isf,
47         isEmpty:_ise,
48         isNumber:_isnum,
49         clone : function(a) {
50             if (_iss(a)) return "" + a;
51             else if (_isb(a)) return !!a;
52             else if (_isd(a)) return new Date(a.getTime());
53             else if (_isf(a)) return a;
54             else if (_isa(a)) {
55                 var b = [];
56                 for (var i = 0; i < a.length; i++)
57                     b.push(this.clone(a[i]));
58                 return b;
59             }
60             else if (_iso(a)) {
61                 var c = {};
62                 for (var j in a)
63                     c[j] = this.clone(a[j]);
64                 return c;               
65             }
66             else return a;
67         },
68         merge : function(a, b) {                
69             var c = this.clone(a);              
70             for (var i in b) {
71                 if (c[i] == null || _iss(b[i]) || _isb(b[i]))
72                     c[i] = b[i];
73                 else {
74                     if (_isa(b[i])/* && this.isArray(c[i])*/) {
75                         var ar = [];
76                         // if c's object is also an array we can keep its values.
77                         if (_isa(c[i])) ar.push.apply(ar, c[i]);
78                         ar.push.apply(ar, b[i]);
79                         c[i] = ar;
80                     }
81                     else if(_iso(b[i])) {       
82                         // overwite c's value with an object if it is not already one.
83                         if (!_iso(c[i])) 
84                             c[i] = {};
85                         for (var j in b[i])
86                             c[i][j] = b[i][j];
87                     }
88                 }
89             }
90             return c;
91         },
92         copyValues:function(names, from, to) {
93             for (var i = 0; i < names.length; i++)
94                 to[names[i]] = from[names[i]];
95         },
96         //
97         // chain a list of functions, supplied by [ object, method name, args ], and return on the first
98         // one that returns the failValue. if none return the failValue, return the successValue.
99         //
100         functionChain : function(successValue, failValue, fns) {        
101             for (var i = 0; i < fns.length; i++) {
102                 var o = fns[i][0][fns[i][1]].apply(fns[i][0], fns[i][2]);
103                 if (o === failValue) {
104                     return o;
105                 }
106             }                
107             return successValue;
108         },
109         // take the given model and expand out any parameters.
110         populate : function(model, values) {            
111             // for a string, see if it has parameter matches, and if so, try to make the substitutions.
112             var getValue = function(fromString) {
113                     var matches = fromString.match(/(\${.*?})/g);
114                     if (matches != null) {
115                         for (var i = 0; i < matches.length; i++) {
116                             var val = values[matches[i].substring(2, matches[i].length - 1)];
117                             if (val != null) {
118                                 fromString = fromString.replace(matches[i], val);
119                             }
120                         }                                                       
121                     }
122                     return fromString;
123                 },              
124                 // process one entry.
125                 _one = function(d) {
126                     if (d != null) {
127                         if (_iss(d)) {
128                             return getValue(d);
129                         }
130                         else if (_isa(d)) {
131                             var r = []; 
132                             for (var i = 0; i < d.length; i++)
133                                 r.push(_one(d[i]));
134                             return r;
135                         }
136                         else if (_iso(d)) {
137                             var s = {};
138                             for (var j in d) {
139                                 s[j] = _one(d[j]);
140                             }
141                             return s;
142                         }
143                         else {
144                             return d;
145                         }
146                     }
147                 };
148             
149             return _one(model); 
150         },
151         convertStyle : function(s, ignoreAlpha) {
152             // TODO: jsPlumb should support a separate 'opacity' style member.
153             if ("transparent" === s) return s;
154             var o = s,
155                 pad = function(n) { return n.length == 1 ? "0" + n : n; },
156                 hex = function(k) { return pad(Number(k).toString(16)); },
157                 pattern = /(rgb[a]?\()(.*)(\))/;
158             if (s.match(pattern)) {
159                 var parts = s.match(pattern)[2].split(",");
160                 o = "#" + hex(parts[0]) + hex(parts[1]) + hex(parts[2]);
161                 if (!ignoreAlpha && parts.length == 4) 
162                     o = o + hex(parts[3]);
163             }
164             return o;
165         },
166         findWithFunction : function(a, f) {
167             if (a)
168                 for (var i = 0; i < a.length; i++) if (f(a[i])) return i;
169             return -1;
170         },
171         clampToGrid : function(x, y, grid, dontClampX, dontClampY) {
172             var _gridClamp = function(n, g) { 
173                 var e = n % g, 
174                     f = Math.floor(n / g), 
175                     inc = e >= (g / 2) ? 1 : 0; 
176                 return (f + inc) * g; 
177             };
178             return [
179                 dontClampX || grid == null ? x : _gridClamp(x, grid[0]),
180                 dontClampY || grid == null ? y : _gridClamp(y, grid[1])
181             ];          
182         },
183         indexOf : function(l, v) {
184             return jsPlumbUtil.findWithFunction(l, function(_v) { return _v == v; });   
185         },
186         removeWithFunction : function(a, f) {
187             var idx = jsPlumbUtil.findWithFunction(a, f);
188             if (idx > -1) a.splice(idx, 1);
189             return idx != -1;
190         },
191         remove : function(l, v) {
192             var idx = jsPlumbUtil.indexOf(l, v);        
193             if (idx > -1) l.splice(idx, 1);
194             return idx != -1;
195         },
196         // TODO support insert index
197         addWithFunction : function(list, item, hashFunction) {
198             if (jsPlumbUtil.findWithFunction(list, hashFunction) == -1) list.push(item);
199         },
200         addToList : function(map, key, value, insertAtStart) {
201             var l = map[key];
202             if (l == null) {
203                 l = [];
204                 map[key] = l;
205             }
206             l[insertAtStart ? "unshift" : "push"](value);
207             return l;
208         },
209         //
210         // extends the given obj (which can be an array) with the given constructor function, prototype functions, and
211         // class members, any of which may be null.
212         //
213         extend : function(child, parent, _protoFn, _protoAtts) {
214             _protoFn = _protoFn || {};
215             _protoAtts = _protoAtts || {};
216             parent = _isa(parent) ? parent : [ parent ];            
217
218             for (var i = 0; i < parent.length; i++) {
219                 for (var j in parent[i].prototype) {
220                     if(parent[i].prototype.hasOwnProperty(j)) {
221                         child.prototype[j] = parent[i].prototype[j];
222                     }
223                 }
224             }
225
226             var _makeFn = function(name) {
227                 return function() {
228                     for (var i = 0; i < parent.length; i++) {
229                         if (parent[i].prototype[name])
230                             parent[i].prototype[name].apply(this, arguments);
231                     }                    
232                     return _protoFn[name].apply(this, arguments);
233                 };
234             };
235
236             for (var k in _protoFn) {
237                 child.prototype[k] = _makeFn(k);
238             }
239
240             return child;
241         },
242         uuid : function() {
243             return ('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
244                 var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
245                 return v.toString(16);
246             }));
247         },
248         logEnabled : true,
249         log : function() {
250             if (jsPlumbUtil.logEnabled && typeof console != "undefined") {
251                 try {
252                     var msg = arguments[arguments.length - 1];
253                     console.log(msg);
254                 }
255                 catch (e) {} 
256             }
257         },
258         group : function(g) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.group(g); },
259         groupEnd : function(g) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.groupEnd(g); },
260         time : function(t) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.time(t); },
261         timeEnd : function(t) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.timeEnd(t); },
262         
263         /**
264                  * helper to remove an element from the DOM.
265                  */
266                 removeElement : function(element) {
267                         if (element != null && element.parentNode != null) {
268                                 element.parentNode.removeChild(element);
269                         }
270                 },
271         /**
272                  * helper to remove a list of elements from the DOM.
273                  */
274                 removeElements : function(elements) {
275                         for ( var i = 0; i < elements.length; i++)
276                                 jsPlumbUtil.removeElement(elements[i]);
277                 },
278         /*
279          * Function: sizeElement 
280          * Helper to size and position an element. You would typically use
281          * this when writing your own Connector or Endpoint implementation.
282          * 
283          * Parameters: 
284          *  x - [int] x position for the element origin 
285          *  y - [int] y position for the element origin 
286          *  w - [int] width of the element 
287          *  h - [int] height of the element
288          *  
289          */
290         sizeElement : function(el, x, y, w, h) {
291             if (el) {
292                 el.style.height = h + "px";
293                 el.height = h;
294                 el.style.width = w + "px";
295                 el.width = w;
296                 el.style.left = x + "px";
297                 el.style.top = y + "px";
298             }
299         },
300         /**
301         * @name jsPlumbUtil.wrap
302         * @desc Wraps one function with another, creating a placeholder for the
303         * wrapped function if it was null. this is used to wrap the various
304         * drag/drop event functions - to allow jsPlumb to be notified of
305         * important lifecycle events without imposing itself on the user's
306         * drag/drop functionality. 
307         * @param {Function} wrappedFunction original function to wrap; may be null.
308         * @param {Function} newFunction function to wrap the original with.
309         * @param {Object} [returnOnThisValue] Optional. Indicates that the wrappedFunction should 
310         * not be executed if the newFunction returns a value matching 'returnOnThisValue'.
311         * note that this is a simple comparison and only works for primitives right now.
312         */        
313         wrap : function(wrappedFunction, newFunction, returnOnThisValue) {
314             wrappedFunction = wrappedFunction || function() { };
315             newFunction = newFunction || function() { };
316             return function() {
317                 var r = null;
318                 try {
319                     r = newFunction.apply(this, arguments);
320                 } catch (e) {
321                     jsPlumbUtil.log("jsPlumb function failed : " + e);
322                 }
323                 if (returnOnThisValue == null || (r !== returnOnThisValue)) {
324                     try {
325                         r = wrappedFunction.apply(this, arguments);
326                     } catch (e) {
327                         jsPlumbUtil.log("wrapped function failed : " + e);
328                     }
329                 }
330                 return r;
331             };
332         }
333     };
334
335     
336     jsPlumbUtil.EventGenerator = function() {
337         var _listeners = {}, eventsSuspended = false;
338         
339         // this is a list of events that should re-throw any errors that occur during their dispatch. as of 1.3.0 this is private to
340         // jsPlumb, but it seems feasible that people might want to manipulate this list.  the thinking is that we don't want event
341         // listeners to bring down jsPlumb - or do we.  i can't make up my mind about this, but i know i want to hear about it if the "ready"
342         // event fails, because then my page has most likely not initialised.  so i have this halfway-house solution.  it will be interesting
343         // to hear what other people think.
344         var eventsToDieOn = [ "ready" ];
345                                         
346         this.bind = function(event, listener, insertAtStart) {
347             jsPlumbUtil.addToList(_listeners, event, listener, insertAtStart);     
348             return this;        
349         };
350                  
351         this.fire = function(event, value, originalEvent) {
352             if (!eventsSuspended && _listeners[event]) {
353                 // instead of looping through the array we get a counter and a length, because it is possible
354                 // that an event fired from here could cause the object to get cleaned up, which would throw
355                 // away the listeners. so after each cycle through the loop we check to ensure we haven't
356                 // been nuked.
357                 var l = _listeners[event].length, i = 0, _gone = false, ret = null;
358                 if (!this.shouldFireEvent || this.shouldFireEvent(event, value, originalEvent)) {
359                     while (!_gone && i < l && ret !== false) {                    
360                     
361                         // doing it this way rather than catching and then possibly re-throwing means that an error propagated by this
362                         // method will have the whole call stack available in the debugger.
363                         if (jsPlumbUtil.findWithFunction(eventsToDieOn, function(e) { return e === event; }) != -1) 
364                             _listeners[event][i](value, originalEvent);
365                         else {
366                             // for events we don't want to die on, catch and log.
367                             try {                            
368                                 ret = _listeners[event][i](value, originalEvent);
369                             } catch (e) {
370                                 jsPlumbUtil.log("jsPlumb: fire failed for event " + event + " : " + e);
371                             }
372                         }
373                         i++;
374                         if (_listeners == null || _listeners[event] == null) _gone = true;                    
375                     }
376                 }
377             }
378             return this;
379         };
380         
381         this.unbind = function(event) {
382             if (event)
383                 delete _listeners[event];
384             else {
385                 _listeners = {};
386             }
387             return this;
388         };
389         
390         this.getListener = function(forEvent) {
391             return _listeners[forEvent];
392         };              
393         this.setSuspendEvents = function(val) {
394             eventsSuspended = val;    
395         };        
396         this.isSuspendEvents = function() {
397             return eventsSuspended;
398         };        
399         this.cleanupListeners = function() {
400             for (var i in _listeners) {
401                 _listeners[i].splice(0);
402                 delete _listeners[i];
403             }
404         };
405     };
406
407
408     jsPlumbUtil.EventGenerator.prototype = {
409         cleanup:function() {
410             this.cleanupListeners();
411         }
412     };
413
414
415     // thanks MDC
416     // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fbind
417     if (!Function.prototype.bind) {
418       Function.prototype.bind = function (oThis) {
419         if (typeof this !== "function") {
420           // closest thing possible to the ECMAScript 5 internal IsCallable function
421           throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
422         }
423
424         var aArgs = Array.prototype.slice.call(arguments, 1), 
425             fToBind = this, 
426             fNOP = function () {},
427             fBound = function () {
428               return fToBind.apply(this instanceof fNOP && oThis ? this : oThis,
429                                    aArgs.concat(Array.prototype.slice.call(arguments)));
430             };
431
432         fNOP.prototype = this.prototype;
433         fBound.prototype = new fNOP();
434
435         return fBound;
436       };
437     }
438
439 })();