1 /**
  2  * Hilo
  3  * Copyright 2015 alibaba.com
  4  * Licensed under the MIT License
  5  */
  6 
  7 var win = window,
  8     doc = document,
  9     docElem = doc.documentElement,
 10     uid = 0;
 11 
 12 var hasWarnedDict = {};
 13 
 14 /**
 15  * @namespace Hilo的基础核心方法集合。
 16  * @static
 17  * @module hilo/core/Hilo
 18  * @requires hilo/util/browser
 19  * @requires hilo/util/util
 20  */
 21 var Hilo = {
 22     /**
 23      * Hilo version
 24      * @type String
 25      */
 26     version: '{{$version}}',
 27     /**
 28      * 获取一个全局唯一的id。如Stage1,Bitmap2等。
 29      * @param {String} prefix 生成id的前缀。
 30      * @returns {String} 全局唯一id。
 31      */
 32     getUid: function(prefix) {
 33         var id = ++uid;
 34         if (prefix) {
 35             var charCode = prefix.charCodeAt(prefix.length - 1);
 36             if (charCode >= 48 && charCode <= 57) prefix += "_"; //0至9之间添加下划线
 37             return prefix + id;
 38         }
 39         return id;
 40     },
 41 
 42     /**
 43      * 为指定的可视对象生成一个包含路径的字符串表示形式。如Stage1.Container2.Bitmap3。
 44      * @param {View} view 指定的可视对象。
 45      * @returns {String} 可视对象的字符串表示形式。
 46      */
 47     viewToString: function(view) {
 48         var result, obj = view;
 49         while (obj) {
 50             result = result ? (obj.id + '.' + result) : obj.id;
 51             obj = obj.parent;
 52         }
 53         return result;
 54     },
 55 
 56     /**
 57      * 简单的浅复制对象。
 58      * @deprecated 使用 Hilo.util.copy
 59      * @param {Object} target 要复制的目标对象。
 60      * @param {Object} source 要复制的源对象。
 61      * @param {Boolean} strict 指示是否复制未定义的属性,默认为false,即不复制未定义的属性。
 62      * @returns {Object} 复制后的对象。
 63      */
 64     copy: function(target, source, strict) {
 65         util.copy(target, source, strict);
 66         if (!hasWarnedDict.copy) {
 67             hasWarnedDict.copy = true;
 68             console.warn('Hilo.copy has been Deprecated! Use Hilo.util.copy instead.');
 69         }
 70         return target;
 71     },
 72 
 73     /**
 74      * 浏览器特性集合。
 75      * @see browser
 76      */
 77     browser: browser,
 78 
 79     /**
 80      * 事件类型枚举对象。包括:
 81      * <ul>
 82      * <li><b>POINTER_START</b> - 鼠标或触碰开始事件。对应touchstart或mousedown。</li>
 83      * <li><b>POINTER_MOVE</b> - 鼠标或触碰移动事件。对应touchmove或mousemove。</li>
 84      * <li><b>POINTER_END</b> - 鼠标或触碰结束事件。对应touchend或mouseup。</li>
 85      * </ul>
 86      */
 87     event: {
 88         POINTER_START: browser.POINTER_START,
 89         POINTER_MOVE: browser.POINTER_MOVE,
 90         POINTER_END: browser.POINTER_END
 91     },
 92 
 93     /**
 94      * 可视对象对齐方式枚举对象。包括:
 95      * <ul>
 96      * <li><b>TOP_LEFT</b> - 左上角对齐。</li>
 97      * <li><b>TOP</b> - 顶部居中对齐。</li>
 98      * <li><b>TOP_RIGHT</b> - 右上角对齐。</li>
 99      * <li><b>LEFT</b> - 左边居中对齐。</li>
100      * <li><b>CENTER</b> - 居中对齐。</li>
101      * <li><b>RIGHT</b> - 右边居中对齐。</li>
102      * <li><b>BOTTOM_LEFT</b> - 左下角对齐。</li>
103      * <li><b>BOTTOM</b> - 底部居中对齐。</li>
104      * <li><b>BOTTOM_RIGHT</b> - 右下角对齐。</li>
105      * </ul>
106      */
107     align: {
108         TOP_LEFT: 'TL', //top & left
109         TOP: 'T', //top & center
110         TOP_RIGHT: 'TR', //top & right
111         LEFT: 'L', //left & center
112         CENTER: 'C', //center
113         RIGHT: 'R', //right & center
114         BOTTOM_LEFT: 'BL', //bottom & left
115         BOTTOM: 'B', //bottom & center
116         BOTTOM_RIGHT: 'BR' //bottom & right
117     },
118 
119     /**
120      * 获取DOM元素在页面中的内容显示区域。
121      * @param {HTMLElement} elem DOM元素。
122      * @returns {Object} DOM元素的可视区域。格式为:{left:0, top:0, width:100, height:100}。
123      */
124     getElementRect: function(elem) {
125         var bounds;
126         try {
127             //this fails if it's a disconnected DOM node
128             bounds = elem.getBoundingClientRect();
129         } catch (e) {
130             bounds = {
131                 top: elem.offsetTop,
132                 left: elem.offsetLeft,
133                 right: elem.offsetLeft + elem.offsetWidth,
134                 bottom: elem.offsetTop + elem.offsetHeight
135             };
136         }
137 
138         var offsetX = ((win.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0)) || 0;
139         var offsetY = ((win.pageYOffset || docElem.scrollTop) - (docElem.clientTop || 0)) || 0;
140         var styles = win.getComputedStyle ? getComputedStyle(elem) : elem.currentStyle;
141         var parseIntFn = parseInt;
142 
143         var padLeft = (parseIntFn(styles.paddingLeft) + parseIntFn(styles.borderLeftWidth)) || 0;
144         var padTop = (parseIntFn(styles.paddingTop) + parseIntFn(styles.borderTopWidth)) || 0;
145         var padRight = (parseIntFn(styles.paddingRight) + parseIntFn(styles.borderRightWidth)) || 0;
146         var padBottom = (parseIntFn(styles.paddingBottom) + parseIntFn(styles.borderBottomWidth)) || 0;
147 
148         var top = bounds.top || 0;
149         var left = bounds.left || 0;
150         var right = bounds.right || 0;
151         var bottom = bounds.bottom || 0;
152 
153         return {
154             left: left + offsetX + padLeft,
155             top: top + offsetY + padTop,
156             width: right - padRight - left - padLeft,
157             height: bottom - padBottom - top - padTop
158         };
159     },
160 
161     /**
162      * 创建一个DOM元素。可指定属性和样式。
163      * @param {String} type 要创建的DOM元素的类型。比如:'div'。
164      * @param {Object} properties 指定DOM元素的属性和样式。
165      * @returns {HTMLElement} 一个DOM元素。
166      */
167     createElement: function(type, properties) {
168         var elem = doc.createElement(type),
169             p, val, s;
170         for (p in properties) {
171             val = properties[p];
172             if (p === 'style') {
173                 for (s in val) elem.style[s] = val[s];
174             } else {
175                 elem[p] = val;
176             }
177         }
178         return elem;
179     },
180 
181     /**
182      * 根据参数id获取一个DOM元素。此方法等价于document.getElementById(id)。
183      * @param {String} id 要获取的DOM元素的id。
184      * @returns {HTMLElement} 一个DOM元素。
185      */
186     getElement: function(id) {
187         return doc.getElementById(id);
188     },
189 
190     /**
191      * 设置可视对象DOM元素的CSS样式。
192      * @param {View} obj 指定要设置CSS样式的可视对象。
193      * @private
194      */
195     setElementStyleByView: function(obj) {
196         var drawable = obj.drawable,
197             style = drawable.domElement.style,
198             stateCache = obj._stateCache || (obj._stateCache = {}),
199             prefix = Hilo.browser.jsVendor,
200             px = 'px',
201             flag = false;
202 
203         if (this.cacheStateIfChanged(obj, ['visible'], stateCache)) {
204             style.display = !obj.visible ? 'none' : '';
205         }
206         if (this.cacheStateIfChanged(obj, ['alpha'], stateCache)) {
207             style.opacity = obj.alpha;
208         }
209         if (!obj.visible || obj.alpha <= 0) return;
210 
211         if (this.cacheStateIfChanged(obj, ['width'], stateCache)) {
212             style.width = obj.width + px;
213         }
214         if (this.cacheStateIfChanged(obj, ['height'], stateCache)) {
215             style.height = obj.height + px;
216         }
217         if (this.cacheStateIfChanged(obj, ['depth'], stateCache)) {
218             style.zIndex = obj.depth + 1;
219         }
220         if (obj.transform){
221             var transform = obj.transform;
222             if (flag = this.cacheStateIfChanged(obj, ['pivotX', 'pivotY'], stateCache)) {
223                 style[prefix + 'TransformOrigin'] = '0 0';
224             }
225             style[prefix + 'Transform'] = 'matrix3d(' + transform.a + ', '+ transform.b + ', 0, 0, '+ transform.c + ', '+ transform.d + ', 0, 0, 0, 0, 1, 0, '+ transform.tx + ', '+ transform.ty + ', 0, 1)';
226         }
227         else{
228             if (flag = this.cacheStateIfChanged(obj, ['pivotX', 'pivotY'], stateCache)) {
229                 style[prefix + 'TransformOrigin'] = obj.pivotX + px + ' ' + obj.pivotY + px;
230             }
231 
232             if (this.cacheStateIfChanged(obj, ['x', 'y', 'rotation', 'scaleX', 'scaleY'], stateCache) || flag) {
233                 style[prefix + 'Transform'] = this.getTransformCSS(obj);
234             }
235         }
236         
237         if (this.cacheStateIfChanged(obj, ['background'], stateCache)) {
238             style.backgroundColor = obj.background;
239         }
240         if (!style.pointerEvents) {
241             style.pointerEvents = 'none';
242         }
243 
244         //render image as background
245         var image = drawable.image;
246         if (image) {
247             var src = image.src;
248             if (src !== stateCache.image) {
249                 stateCache.image = src;
250                 style.backgroundImage = 'url(' + src + ')';
251             }
252 
253             var rect = drawable.rect;
254             if (rect) {
255                 var sx = rect[0],
256                     sy = rect[1];
257                 if (sx !== stateCache.sx) {
258                     stateCache.sx = sx;
259                     style.backgroundPositionX = -sx + px;
260                 }
261                 if (sy !== stateCache.sy) {
262                     stateCache.sy = sy;
263                     style.backgroundPositionY = -sy + px;
264                 }
265             }
266         }
267 
268         //render mask
269         var mask = obj.mask;
270         if (mask) {
271             var maskImage = mask.drawable.domElement.style.backgroundImage;
272             if (maskImage !== stateCache.maskImage) {
273                 stateCache.maskImage = maskImage;
274                 style[prefix + 'MaskImage'] = maskImage;
275                 style[prefix + 'MaskRepeat'] = 'no-repeat';
276             }
277 
278             var maskX = mask.x,
279                 maskY = mask.y;
280             if (maskX !== stateCache.maskX || maskY !== stateCache.maskY) {
281                 stateCache.maskX = maskX;
282                 stateCache.maskY = maskY;
283                 style[prefix + 'MaskPosition'] = maskX + px + ' ' + maskY + px;
284             }
285         }
286     },
287 
288     /**
289      * @private
290      */
291     cacheStateIfChanged: function(obj, propNames, stateCache) {
292         var i, len, name, value, changed = false;
293         for (i = 0, len = propNames.length; i < len; i++) {
294             name = propNames[i];
295             value = obj[name];
296             if (value != stateCache[name]) {
297                 stateCache[name] = value;
298                 changed = true;
299             }
300         }
301         return changed;
302     },
303 
304     /**
305      * 生成可视对象的CSS变换样式。
306      * @param {View} obj 指定生成CSS变换样式的可视对象。
307      * @returns {String} 生成的CSS样式字符串。
308      */
309     getTransformCSS: function(obj) {
310         var use3d = this.browser.supportTransform3D,
311             str3d = use3d ? '3d' : '';
312 
313         return 'translate' + str3d + '(' + (obj.x - obj.pivotX) + 'px, ' + (obj.y - obj.pivotY) + (use3d ? 'px, 0px)' : 'px)') +
314             'rotate' + str3d + (use3d ? '(0, 0, 1, ' : '(') + obj.rotation + 'deg)' +
315             'scale' + str3d + '(' + obj.scaleX + ', ' + obj.scaleY + (use3d ? ', 1)' : ')');
316     }
317 };