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