selector.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. define(["dojo/_base/kernel", "dojo/_base/array", "dojo/on", "dojo/aspect", "dojo/_base/sniff", "put-selector/put"],
  2. function(kernel, arrayUtil, on, aspect, has, put){
  3. return function(column, type){
  4. var listeners = [],
  5. grid, headerCheckbox;
  6. if(!column){ column = {}; }
  7. if(column.type){
  8. column.selectorType = column.type;
  9. kernel.deprecated("columndef.type", "use columndef.selectorType instead", "dgrid 0.4");
  10. }
  11. // accept type as argument to Selector function, or from column def
  12. column.selectorType = type = type || column.selectorType || "checkbox";
  13. column.sortable = false;
  14. function disabled(item) {
  15. return !grid.allowSelect(grid.row(item));
  16. }
  17. function changeInput(value){
  18. // creates a function that modifies the input on an event
  19. return function(event){
  20. var rows = event.rows,
  21. len = rows.length,
  22. state = "false",
  23. selection, mixed, i;
  24. for(i = 0; i < len; i++){
  25. var element = grid.cell(rows[i], column.id).element;
  26. if(!element){ continue; } // skip if row has been entirely removed
  27. element = (element.contents || element).input;
  28. if(element && !element.disabled){
  29. // only change the value if it is not disabled
  30. element.checked = value;
  31. element.setAttribute("aria-checked", value);
  32. }
  33. }
  34. if(headerCheckbox.type == "checkbox"){
  35. selection = grid.selection;
  36. mixed = false;
  37. // see if the header checkbox needs to be indeterminate
  38. for(i in selection){
  39. // if there is anything in the selection, than it is indeterminate
  40. if(selection[i] != grid.allSelected){
  41. mixed = true;
  42. break;
  43. }
  44. }
  45. headerCheckbox.indeterminate = mixed;
  46. headerCheckbox.checked = grid.allSelected;
  47. if (mixed) {
  48. state = "mixed";
  49. } else if (grid.allSelected) {
  50. state = "true";
  51. }
  52. headerCheckbox.setAttribute("aria-checked", state);
  53. }
  54. };
  55. }
  56. function onSelect(event){
  57. // we would really only care about click, since other input sources, like spacebar
  58. // trigger a click, but the click event doesn't provide access to the shift key in firefox, so
  59. // listen for keydown's as well to get an event in firefox that we can properly retrieve
  60. // the shiftKey property from
  61. if(event.type == "click" || event.keyCode == 32 || (!has("opera") && event.keyCode == 13) || event.keyCode === 0){
  62. var row = grid.row(event),
  63. lastRow = grid._lastSelected && grid.row(grid._lastSelected);
  64. grid._selectionTriggerEvent = event;
  65. if(type == "radio"){
  66. if(!lastRow || lastRow.id != row.id){
  67. grid.clearSelection();
  68. grid.select(row, null, true);
  69. grid._lastSelected = row.element;
  70. }
  71. }else{
  72. if(row){
  73. if(event.shiftKey){
  74. // make sure the last input always ends up checked for shift key
  75. changeInput(true)({rows: [row]});
  76. }else{
  77. // no shift key, so no range selection
  78. lastRow = null;
  79. }
  80. lastRow = event.shiftKey ? lastRow : null;
  81. grid.select(lastRow || row, row, lastRow ? undefined : null);
  82. grid._lastSelected = row.element;
  83. }else{
  84. // No row resolved; must be the select-all checkbox.
  85. put(this, (grid.allSelected ? "!" : ".") + "dgrid-select-all");
  86. grid[grid.allSelected ? "clearSelection" : "selectAll"]();
  87. }
  88. }
  89. grid._selectionTriggerEvent = null;
  90. }
  91. }
  92. function setupSelectionEvents(){
  93. // register one listener at the top level that receives events delegated
  94. grid._hasSelectorInputListener = true;
  95. listeners.push(grid.on(".dgrid-selector:click,.dgrid-selector:keydown", onSelect));
  96. var handleSelect = grid._handleSelect;
  97. grid._handleSelect = function(event){
  98. // ignore the default select handler for events that originate from the selector column
  99. if(this.cell(event).column != column){
  100. handleSelect.apply(this, arguments);
  101. }
  102. };
  103. // Set up disabled and grid.allowSelect to match each other's behaviors
  104. if(typeof column.disabled == "function"){
  105. var originalAllowSelect = grid.allowSelect,
  106. originalDisabled = column.disabled;
  107. // Wrap allowSelect to consult both the original allowSelect and disabled
  108. grid.allowSelect = function(row){
  109. var allow = originalAllowSelect.call(this, row);
  110. if (originalDisabled === disabled) {
  111. return allow;
  112. } else {
  113. return allow && !originalDisabled.call(column, row.data);
  114. }
  115. };
  116. // Then wrap disabled to simply call the new allowSelect
  117. column.disabled = disabled;
  118. }else{
  119. // If no disabled function was specified, institute a default one
  120. // which honors allowSelect
  121. column.disabled = disabled;
  122. }
  123. // register listeners to the select and deselect events to change the input checked value
  124. listeners.push(grid.on("dgrid-select", changeInput(true)));
  125. listeners.push(grid.on("dgrid-deselect", changeInput(false)));
  126. }
  127. var renderInput = typeof type == "function" ? type : function(value, cell, object){
  128. var parent = cell.parentNode,
  129. disabled;
  130. if(!grid._hasSelectorInputListener){
  131. setupSelectionEvents();
  132. }
  133. // column.disabled gets initialized or wrapped in setupSelectionEvents
  134. disabled = column.disabled;
  135. // must set the class name on the outer cell in IE for keystrokes to be intercepted
  136. put(parent && parent.contents ? parent : cell, ".dgrid-selector");
  137. var input = cell.input || (cell.input = put(cell, "input[type="+type + "]", {
  138. tabIndex: isNaN(column.tabIndex) ? -1 : column.tabIndex,
  139. disabled: disabled && (typeof disabled == "function" ?
  140. disabled.call(column, object) : disabled),
  141. checked: value
  142. }));
  143. input.setAttribute("aria-checked", !!value);
  144. return input;
  145. };
  146. aspect.after(column, "init", function(){
  147. grid = column.grid;
  148. });
  149. aspect.after(column, "destroy", function(){
  150. arrayUtil.forEach(listeners, function(l){ l.remove(); });
  151. grid._hasSelectorInputListener = false;
  152. });
  153. column.renderCell = function(object, value, cell, options, header){
  154. var row = object && grid.row(object);
  155. value = row && grid.selection[row.id];
  156. renderInput(value, cell, object);
  157. };
  158. column.renderHeaderCell = function(th){
  159. var label = "label" in column ? column.label :
  160. column.field || "";
  161. if(type == "radio" || !grid.allowSelectAll){
  162. th.appendChild(document.createTextNode(label));
  163. if(!grid._hasSelectorInputListener){
  164. setupSelectionEvents();
  165. }
  166. }else{
  167. renderInput(false, th, {});
  168. }
  169. headerCheckbox = th.lastChild;
  170. };
  171. return column;
  172. };
  173. });