selectionmodel.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Single-selection model implemenation.
  16. *
  17. * TODO(attila): Add keyboard & mouse event hooks?
  18. * TODO(attila): Add multiple selection?
  19. *
  20. * @author attila@google.com (Attila Bodis)
  21. */
  22. goog.provide('goog.ui.SelectionModel');
  23. goog.require('goog.array');
  24. goog.require('goog.events.EventTarget');
  25. goog.require('goog.events.EventType');
  26. /**
  27. * Single-selection model. Dispatches a {@link goog.events.EventType.SELECT}
  28. * event when a selection is made.
  29. * @param {Array<Object>=} opt_items Array of items; defaults to empty.
  30. * @extends {goog.events.EventTarget}
  31. * @constructor
  32. */
  33. goog.ui.SelectionModel = function(opt_items) {
  34. goog.events.EventTarget.call(this);
  35. /**
  36. * Array of items controlled by the selection model. If the items support
  37. * the {@code setSelected(Boolean)} interface, they will be (de)selected
  38. * as needed.
  39. * @type {!Array<Object>}
  40. * @private
  41. */
  42. this.items_ = [];
  43. this.addItems(opt_items);
  44. };
  45. goog.inherits(goog.ui.SelectionModel, goog.events.EventTarget);
  46. goog.tagUnsealableClass(goog.ui.SelectionModel);
  47. /**
  48. * The currently selected item (null if none).
  49. * @type {Object}
  50. * @private
  51. */
  52. goog.ui.SelectionModel.prototype.selectedItem_ = null;
  53. /**
  54. * Selection handler function. Called with two arguments (the item to be
  55. * selected or deselected, and a Boolean indicating whether the item is to
  56. * be selected or deselected).
  57. * @type {Function}
  58. * @private
  59. */
  60. goog.ui.SelectionModel.prototype.selectionHandler_ = null;
  61. /**
  62. * Returns the selection handler function used by the selection model to change
  63. * the internal selection state of items under its control.
  64. * @return {Function} Selection handler function (null if none).
  65. */
  66. goog.ui.SelectionModel.prototype.getSelectionHandler = function() {
  67. return this.selectionHandler_;
  68. };
  69. /**
  70. * Sets the selection handler function to be used by the selection model to
  71. * change the internal selection state of items under its control. The
  72. * function must take two arguments: an item and a Boolean to indicate whether
  73. * the item is to be selected or deselected. Selection handler functions are
  74. * only needed if the items in the selection model don't natively support the
  75. * {@code setSelected(Boolean)} interface.
  76. * @param {Function} handler Selection handler function.
  77. */
  78. goog.ui.SelectionModel.prototype.setSelectionHandler = function(handler) {
  79. this.selectionHandler_ = handler;
  80. };
  81. /**
  82. * Returns the number of items controlled by the selection model.
  83. * @return {number} Number of items.
  84. */
  85. goog.ui.SelectionModel.prototype.getItemCount = function() {
  86. return this.items_.length;
  87. };
  88. /**
  89. * Returns the 0-based index of the given item within the selection model, or
  90. * -1 if no such item is found.
  91. * @param {Object|undefined} item Item to look for.
  92. * @return {number} Index of the given item (-1 if none).
  93. */
  94. goog.ui.SelectionModel.prototype.indexOfItem = function(item) {
  95. return item ? goog.array.indexOf(this.items_, item) : -1;
  96. };
  97. /**
  98. * @return {Object|undefined} The first item, or undefined if there are no items
  99. * in the model.
  100. */
  101. goog.ui.SelectionModel.prototype.getFirst = function() {
  102. return this.items_[0];
  103. };
  104. /**
  105. * @return {Object|undefined} The last item, or undefined if there are no items
  106. * in the model.
  107. */
  108. goog.ui.SelectionModel.prototype.getLast = function() {
  109. return this.items_[this.items_.length - 1];
  110. };
  111. /**
  112. * Returns the item at the given 0-based index.
  113. * @param {number} index Index of the item to return.
  114. * @return {Object} Item at the given index (null if none).
  115. */
  116. goog.ui.SelectionModel.prototype.getItemAt = function(index) {
  117. return this.items_[index] || null;
  118. };
  119. /**
  120. * Bulk-adds items to the selection model. This is more efficient than calling
  121. * {@link #addItem} for each new item.
  122. * @param {Array<Object>|undefined} items New items to add.
  123. */
  124. goog.ui.SelectionModel.prototype.addItems = function(items) {
  125. if (items) {
  126. // New items shouldn't be selected.
  127. goog.array.forEach(
  128. items, function(item) { this.selectItem_(item, false); }, this);
  129. goog.array.extend(this.items_, items);
  130. }
  131. };
  132. /**
  133. * Adds an item at the end of the list.
  134. * @param {Object} item Item to add.
  135. */
  136. goog.ui.SelectionModel.prototype.addItem = function(item) {
  137. this.addItemAt(item, this.getItemCount());
  138. };
  139. /**
  140. * Adds an item at the given index.
  141. * @param {Object} item Item to add.
  142. * @param {number} index Index at which to add the new item.
  143. */
  144. goog.ui.SelectionModel.prototype.addItemAt = function(item, index) {
  145. if (item) {
  146. // New items must not be selected.
  147. this.selectItem_(item, false);
  148. goog.array.insertAt(this.items_, item, index);
  149. }
  150. };
  151. /**
  152. * Removes the given item (if it exists). Dispatches a {@code SELECT} event if
  153. * the removed item was the currently selected item.
  154. * @param {Object} item Item to remove.
  155. */
  156. goog.ui.SelectionModel.prototype.removeItem = function(item) {
  157. if (item && goog.array.remove(this.items_, item)) {
  158. if (item == this.selectedItem_) {
  159. this.selectedItem_ = null;
  160. this.dispatchEvent(goog.events.EventType.SELECT);
  161. }
  162. }
  163. };
  164. /**
  165. * Removes the item at the given index.
  166. * @param {number} index Index of the item to remove.
  167. */
  168. goog.ui.SelectionModel.prototype.removeItemAt = function(index) {
  169. this.removeItem(this.getItemAt(index));
  170. };
  171. /**
  172. * @return {Object} The currently selected item, or null if none.
  173. */
  174. goog.ui.SelectionModel.prototype.getSelectedItem = function() {
  175. return this.selectedItem_;
  176. };
  177. /**
  178. * @return {!Array<Object>} All items in the selection model.
  179. */
  180. goog.ui.SelectionModel.prototype.getItems = function() {
  181. return goog.array.clone(this.items_);
  182. };
  183. /**
  184. * Selects the given item, deselecting any previously selected item, and
  185. * dispatches a {@code SELECT} event.
  186. * @param {Object} item Item to select (null to clear the selection).
  187. */
  188. goog.ui.SelectionModel.prototype.setSelectedItem = function(item) {
  189. if (item != this.selectedItem_) {
  190. this.selectItem_(this.selectedItem_, false);
  191. this.selectedItem_ = item;
  192. this.selectItem_(item, true);
  193. }
  194. // Always dispatch a SELECT event; let listeners decide what to do if the
  195. // selected item hasn't changed.
  196. this.dispatchEvent(goog.events.EventType.SELECT);
  197. };
  198. /**
  199. * @return {number} The 0-based index of the currently selected item, or -1
  200. * if none.
  201. */
  202. goog.ui.SelectionModel.prototype.getSelectedIndex = function() {
  203. return this.indexOfItem(this.selectedItem_);
  204. };
  205. /**
  206. * Selects the item at the given index, deselecting any previously selected
  207. * item, and dispatches a {@code SELECT} event.
  208. * @param {number} index Index to select (-1 to clear the selection).
  209. */
  210. goog.ui.SelectionModel.prototype.setSelectedIndex = function(index) {
  211. this.setSelectedItem(this.getItemAt(index));
  212. };
  213. /**
  214. * Clears the selection model by removing all items from the selection.
  215. */
  216. goog.ui.SelectionModel.prototype.clear = function() {
  217. goog.array.clear(this.items_);
  218. this.selectedItem_ = null;
  219. };
  220. /** @override */
  221. goog.ui.SelectionModel.prototype.disposeInternal = function() {
  222. goog.ui.SelectionModel.superClass_.disposeInternal.call(this);
  223. delete this.items_;
  224. this.selectedItem_ = null;
  225. };
  226. /**
  227. * Private helper; selects or deselects the given item based on the value of
  228. * the {@code select} argument. If a selection handler has been registered
  229. * (via {@link #setSelectionHandler}, calls it to update the internal selection
  230. * state of the item. Otherwise, attempts to call {@code setSelected(Boolean)}
  231. * on the item itself, provided the object supports that interface.
  232. * @param {Object} item Item to select or deselect.
  233. * @param {boolean} select If true, the object will be selected; if false, it
  234. * will be deselected.
  235. * @private
  236. */
  237. goog.ui.SelectionModel.prototype.selectItem_ = function(item, select) {
  238. if (item) {
  239. if (typeof this.selectionHandler_ == 'function') {
  240. // Use the registered selection handler function.
  241. this.selectionHandler_(item, select);
  242. } else if (typeof item.setSelected == 'function') {
  243. // Call setSelected() on the item, if it supports it.
  244. item.setSelected(select);
  245. }
  246. }
  247. };