1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 // AjxVector class 25 26 /** 27 * Creates a vector. 28 * @class 29 * This class represents a vector. 30 * 31 */ 32 AjxVector = function(array) { 33 this._array = array || []; 34 }; 35 36 AjxVector.prototype.isAjxVector = true; 37 38 /** 39 * Returns a string representation of the object. 40 * 41 * @param {string} sep the seperator 42 * @param {boolean} compress if <code>true</code>, compress 43 * 44 * @return {string} a string representation of the object 45 */ 46 AjxVector.prototype.toString = 47 function(sep, compress) { 48 if (compress !== true) 49 return this._array.join(sep); 50 51 var a = new Array(); 52 for (var i = 0; i < this._array.length; i++) { 53 var x = this._array[i]; 54 if (x != undefined && x != null && x != "") 55 a.push(x); 56 } 57 return a.join(sep); 58 }; 59 60 /** 61 * Creates a vector from a given array. 62 * 63 * @param {array} list an array 64 * @return {AjxVector} the vector 65 */ 66 AjxVector.fromArray = 67 function(list) { 68 var vec = new AjxVector(); 69 if (AjxUtil.isArray1(list)) { 70 vec._array = list; 71 } 72 return vec; 73 }; 74 75 /** 76 * Gets the size of the vector. 77 * 78 * @return {number} the size 79 */ 80 AjxVector.prototype.size = 81 function() { 82 return this._array.length; 83 }; 84 85 /** 86 * Adds a object to the vector. 87 * 88 * @param {Object} obj the object 89 * @param {number} index the index where to add 90 * @param {boolean} noDuplicates if <code>true</code>, confirm the object is not in vector before adding 91 */ 92 AjxVector.prototype.add = 93 function(obj, index, noDuplicates) { 94 // if no duplicates, search for the obj in list and return if found. 95 if (noDuplicates && this.contains(obj)) { 96 return; 97 } 98 99 AjxUtil.arrayAdd(this._array, obj, index); 100 }; 101 102 /** 103 * Adds the given array. 104 * 105 * @param {array} list an array 106 */ 107 AjxVector.prototype.addList = 108 function(list) { 109 if (!list) return; 110 111 if (list.length) {// array 112 this._array = this._array.concat(list); 113 } else if (list.size && list.size()) {// AjxVector 114 // in new window, IE seems to lose its rtti :( 115 if (AjxEnv.isIE && (!(list._array instanceof Array))) { 116 var newList = []; 117 for (var i = 0; i < list._array.length; i++) { 118 newList.push(list._array[i]); 119 } 120 list._array = newList; 121 } 122 123 this._array = this._array.concat(list._array); 124 } 125 }; 126 127 /** 128 * Removes the object. 129 * 130 * @param {Object} obj the object to remove 131 * @return {boolean} <code>true</code> if the object is removed 132 */ 133 AjxVector.prototype.remove = 134 function(obj) { 135 return AjxUtil.arrayRemove(this._array, obj); 136 }; 137 138 /** 139 * Removes the object at the given index. 140 * 141 * @param {number} index the index 142 * @return {Object} the object at the index or <code>null</code> if no object at index 143 */ 144 AjxVector.prototype.removeAt = 145 function(index) { 146 if (index >= this._array.length || index < 0) 147 return null; 148 149 var delArr = this._array.splice(index, 1); 150 var ret = null; 151 if (delArr) { 152 ret = delArr[0]; 153 } 154 return ret; 155 }; 156 157 /** 158 * Removes all objects from vector. 159 * 160 */ 161 AjxVector.prototype.removeAll = 162 function() { 163 // Actually blow away the array items so that garbage 164 // collection can take place (XXX: does this really force GC?) 165 for (var i = 0; i < this._array.length; i++) 166 this._array[i] = null; 167 this._array.length = 0; 168 }; 169 170 /** 171 * Removes the last object in the vector. 172 * 173 */ 174 AjxVector.prototype.removeLast = 175 function() { 176 return this._array.length > 0 ? this._array.pop() : null; 177 }; 178 179 /** 180 * Reverses the order of the objects in the vector. 181 * 182 */ 183 AjxVector.prototype.reverse = 184 function() { 185 this._array.reverse(); 186 }; 187 188 /** 189 * Replaces the object at a given index. 190 * 191 * @param {number} index the index 192 * @param {Object} newObj the new object 193 * @return {Object} the old object 194 */ 195 AjxVector.prototype.replace = 196 function(index, newObj) { 197 var oldObj = this._array[index]; 198 this._array[index] = newObj; 199 return oldObj; 200 }; 201 202 /** 203 * Replaces an object. 204 * 205 * @param {Object} obj the object to replace 206 * @param {Object} newObj the new object 207 * @return {Object} the replaced object or <code>null</code> if not replaced 208 */ 209 AjxVector.prototype.replaceObject = 210 function(obj, newObj) { 211 for (var i = 0; i < this._array.length; i++) { 212 if (this._array[i] == obj) { 213 this._array[i] = newObj; 214 return obj; 215 } 216 } 217 return null; 218 }; 219 220 /** 221 * Returns the index of the obj given w/in vector 222 * 223 * @param {Object} obj the object being looked for 224 * @param {function} func (optional) a function for transforming objects 225 * 226 * @return {number} the index or -1 if not found 227 */ 228 AjxVector.prototype.indexOf = function(obj, func) { 229 230 if (obj == null) { 231 return -1; 232 } 233 obj = func ? func.call(obj) : obj; 234 235 for (var i = 0; i < this._array.length; i++) { 236 var member = this._array[i], 237 test = func ? func.call(member) : member; 238 if (test === obj) { 239 return i; 240 } 241 } 242 return -1; 243 }; 244 245 AjxVector.prototype.indexOfLike = AjxVector.prototype.indexOf; 246 247 /** 248 * Returns the last index of the obj given w/in vector 249 * 250 * @param {Object} obj the object being looked for 251 * @param {function} func (optional) a function for transforming objects 252 * 253 * @return {number} the index or -1 if not found 254 */ 255 AjxVector.prototype.lastIndexOf = function(obj, func) { 256 257 if (obj == null) { 258 return -1; 259 } 260 obj = func ? func.call(obj) : obj; 261 262 for (var i = this._array.length - 1; i >= 0; i--) { 263 var member = this._array[i], 264 test = func ? func.call(member) : member; 265 if (member === obj) { 266 return i; 267 } 268 } 269 return -1; 270 }; 271 272 AjxVector.prototype.lastIndexOfLike = AjxVector.prototype.lastIndexOf; 273 274 /** 275 * Returns the last index of the obj given w/in vector 276 * 277 * @param {Object} obj the object being looked for 278 * @param {function} keyFunc a function for transforming objects 279 * @return {number} the index or -1 if not found 280 */ 281 AjxVector.prototype.lastIndexOfLike = 282 function(obj, keyFunc) { 283 var value = keyFunc.call(obj); 284 285 for (var i = this._array.length - 1; i >= 0; i--) { 286 var test = keyFunc.call(this._array[i]); 287 if (test == value) 288 return i; 289 } 290 return -1; 291 }; 292 293 /** 294 * Clones the vector. 295 * 296 * @return {AjxVector} the new vector 297 */ 298 AjxVector.prototype.clone = 299 function() { 300 var vec = new AjxVector(); 301 vec.addList(this); 302 return vec; 303 }; 304 305 /** 306 * Checks if the vector contains an object. 307 * 308 * @param {Object} obj the object 309 * @return {boolean} <code>true</code> if the object is found 310 */ 311 AjxVector.prototype.contains = 312 function(obj) { 313 return AjxUtil.arrayContains(this._array, obj); 314 }; 315 316 317 /** 318 * Returns true if the vector contains the given object, using the given 319 * function to compare objects. The comparison function should return a 320 * type for which the equality test (==) is meaningful, such as a string 321 * or a base type. 322 * 323 * @param {Object} obj the object being looked for 324 * @param {function} keyFunc a function for transforming objects 325 * @return {boolean} <code>true</code> if the object is found 326 */ 327 AjxVector.prototype.containsLike = 328 function(obj, keyFunc) { 329 var value = keyFunc.call(obj); 330 for (var i = 0; i < this._array.length; i++) { 331 var test = keyFunc.call(this._array[i]); 332 if (test == value) 333 return true; 334 } 335 return false; 336 }; 337 338 /** 339 * Gets the object at a given index. 340 * 341 * @param {number} index the index 342 * @return {Object} the object or <code>null</code> if not found 343 */ 344 AjxVector.prototype.get = 345 function(index) { 346 return index >= this._array.length || index < 0 347 ? null : this._array[index]; 348 }; 349 350 /** 351 * Gets an array of the vector. 352 * 353 * @return {array} an array 354 */ 355 AjxVector.prototype.getArray = 356 function() { 357 return this._array; 358 }; 359 360 /** 361 * Gets the last object in the vector. 362 * 363 * @return {Object} the object or <code>null</code> if vector is empty 364 */ 365 AjxVector.prototype.getLast = 366 function() { 367 return this._array.length == 0 368 ? null : this._array[this._array.length-1]; 369 }; 370 371 /** 372 * Gets the next object in the vector after a given object. 373 * 374 * @param {Object} obj the object 375 * @return {Object} the object or <code>null</code> if object not found 376 */ 377 AjxVector.prototype.getNext = 378 function(obj) { 379 var idx = this.indexOf(obj); 380 if (idx == -1) 381 return null; 382 return this.get(++idx); 383 }; 384 385 /** 386 * Gets the previous object in the vector before a given object. 387 * 388 * @param {Object} obj the object 389 * @return {Object} the object or <code>null</code> if object not found 390 */ 391 AjxVector.prototype.getPrev = 392 function(obj) { 393 var idx = this.indexOf(obj); 394 if (idx == -1) 395 return null; 396 return this.get(--idx); 397 }; 398 399 /** 400 * Sorts the vector. 401 * 402 * @param {function} sortFunc the function 403 */ 404 AjxVector.prototype.sort = 405 function(sortFunc) { 406 if (!sortFunc) { 407 sortFunc = AjxVector._defaultArrayComparator; 408 } 409 this._array.sort(sortFunc); 410 }; 411 412 /** 413 * Performs a binary search. 414 * 415 * @param {Object} valueToFind the value 416 * @param {function} sortFunc the sort function 417 * @return {number} the index 418 */ 419 AjxVector.prototype.binarySearch = 420 function(valueToFind, sortFunc) { 421 if (!sortFunc) { 422 sortFunc = AjxVector._defaultArrayComparator; 423 } 424 425 var l = 0; 426 var arr = this._array; 427 var u = arr.length - 1; 428 429 while(true) { 430 if (u < l) { 431 return -1; 432 } 433 434 var i = Math.floor((l + u)/ 2); 435 var comparisonResult = sortFunc(valueToFind, arr[i]); 436 437 if (comparisonResult < 0) { 438 u = i - 1; 439 } else if (comparisonResult > 0) { 440 l = i + 1; 441 } else { 442 return i; 443 } 444 } 445 }; 446 447 AjxVector.prototype.merge = 448 function(offset, list) { 449 450 if (offset < 0) 451 return; 452 453 var rawList = list instanceof AjxVector ? list.getArray() : list; 454 455 var limit = this._array.length < (offset+rawList.length) 456 ? this._array.length 457 : offset+rawList.length; 458 459 if (offset < this._array.length) { 460 // replace any overlapping items in vector 461 var count = 0; 462 for (var i=offset; i<limit; i++) 463 this._array[i] = rawList[count++]; 464 465 // and append the rest 466 if (count < rawList.length) 467 this._array = this._array.concat(rawList.slice(count)); 468 } else { 469 // otherwise, just append the raw list to the end 470 this._array = this._array.concat(rawList); 471 } 472 }; 473 474 475 // Static methods 476 477 AjxVector._defaultArrayComparator = 478 function(a, b) { 479 return a < b ? -1 : (a > b ? 1 : 0); 480 }; 481 482 // Apply function f for each element of the array. Optionally call it 483 // in the context of obj object. If "f" is a string, then for each 484 // non-null array element call its "f" member function. 485 AjxVector.prototype.foreach = function(f, obj) { 486 var l = this.size(), i = 0, el; 487 if (typeof f == "function") { 488 while (--l >= 0) 489 f.call(obj, this.get(i), i++); 490 } else { 491 while (--l >= 0) { 492 el = this.get(i++); 493 if (el != null) 494 el[f].call(el); // assuming function 495 } 496 } 497 }; 498 499 /** 500 * Return a new AjxVector which contains the results of calling f 501 * (optionally in the context obj) for each element of this array. 502 * <ul> 503 * <li>If "f" is a string, then for each element el: 504 * <ul> 505 * <li>if el[f] is a function, call el[f] and push the result in the returned array.</li> 506 * <li>otherwise push el[f]</li> 507 * </ul> 508 * </li> 509 * </ul> 510 * 511 * @param {function} f the function 512 * @param {Object} obj the obj context 513 * @return {AjxVector} the resulting vector 514 */ 515 AjxVector.prototype.map = function(f, obj) { 516 var a = [], i = this.size(), el; 517 if (typeof f == "function") { 518 while (--i >= 0) 519 a[i] = f.call(obj, this.get(i), i); 520 } else if (f instanceof AjxCallback) { 521 while (--i >= 0) 522 a[i] = f.run(this.get(i), i); 523 } else { 524 while (--i >= 0) { 525 el = this.get(i); 526 if (el != null) { 527 if (typeof el[f] == "function") 528 a.unshift(el[f].call(el)); 529 else 530 a.unshift(el[f]); 531 } 532 } 533 } 534 return AjxVector.fromArray(a); 535 }; 536 537 /** 538 * Returns an AjxVector with all the members of the given array for which the 539 * filtering function returns true. 540 * 541 * @param {Function} func filtering function 542 * @param {Object} context scope for filtering function 543 * 544 * @returns {Array} array of members for which the filtering function returns true 545 */ 546 AjxVector.prototype.filter = function(func, context) { 547 return AjxVector.fromArray(AjxUtil.filter(this._array, func, context)); 548 }; 549 550 /** 551 * Joins the vector. 552 * 553 * @param {string} sep the string separator 554 * @return {string} a string representation of the vector 555 */ 556 AjxVector.prototype.join = function(sep) { 557 return this._array.join(sep); 558 }; 559 560 /** 561 * Return true if the given function returns true for a member of this vector, 562 * otherwise false. 563 * 564 * @param {function} f the function 565 * @param {Object} obj the obj context 566 */ 567 AjxVector.prototype.some = 568 function(f, obj) { 569 return this._array.some(f, obj); 570 }; 571 572 573 /** 574 * Return a new AjxVector containing the elements from this vector 575 * except those for which f(el) returns true. Otherwise said, 576 * "SUBtracts" from this vector those elements for which f(el) returns true. 577 * 578 * @param {function} f the function 579 * @param {Object} obj the obj context 580 * @return {AjxVector} the resulting vector 581 */ 582 AjxVector.prototype.sub = function(f, obj) { 583 var a = [], l = this.size(), i = 0, el; 584 while (--l >= 0) { 585 el = this.get(i++); 586 if (!f.call(obj, el, i)) 587 a.push(el); 588 } 589 return AjxVector.fromArray(a); 590 }; 591 592 AjxVector.prototype.slice = 593 function(start, end) { 594 return AjxVector.fromArray(this._array.slice(start, end)); 595 }; 596