_PaletteMixin.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. define("dijit/_PaletteMixin", [
  2. "dojo/_base/declare", // declare
  3. "dojo/dom-attr", // domAttr.set
  4. "dojo/dom-class", // domClass.add domClass.remove
  5. "dojo/dom-construct", // domConstruct.create domConstruct.place
  6. "dojo/_base/event", // event.stop
  7. "dojo/keys", // keys
  8. "dojo/_base/lang", // lang.getObject
  9. "./_CssStateMixin",
  10. "./focus",
  11. "./typematic"
  12. ], function(declare, domAttr, domClass, domConstruct, event, keys, lang, _CssStateMixin, focus, typematic){
  13. /*=====
  14. var _CssStateMixin = dijit._CssStateMixin;
  15. =====*/
  16. // module:
  17. // dijit/_PaletteMixin
  18. // summary:
  19. // A keyboard accessible palette, for picking a color/emoticon/etc.
  20. return declare("dijit._PaletteMixin", [_CssStateMixin], {
  21. // summary:
  22. // A keyboard accessible palette, for picking a color/emoticon/etc.
  23. // description:
  24. // A mixin for a grid showing various entities, so the user can pick a certain entity.
  25. // defaultTimeout: Number
  26. // Number of milliseconds before a held key or button becomes typematic
  27. defaultTimeout: 500,
  28. // timeoutChangeRate: Number
  29. // Fraction of time used to change the typematic timer between events
  30. // 1.0 means that each typematic event fires at defaultTimeout intervals
  31. // < 1.0 means that each typematic event fires at an increasing faster rate
  32. timeoutChangeRate: 0.90,
  33. // value: String
  34. // Currently selected color/emoticon/etc.
  35. value: "",
  36. // _selectedCell: [private] Integer
  37. // Index of the currently selected cell. Initially, none selected
  38. _selectedCell: -1,
  39. /*=====
  40. // _currentFocus: [private] DomNode
  41. // The currently focused cell (if the palette itself has focus), or otherwise
  42. // the cell to be focused when the palette itself gets focus.
  43. // Different from value, which represents the selected (i.e. clicked) cell.
  44. _currentFocus: null,
  45. =====*/
  46. /*=====
  47. // _xDim: [protected] Integer
  48. // This is the number of cells horizontally across.
  49. _xDim: null,
  50. =====*/
  51. /*=====
  52. // _yDim: [protected] Integer
  53. // This is the number of cells vertically down.
  54. _yDim: null,
  55. =====*/
  56. // tabIndex: String
  57. // Widget tab index.
  58. tabIndex: "0",
  59. // cellClass: [protected] String
  60. // CSS class applied to each cell in the palette
  61. cellClass: "dijitPaletteCell",
  62. // dyeClass: [protected] String
  63. // Name of javascript class for Object created for each cell of the palette.
  64. // dyeClass should implements dijit.Dye interface
  65. dyeClass: '',
  66. // summary: String
  67. // Localized summary for the palette table
  68. summary: '',
  69. _setSummaryAttr: "paletteTableNode",
  70. _dyeFactory: function(value /*===== , row, col =====*/){
  71. // summary:
  72. // Return instance of dijit.Dye for specified cell of palette
  73. // tags:
  74. // extension
  75. var dyeClassObj = lang.getObject(this.dyeClass);
  76. return new dyeClassObj(value);
  77. },
  78. _preparePalette: function(choices, titles) {
  79. // summary:
  80. // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
  81. // for each cell
  82. // choices: String[][]
  83. // id's for each cell of the palette, used to create Dye JS object for each cell
  84. // titles: String[]
  85. // Localized tooltip for each cell
  86. this._cells = [];
  87. var url = this._blankGif;
  88. this.connect(this.gridNode, "ondijitclick", "_onCellClick");
  89. for(var row=0; row < choices.length; row++){
  90. var rowNode = domConstruct.create("tr", {tabIndex: "-1"}, this.gridNode);
  91. for(var col=0; col < choices[row].length; col++){
  92. var value = choices[row][col];
  93. if(value){
  94. var cellObject = this._dyeFactory(value, row, col);
  95. var cellNode = domConstruct.create("td", {
  96. "class": this.cellClass,
  97. tabIndex: "-1",
  98. title: titles[value],
  99. role: "gridcell"
  100. });
  101. // prepare cell inner structure
  102. cellObject.fillCell(cellNode, url);
  103. domConstruct.place(cellNode, rowNode);
  104. cellNode.index = this._cells.length;
  105. // save cell info into _cells
  106. this._cells.push({node:cellNode, dye:cellObject});
  107. }
  108. }
  109. }
  110. this._xDim = choices[0].length;
  111. this._yDim = choices.length;
  112. // Now set all events
  113. // The palette itself is navigated to with the tab key on the keyboard
  114. // Keyboard navigation within the Palette is with the arrow keys
  115. // Spacebar selects the cell.
  116. // For the up key the index is changed by negative the x dimension.
  117. var keyIncrementMap = {
  118. UP_ARROW: -this._xDim,
  119. // The down key the index is increase by the x dimension.
  120. DOWN_ARROW: this._xDim,
  121. // Right and left move the index by 1.
  122. RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
  123. LEFT_ARROW: this.isLeftToRight() ? -1 : 1
  124. };
  125. for(var key in keyIncrementMap){
  126. this._connects.push(
  127. typematic.addKeyListener(
  128. this.domNode,
  129. {charOrCode:keys[key], ctrlKey:false, altKey:false, shiftKey:false},
  130. this,
  131. function(){
  132. var increment = keyIncrementMap[key];
  133. return function(count){ this._navigateByKey(increment, count); };
  134. }(),
  135. this.timeoutChangeRate,
  136. this.defaultTimeout
  137. )
  138. );
  139. }
  140. },
  141. postCreate: function(){
  142. this.inherited(arguments);
  143. // Set initial navigable node.
  144. this._setCurrent(this._cells[0].node);
  145. },
  146. focus: function(){
  147. // summary:
  148. // Focus this widget. Puts focus on the most recently focused cell.
  149. // The cell already has tabIndex set, just need to set CSS and focus it
  150. focus.focus(this._currentFocus);
  151. },
  152. _onCellClick: function(/*Event*/ evt){
  153. // summary:
  154. // Handler for click, enter key & space key. Selects the cell.
  155. // evt:
  156. // The event.
  157. // tags:
  158. // private
  159. var target = evt.target;
  160. // Find TD associated with click event. For ColorPalette user likely clicked IMG inside of TD
  161. while(target.tagName != "TD"){
  162. if(!target.parentNode || target == this.gridNode){ // probably can never happen, but just in case
  163. return;
  164. }
  165. target = target.parentNode;
  166. }
  167. var value = this._getDye(target).getValue();
  168. // First focus the clicked cell, and then send onChange() notification.
  169. // onChange() (via _setValueAttr) must be after the focus call, because
  170. // it may trigger a refocus to somewhere else (like the Editor content area), and that
  171. // second focus should win.
  172. this._setCurrent(target);
  173. focus.focus(target);
  174. this._setValueAttr(value, true);
  175. event.stop(evt);
  176. },
  177. _setCurrent: function(/*DomNode*/ node){
  178. // summary:
  179. // Sets which node is the focused cell.
  180. // description:
  181. // At any point in time there's exactly one
  182. // cell with tabIndex != -1. If focus is inside the palette then
  183. // focus is on that cell.
  184. //
  185. // After calling this method, arrow key handlers and mouse click handlers
  186. // should focus the cell in a setTimeout().
  187. // tags:
  188. // protected
  189. if("_currentFocus" in this){
  190. // Remove tabIndex on old cell
  191. domAttr.set(this._currentFocus, "tabIndex", "-1");
  192. }
  193. // Set tabIndex of new cell
  194. this._currentFocus = node;
  195. if(node){
  196. domAttr.set(node, "tabIndex", this.tabIndex);
  197. }
  198. },
  199. _setValueAttr: function(value, priorityChange){
  200. // summary:
  201. // This selects a cell. It triggers the onChange event.
  202. // value: String value of the cell to select
  203. // tags:
  204. // protected
  205. // priorityChange:
  206. // Optional parameter used to tell the select whether or not to fire
  207. // onChange event.
  208. // clear old selected cell
  209. if(this._selectedCell >= 0){
  210. domClass.remove(this._cells[this._selectedCell].node, this.cellClass + "Selected");
  211. }
  212. this._selectedCell = -1;
  213. // search for cell matching specified value
  214. if(value){
  215. for(var i = 0; i < this._cells.length; i++){
  216. if(value == this._cells[i].dye.getValue()){
  217. this._selectedCell = i;
  218. domClass.add(this._cells[i].node, this.cellClass + "Selected");
  219. break;
  220. }
  221. }
  222. }
  223. // record new value, or null if no matching cell
  224. this._set("value", this._selectedCell >= 0 ? value : null);
  225. if(priorityChange || priorityChange === undefined){
  226. this.onChange(value);
  227. }
  228. },
  229. onChange: function(/*===== value =====*/){
  230. // summary:
  231. // Callback when a cell is selected.
  232. // value: String
  233. // Value corresponding to cell.
  234. },
  235. _navigateByKey: function(increment, typeCount){
  236. // summary:
  237. // This is the callback for typematic.
  238. // It changes the focus and the highlighed cell.
  239. // increment:
  240. // How much the key is navigated.
  241. // typeCount:
  242. // How many times typematic has fired.
  243. // tags:
  244. // private
  245. // typecount == -1 means the key is released.
  246. if(typeCount == -1){ return; }
  247. var newFocusIndex = this._currentFocus.index + increment;
  248. if(newFocusIndex < this._cells.length && newFocusIndex > -1){
  249. var focusNode = this._cells[newFocusIndex].node;
  250. this._setCurrent(focusNode);
  251. // Actually focus the node, for the benefit of screen readers.
  252. // Use setTimeout because IE doesn't like changing focus inside of an event handler
  253. setTimeout(lang.hitch(dijit, "focus", focusNode), 0);
  254. }
  255. },
  256. _getDye: function(/*DomNode*/ cell){
  257. // summary:
  258. // Get JS object for given cell DOMNode
  259. return this._cells[cell.index].dye;
  260. }
  261. });
  262. /*=====
  263. declare("dijit.Dye",
  264. null,
  265. {
  266. // summary:
  267. // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
  268. constructor: function(alias, row, col){
  269. // summary:
  270. // Initialize according to value or alias like "white"
  271. // alias: String
  272. },
  273. getValue: function(){
  274. // summary:
  275. // Return "value" of cell; meaning of "value" varies by subclass.
  276. // description:
  277. // For example color hex value, emoticon ascii value etc, entity hex value.
  278. },
  279. fillCell: function(cell, blankGif){
  280. // summary:
  281. // Add cell DOMNode inner structure
  282. // cell: DomNode
  283. // The surrounding cell
  284. // blankGif: String
  285. // URL for blank cell image
  286. }
  287. }
  288. );
  289. =====*/
  290. });