namedcollection.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. /**
  7. * Named collections are key => model maps.
  8. *
  9. * See also {@link core/Collection}.
  10. *
  11. * @class NamedCollection
  12. * @mixins EventEmitter
  13. */
  14. CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], function( EmitterMixin, CKEditorError, utils ) {
  15. class NamedCollection {
  16. /**
  17. * Creates a new NamedCollection instance.
  18. *
  19. * @constructor
  20. */
  21. constructor() {
  22. /**
  23. * The internal map of models in the collection.
  24. *
  25. * @property _models
  26. * @private
  27. */
  28. this._models = new Map();
  29. }
  30. /**
  31. * The number of items available in the collection.
  32. *
  33. * @property length
  34. */
  35. get length() {
  36. return this._models.size;
  37. }
  38. /**
  39. * Adds an item into the collection.
  40. *
  41. * Throws exception if an item with this name already exists or if the item does not have a name.
  42. *
  43. * @param {Model} model The item to be added.
  44. */
  45. add( model ) {
  46. var name = model.name;
  47. if ( !name || this._models.has( name ) ) {
  48. /**
  49. * Model isn't named or such model already exists in this collection
  50. *
  51. * Thrown when:
  52. *
  53. * * Model without a name was given.
  54. * * Model with this name already exists in the collection.
  55. *
  56. * @error namedcollection-add
  57. * @param {String} name Name of the model.
  58. */
  59. throw new CKEditorError(
  60. 'namedcollection-add: Model isn\'t named or such model already exists in this collection',
  61. { name: name }
  62. );
  63. }
  64. this._models.set( name, model );
  65. this.fire( 'add', model );
  66. }
  67. /**
  68. * Gets one item from the collection.
  69. *
  70. * @param {String} name The name of the item to take.
  71. * @returns {Model} The requested item or `null` if such item does not exist.
  72. */
  73. get( name ) {
  74. return this._models.get( name ) || null;
  75. }
  76. /**
  77. * Removes an item from the collection.
  78. *
  79. * @param {Model|String} modelOrName Either the item itself (it must have a `name` property)
  80. * or its name inside the collection.
  81. * @returns {Model} The removed item.
  82. */
  83. remove( modelOrName ) {
  84. var nameGiven = typeof modelOrName == 'string';
  85. var name = nameGiven ? modelOrName : modelOrName.name;
  86. var model = this._models.get( name );
  87. if ( nameGiven ? !model : ( model !== modelOrName ) ) {
  88. /**
  89. * Model not found or other model exists under its name.
  90. *
  91. * Thrown when:
  92. *
  93. * * a model without a name was given,
  94. * * no model found when a name was given,
  95. * * a model was given and it does not exist in the collection or some other model was found under its name.
  96. *
  97. * @error namedcollection-remove
  98. * @param {String} name Name of the model to remove.
  99. * @param {Model} model The model which was found under the given name.
  100. */
  101. throw new CKEditorError(
  102. 'namedcollection-remove: Model not found or other model exists under its name.',
  103. { name: name, model: model }
  104. );
  105. }
  106. this._models.delete( name );
  107. this.fire( 'remove', model );
  108. return model;
  109. }
  110. /**
  111. * Executes the callback for each model in the collection.
  112. *
  113. * @param {Function} callback
  114. * @param {Model} callback.item
  115. * @param {String} callback.index
  116. * @params {Object} ctx Context in which the `callback` will be called.
  117. */
  118. forEach( callback, ctx ) {
  119. this._models.forEach( callback, ctx );
  120. }
  121. /**
  122. * Finds the first item in the collection for which the `callback` returns a true value.
  123. *
  124. * @param {Function} callback
  125. * @param {Model} callback.item
  126. * @param {String} callback.name
  127. * @params {Object} ctx Context in which the `callback` will be called.
  128. * @returns {Model} The first item for which `callback` returned a true value.
  129. */
  130. find( callback, ctx ) {
  131. // TODO: Use ES6 destructuring.
  132. for ( let pair of this._models ) {
  133. if ( callback.call( ctx, pair[ 1 ], pair[ 0 ] ) ) {
  134. return pair[ 1 ];
  135. }
  136. }
  137. }
  138. /**
  139. * Returns an object (`name => item`) with items for which the `callback` returned a true value.
  140. *
  141. * @param {Function} callback
  142. * @param {Model} callback.item
  143. * @param {String} callback.name
  144. * @params {Object} ctx Context in which the `callback` will be called.
  145. * @returns {Object} The object with matching items.
  146. */
  147. filter( callback, ctx ) {
  148. var ret = {};
  149. // TODO: Use ES6 destructuring.
  150. for ( let pair of this._models ) {
  151. if ( callback.call( ctx, pair[ 1 ], pair[ 0 ] ) ) {
  152. ret[ pair[ 0 ] ] = pair[ 1 ];
  153. }
  154. }
  155. return ret;
  156. }
  157. }
  158. utils.extend( NamedCollection.prototype, EmitterMixin );
  159. return NamedCollection;
  160. } );
  161. /**
  162. * Fired when an item is added to the collection.
  163. *
  164. * @event add
  165. * @param {Model} model The added item.
  166. */
  167. /**
  168. * Fired when an item is removed from the collection.
  169. *
  170. * @event remove
  171. * @param {Model} model The removed item.
  172. */