1 /** 2 * Takes id and returns an element with that id (if one exists in a document) 3 * @method getById 4 * @memberOf fabric.util 5 * @param {String|HTMLElement} id 6 * @return {HTMLElement|null} 7 */ 8 function getById(id) { 9 return typeof id === 'string' ? document.getElementById(id) : id; 10 } 11 12 /** 13 * Converts an array-like object (e.g. arguments or NodeList) to an array 14 * @method toArray 15 * @memberOf fabric.util 16 * @param {Object} arrayLike 17 * @return {Array} 18 */ 19 function toArray(arrayLike) { 20 var arr = [ ], i = arrayLike.length; 21 while (i--) { 22 arr[i] = arrayLike[i]; 23 } 24 return arr; 25 } 26 27 /** 28 * Creates specified element with specified attributes 29 * @method makeElement 30 * @memberOf fabric.util 31 * @param {String} tagName Type of an element to create 32 * @param {Object} [attributes] Attributes to set on an element 33 * @return {HTMLElement} Newly created element 34 */ 35 function makeElement(tagName, attributes) { 36 var el = document.createElement(tagName); 37 for (var prop in attributes) { 38 if (prop === 'class') { 39 el.className = attributes[prop]; 40 } 41 else if (prop === 'for') { 42 el.htmlFor = attributes[prop]; 43 } 44 else { 45 el.setAttribute(prop, attributes[prop]); 46 } 47 } 48 return el; 49 } 50 51 /** 52 * Adds class to an element 53 * @method addClass 54 * @memberOf fabric.util 55 * @param {HTMLElement} element Element to add class to 56 * @param {String} className Class to add to an element 57 */ 58 function addClass(element, className) { 59 if ((' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) { 60 element.className += (element.className ? ' ' : '') + className; 61 } 62 } 63 64 /** 65 * Wraps element with another element 66 * @method wrapElement 67 * @memberOf fabric.util 68 * @param {HTMLElement} element Element to wrap 69 * @param {HTMLElement|String} wrapper Element to wrap with 70 * @param {Object} [attributes] Attributes to set on a wrapper 71 * @return {HTMLElement} wrapper 72 */ 73 function wrapElement(element, wrapper, attributes) { 74 if (typeof wrapper === 'string') { 75 wrapper = makeElement(wrapper, attributes); 76 } 77 if (element.parentNode) { 78 element.parentNode.replaceChild(wrapper, element); 79 } 80 wrapper.appendChild(element); 81 return wrapper; 82 } 83 84 /** 85 * Returns offset for a given element 86 * @method getElementOffset 87 * @function 88 * @memberOf fabric.util 89 * @param {HTMLElement} element Element to get offset for 90 * @return {Object} Object with "left" and "top" properties 91 */ 92 function getElementOffset(element) { 93 // TODO (kangax): need to fix this method 94 var valueT = 0, valueL = 0; 95 do { 96 valueT += element.offsetTop || 0; 97 valueL += element.offsetLeft || 0; 98 element = element.offsetParent; 99 } 100 while (element); 101 return ({ left: valueL, top: valueT }); 102 } 103 104 (function () { 105 var style = document.documentElement.style; 106 107 var selectProp = 'userSelect' in style 108 ? 'userSelect' 109 : 'MozUserSelect' in style 110 ? 'MozUserSelect' 111 : 'WebkitUserSelect' in style 112 ? 'WebkitUserSelect' 113 : 'KhtmlUserSelect' in style 114 ? 'KhtmlUserSelect' 115 : ''; 116 117 /** 118 * Makes element unselectable 119 * @method makeElementUnselectable 120 * @memberOf fabric.util 121 * @param {HTMLElement} element Element to make unselectable 122 * @return {HTMLElement} Element that was passed in 123 */ 124 function makeElementUnselectable(element) { 125 if (typeof element.onselectstart !== 'undefined') { 126 element.onselectstart = fabric.util.falseFunction; 127 } 128 if (selectProp) { 129 element.style[selectProp] = 'none'; 130 } 131 else if (typeof element.unselectable == 'string') { 132 element.unselectable = 'on'; 133 } 134 return element; 135 } 136 137 fabric.util.makeElementUnselectable = makeElementUnselectable; 138 })(); 139 140 (function() { 141 142 /** 143 * Inserts a script element with a given url into a document; invokes callback, when that script is finished loading 144 * @method getScript 145 * @memberOf fabric.util 146 * @param {String} url URL of a script to load 147 * @param {Function} callback Callback to execute when script is finished loading 148 */ 149 function getScript(url, callback) { 150 var headEl = document.getElementsByTagName("head")[0], 151 scriptEl = document.createElement('script'), 152 loading = true; 153 154 scriptEl.type = 'text/javascript'; 155 scriptEl.setAttribute('runat', 'server'); 156 157 /** @ignore */ 158 scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) { 159 if (loading) { 160 if (typeof this.readyState == 'string' && 161 this.readyState !== 'loaded' && 162 this.readyState !== 'complete') return; 163 loading = false; 164 callback(e || window.event); 165 scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null; 166 } 167 }; 168 scriptEl.src = url; 169 headEl.appendChild(scriptEl); 170 // causes issue in Opera 171 // headEl.removeChild(scriptEl); 172 } 173 174 function getScriptJaxer(url, callback) { 175 Jaxer.load(url); 176 callback(); 177 } 178 179 fabric.util.getScript = getScript; 180 181 var Jaxer = global.Jaxer; 182 if (Jaxer && Jaxer.load) { 183 fabric.util.getScript = getScriptJaxer; 184 } 185 })(); 186 187 /** 188 * Changes value from one to another within certain period of time, invoking callbacks as value is being changed. 189 * @method animate 190 * @memberOf fabric.util 191 * @param {Object} [options] Animation options 192 * @param {Function} [options.onChange] Callback; invoked on every value change 193 * @param {Function} [options.onComplete] Callback; invoked when value change is completed 194 * @param {Number} [options.startValue=0] Starting value 195 * @param {Number} [options.endValue=100] Ending value 196 * @param {Function} [options.easing] Easing function 197 * @param {Number} [options.duration=500] Duration of change 198 */ 199 function animate(options) { 200 options || (options = { }); 201 var start = +new Date(), 202 duration = options.duration || 500, 203 finish = start + duration, time, pos, 204 onChange = options.onChange || function() { }, 205 easing = options.easing || function(pos) { return (-Math.cos(pos * Math.PI) / 2) + 0.5; }, 206 startValue = 'startValue' in options ? options.startValue : 0, 207 endValue = 'endValue' in options ? options.endValue : 100, 208 isReversed = startValue > endValue 209 210 options.onStart && options.onStart(); 211 212 var interval = setInterval(function() { 213 time = +new Date(); 214 pos = time > finish ? 1 : (time - start) / duration; 215 onChange(isReversed 216 ? (startValue - (startValue - endValue) * easing(pos)) 217 : (startValue + (endValue - startValue) * easing(pos))); 218 if (time > finish) { 219 clearInterval(interval); 220 options.onComplete && options.onComplete(); 221 } 222 }, 10); 223 } 224 225 fabric.util.getById = getById; 226 fabric.util.toArray = toArray; 227 fabric.util.makeElement = makeElement; 228 fabric.util.addClass = addClass; 229 fabric.util.wrapElement = wrapElement; 230 fabric.util.getElementOffset = getElementOffset; 231 fabric.util.animate = animate;