collection.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. module.metadata = {
  6. "stability": "experimental"
  7. };
  8. exports.Collection = Collection;
  9. /**
  10. * Adds a collection property to the given object. Setting the property to a
  11. * scalar value empties the collection and adds the value. Setting it to an
  12. * array empties the collection and adds all the items in the array.
  13. *
  14. * @param obj
  15. * The property will be defined on this object.
  16. * @param propName
  17. * The name of the property.
  18. * @param array
  19. * If given, this will be used as the collection's backing array.
  20. */
  21. exports.addCollectionProperty = function addCollProperty(obj, propName, array) {
  22. array = array || [];
  23. let publicIface = new Collection(array);
  24. Object.defineProperty(obj, propName, {
  25. configurable: true,
  26. enumerable: true,
  27. set: function set(itemOrItems) {
  28. array.splice(0, array.length);
  29. publicIface.add(itemOrItems);
  30. },
  31. get: function get() {
  32. return publicIface;
  33. }
  34. });
  35. };
  36. /**
  37. * A collection is ordered, like an array, but its items are unique, like a set.
  38. *
  39. * @param array
  40. * The collection is backed by an array. If this is given, it will be
  41. * used as the backing array. This way the caller can fully control the
  42. * collection. Otherwise a new empty array will be used, and no one but
  43. * the collection will have access to it.
  44. */
  45. function Collection(array) {
  46. array = array || [];
  47. /**
  48. * Provides iteration over the collection. Items are yielded in the order
  49. * they were added.
  50. */
  51. this.__iterator__ = function Collection___iterator__() {
  52. let items = array.slice();
  53. for (let i = 0; i < items.length; i++)
  54. yield items[i];
  55. };
  56. /**
  57. * The number of items in the collection.
  58. */
  59. this.__defineGetter__("length", function Collection_get_length() {
  60. return array.length;
  61. });
  62. /**
  63. * Adds a single item or an array of items to the collection. Any items
  64. * already contained in the collection are ignored.
  65. *
  66. * @param itemOrItems
  67. * An item or array of items.
  68. * @return The collection.
  69. */
  70. this.add = function Collection_add(itemOrItems) {
  71. let items = toArray(itemOrItems);
  72. for (let i = 0; i < items.length; i++) {
  73. let item = items[i];
  74. if (array.indexOf(item) < 0)
  75. array.push(item);
  76. }
  77. return this;
  78. };
  79. /**
  80. * Removes a single item or an array of items from the collection. Any items
  81. * not contained in the collection are ignored.
  82. *
  83. * @param itemOrItems
  84. * An item or array of items.
  85. * @return The collection.
  86. */
  87. this.remove = function Collection_remove(itemOrItems) {
  88. let items = toArray(itemOrItems);
  89. for (let i = 0; i < items.length; i++) {
  90. let idx = array.indexOf(items[i]);
  91. if (idx >= 0)
  92. array.splice(idx, 1);
  93. }
  94. return this;
  95. };
  96. };
  97. function toArray(itemOrItems) {
  98. let isArr = itemOrItems &&
  99. itemOrItems.constructor &&
  100. itemOrItems.constructor.name === "Array";
  101. return isArr ? itemOrItems : [itemOrItems];
  102. }