1 function getById(id) {
  2   return typeof id === 'string' ? document.getElementById(id) : id;
  3 }
  4 
  5 function toArray(arrayLike) {
  6   var arr = [ ], i = arrayLike.length;
  7   while (i--) {
  8     arr[i] = arrayLike[i];
  9   }
 10   return arr;
 11 }
 12 
 13 function makeElement(tagName, attributes) {
 14   var el = document.createElement(tagName);
 15   for (var prop in attributes) {
 16     if (prop === 'class') {
 17       el.className = attributes[prop];
 18     }
 19     else if (prop === 'for') {
 20       el.htmlFor = attributes[prop];
 21     }
 22     else {
 23       el.setAttribute(prop, attributes[prop]);
 24     }
 25   }
 26   return el;
 27 }
 28 
 29 function addClass(element, className) {
 30   if ((' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
 31     element.className += (element.className ? ' ' : '') + className;
 32   }  
 33 }
 34 
 35 function wrapElement(element, wrapper, attributes) {
 36   if (typeof wrapper === 'string') {
 37     wrapper = makeElement(wrapper, attributes);
 38   }
 39   if (element.parentNode) {
 40     element.parentNode.replaceChild(wrapper, element);
 41   }
 42   wrapper.appendChild(element);
 43   return wrapper;
 44 }
 45 
 46 // TODO (kangax): need to fix this method
 47 function getElementOffset(element) {
 48   var valueT = 0, valueL = 0;
 49   do {
 50     valueT += element.offsetTop  || 0;
 51     valueL += element.offsetLeft || 0;
 52     element = element.offsetParent;
 53   } 
 54   while (element);
 55   return ({ left: valueL, top: valueT });
 56 }
 57 
 58 (function () {
 59   var style = document.documentElement.style;
 60 
 61   var selectProp = 'userSelect' in style
 62     ? 'userSelect'
 63     : 'MozUserSelect' in style 
 64       ? 'MozUserSelect' 
 65       : 'WebkitUserSelect' in style 
 66         ? 'WebkitUserSelect' 
 67         : 'KhtmlUserSelect' in style 
 68           ? 'KhtmlUserSelect' 
 69           : '';
 70 
 71   function makeElementUnselectable(element) {
 72     if (typeof element.onselectstart !== 'undefined') {
 73       element.onselectstart = fabric.util.falseFunction;
 74     }
 75     if (selectProp) {
 76       element.style[selectProp] = 'none';
 77     }
 78     else if (typeof element.unselectable == 'string') {
 79       element.unselectable = 'on';
 80     }
 81     return element;
 82   }
 83   
 84   fabric.util.makeElementUnselectable = makeElementUnselectable;
 85 })();
 86 
 87 (function(){
 88   
 89   function getScript(url, callback) {
 90   	var headEl = document.getElementsByTagName("head")[0],
 91   	    scriptEl = document.createElement('script'), 
 92   	    loading = true;
 93 
 94   	scriptEl.type = 'text/javascript';
 95   	scriptEl.setAttribute('runat', 'server');
 96   	
 97   	/** @ignore */
 98   	scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
 99   	  if (loading) {
100   	    if (typeof this.readyState == 'string' && 
101   	        this.readyState !== 'loaded' && 
102   	        this.readyState !== 'complete') return;
103     	  loading = false;
104     		callback(e || window.event);
105     		scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
106     	}
107   	};
108   	scriptEl.src = url;
109   	headEl.appendChild(scriptEl);
110   	// causes issue in Opera
111   	// headEl.removeChild(scriptEl);
112   }
113   
114   function getScriptJaxer(url, callback) {
115     Jaxer.load(url);
116     callback();
117   }
118   
119   fabric.util.getScript = getScript;
120   
121   var Jaxer = this.Jaxer;
122   if (Jaxer && Jaxer.load) {
123     fabric.util.getScript = getScriptJaxer;
124   }
125 })();
126 
127 function animate(options) {
128   options || (options = { });
129   var start = +new Date(), 
130       duration = options.duration || 500,
131       finish = start + duration, time, pos,
132       onChange = options.onChange || function() { },
133       easing = options.easing || function(pos) { return (-Math.cos(pos * Math.PI) / 2) + 0.5; },
134       startValue = 'startValue' in options ? options.startValue : 0,
135       endValue = 'endValue' in options ? options.endValue : 100,
136       isReversed = startValue > endValue
137   
138   options.onStart && options.onStart();
139 
140   var interval = setInterval(function() {
141     time = +new Date();
142     pos = time > finish ? 1 : (time - start) / duration;
143     onChange(isReversed 
144       ? (startValue - (startValue - endValue) * easing(pos)) 
145       : (startValue + (endValue - startValue) * easing(pos)));
146     if (time > finish) {
147       clearInterval(interval);
148       options.onComplete && options.onComplete();
149     }
150   }, 10);
151 }
152 
153 fabric.util.getById = getById;
154 fabric.util.toArray = toArray;
155 fabric.util.makeElement = makeElement;
156 fabric.util.addClass = addClass;
157 fabric.util.wrapElement = wrapElement;
158 fabric.util.getElementOffset = getElementOffset;
159 fabric.util.animate = animate;