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