1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 8 /** 9 * @language=en 10 * @namespace Hilo The underlying core set of methods. 11 * @static 12 * @module hilo/core/Hilo 13 */ 14 var Hilo = (function(){ 15 16 var win = window, doc = document, docElem = doc.documentElement, 17 uid = 0; 18 19 return { 20 /** 21 * @language=en 22 * Gets a globally unique id. Such as Stage1, Bitmap2 etc. 23 * @param {String} prefix Generated id's prefix. 24 * @returns {String} Globally unique id. 25 */ 26 getUid: function(prefix){ 27 var id = ++uid; 28 if(prefix){ 29 var charCode = prefix.charCodeAt(prefix.length - 1); 30 if (charCode >= 48 && charCode <= 57) prefix += "_"; //0至9之间添加下划线 31 return prefix + id; 32 } 33 return id; 34 }, 35 36 /** 37 * @language=en 38 * Generates a string representation that contains a path to the specified visual object. Such as Stage1.Container2.Bitmap3. 39 * @param {View} view Specified visual object. 40 * @returns {String} String representation of the visual object. 41 */ 42 viewToString: function(view){ 43 var result, obj = view; 44 while(obj){ 45 result = result ? (obj.id + '.' + result) : obj.id; 46 obj = obj.parent; 47 } 48 return result; 49 }, 50 51 /** 52 * @language=en 53 * Simple shallow copy objects. 54 * @param {Object} target Target object to copy to. 55 * @param {Object} source Source object to copy. 56 * @param {Boolean} strict Indicates whether replication is undefined property, default is false, i.e., undefined attributes are not copied. 57 * @returns {Object} Object after copying. 58 */ 59 copy: function(target, source, strict){ 60 for(var key in source){ 61 if(!strict || target.hasOwnProperty(key) || target[key] !== undefined){ 62 target[key] = source[key]; 63 } 64 } 65 return target; 66 }, 67 68 /** 69 * @language=en 70 * Browser feature set includes: 71 * <ul> 72 * <li><b>jsVendor</b> - Browser vendors js value CSS prefix. For example: webkit.</li> 73 * <li><b>cssVendor</b> - Browser vendors css value CSS prefix.</li> 74 * <li><b>supportTransform</b> - Whether to support CSS Transform transformation.</li> 75 * <li><b>supportTransform3D</b> - Whether to support CSS Transform 3D transformation.</li> 76 * <li><b>supportStorage</b> - Whether to support local stores like localStorage.</li> 77 * <li><b>supportTouch</b> - Whether to support the touch event.</li> 78 * <li><b>supportCanvas</b> - Whether to support the canvas element.</li> 79 * </ul> 80 */ 81 browser: (function(){ 82 var ua = navigator.userAgent; 83 var data = { 84 iphone: /iphone/i.test(ua), 85 ipad: /ipad/i.test(ua), 86 ipod: /ipod/i.test(ua), 87 ios: /iphone|ipad|ipod/i.test(ua), 88 android: /android/i.test(ua), 89 webkit: /webkit/i.test(ua), 90 chrome: /chrome/i.test(ua), 91 safari: /safari/i.test(ua), 92 firefox: /firefox/i.test(ua), 93 ie: /msie/i.test(ua), 94 opera: /opera/i.test(ua), 95 supportTouch: 'ontouchstart' in win, 96 supportCanvas: doc.createElement('canvas').getContext != null, 97 supportStorage: false, 98 supportOrientation: 'orientation' in win, 99 supportDeviceMotion: 'ondevicemotion' in win 100 }; 101 102 //`localStorage` is null or `localStorage.setItem` throws error in some cases (e.g. localStorage is disabled) 103 try{ 104 var value = 'hilo'; 105 localStorage.setItem(value, value); 106 localStorage.removeItem(value); 107 data.supportStorage = true; 108 }catch(e){ }; 109 110 //vendro prefix 111 var jsVendor = data.jsVendor = data.webkit ? 'webkit' : data.firefox ? 'Moz' : data.opera ? 'O' : data.ie ? 'ms' : ''; 112 var cssVendor = data.cssVendor = '-' + jsVendor + '-'; 113 114 //css transform/3d feature dectection 115 var testElem = doc.createElement('div'), style = testElem.style; 116 var supportTransform = style[jsVendor + 'Transform'] != undefined; 117 var supportTransform3D = style[jsVendor + 'Perspective'] != undefined; 118 if(supportTransform3D){ 119 testElem.id = 'test3d'; 120 style = doc.createElement('style'); 121 style.textContent = '@media ('+ cssVendor +'transform-3d){#test3d{height:3px}}'; 122 doc.head.appendChild(style); 123 124 docElem.appendChild(testElem); 125 supportTransform3D = testElem.offsetHeight == 3; 126 doc.head.removeChild(style); 127 docElem.removeChild(testElem); 128 }; 129 data.supportTransform = supportTransform; 130 data.supportTransform3D = supportTransform3D; 131 132 return data; 133 })(), 134 135 /** 136 * @language=en 137 * Event enumeration objects include: 138 * <ul> 139 * <li><b>POINTER_START</b> - Mouse or touch start event. Corresponds to touchstart or mousedown.</li> 140 * <li><b>POINTER_MOVE</b> - Mouse or touch move event. Corresponds to touchmove or mousemove.</li> 141 * <li><b>POINTER_END</b> - Mouse or touch end event. Corresponds to touchend or mouseup.</li> 142 * </ul> 143 */ 144 event: (function(){ 145 var supportTouch = 'ontouchstart' in win; 146 return { 147 POINTER_START: supportTouch ? 'touchstart' : 'mousedown', 148 POINTER_MOVE: supportTouch ? 'touchmove' : 'mousemove', 149 POINTER_END: supportTouch ? 'touchend' : 'mouseup' 150 }; 151 })(), 152 153 /** 154 * @language=en 155 * Visual object alinment enumeration objects include: 156 * <ul> 157 * <li><b>TOP_LEFT</b> - Align the top left corner.</li> 158 * <li><b>TOP</b> - Top center alignment.</li> 159 * <li><b>TOP_RIGHT</b> - Align the top right corner.</li> 160 * <li><b>LEFT</b> - Left center alignment.</li> 161 * <li><b>CENTER</b> - Align center.</li> 162 * <li><b>RIGHT</b> - Right center alignment.</li> 163 * <li><b>BOTTOM_LEFT</b> - Align the bottom left corner.</li> 164 * <li><b>BOTTOM</b> - Bottom center alignment.</li> 165 * <li><b>BOTTOM_RIGHT</b> - Align the bottom right corner.</li> 166 * </ul> 167 */ 168 align: { 169 TOP_LEFT: 'TL', //top & left 170 TOP: 'T', //top & center 171 TOP_RIGHT: 'TR', //top & right 172 LEFT: 'L', //left & center 173 CENTER: 'C', //center 174 RIGHT: 'R', //right & center 175 BOTTOM_LEFT: 'BL', //bottom & left 176 BOTTOM: 'B', //bottom & center 177 BOTTOM_RIGHT: 'BR' //bottom & right 178 }, 179 180 /** 181 * @language=en 182 * Get DOM element content in the page display area. 183 * @param {HTMLElement} elem DOM elements. 184 * @returns {Object} Viewable area DOM elements. Format is: {left:0, top:0, width:100, height:100}. 185 */ 186 getElementRect: function(elem){ 187 try{ 188 //this fails if it's a disconnected DOM node 189 var bounds = elem.getBoundingClientRect(); 190 }catch(e){ 191 bounds = {top:elem.offsetTop, left:elem.offsetLeft, right:elem.offsetLeft + elem.offsetWidth, bottom:elem.offsetTop + elem.offsetHeight}; 192 } 193 194 var offsetX = ((win.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0)) || 0; 195 var offsetY = ((win.pageYOffset || docElem.scrollTop) - (docElem.clientTop || 0)) || 0; 196 var styles = win.getComputedStyle ? getComputedStyle(elem) : elem.currentStyle; 197 var parseIntFn = parseInt; 198 199 var padLeft = (parseIntFn(styles.paddingLeft) + parseIntFn(styles.borderLeftWidth)) || 0; 200 var padTop = (parseIntFn(styles.paddingTop) + parseIntFn(styles.borderTopWidth)) || 0; 201 var padRight = (parseIntFn(styles.paddingRight) + parseIntFn(styles.borderRightWidth)) || 0; 202 var padBottom = (parseIntFn(styles.paddingBottom) + parseIntFn(styles.borderBottomWidth)) || 0; 203 204 var top = bounds.top || 0; 205 var left = bounds.left || 0; 206 var right = bounds.right || 0; 207 var bottom = bounds.bottom || 0; 208 209 return { 210 left: left + offsetX + padLeft, 211 top: top + offsetY + padTop, 212 width: right - padRight - left - padLeft, 213 height: bottom - padBottom - top - padTop 214 }; 215 }, 216 217 /** 218 * @language=en 219 * Create a DOM element. You can specify properties and styles. 220 * @param {String} type DOM element type to be created. Such as: 'div'. 221 * @param {Object} properties Properties and styles for DOM element. 222 * @returns {HTMLElement} A DOM element. 223 */ 224 createElement: function(type, properties){ 225 var elem = doc.createElement(type), p, val, s; 226 for(p in properties){ 227 val = properties[p]; 228 if(p === 'style'){ 229 for(s in val) elem.style[s] = val[s]; 230 }else{ 231 elem[p] = val; 232 } 233 } 234 return elem; 235 }, 236 237 /** 238 * @language=en 239 * Gets a DOM element according to the parameter id. This method is equivalent to document.getElementById(id). 240 * @param {String} id id of the DOM element you want to get. 241 * @returns {HTMLElement} A DOM element. 242 */ 243 getElement: function(id){ 244 return doc.getElementById(id); 245 }, 246 247 /** 248 * @language=en 249 * Set visual object DOM element CSS style. 250 * @param {View} obj Specifies the CSS style to set the visual object. 251 * @private 252 */ 253 setElementStyleByView: function(obj){ 254 var drawable = obj.drawable, 255 style = drawable.domElement.style, 256 stateCache = obj._stateCache || (obj._stateCache = {}), 257 prefix = Hilo.browser.jsVendor, px = 'px', flag = false; 258 259 if(this.cacheStateIfChanged(obj, ['visible'], stateCache)){ 260 style.display = !obj.visible ? 'none' : ''; 261 } 262 if(this.cacheStateIfChanged(obj, ['alpha'], stateCache)){ 263 style.opacity = obj.alpha; 264 } 265 if(!obj.visible || obj.alpha <= 0) return; 266 267 if(this.cacheStateIfChanged(obj, ['width'], stateCache)){ 268 style.width = obj.width + px; 269 } 270 if(this.cacheStateIfChanged(obj, ['height'], stateCache)){ 271 style.height = obj.height + px; 272 } 273 if(this.cacheStateIfChanged(obj, ['depth'], stateCache)){ 274 style.zIndex = obj.depth + 1; 275 } 276 if(flag = this.cacheStateIfChanged(obj, ['pivotX', 'pivotY'], stateCache)){ 277 style[prefix + 'TransformOrigin'] = obj.pivotX + px + ' ' + obj.pivotY + px; 278 } 279 if(this.cacheStateIfChanged(obj, ['x', 'y', 'rotation', 'scaleX', 'scaleY'], stateCache) || flag){ 280 style[prefix + 'Transform'] = this.getTransformCSS(obj); 281 } 282 if(this.cacheStateIfChanged(obj, ['background'], stateCache)){ 283 style.backgroundColor = obj.background; 284 } 285 if(!style.pointerEvents){ 286 style.pointerEvents = 'none'; 287 } 288 289 //render image as background 290 var image = drawable.image; 291 if(image){ 292 var src = image.src; 293 if(src !== stateCache.image){ 294 stateCache.image = src; 295 style.backgroundImage = 'url(' + src + ')'; 296 } 297 298 var rect = drawable.rect; 299 if(rect){ 300 var sx = rect[0], sy = rect[1]; 301 if(sx !== stateCache.sx){ 302 stateCache.sx = sx; 303 style.backgroundPositionX = -sx + px; 304 } 305 if(sy !== stateCache.sy){ 306 stateCache.sy = sy; 307 style.backgroundPositionY = -sy + px; 308 } 309 } 310 } 311 312 //render mask 313 var mask = obj.mask; 314 if(mask){ 315 var maskImage = mask.drawable.domElement.style.backgroundImage; 316 if(maskImage !== stateCache.maskImage){ 317 stateCache.maskImage = maskImage; 318 style[prefix + 'MaskImage'] = maskImage; 319 style[prefix + 'MaskRepeat'] = 'no-repeat'; 320 } 321 322 var maskX = mask.x, maskY = mask.y; 323 if(maskX !== stateCache.maskX || maskY !== stateCache.maskY){ 324 stateCache.maskX = maskX; 325 stateCache.maskY = maskY; 326 style[prefix + 'MaskPosition'] = maskX + px + ' ' + maskY + px; 327 } 328 } 329 }, 330 331 /** 332 * @private 333 */ 334 cacheStateIfChanged: function(obj, propNames, stateCache){ 335 var i, len, name, value, changed = false; 336 for(i = 0, len = propNames.length; i < len; i++){ 337 name = propNames[i]; 338 value = obj[name]; 339 if(value != stateCache[name]){ 340 stateCache[name] = value; 341 changed = true; 342 } 343 } 344 return changed; 345 }, 346 347 /** 348 * @language=en 349 * Generated visual object CSS style transformation. 350 * @param {View} obj Specifies visual object whose CSS style must be got. 351 * @returns {String} String representation of the CSS style. 352 */ 353 getTransformCSS: function(obj){ 354 var use3d = this.browser.supportTransform3D, 355 str3d = use3d ? '3d' : ''; 356 357 return 'translate' + str3d + '(' + (obj.x - obj.pivotX) + 'px, ' + (obj.y - obj.pivotY) + (use3d ? 'px, 0px)' : 'px)') 358 + 'rotate' + str3d + (use3d ? '(0, 0, 1, ' : '(') + obj.rotation + 'deg)' 359 + 'scale' + str3d + '(' + obj.scaleX + ', ' + obj.scaleY + (use3d ? ', 1)' : ')'); 360 } 361 }; 362 363 })();