1 /** 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2016 DeNA Co., Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 /// <reference path="base.js"/> 26 /// <reference path="object.js"/> 27 28 /** 29 * A class that encapsulates an array that is safe to be modified in a loop. It 30 * is not safe to modify an array in a loop, e.g. the following loop does not 31 * iterate the second item 'b'. 32 * 33 * var list = ['a', 'b', 'c']; 34 * for (var i = 0; i < list.length; ++i) { 35 * console.log(list[i]); 36 * if (list[i] == 'a') { 37 * list.splice(i, 1); 38 * } 39 * } 40 * 41 * Even though iterating a clone solves this problem, it is an overkill to 42 * create a clone every time when an application iterates an array. 43 * 44 * var list = ['a', 'b', 'c']; 45 * var clone = list.slice(); 46 * for (var i = 0; i < clone.length; ++i) { 47 * console.log(clone[i]); 48 * if (clone[i] == 'a') { 49 * list.splice(i, 1); 50 * } 51 * } 52 * 53 * This class lazily creates a clone of its array for the first time when an 54 * application either adds an item to the array or removes one in a loop. 55 * 56 * var list = object_list.lock(); 57 * for (var i = 0; i < list.length; ++i) { 58 * console.log(list[i]); 59 * if (list[i] == 'a') { 60 * object_list.removeItemAt(i); 61 * } 62 * } 63 * object_list.unlock(); 64 * 65 * @constructor 66 */ 67 createjs.ObjectList = function() { 68 /** 69 * A list of objects. 70 * @type {Array.<*>} 71 */ 72 this.items_ = []; 73 }; 74 75 /** 76 * A clone of this list. 77 * @type {Array.<*>} 78 */ 79 createjs.ObjectList.prototype.clone_ = null; 80 81 /** 82 * Whether this list is locked. 83 * @type {boolean} 84 */ 85 createjs.ObjectList.prototype.locked_ = false; 86 87 /** 88 * Retrieves the editable items of this list. 89 * @return {Array.<*>} 90 * @protected 91 */ 92 createjs.ObjectList.prototype.getItems = function() { 93 /// <returns type="Array"/> 94 if (!this.locked_) { 95 return this.items_; 96 } 97 if (!this.clone_) { 98 this.clone_ = this.items_.slice(); 99 } 100 return this.clone_; 101 }; 102 103 /** 104 * Returns whether this list is locked. 105 * @return {boolean} 106 * @protected 107 */ 108 createjs.ObjectList.prototype.isLocked = function() { 109 /// <returns type="boolean"/> 110 return this.locked_; 111 }; 112 113 /** 114 * Returns the number of items in this list. 115 * @return {number} 116 * @const 117 */ 118 createjs.ObjectList.prototype.getLength = function() { 119 /// <returns type="number"/> 120 return this.items_.length; 121 }; 122 123 /** 124 * Locks this list for iteration. This method changes the state of this list to 125 * 'locked' to apply succeeding add operations and remove ones to its clone. 126 * @return {Array.<*>} 127 * @const 128 */ 129 createjs.ObjectList.prototype.lock = function() { 130 /// <returns type="Array"/> 131 createjs.assert(!this.locked_); 132 this.locked_ = true; 133 return this.items_; 134 }; 135 136 /** 137 * Unlocks this list. This method changes the stage of this list to 'unlocked' 138 * and copies its clone if an application edits the list while it is locked. 139 * @const 140 */ 141 createjs.ObjectList.prototype.unlock = function() { 142 createjs.assert(this.locked_); 143 this.locked_ = false; 144 if (this.clone_) { 145 this.items_ = this.clone_; 146 this.clone_ = null; 147 } 148 }; 149 150 /** 151 * Adds an item to the beginning of this list. 152 * @param {*} item 153 * @const 154 */ 155 createjs.ObjectList.prototype.unshiftItem = function(item) { 156 /// <param name="item"/> 157 var list = this.getItems(); 158 list.unshift(item); 159 }; 160 161 /** 162 * Adds an item to the end of this list. 163 * @param {*} item 164 * @const 165 */ 166 createjs.ObjectList.prototype.pushItem = function(item) { 167 /// <param name="item"/> 168 var list = this.getItems(); 169 list.push(item); 170 }; 171 172 /** 173 * Adds an item to the specified position of this list. 174 * @param {number} index 175 * @param {*} item 176 * @const 177 */ 178 createjs.ObjectList.prototype.insertItem = function(index, item) { 179 /// <param type="number" name="index"/> 180 /// <param name="item"/> 181 var list = this.getItems(); 182 list.splice(index, 0, item); 183 }; 184 185 /** 186 * Removes an item from this list. 187 * @param {*} item 188 * @const 189 */ 190 createjs.ObjectList.prototype.removeItem = function(item) { 191 /// <param name="item"/> 192 var list = this.getItems(); 193 for (var i = 0; i < list.length; ++i) { 194 if (list[i] === item) { 195 list.splice(i, 1); 196 return; 197 } 198 } 199 }; 200 201 /** 202 * Finds an item from this list. 203 * @param {*} item 204 * @return {number} 205 * @const 206 */ 207 createjs.ObjectList.prototype.findItem = function(item) { 208 /// <param name="item"/> 209 /// <returns type="number"/> 210 var list = this.getItems(); 211 for (var i = 0; i < list.length; ++i) { 212 if (list[i] === item) { 213 return i; 214 } 215 } 216 return -1; 217 }; 218 219 /** 220 * Adds an item to the end of this list only if this list does not have it. 221 * @param {*} item 222 * @const 223 */ 224 createjs.ObjectList.prototype.pushUniqueItem = function(item) { 225 /// <param name="item"/> 226 var list = this.getItems(); 227 for (var i = 0; i < list.length; ++i) { 228 if (list[i] === item) { 229 return; 230 } 231 } 232 list.push(item); 233 }; 234 235 /** 236 * Swaps two items in this list. 237 * @param {*} item1 238 * @param {*} item2 239 * @const 240 */ 241 createjs.ObjectList.prototype.swapItems = function(item1, item2) { 242 /// <param name="item1"/> 243 /// <param name="item2"/> 244 var list = this.getItems(); 245 var index1 = -1; 246 var index2 = -1; 247 for (var i = 0; i < list.length; ++i) { 248 var item = list[i]; 249 if (item === item1) { 250 index1 = i; 251 } else if (item === item2) { 252 index2 = i; 253 } 254 if (index1 >= 0 && index2 >= 0) { 255 list[index1] = item2; 256 list[index2] = item1; 257 return; 258 } 259 } 260 }; 261 262 /** 263 * Returns an item at the specified index. 264 * @param {number} index 265 * @return {*} 266 * @const 267 */ 268 createjs.ObjectList.prototype.getItemAt = function(index) { 269 /// <param type="number" name="index"/> 270 /// <returns type="Object"/> 271 var list = this.getItems(); 272 return list[index]; 273 }; 274 275 /** 276 * Removes an item from this list. 277 * @param {number} index 278 * @const 279 */ 280 createjs.ObjectList.prototype.removeItemAt = function(index) { 281 /// <param type="number" name="index"/> 282 var list = this.getItems(); 283 list.splice(index, 1); 284 }; 285 286 /** 287 * Finds an item from this list. 288 * @param {number} index1 289 * @param {number} index2 290 * @const 291 */ 292 createjs.ObjectList.prototype.swapItemsAt = function(index1, index2) { 293 /// <param type="number" name="index1"/> 294 /// <param type="number" name="index2"/> 295 var list = this.getItems(); 296 if (index1 >= list.length || index2 >= list.length) { 297 return; 298 } 299 var object1 = list[index1]; 300 var object2 = list[index2]; 301 list[index1] = object2; 302 list[index2] = object1; 303 }; 304 305 /** 306 * Removes all items from this list. 307 * @const 308 */ 309 createjs.ObjectList.prototype.removeAllItems = function() { 310 if (!this.locked_) { 311 this.items_ = []; 312 } else { 313 this.clone_ = []; 314 } 315 }; 316 317 /** 318 * Returns a clone of this list. 319 * @return {Array.<*>} 320 * @const 321 */ 322 createjs.ObjectList.prototype.cloneItems = function() { 323 /// <returns type="Array"/> 324 var items = this.clone_ ? this.clone_ : this.items_; 325 return items.slice(); 326 }; 327