CMultiSelect.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /*##############################################################################
  2. # HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. ############################################################################## */
  16. /* This file defines CMultiSelect class, which is used to manage multiple selection
  17. across a column of a given html table. A web page can instantiate more than one such
  18. objects to support multiple selection over multiple columns (individually). This
  19. class should not be instantiated directly but rather via the factory method
  20. "createMultiSelect" which is defined at the bottom of the file. By default,
  21. checkboxes are managed but conceptually different html objects (for instance,
  22. drop down lists, edit boxes, etc.) can be managed instead in future.
  23. If the callback methods onColumnSelChanged and onItemSelChanged are specified then
  24. they must be implemented by the html page instantiating this CMultiSelect object.
  25. function onColumnSelChanged(msid) {...}
  26. This callback gets invoked when selection status across all rows changes (i.e. at
  27. least one row is selected or unselected).
  28. function onItemSelChanged(o, msid) {...}
  29. This callback is invoked for every single html object o (usually a checkbox)
  30. that is being managed by the multiselect object when its selection changes.
  31. */
  32. function CMultiSelect( id,
  33. tableId,
  34. onColumnSelChanged/*=null*/,
  35. onItemSelChanged/*=null*/,
  36. columnToSort/*=0*/,
  37. inputTag/*='INPUT'*/,
  38. inputType/*='checkbox'*/,
  39. bRowsAreTables/*=false*/)
  40. {
  41. //save parameters as instance data
  42. //
  43. this.b_initialized = false;
  44. this.id = id;
  45. this.tableId = tableId;
  46. this.o_table = null; //to be initialized
  47. this.o_htable= null; //to be initialized
  48. this.n_column = typeof columnToSort == 'undefined' ? 0 : columnToSort;
  49. this.onColumnSelChanged = onColumnSelChanged ? onColumnSelChanged : null;
  50. this.onItemSelChanged = onItemSelChanged ? onItemSelChanged : null;
  51. this.inputTag = inputTag ? inputTag : 'INPUT';
  52. this.inputType = inputType ? inputType : 'checkbox';
  53. this.b_rowsAreTables = typeof bRowsAreTables == 'undefined' ? false : bRowsAreTables;
  54. this.b_singleSelect = false;
  55. // define ids used by multiselect controls (usually checkboxes) as tableId_[x]_[y]
  56. // where x is id of this multiselect and y is index of multiselect cell
  57. // (for instance, checkboxes at top and bottom of this table
  58. //
  59. this.selectAllIdPrefixForTable = tableId + '_ms';
  60. // define ids used by multiselect controls (usually checkboxes) as tableId_[x]_[y]
  61. // where x is id of this multiselect and y is index of multiselect cell
  62. // (for instance, checkboxes at top and bottom of this table
  63. //
  64. this.selectAllIdPrefix = this.selectAllIdPrefixForTable + this.n_column + '_';
  65. //bind class methods to this object
  66. //
  67. this.initialize = initialize;
  68. this.getManagedCell = getManagedCell;
  69. this._determineNewSetAllValue = _determineNewSetAllValue;
  70. this.setAll = setAll;
  71. this.getSelectionCount = getSelectionCount;
  72. this.makeSetAllId = makeSetAllId;
  73. this._setRange = _setRange;
  74. this._setSelectAllControls = _setSelectAllControls;
  75. this._onChange = _onChange;
  76. this._getValue = _getValue;
  77. this._setValue = _setValue;
  78. this._updateSelectionCount = _updateSelectionCount;
  79. }
  80. /*------------------------------------------------------------------------------*/
  81. /* PUBLIC METHODS */
  82. /*------------------------------------------------------------------------------*/
  83. //public:
  84. /* define the "constructor"
  85. function initialize()
  86. */
  87. function initialize()
  88. {
  89. this.b_checkbox = this.inputTag == 'INPUT' && this.inputType == 'checkbox';
  90. if (this.b_singleSelect && !this.b_checkbox)
  91. {
  92. alert("Single select is only supported for checkboxes!");
  93. debugger;
  94. }
  95. this.o_table = document.getElementById(this.tableId);
  96. if (!this.o_table)//it is possible that the table was nested within another table's row and got deleted
  97. return;
  98. //this table may be split with its header (separate table w/ just header)
  99. //in one div and body (separate table w/ just body) in another div
  100. //in order to keep its header fixed while the body is scolled up/down
  101. //
  102. this.o_htable = document.getElementById('H.' + this.tableId);
  103. this.lastValueSet = undefined;
  104. this.n_lastRowClicked= -1;
  105. this.inputTag = this.inputTag.toUpperCase();
  106. //updates this.n_selected, this.n_totalItems and this.setAllValue
  107. //
  108. this.n_selected = 0;
  109. this.n_totalItems= 0;
  110. this.setAllValue = undefined;
  111. this.setAllSet = false;
  112. var numRows = this.o_table.rows.length;
  113. var firstRowIndex = this.o_htable ? 0 : 1;
  114. for (var r=firstRowIndex; r<numRows; r++)
  115. {
  116. var row = this.o_table.rows[r];
  117. var numCells = row.cells.length;
  118. var cell = this.getManagedCell(row);
  119. if (cell && cell.childNodes.length > 0)
  120. {
  121. var o = cell.childNodes[0];
  122. if (o.tagName==this.inputTag && o.type==this.inputType && !o.disabled &&
  123. (!o.id || o.id.indexOf(this.selectAllIdPrefixForTable)== -1))
  124. {
  125. this.n_totalItems++;
  126. var val = this._getValue(o);
  127. if (val != undefined)
  128. {
  129. this.n_lastRowClicked = r;
  130. this.lastValueSet = val;
  131. if (this.b_checkbox)
  132. {
  133. if (val)
  134. this.n_selected++;
  135. }
  136. else
  137. {
  138. if (r == 1) {
  139. this.setAllValue = val;
  140. this.setAllSet = true;
  141. }
  142. else
  143. if (this.setAllValue != val) {
  144. this.setAllSet = false;
  145. this.setAllValue = undefined;
  146. }
  147. }
  148. }
  149. }
  150. }
  151. }//for
  152. if (this.b_checkbox) {
  153. this.setAllValue = this.n_totalItems && (this.n_selected == this.n_totalItems);
  154. this.setAllSet = true;
  155. }
  156. this._setSelectAllControls(this.setAllValue);
  157. this.b_initialized = true;
  158. if (this.onColumnSelChanged)
  159. this.onColumnSelChanged( this.id );
  160. }
  161. function getManagedCell(row)
  162. {
  163. if (this.b_rowsAreTables)
  164. {
  165. var cells = row.cells;
  166. if (cells.length > 0)
  167. {
  168. var childNodes = cells[0].childNodes;
  169. var table = childNodes.length > 0 && childNodes[0].tagName == 'TABLE' ? childNodes[0] : null;
  170. if (table)
  171. {
  172. var rows = table.rows;
  173. if (rows.length > 0)
  174. managedRow = rows[0];
  175. }
  176. }
  177. }
  178. else
  179. managedRow = row;
  180. var found = null;
  181. if (managedRow)
  182. {
  183. var cells = managedRow.cells;
  184. if (this.n_column < cells.length)
  185. found = cells[ this.n_column ];
  186. }
  187. return found;
  188. }
  189. /*
  190. function _getNewValueForSetAll()
  191. */
  192. function _determineNewSetAllValue(newValue)
  193. {
  194. if (this.b_checkbox)
  195. newValue = this.n_selected == this.n_totalItems;
  196. else
  197. if (this.n_totalItems > 0)
  198. {
  199. //return value of controls in the column if all cells in that column
  200. //have same value, else return empty string
  201. var rows = this.o_table.rows;
  202. var nRows = rows.length;
  203. var firstRowIndex = this.o_htable ? 0 : 1;
  204. for (var i=firstRowIndex; i<nRows; i++)
  205. {
  206. var cell = this.getManagedCell(rows[i]);
  207. if (cell)
  208. {
  209. var input = cell.firstChild;
  210. if (input && !input.disabled)
  211. {
  212. var val = this._getValue(input);
  213. if (i == 1 && newValue == undefined)
  214. newValue = val;
  215. else
  216. if (newValue != val)
  217. {
  218. newValue = undefined;
  219. break;
  220. }
  221. }
  222. }
  223. }
  224. }
  225. return newValue;
  226. }
  227. /*
  228. function setAll(o)
  229. */
  230. function setAll(o, notify)
  231. {
  232. newValue = this._getValue(o);
  233. var firstRowIndex = this.o_htable ? 0 : 1;
  234. this._setRange(newValue, firstRowIndex, this.o_table.rows.length-1);//row 0 is heading
  235. if (this.b_checkbox)
  236. this.n_selected = newValue ? this.n_totalItems : 0;
  237. if (newValue != this.setAllValue)
  238. this._setSelectAllControls(newValue);
  239. this.setAllSet = true;
  240. if (this.onColumnSelChanged && (typeof notify == 'undefined' || notify)) {
  241. this.onColumnSelChanged( this.id );
  242. }
  243. this.setAllSet = false;
  244. }
  245. /*
  246. function getSelectionCount()
  247. */
  248. function getSelectionCount()
  249. {
  250. return this.b_checkbox ? this.n_selected : undefined;
  251. }
  252. /*
  253. function makeSetAllId(topOfTable)
  254. */
  255. function makeSetAllId(topOfTable)
  256. {
  257. return this.selectAllIdPrefix + (topOfTable ? 'T':'B');
  258. }
  259. /*------------------------------------------------------------------------------*/
  260. /* PRIVATE METHODS */
  261. /*------------------------------------------------------------------------------*/
  262. //private:
  263. //associate the methods with this class
  264. //
  265. /*
  266. function _setRange(newValue, from, to)
  267. */
  268. function _setRange(newValue, from, to)
  269. {
  270. var selectAllIdPrefixForAnyColumn = this.o_table.id + '_ms';
  271. for (var r=from; r<=to; r++)
  272. {
  273. var row = this.o_table.rows[r];
  274. var numCells = row.cells.length;
  275. var cell = this.getManagedCell(row);
  276. if (cell.childNodes.length > 0)
  277. {
  278. //is the first child of this cell a checkbox (not the select all checkbox)
  279. //which has different check state than intended?
  280. //
  281. var o = cell.childNodes[0];
  282. if (o.tagName==this.inputTag && o.type==this.inputType && !o.disabled &&
  283. (!o.id || o.id.indexOf(selectAllIdPrefixForAnyColumn) == -1) && this._getValue(o) != newValue)
  284. {
  285. this._setValue(o, newValue);
  286. this._updateSelectionCount(newValue);
  287. if (this.onItemSelChanged)
  288. this.onItemSelChanged(o, this.id);
  289. }
  290. }
  291. }
  292. }
  293. /*
  294. function _setSelectAllControls(val)
  295. */
  296. function _setSelectAllControls(val)
  297. {
  298. for (var i=0; i<2; i++)//top and bottom
  299. {
  300. selectAllCell = document.getElementById( this.makeSetAllId(i==0) );
  301. if (selectAllCell)
  302. {
  303. var inputs = selectAllCell.getElementsByTagName(this.inputTag);
  304. if (inputs.length > 0)
  305. {
  306. var input = inputs[0];
  307. this._setValue(input, val);
  308. if (input.disabled)
  309. {
  310. if (this.n_totalItems > 0)
  311. input.disabled = false;
  312. }
  313. else
  314. if (this.n_totalItems == 0)
  315. input.disabled = true;
  316. }
  317. }
  318. }
  319. this.setAllValue = val;
  320. this.setAllSet = false;
  321. }
  322. /*
  323. function _onChange(o)
  324. */
  325. function _onChange(o)
  326. {
  327. if (this.b_singleSelect && this._getValue(o)) //note that this.b_checkbox is implied
  328. {
  329. this._setValue(o, false);
  330. this.setAll(o, false);
  331. this._setValue(o, true);
  332. }
  333. var cell = o.parentNode;
  334. var row = cell.parentNode;
  335. var rowNum = row.rowIndex;
  336. if (!this.b_singleSelect && window.event && window.event.shiftKey &&
  337. this.n_lastRowClicked != -1 && this.n_lastRowClicked != rowNum)
  338. {
  339. rc = this.lastValueSet == this._getValue(o);
  340. if (this.n_lastRowClicked < rowNum)
  341. this._setRange(this.lastValueSet, this.n_lastRowClicked, rowNum);
  342. else
  343. this._setRange(this.lastValueSet, rowNum, this.n_lastRowClicked);
  344. }
  345. else
  346. {
  347. this.n_lastRowClicked = rowNum;
  348. this.lastValueSet = this._getValue(o);
  349. rc = true;
  350. }
  351. val = this._getValue(o);
  352. if (rc)
  353. {
  354. this._updateSelectionCount( val );
  355. var newSetAllValue = this._determineNewSetAllValue(val);
  356. if (newSetAllValue != this.setAllValue)
  357. this._setSelectAllControls(newSetAllValue);
  358. }
  359. else
  360. {
  361. //the window has already changed selection for the checkbox which
  362. //should not have been done since shift key was pressed
  363. if (this.b_checkbox)
  364. this._updateSelectionCount(! val ); //compensation
  365. }
  366. if (this.onColumnSelChanged)
  367. this.onColumnSelChanged(this.id);
  368. return rc;
  369. }
  370. /*
  371. function _getValue(o)
  372. */
  373. function _getValue(o)
  374. {
  375. if (this.b_checkbox)
  376. return o.checked;
  377. else
  378. if (this.inputTag == 'SELECT')
  379. return o.selectedIndex;
  380. else
  381. return o.value;
  382. }
  383. /*
  384. function _setValue(o, val)
  385. */
  386. function _setValue(o, val)
  387. {
  388. if (!o.disabled)
  389. {
  390. if (this.b_checkbox)
  391. o.checked = val == undefined ? false : val;
  392. else if (this.inputTag == 'SELECT')
  393. o.selectedIndex = val == undefined ? -1 : val;
  394. else
  395. o.value = val == undefined ? '' : val;
  396. }
  397. }
  398. /*
  399. function _updateSelectionCount(select)
  400. */
  401. function _updateSelectionCount(select)
  402. {
  403. if (this.b_checkbox)
  404. {
  405. if (select)
  406. this.n_selected++;
  407. else
  408. this.n_selected--;
  409. }
  410. }
  411. /*------------------------------------------------------------------------------*/
  412. /* GLOBAL METHODS */
  413. /*------------------------------------------------------------------------------*/
  414. var a_multiselect = [];
  415. /* instantiates a new CMultiSelect object. The caller must initialize it using its
  416. initialize() method appropriately (for instance, after populating table rows).
  417. */
  418. function ms_create( tableId,
  419. onColumnSelChanged/*=null*/,
  420. onItemSelChanged/*=null*/,
  421. columnToSort/*=0*/,
  422. inputTag/*='INPUT'*/,
  423. inputType/*='checkbox'*/,
  424. bRowsAreTables/*=false*/)
  425. {
  426. //add this class instance to the array of multiselects that is used to
  427. //work with multiple such objects in the same page
  428. //
  429. var id = a_multiselect.length;
  430. var ms = new CMultiSelect( id, tableId, onColumnSelChanged,
  431. onItemSelChanged, columnToSort, inputTag, inputType,
  432. bRowsAreTables);
  433. a_multiselect[ id ] = ms;
  434. return ms;
  435. }
  436. function ms_lookup(msid/*=0*/)
  437. {
  438. if (msid == undefined)
  439. msid = 0;
  440. return (msid >= 0 && msid < a_multiselect.length) ? a_multiselect[ msid ] : null;
  441. }
  442. function ms_lookupByTableAndColumn(tableId, columnNum/*=0*/)
  443. {
  444. if (columnNum == undefined)
  445. columnNum = 0;
  446. var count = a_multiselect.length;
  447. for (var i=0; i<count; i++)
  448. {
  449. ms = a_multiselect[i];
  450. if (ms.tableId == tableId && ms.n_column == columnNum)
  451. return ms;
  452. }
  453. return null;
  454. }
  455. /*public*/
  456. function ms_onChange(o, tableId, columnNum/*=0*/)
  457. {
  458. o_ms = ms_lookupByTableAndColumn(tableId, columnNum);
  459. o_ms._onChange(o);
  460. }
  461. /*public*/
  462. function ms_setAll(o, tableId, columnNum/*=0*/)
  463. {
  464. o_ms = ms_lookupByTableAndColumn(tableId, columnNum);
  465. o_ms.setAll(o);
  466. }
  467. /*public*/
  468. function ms_getSelectionCount(msid/*=0*/)
  469. {
  470. o_ms = ms_lookup(msid);
  471. return o_ms.getSelectionCount();
  472. }
  473. /*public*/
  474. function ms_initialize(startColumn/*=0*/, endColumn/*=a_multiselect.length*/)
  475. {
  476. var len = a_multiselect.length;
  477. if (typeof startColumn == 'undefined' || startColumn < 0)
  478. startColumn = 0;
  479. if (typeof endColumn == 'undefined' || endColumn >= len)
  480. endColumn = a_multiselect.length-1;
  481. for (var i=startColumn; i<=endColumn; i++)
  482. a_multiselect[i].initialize();
  483. }
  484. /*public*/
  485. function ms_initializeAll()
  486. {
  487. var len = a_multiselect.length;
  488. for (var i=0; i<len; i++)
  489. a_multiselect[i].initialize();
  490. }
  491. /*public*/
  492. function ms_reinitializeAll()
  493. {
  494. var len = a_multiselect.length;
  495. for (var i=0; i<len; i++)
  496. {
  497. ms = a_multiselect[i];
  498. if (ms.b_initialized)
  499. ms.initialize();
  500. }
  501. }