_PaletteMixin.js 9.2 KB

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