Reusable Accessible Mapping Platform

API Docs for: 5.0.0
Show:

File: src\js\RAMP\Utils\checkboxGroup.js

  1. /* global define, console, $ */
  2.  
  3. /**
  4. * @module Utils
  5. */
  6.  
  7. /**
  8. * Creates a Checkbox group bound from the supplied JArray of input nodes. Optionally, the group is bound to the supplied master input node
  9. * which acts as a global toggle for the group.
  10. *
  11. *
  12. * @class CheckboxGroup
  13. * @constructor
  14. * @uses dojo/Evented
  15. * @uses dojo/_base/declare
  16. * @uses dojo/_base/lang
  17. * @uses dojo/_base/array
  18. * @uses Checkbox
  19. *
  20. * @param {JArray} nodes a jQuery object representing the checkboxes to be grouped
  21. * @param {Object} [options] Additional options
  22. * @param {String} [options.nodeIdAttr] Name of the "data-*" attribute set on the checkbox node to be treated as the checkbox id. If no appropriate "data-*" attribute found,
  23. * `nodeIdAttr` is used directly, failing that, regular `id` is used.
  24. * @param {Object} [options.cssClass] `active`, `focus`, and `check` CSS class to be applied to the Checkbox correspondingly.
  25. * @param {Object} [options.cssClass.active] CSS class to be set when the Checkbox is `active`.
  26. * @param {Object} [options.cssClass.focus] CSS class to be set when the Checkbox is `focused`.
  27. * @param {Object} [options.cssClass.check] CSS class to be set when the Checkbox is `checked`.
  28. * @param {Object} [options.label] `check` and `uncheck` label texts to be applied to the Checkbox labels.
  29. * @param {Object} [options.label.check] A text to be set as a label when the Checkbox is `checked`
  30. * @param {Object} [options.label.uncheck] A text to be set as a label when the Checkbox is `unchecked`
  31. * @param {Function} [options.onChnage] A function to be called when the state of the Checkbox changes.
  32. * @param {Object} [options.master] Additional options applied to the master Checkbox.
  33. * @param {Object} [options.master.node] An `input` node to serve as the master Checkbox for the group.
  34. * @param {Object} [options.master.cssClass] `active`, `focus`, and `check` CSS class to be applied to the master Checkbox correspondingly.
  35. * @param {Object} [options.master.cssClass.active] CSS class to be set when the Checkbox is `active`.
  36. * @param {Object} [options.master.cssClass.focus] CSS class to be set when the master Checkbox is `focused`.
  37. * @param {Object} [options.master.cssClass.check] CSS class to be set when the master Checkbox is `checked`.
  38. * @param {Object} [options.master.label] `check` and `uncheck` label texts to be applied to the master Checkbox labels.
  39. * @param {Object} [options.master.label.check] A text to be set as a label when the master Checkbox is `checked`
  40. * @param {Object} [options.master.label.uncheck] A text to be set as a label when the master Checkbox is `unchecked`
  41. * @param {Function} [options.master.onChnage] A function to be called when the state of the master Checkbox changes.
  42. *
  43. * @return {CheckboxGroup} A control objects allowing to toggle individual checkboxes in a group as well as the group as a whole.
  44. */
  45.  
  46. define(["dojo/Evented", "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/array",
  47.  
  48. "utils/checkbox", "utils/array"],
  49. function (Evented, declare, lang, dojoArray,
  50. Checkbox, Array) {
  51. "use strict";
  52.  
  53. var CheckboxGroup;
  54.  
  55. CheckboxGroup = declare([Evented], {
  56. constructor: function (nodes, options) {
  57. var that = this,
  58. masterCheckboxOptions;
  59.  
  60. // declare individual properties inside the constructor: http://dojotoolkit.org/reference-guide/1.9/dojo/_base/declare.html#id6
  61. lang.mixin(this,
  62. {
  63. /**
  64. * Nodes of the checkbox nodes originally supplied to the CheckboxGroup.
  65. *
  66. * @property nodes
  67. * @type JArray
  68. * @default []
  69. */
  70. nodes: [],
  71.  
  72. /**
  73. * Name of the "data-*" attribute set on the checkbox node to be treated as the checkbox id.
  74. *
  75. * @property nodeIdAttr
  76. * @type String
  77. * @default "id"
  78. */
  79. nodeIdAttr: "id",
  80.  
  81. /**
  82. * An array of the Checkbox object belonging to the body of the group.
  83. *
  84. * @property nodes
  85. * @type Array
  86. * @default []
  87. */
  88. checkboxes: [],
  89.  
  90. /**
  91. * `active`, `focus`, and `check` CSS class to be applied to the Checkbox correspondingly.
  92. *
  93. * @property cssClass
  94. * @type {Object}
  95. * @default
  96. * @example
  97. * cssClass: {
  98. * active: "active",
  99. * focus: "focused",
  100. * check: "checked"
  101. * }
  102. */
  103. cssClass: {
  104. active: "active",
  105. focus: "focused",
  106. check: "checked"
  107. },
  108.  
  109. /**
  110. * `check` and `uncheck` label texts to be applied to the Checkbox labels.
  111. *
  112. * @property label
  113. * @type {Object}
  114. * @default
  115. * @example
  116. * label: {
  117. * check: "check",
  118. * uncheck: "unchecked"
  119. * }
  120. */
  121. label: {
  122. check: "checked",
  123. uncheck: "unchecked"
  124. },
  125.  
  126. /**
  127. * A function to be called when the state of the Checkbox changes.
  128. *
  129. * @property onChnage
  130. * @type Function
  131. * @default
  132. * @example function () { }
  133. */
  134. onChange: function () { },
  135.  
  136. /**
  137. * Options for the master Checkbox.
  138. *
  139. * @property master
  140. * @type Object
  141. * @default
  142. * @example
  143. * master: {
  144. * node: null,
  145. * checkbox: null,
  146. * nodeIdAttr: null,
  147. *
  148. * cssClass: {
  149. * active: "active",
  150. * focus: "focused",
  151. * check: "checked"
  152. * },
  153. *
  154. * label: {
  155. * check: "checked",
  156. * uncheck: "unchecked"
  157. * },
  158. *
  159. * onChange: function () { }
  160. * }
  161. *
  162. */
  163. master: {
  164. node: null,
  165. checkbox: null,
  166. nodeIdAttr: null,
  167.  
  168. cssClass: {
  169. active: "active",
  170. focus: "focused",
  171. check: "checked"
  172. },
  173. label: {
  174. check: "checked",
  175. uncheck: "unchecked"
  176. },
  177.  
  178. onChange: function () { }
  179. }
  180. },
  181. options,
  182. {
  183. nodes: nodes
  184. }
  185. );
  186. masterCheckboxOptions = {
  187. nodeIdAttr: this.nodeIdAttr,
  188. cssClass: this.cssClass,
  189. label: this.label,
  190. onChange: this.onChange
  191. };
  192. if (this.master.node) {
  193. this.master.checkbox = new Checkbox(
  194. this.master.node,
  195. lang.mixin(masterCheckboxOptions, this.master)
  196. );
  197.  
  198. this.master.checkbox.on(Checkbox.event.TOGGLE, function (evt) {
  199. // re-emit individual checkbox's toggle event as groups;
  200. console.log("CheckboxGroup Master ->", evt.checkbox.id, "set by", evt.agency, "to", evt.checkbox.state);
  201.  
  202. that.emit(CheckboxGroup.event.MASTER_TOGGLE, evt);
  203.  
  204. if (evt.agency === Checkbox.agency.USER) {
  205. that.setState(evt.checkbox.state);
  206. }
  207. });
  208. } else {
  209. this.master = null;
  210. }
  211.  
  212. this.addCheckbox(this.nodes);
  213. },
  214.  
  215. addCheckbox: function (nodes) {
  216. var that = this,
  217. checkbox,
  218. checkboxOptions = {
  219. nodeIdAttr: this.nodeIdAttr,
  220. cssClass: this.cssClass,
  221. label: this.label,
  222. onChange: this.onChange
  223. },
  224. cbIndex;
  225.  
  226. // Create individual Checkboxes
  227. nodes.each(function (index, node) {
  228. node = $(node);
  229.  
  230. cbIndex = Array.indexOf(that.checkboxes, function (cb) {
  231. return cb.node.is(node);
  232. });
  233.  
  234. if (cbIndex === -1) {
  235.  
  236. checkbox = new Checkbox(node, checkboxOptions);
  237. that.checkboxes.push(checkbox);
  238.  
  239. checkbox.on(Checkbox.event.TOGGLE, function (evt) {
  240. // re-emit individual checkbox's toggle event as groups;
  241. //console.log("CheckboxGroup ->", evt.checkbox.id, "set by", evt.agency, "to", evt.checkbox.state);
  242.  
  243. that.emit(CheckboxGroup.event.MEMBER_TOGGLE, evt);
  244.  
  245. if (evt.agency === Checkbox.agency.USER) {
  246. that._checkMaster();
  247. }
  248. });
  249. } else {
  250. // reset the checkbox, just in case
  251. that.checkboxes[cbIndex].reset();
  252. }
  253. });
  254.  
  255. this._checkMaster();
  256. },
  257.  
  258. /**
  259. * Synchronizes the state of the master Checkbox with the state of the group.
  260. * All group members checked -> master checked
  261. * Any of the group members unchecked -> master unchecked
  262. *
  263. * @method _checkMaster
  264. * @private
  265. */
  266. _checkMaster: function () {
  267. var allChecked;
  268.  
  269. allChecked = dojoArray.every(this.checkboxes, function (checkbox) {
  270. // "INVALID" state is parsed as "true" so it's counted in, but is ignored otherwise
  271. return checkbox
  272. .validate()
  273. .state;
  274. });
  275.  
  276. // set state to the master checkbox only if you have one
  277. if (this.master) {
  278. this.master.checkbox.setState(allChecked);
  279. }
  280. },
  281.  
  282. /**
  283. * Toggles the state of the specified Checkbox. If checkboxId is not supplied, toggles the whole group.
  284. *
  285. * @method setState
  286. * @param {Boolean} state Specifies the state of the checkbox: true, false
  287. * @param {String} [checkboxId] Specifies the checkbox to toggle.
  288. * @return CheckboxGroup
  289. * @chainable
  290. */
  291. setState: function (state, checkboxId) {
  292. var checkbox,
  293. masterCheckboxId = this.master.checkbox ? this.master.checkbox.id : undefined;
  294.  
  295. if (!checkboxId || masterCheckboxId === checkboxId) {
  296. this.master.checkbox.setState(state);
  297.  
  298. this.checkboxes.forEach(function (checkbox) {
  299. checkbox.setState(state);
  300. });
  301. } else {
  302. // use for loop because you can't break out of forEach loop
  303. for (var i = 0; i < this.checkboxes.length; i++) {
  304. checkbox = this.checkboxes[i];
  305. if (checkbox.id === checkboxId) {
  306. break;
  307. }
  308. }
  309.  
  310. checkbox.setState(state);
  311.  
  312. this._checkMaster();
  313. }
  314.  
  315. return this;
  316. },
  317.  
  318. /**
  319. * Toggle all the checkboxes based on the return value of the given function.
  320. *
  321. * @param {Function} fcn a function that takes a checkbox as an argument and returns
  322. * true if the given checkbox should be toggled on, false if it should be toggled off
  323. * @method setEachState
  324. * @chainable
  325. */
  326. setEachState: function (fcn) {
  327. this.checkboxes.forEach(function (checkbox) {
  328. checkbox.setState(fcn(checkbox));
  329. });
  330. this._checkMaster();
  331. return this;
  332. },
  333.  
  334. _purgeInvalid: function () {
  335. var i,
  336. checkbox;
  337.  
  338. for (i = this.checkboxes.length - 1; i >= 0; i--) {
  339. checkbox = this.checkboxes[i];
  340. if (checkbox.state === Checkbox.state.INVALID) {
  341. Array.remove(this.checkboxes, i);
  342. }
  343. }
  344. }
  345. });
  346.  
  347. lang.mixin(CheckboxGroup,
  348. {
  349. /**
  350. * Event names published by the Checkbox
  351. *
  352. * @private
  353. * @property event
  354. * @type Object
  355. * @default null
  356. * @example
  357. * {
  358. * MEMBER_TOGGLE: "checkbox/toggled",
  359. * MASTER_TOGGLE: "checkbox/toggled"
  360. * }
  361. */
  362. event: {
  363. /**
  364. * This event is not published by CheckboxGroup. __Ignore this.__
  365. *
  366. * @event TOGGLE
  367. * @private
  368. */
  369.  
  370. /**
  371. * Published whenever a Checkbox get toggled.
  372. *
  373. * @event MEMBER_TOGGLE
  374. * @param event {Object}
  375. * @param event.checkbox {Checkbox} Checkbox object that has been toggled
  376. * @param event.agency {String} Agency that toggled the Checkbox
  377. */
  378. MEMBER_TOGGLE: "checkbox/member-toggle",
  379.  
  380. /**
  381. * Published whenever the master Checkbox get toggled.
  382. *
  383. * @event MASTER_TOGGLE
  384. * @param event {Object}
  385. * @param event.checkbox {Checkbox} master Checkbox object that has been toggled
  386. * @param event.agency {String} Agency that toggled the Checkbox
  387. */
  388. MASTER_TOGGLE: "checkbox/master-toggle"
  389. }
  390. }
  391. );
  392.  
  393. return CheckboxGroup;
  394. });