source: Dev/branches/rest-dojo-ui/client/dijit/_PaletteMixin.js @ 274

Last change on this file since 274 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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