      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 };