source: Dev/trunk/src/client/dijit/_PaletteMixin.js @ 527

Last change on this file since 527 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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