1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * @language=en 9 * @class Container is the base class to all container classes. Each Container can add other view object as children. 10 * @augments View 11 * @param {Object} properties Properties parameters of the object to create. Contains all writable properties of this class. 12 * @module hilo/view/Container 13 * @requires hilo/core/Hilo 14 * @requires hilo/core/Class 15 * @requires hilo/view/View 16 * @property {Array} children List of children elements of the container, readonly! 17 * @property {Boolean} pointerChildren Whether children elements of the container can response to user interactive events, default value is true. 18 * @property {Boolean} clipChildren Whether clip children elements which are out of the container, default value is false. 19 */ 20 var Container = Class.create(/** @lends Container.prototype */{ 21 Extends: View, 22 constructor: function(properties){ 23 properties = properties || {}; 24 this.id = this.id || properties.id || Hilo.getUid("Container"); 25 Container.superclass.constructor.call(this, properties); 26 27 if(this.children) this._updateChildren(); 28 else this.children = []; 29 }, 30 31 children: null, 32 pointerChildren: true, 33 clipChildren: false, 34 35 /** 36 * @language=en 37 * Return the amount of the children elements of the container. 38 * @returns {Uint} The amount of the children elements of the container. 39 */ 40 getNumChildren: function(){ 41 return this.children.length; 42 }, 43 44 /** 45 * @language=en 46 * Add child element at given index. 47 * @param {View} child Element to add. 48 * @param {Number} index The given index position, range from 0. 49 */ 50 addChildAt: function(child, index){ 51 var children = this.children, 52 len = children.length, 53 parent = child.parent; 54 55 index = index < 0 ? 0 : index > len ? len : index; 56 var childIndex = this.getChildIndex(child); 57 if(childIndex == index){ 58 return this; 59 }else if(childIndex >= 0){ 60 children.splice(childIndex, 1); 61 index = index == len ? len - 1 : index; 62 }else if(parent){ 63 parent.removeChild(child); 64 } 65 66 children.splice(index, 0, child); 67 68 //直接插入,影响插入位置之后的深度 69 //Insert directly, this will affect depth of elements after the index. 70 if(childIndex < 0){ 71 this._updateChildren(index); 72 } 73 //只是移动时影响中间段的深度 74 //Will affect depth of elements in the middle during moving 75 else{ 76 var startIndex = childIndex < index ? childIndex : index; 77 var endIndex = childIndex < index ? index : childIndex;; 78 this._updateChildren(startIndex, endIndex + 1); 79 } 80 81 return this; 82 }, 83 84 /** 85 * @language=en 86 * Add child element at the top. 87 * @param {View} child Elements to add. 88 */ 89 addChild: function(child){ 90 var total = this.children.length, 91 args = arguments; 92 93 for(var i = 0, len = args.length; i < len; i++){ 94 this.addChildAt(args[i], total + i); 95 } 96 return this; 97 }, 98 99 /** 100 * @language=en 101 * Remove element at the index. 102 * @param {Int} index Index of the element to remove, range from 0. 103 * @returns {View} Element had been removed. 104 */ 105 removeChildAt: function(index){ 106 var children = this.children; 107 if(index < 0 || index >= children.length) return null; 108 109 var child = children[index]; 110 if(child){ 111 //NOTE: use `__renderer` for fixing child removal (DOMRenderer and FlashRenderer only). 112 //Do `not` use it in any other case. 113 if(!child.__renderer){ 114 var obj = child; 115 while(obj = obj.parent){ 116 //obj is stage 117 if(obj.renderer){ 118 child.__renderer = obj.renderer; 119 break; 120 } 121 else if(obj.__renderer){ 122 child.__renderer = obj.__renderer; 123 break; 124 } 125 } 126 } 127 128 if(child.__renderer){ 129 child.__renderer.remove(child); 130 } 131 132 child.parent = null; 133 child.depth = -1; 134 } 135 136 children.splice(index, 1); 137 this._updateChildren(index); 138 139 return child; 140 }, 141 142 /** 143 * @language=en 144 * Remove the given child element. 145 * @param {View} child The child element to remove. 146 * @returns {View} Element had been removed. 147 */ 148 removeChild: function(child){ 149 return this.removeChildAt(this.getChildIndex(child)); 150 }, 151 152 /** 153 * @language=en 154 * Remove child element by its id. 155 * @param {String} id The id of element to remove. 156 * @returns {View} Element had been removed. 157 */ 158 removeChildById: function(id){ 159 var children = this.children, child; 160 for(var i = 0, len = children.length; i < len; i++){ 161 child = children[i]; 162 if(child.id === id){ 163 this.removeChildAt(i); 164 return child; 165 } 166 } 167 return null; 168 }, 169 170 /** 171 * @language=en 172 * Remove all children elements. 173 * @returns {Container} Container itself. 174 */ 175 removeAllChildren: function(){ 176 while(this.children.length) this.removeChildAt(0); 177 return this; 178 }, 179 180 /** 181 * @language=en 182 * Return child element at the given index. 183 * @param {Number} index The index of the element, range from 0. 184 */ 185 getChildAt: function(index){ 186 var children = this.children; 187 if(index < 0 || index >= children.length) return null; 188 return children[index]; 189 }, 190 191 /** 192 * @language=en 193 * Return child element at the given id. 194 * @param {String} id The id of child element to return. 195 */ 196 getChildById: function(id){ 197 var children = this.children, child; 198 for(var i = 0, len = children.length; i < len; i++){ 199 child = children[i]; 200 if(child.id === id) return child; 201 } 202 return null; 203 }, 204 205 /** 206 * @language=en 207 * Return index value of the given child element. 208 * @param {View} child The child element need to get its index. 209 */ 210 getChildIndex: function(child){ 211 return this.children.indexOf(child); 212 }, 213 214 /** 215 * @language=en 216 * Set the index of child element. 217 * @param {View} child The child element need to set index. 218 * @param {Number} index The index to set to the element. 219 */ 220 setChildIndex: function(child, index){ 221 var children = this.children, 222 oldIndex = children.indexOf(child); 223 224 if(oldIndex >= 0 && oldIndex != index){ 225 var len = children.length; 226 index = index < 0 ? 0 : index >= len ? len - 1 : index; 227 children.splice(oldIndex, 1); 228 children.splice(index, 0, child); 229 this._updateChildren(); 230 } 231 return this; 232 }, 233 234 /** 235 * @language=en 236 * Swap index between two child elements. 237 * @param {View} child1 Child element A. 238 * @param {View} child2 Child element B. 239 */ 240 swapChildren: function(child1, child2){ 241 var children = this.children, 242 index1 = this.getChildIndex(child1), 243 index2 = this.getChildIndex(child2); 244 245 child1.depth = index2; 246 children[index2] = child1; 247 child2.depth = index1; 248 children[index1] = child2; 249 }, 250 251 /** 252 * @language=en 253 * Swap two children elements at given indexes. 254 * @param {Number} index1 Given index A. 255 * @param {Number} index2 Given index B. 256 */ 257 swapChildrenAt: function(index1, index2){ 258 var children = this.children, 259 child1 = this.getChildAt(index1), 260 child2 = this.getChildAt(index2); 261 262 child1.depth = index2; 263 children[index2] = child1; 264 child2.depth = index1; 265 children[index1] = child2; 266 }, 267 268 /** 269 * @language=en 270 * Sort children elements by the given key or function. 271 * @param {Object} keyOrFunction If is String, sort children elements by the given property string; If is Function, sort by the function. 272 */ 273 sortChildren: function(keyOrFunction){ 274 var fn = keyOrFunction, 275 children = this.children; 276 if(typeof fn == "string"){ 277 var key = fn; 278 fn = function(a, b){ 279 return b[key] - a[key]; 280 }; 281 } 282 children.sort(fn); 283 this._updateChildren(); 284 }, 285 286 /** 287 * @language=en 288 * Update children elements. 289 * @private 290 */ 291 _updateChildren: function(start, end){ 292 var children = this.children, child, 293 start = start || 0, 294 end = end || children.length; 295 for(var i = start; i < end; i++){ 296 child = children[i]; 297 child.depth = i + 1; 298 child.parent = this; 299 } 300 }, 301 302 /** 303 * @language=en 304 * Return whether this container contains the parameter described child element. 305 * @param {View} child The child element to test. 306 */ 307 contains: function(child){ 308 while(child = child.parent){ 309 if(child === this){ 310 return true; 311 } 312 } 313 return false; 314 }, 315 316 /** 317 * @language=en 318 * Return object at the point positioned by given values on x axis and y axis. 319 * @param {Number} x The point's value on the coordinate's x axis. 320 * @param {Number} y The point's value on the coordinate's y asix. 321 * @param {Boolean} usePolyCollision Whether use polygon collision detection, default value is false. 322 * @param {Boolean} global Whether return all elements that match the condition, default value is false. 323 * @param {Boolean} eventMode Whether find elements under event mode, default value is false. 324 */ 325 getViewAtPoint: function(x, y, usePolyCollision, global, eventMode){ 326 var result = global ? [] : null, 327 children = this.children, child, obj; 328 329 for(var i = children.length - 1; i >= 0; i--){ 330 child = children[i]; 331 //skip child which is not shown or pointer enabled 332 if(!child || !child.visible || child.alpha <= 0 || (eventMode && !child.pointerEnabled)) continue; 333 //find child recursively 334 if(child.children && child.children.length && !(eventMode && !child.pointerChildren)){ 335 obj = child.getViewAtPoint(x, y, usePolyCollision, global, eventMode); 336 } 337 338 if(obj){ 339 if(!global) return obj; 340 else if(obj.length) result = result.concat(obj); 341 }else if(child.hitTestPoint(x, y, usePolyCollision)){ 342 if(!global) return child; 343 else result.push(child); 344 } 345 } 346 347 return global && result.length ? result : null; 348 }, 349 350 /** 351 * @language=en 352 * Rewrite render method. 353 * @private 354 */ 355 render: function(renderer, delta){ 356 Container.superclass.render.call(this, renderer, delta); 357 358 var children = this.children.slice(0), i, len, child; 359 for(i = 0, len = children.length; i < len; i++){ 360 child = children[i]; 361 //NOTE: the child could remove or change it's parent 362 if(child.parent === this) child._render(renderer, delta); 363 } 364 } 365 366 }); 367