source: Dev/trunk/src/client/dojox/grid/enhanced/_FocusManager.js

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

Added Dojo 1.9.3 release.

File size: 22.5 KB
Line 
1define([
2        "dojo/_base/kernel",
3        "dojo/_base/lang",
4        "dojo/_base/declare",
5        "dojo/_base/array",
6        "dojo/_base/connect",
7        "dojo/_base/event",
8        "dojo/_base/sniff",
9        "dojo/_base/html",
10        "dojo/keys",
11        "dijit/a11y",
12        "dijit/focus",
13        "../_FocusManager"
14], function(dojo, lang, declare, array, connect, event, has, html, keys, dijitA11y, dijitFocus, _FocusManager){
15
16var _FocusArea = declare("dojox.grid.enhanced._FocusArea", null, {
17        // summary:
18        //              This is a friend class of _FocusManager
19/*=====
20                // name: string
21                //              Name of this area.
22                name: "",
23
24                onFocus: function(event, step){
25                        // summary:
26                        //              Called when this area logically gets focus.
27                        // event: Event object
28                        //              May be unavailable, should check before use.
29                        // step: Integer
30                        //              The distance in the tab sequence from last focused area to this area.
31                        // returns:
32                        //              whether this area is successfully focused. If not, the next area will get focus.
33
34                        return true;
35                },
36               
37                onBlur: function(event, step){
38                        // summary:
39                        //              Called when this area logically loses focus.
40                        // event: Event object
41                        //              May be unavailable, should check before use.
42                        // step: Integer
43                        //              The distance in the tab sequence from this area to the area to focus.
44                        // returns:
45                        //              If Boolean, means whether this area has successfully blurred. If not, the next area to focus is still this one.
46                        //              If String, means the next area to focus is given by this returned name.
47
48                        return true;
49                },
50               
51                onMove: function(rowStep, colStep, event){
52                        // summary:
53                        //              Called when focus is moving around within this area.
54                        // rowStep: Integer
55                        // colStep: Integer
56                        // event: Event object
57                        //              May be unavailable, should check before use.
58                },
59               
60                onKey: function(event, isBubble){
61                        // summary:
62                        //              Called when some key is pressed when focus is logically in this area.
63                        // event: Event object
64                        // isBubble: Boolean
65                        //              Whether is in bubble stage (true) or catch stage (false).
66                        // returns:
67                        //              If you do NOT want the event to propagate any further along the area stack, return exactly false.
68                        //              So if you return nothing (undefined), this event is still propagating.
69                        return true;
70                },
71               
72                getRegions: function(){
73                        // summary:
74                        //              Define the small regions (dom nodes) in this area.
75                        // returns:
76                        //              Array of dom nodes.
77                },
78               
79                onRegionFocus: function(event){
80                        // summary:
81                        //              Connected to the onfocus event of the defined regions (if any)
82                },
83               
84                onRegionBlur: function(event){
85                        // summary:
86                        //              Connected to the onblur event of the defined regions (if any)
87                },
88=====*/
89        constructor: function(area, focusManager){
90                this._fm = focusManager;
91                this._evtStack = [area.name];
92                var dummy = function(){return true;};
93                area.onFocus = area.onFocus || dummy;
94                area.onBlur = area.onBlur || dummy;
95                area.onMove = area.onMove || dummy;
96                area.onKeyUp = area.onKeyUp || dummy;
97                area.onKeyDown = area.onKeyDown || dummy;
98                lang.mixin(this, area);
99        },
100        move: function(rowStep, colStep, evt){
101                if(this.name){
102                        var i, len = this._evtStack.length;
103                        for(i = len - 1; i >= 0; --i){
104                                if(this._fm._areas[this._evtStack[i]].onMove(rowStep, colStep, evt) === false){
105                                        return false;
106                                }
107                        }
108                }
109                return true;
110        },
111        _onKeyEvent: function(evt, funcName){
112                if(this.name){
113                        var i, len = this._evtStack.length;
114                        for(i = len - 1; i >= 0; --i){
115                                if(this._fm._areas[this._evtStack[i]][funcName](evt, false) === false){
116                                        return false;
117                                }
118                        }
119                        for(i = 0; i < len; ++i){
120                                if(this._fm._areas[this._evtStack[i]][funcName](evt, true) === false){
121                                        return false;
122                                }
123                        }
124                }
125                return true;
126        },
127        keydown: function(evt){
128                return this._onKeyEvent(evt, "onKeyDown");
129        },
130        keyup: function(evt){
131                return this._onKeyEvent(evt, "onKeyUp");
132        },
133        contentMouseEventPlanner: function(){
134                return 0;
135        },
136        headerMouseEventPlanner: function(){
137                return 0;
138        }
139});
140
141return declare("dojox.grid.enhanced._FocusManager", _FocusManager, {
142        _stopEvent: function(evt){
143                try{
144                        if(evt && evt.preventDefault){
145                                event.stop(evt);
146                        }
147                }catch(e){}
148        },
149       
150        constructor: function(grid){
151                this.grid = grid;
152                this._areas = {};
153                this._areaQueue = [];
154                this._contentMouseEventHandlers = [];
155                this._headerMouseEventHandlers = [];
156                this._currentAreaIdx = -1;
157                this._gridBlured = true;
158                this._connects.push(connect.connect(grid, "onBlur", this, "_doBlur"));
159                this._connects.push(connect.connect(grid.scroller, "renderPage", this, "_delayedCellFocus"));
160               
161                this.addArea({
162                        name: "header",
163                        onFocus: lang.hitch(this, this.focusHeader),
164                        onBlur: lang.hitch(this, this._blurHeader),
165                        onMove: lang.hitch(this, this._navHeader),
166                        getRegions: lang.hitch(this, this._findHeaderCells),
167                        onRegionFocus: lang.hitch(this, this.doColHeaderFocus),
168                        onRegionBlur: lang.hitch(this, this.doColHeaderBlur),
169                        onKeyDown: lang.hitch(this, this._onHeaderKeyDown)
170                });
171                this.addArea({
172                        name: "content",
173                        onFocus: lang.hitch(this, this._focusContent),
174                        onBlur: lang.hitch(this, this._blurContent),
175                        onMove: lang.hitch(this, this._navContent),
176                        onKeyDown: lang.hitch(this, this._onContentKeyDown)
177                });
178                this.addArea({
179                        name: "editableCell",
180                        onFocus: lang.hitch(this, this._focusEditableCell),
181                        onBlur: lang.hitch(this, this._blurEditableCell),
182                        onKeyDown: lang.hitch(this, this._onEditableCellKeyDown),
183                        onContentMouseEvent: lang.hitch(this, this._onEditableCellMouseEvent),
184                        contentMouseEventPlanner: function(evt, areas){ return -1; }
185                });
186                this.placeArea("header");
187                this.placeArea("content");
188                this.placeArea("editableCell");
189                this.placeArea("editableCell","above","content");
190        },
191        destroy: function(){
192                for(var name in this._areas){
193                        var area = this._areas[name];
194                        array.forEach(area._connects, connect.disconnect);
195                        area._connects = null;
196                        if(area.uninitialize){
197                                area.uninitialize();
198                        }
199                }
200                this.inherited(arguments);
201        },
202        addArea: function(area){
203                if(area.name && lang.isString(area.name)){
204                        if(this._areas[area.name]){
205                                //Just replace the original area, instead of remove it, so the position does not change.
206                                array.forEach(area._connects, connect.disconnect);
207                        }
208                        this._areas[area.name] = new _FocusArea(area, this);
209                        if(area.onHeaderMouseEvent){
210                                this._headerMouseEventHandlers.push(area.name);
211                        }
212                        if(area.onContentMouseEvent){
213                                this._contentMouseEventHandlers.push(area.name);
214                        }
215                }
216        },
217        getArea: function(areaName){
218                return this._areas[areaName];
219        },
220        _bindAreaEvents: function(){
221                var area, hdl, areas = this._areas;
222                array.forEach(this._areaQueue, function(name){
223                        area = areas[name];
224                        if(!area._initialized && lang.isFunction(area.initialize)){
225                                area.initialize();
226                                area._initialized = true;
227                        }
228                        if(area.getRegions){
229                                area._regions = area.getRegions() || [];
230                                array.forEach(area._connects || [], connect.disconnect);
231                                area._connects = [];
232                                array.forEach(area._regions, function(r){
233                                        if(area.onRegionFocus){
234                                                hdl = connect.connect(r, "onfocus", area.onRegionFocus);
235                                                area._connects.push(hdl);
236                                        }
237                                        if(area.onRegionBlur){
238                                                hdl = connect.connect(r, "onblur", area.onRegionBlur);
239                                                area._connects.push(hdl);
240                                        }
241                                });
242                        }
243                });
244        },
245        removeArea: function(areaName){
246                var area = this._areas[areaName];
247                if(area){
248                        this.ignoreArea(areaName);
249                        var i = array.indexOf(this._contentMouseEventHandlers, areaName);
250                        if(i >= 0){
251                                this._contentMouseEventHandlers.splice(i, 1);
252                        }
253                        i = array.indexOf(this._headerMouseEventHandlers, areaName);
254                        if(i >= 0){
255                                this._headerMouseEventHandlers.splice(i, 1);
256                        }
257                        array.forEach(area._connects, connect.disconnect);
258                        if(area.uninitialize){
259                                area.uninitialize();
260                        }
261                        delete this._areas[areaName];
262                }
263        },
264        currentArea: function(areaName, toBlurOld){
265                // summary:
266                //              Set current area to the one areaName refers.
267                // areaName: String
268                var idx, cai = this._currentAreaIdx;
269                if(lang.isString(areaName) && (idx = array.indexOf(this._areaQueue, areaName)) >= 0){
270                        if(cai != idx){
271                                this.tabbingOut = false;
272                                if(toBlurOld && cai >= 0 && cai < this._areaQueue.length){
273                                        this._areas[this._areaQueue[cai]].onBlur();
274                                }
275                                this._currentAreaIdx = idx;
276                        }
277                }else{
278                        return (cai < 0 || cai >= this._areaQueue.length) ?
279                                new _FocusArea({}, this) :
280                                this._areas[this._areaQueue[this._currentAreaIdx]];
281                }
282                return null;
283        },
284        placeArea: function(name, pos, otherAreaName){
285                // summary:
286                //              Place the area refered by *name* at some logical position relative to an existing area.
287                // example:
288                //              placeArea("myarea","before"|"after",...)
289                //              placeArea("myarea","below"|"above",...)
290                if(!this._areas[name]){ return; }
291                var idx = array.indexOf(this._areaQueue,otherAreaName);
292                switch(pos){
293                        case "after":
294                                if(idx >= 0){ ++idx; }
295                                //intentional drop through
296                        case "before":
297                                if(idx >= 0){
298                                        this._areaQueue.splice(idx,0,name);
299                                        break;
300                                }
301                                //intentional drop through
302                        default:
303                                this._areaQueue.push(name);
304                                break;
305                        case "above":
306                                var isAbove = true;
307                                //intentional drop through
308                        case "below":
309                                var otherArea = this._areas[otherAreaName];
310                                if(otherArea){
311                                        if(isAbove){
312                                                otherArea._evtStack.push(name);
313                                        }else{
314                                                otherArea._evtStack.splice(0,0,name);
315                                        }
316                                }
317                }
318        },
319        ignoreArea: function(name){
320                this._areaQueue = array.filter(this._areaQueue,function(areaName){
321                        return areaName != name;
322                });
323        },
324        focusArea: function(/* int|string|areaObj */areaId,evt){
325                var idx;
326                if(typeof areaId == "number"){
327                        idx = areaId < 0 ? this._areaQueue.length + areaId : areaId;
328                }else{
329                        idx = array.indexOf(this._areaQueue,
330                                lang.isString(areaId) ? areaId : (areaId && areaId.name));
331                }
332                if(idx < 0){ idx = 0; }
333                var step = idx - this._currentAreaIdx;
334                this._gridBlured = false;
335                if(step){
336                        this.tab(step, evt);
337                }else{
338                        this.currentArea().onFocus(evt, step);
339                }
340        },
341        tab: function(step,evt){
342                //console.log("===========tab",step,"curArea",this._currentAreaIdx,"areaCnt",this._areaQueue.length);
343                this._gridBlured = false;
344                this.tabbingOut = false;
345                if(step === 0){
346                        return;
347                }
348                var cai = this._currentAreaIdx;
349                var dir = step > 0 ? 1:-1;
350                if(cai < 0 || cai >= this._areaQueue.length){
351                        cai = (this._currentAreaIdx += step);
352                }else{
353                        var nextArea = this._areas[this._areaQueue[cai]].onBlur(evt,step);
354                        if(nextArea === true){
355                                cai = (this._currentAreaIdx += step);
356                        }else if(lang.isString(nextArea) && this._areas[nextArea]){
357                                cai = this._currentAreaIdx = array.indexOf(this._areaQueue,nextArea);
358                        }
359                }
360                //console.log("target area:",cai);
361                for(; cai >= 0 && cai < this._areaQueue.length; cai += dir){
362                        this._currentAreaIdx = cai;
363                        if(this._areaQueue[cai] && this._areas[this._areaQueue[cai]].onFocus(evt,step)){
364                                //console.log("final target area:",this._currentAreaIdx);
365                                return;
366                        }
367                }
368                //console.log("tab out");
369                this.tabbingOut = true;
370                if(step < 0){
371                        this._currentAreaIdx = -1;
372                        dijitFocus.focus(this.grid.domNode);
373                }else{
374                        this._currentAreaIdx = this._areaQueue.length;
375                        dijitFocus.focus(this.grid.lastFocusNode);
376                }
377        },
378        _onMouseEvent: function(type, evt){
379                var lowercase = type.toLowerCase(),
380                        handlers = this["_" + lowercase + "MouseEventHandlers"],
381                        res = array.map(handlers, function(areaName){
382                                return {
383                                        "area": areaName,
384                                        "idx": this._areas[areaName][lowercase + "MouseEventPlanner"](evt, handlers)
385                                };
386                        }, this).sort(function(a, b){
387                                return b.idx - a.idx;
388                        }),
389                        resHandlers = array.map(res, function(handler){
390                                return res.area;
391                        }),
392                        i = res.length;
393                while(--i >= 0){
394                        if(this._areas[res[i].area]["on" + type + "MouseEvent"](evt, resHandlers) === false){
395                                return;
396                        }
397                }
398        },
399        contentMouseEvent: function(evt){
400                this._onMouseEvent("Content", evt);
401        },
402        headerMouseEvent: function(evt){
403                this._onMouseEvent("Header", evt);
404        },
405        initFocusView: function(){
406                // summary:
407                //              Overwritten
408                this.focusView = this.grid.views.getFirstScrollingView() || this.focusView || this.grid.views.views[0];
409                this._bindAreaEvents();
410        },
411        isNavHeader: function(){
412                // summary:
413                //              Overwritten
414                //              Check whether currently navigating among column headers.
415                // returns:
416                //              true - focus is on a certain column header | false otherwise
417                return this._areaQueue[this._currentAreaIdx] == "header";
418        },
419        previousKey: function(e){
420                // summary:
421                //              Overwritten
422                this.tab(-1,e);
423        },
424        nextKey: function(e){
425                // summary:
426                //              Overwritten
427                this.tab(1,e);
428        },
429        setFocusCell: function(/* Object */inCell, /* Integer */inRowIndex){
430                // summary:
431                //              Overwritten - focuses the given grid cell
432                if(inCell){
433                        this.currentArea(this.grid.edit.isEditing() ? "editableCell" : "content", true);
434                        //This is very slow when selecting cells!
435                        //this.focusGridView();
436                        this._focusifyCellNode(false);
437                        this.cell = inCell;
438                        this.rowIndex = inRowIndex;
439                        this._focusifyCellNode(true);
440                }
441                this.grid.onCellFocus(this.cell, this.rowIndex);
442        },
443        doFocus: function(e){
444                // summary:
445                //              Overwritten
446                //              trap focus only for grid dom node
447                //              do not focus for scrolling if grid is about to blur
448                if(e && e.target == e.currentTarget && !this.tabbingOut){
449                        if(this._gridBlured){
450                                this._gridBlured = false;
451                                if(this._currentAreaIdx < 0 || this._currentAreaIdx >= this._areaQueue.length){
452                                        this.focusArea(0, e);
453                                }else{
454                                        this.focusArea(this._currentAreaIdx, e);
455                                }
456                        }
457                }else{
458                        this.tabbingOut = false;
459                }
460                event.stop(e);
461        },
462        _doBlur: function(){
463                this._gridBlured = true;
464        },
465        doLastNodeFocus: function(e){
466                // summary:
467                //              Overwritten
468                if(this.tabbingOut){
469                        this.tabbingOut = false;
470                }else{
471                        this.focusArea(-1, e);
472                }
473        },
474        _delayedHeaderFocus: function(){
475                // summary:
476                //              Overwritten
477                if(this.isNavHeader() && !has('ie')){
478                        this.focusHeader();
479                }
480        },
481        _delayedCellFocus: function(){
482                // summary:
483                //              Overwritten
484               
485                //If focus header here, the page will scroll to grid when the grid is created.
486                //this.focusArea("header");
487        },
488        _changeMenuBindNode: function(oldBindNode, newBindNode){
489                var hm = this.grid.headerMenu;
490                if(hm && this._contextMenuBindNode == oldBindNode){
491                        hm.unBindDomNode(oldBindNode);
492                        hm.bindDomNode(newBindNode);
493                        this._contextMenuBindNode = newBindNode;
494                }
495        },
496        //---------------Header Area------------------------------------------
497        focusHeader: function(evt, step){ //need a further look why these changes to parent's
498                // summary:
499                //              Overwritten
500                var didFocus = false;
501                this.inherited(arguments);
502                if(this._colHeadNode && html.style(this._colHeadNode, 'display') != "none"){
503                        dijitFocus.focus(this._colHeadNode);
504                        this._stopEvent(evt);
505                        didFocus = true;
506                }
507                return didFocus;
508        },
509        _blurHeader: function(evt,step){
510                // summary:
511                //              Overwritten
512                if(this._colHeadNode){
513                        html.removeClass(this._colHeadNode, this.focusClass);
514                }
515                html.removeAttr(this.grid.domNode,"aria-activedescendant");
516                // reset contextMenu onto viewsHeaderNode so right mouse on header will invoke (see focusHeader)
517                this._changeMenuBindNode(this.grid.domNode,this.grid.viewsHeaderNode);
518                //moved here from nextKey
519                this._colHeadNode = this._colHeadFocusIdx = null;
520                return true;
521        },
522        _navHeader: function(rowStep, colStep, evt){
523                var colDir = colStep < 0 ? -1 : 1,
524                        savedIdx = array.indexOf(this._findHeaderCells(), this._colHeadNode);
525                if(savedIdx >= 0 && (evt.shiftKey && evt.ctrlKey)){
526                        this.colSizeAdjust(evt, savedIdx, colDir * 5);
527                        return;
528                }
529                this.move(rowStep, colStep);
530        },
531        _onHeaderKeyDown: function(e, isBubble){
532                if(isBubble){
533                        var dk = keys;
534                        switch(e.keyCode){
535                                case dk.ENTER:
536                                case dk.SPACE:
537                                        var colIdx = this.getHeaderIndex();
538                                        if(colIdx >= 0 && !this.grid.pluginMgr.isFixedCell(e.cell)/*TODO*/){
539                                                this.grid.setSortIndex(colIdx, null, e);
540                                                event.stop(e);
541                                        }
542                                        break;
543                        }
544                }
545                return true;
546        },
547        _setActiveColHeader: function(){
548                // summary:
549                //              Overwritten
550                this.inherited(arguments);
551                //EDG now will decorate event on header key events, if no focus, the cell will be wrong
552                dijitFocus.focus(this._colHeadNode);
553        },
554        //---------------Content Area------------------------------------------
555        findAndFocusGridCell: function(){
556                // summary:
557                //              Overwritten
558                this._focusContent();
559        },
560        _focusContent: function(evt,step){
561                var didFocus = true;
562                var isEmpty = (this.grid.rowCount === 0); // If grid is empty this.grid.rowCount == 0
563                if(this.isNoFocusCell() && !isEmpty){
564                        //skip all the hidden cells
565                        for(var i = 0, cell = this.grid.getCell(0); cell && cell.hidden; cell = this.grid.getCell(++i)){}
566                        this.setFocusIndex(0, cell ? i : 0);
567                }else if(this.cell && !isEmpty){
568                        if(this.focusView && !this.focusView.rowNodes[this.rowIndex]){
569                                // if rowNode for current index is undefined (likely as a result of a sort and because of #7304)
570                                // scroll to that row
571                                this.grid.scrollToRow(this.rowIndex);
572                                this.focusGrid();
573                        }else{
574                                this.setFocusIndex(this.rowIndex, this.cell.index);
575                        }
576                }else{
577                        didFocus = false;
578                }
579                if(didFocus){ this._stopEvent(evt); }
580                return didFocus;
581        },
582        _blurContent: function(evt,step){
583                this._focusifyCellNode(false);
584                return true;
585        },
586        _navContent: function(rowStep, colStep, evt){
587                if((this.rowIndex === 0 && rowStep < 0) || (this.rowIndex === this.grid.rowCount - 1 && rowStep > 0)){
588                        return;
589                }
590                this._colHeadNode = null;
591                this.move(rowStep, colStep, evt);
592                if(evt){
593                        event.stop(evt);
594                }
595        },
596        _onContentKeyDown: function(e, isBubble){
597                if(isBubble){
598                        var dk = keys, s = this.grid.scroller;
599                        switch(e.keyCode){
600                                case dk.ENTER:
601                                case dk.SPACE:
602                                        var g = this.grid;
603                                        if(g.indirectSelection){ break; }
604                                        g.selection.clickSelect(this.rowIndex, connect.isCopyKey(e), e.shiftKey);
605                                        g.onRowClick(e);
606                                        event.stop(e);
607                                        break;
608                                case dk.PAGE_UP:
609                                        if(this.rowIndex !== 0){
610                                                if(this.rowIndex != s.firstVisibleRow + 1){
611                                                        this._navContent(s.firstVisibleRow - this.rowIndex, 0);
612                                                }else{
613                                                        this.grid.setScrollTop(s.findScrollTop(this.rowIndex - 1));
614                                                        this._navContent(s.firstVisibleRow - s.lastVisibleRow + 1, 0);
615                                                }
616                                                event.stop(e);
617                                        }
618                                        break;
619                                case dk.PAGE_DOWN:
620                                        if(this.rowIndex + 1 != this.grid.rowCount){
621                                                event.stop(e);
622                                                if(this.rowIndex != s.lastVisibleRow - 1){
623                                                        this._navContent(s.lastVisibleRow - this.rowIndex - 1, 0);
624                                                }else{
625                                                        this.grid.setScrollTop(s.findScrollTop(this.rowIndex + 1));
626                                                        this._navContent(s.lastVisibleRow - s.firstVisibleRow - 1, 0);
627                                                }
628                                                event.stop(e);
629                                        }
630                                        break;
631                        }
632                }
633                return true;
634        },
635        //------------------editable content area-------------------------
636        _blurFromEditableCell: false,
637        _isNavigating: false,
638        _navElems: null,
639        _focusEditableCell: function(evt,step){
640                var didFocus = false;
641                if(this._isNavigating){
642                        didFocus = true;
643                }else if(this.grid.edit.isEditing() && this.cell){
644                        if(this._blurFromEditableCell || !this._blurEditableCell(evt, step)){
645                                this.setFocusIndex(this.rowIndex,this.cell.index);
646                                didFocus = true;
647                        }
648                        this._stopEvent(evt);
649                }
650                return didFocus;
651        },
652        _applyEditableCell: function(){
653                try{
654                        this.grid.edit.apply();
655                }catch(e){
656                        console.warn("_FocusManager._applyEditableCell() error:", e);
657                }
658        },
659        _blurEditableCell: function(evt,step){
660                this._blurFromEditableCell = false;
661                if(this._isNavigating){
662                        var toBlur = true;
663                        if(evt){
664                                var elems = this._navElems;
665                                var firstElem = elems.lowest || elems.first;
666                                var lastElem = elems.last || elems.highest || firstElem;
667                                var target = has('ie') ? evt.srcElement : evt.target;
668                                toBlur = target == (step > 0 ? lastElem : firstElem);
669                        }
670                        if(toBlur){
671                                this._isNavigating = false;
672                                html.setSelectable(this.cell.getNode(this.rowIndex), false);
673                                return "content";
674                        }
675                        return false;
676                }else if(this.grid.edit.isEditing() && this.cell){
677                        if(!step || typeof step != "number"){ return false; }
678                        var dir = step > 0 ? 1 : -1;
679                        var cc = this.grid.layout.cellCount;
680                        for(var cell, col = this.cell.index + dir; col >= 0 && col < cc; col += dir){
681                                cell = this.grid.getCell(col);
682                                if(cell.editable){
683                                        this.cell = cell;
684                                        this._blurFromEditableCell = true;
685                                        return false;
686                                }
687                        }
688                        if((this.rowIndex > 0 || dir == 1) && (this.rowIndex < this.grid.rowCount || dir == -1)){
689                                this.rowIndex += dir;
690                                //this.cell = this.grid.getCell(0); //There must be an editable cell, so this is not necessary.
691                                for(col = dir > 0 ? 0 : cc - 1; col >= 0 && col < cc; col += dir){
692                                        cell = this.grid.getCell(col);
693                                        if(cell.editable){
694                                                this.cell = cell;
695                                                break;
696                                        }
697                                }
698                                this._applyEditableCell();
699                                return "content";
700                        }
701                }
702                return true;
703        },
704        _initNavigatableElems: function(){
705                this._navElems = dijitA11y._getTabNavigable(this.cell.getNode(this.rowIndex));
706        },
707        _onEditableCellKeyDown: function(e, isBubble){
708                var dk = keys,
709                        g = this.grid,
710                        edit = g.edit,
711                        editApplied = false,
712                        toPropagate = true;
713                switch(e.keyCode){
714                        case dk.ENTER:
715                                if(isBubble && edit.isEditing()){
716                                        this._applyEditableCell();
717                                        editApplied = true;
718                                        event.stop(e);
719                                }
720                                //intentional drop through
721                        case dk.SPACE:
722                                if(!isBubble && this._isNavigating){
723                                        toPropagate = false;
724                                        break;
725                                }
726                                if(isBubble){
727                                        if(!this.cell.editable && this.cell.navigatable){
728                                                this._initNavigatableElems();
729                                                var toFocus = this._navElems.lowest || this._navElems.first;
730                                                if(toFocus){
731                                                        this._isNavigating = true;
732                                                        html.setSelectable(this.cell.getNode(this.rowIndex), true);
733                                                        dijitFocus.focus(toFocus);
734                                                        event.stop(e);
735                                                        this.currentArea("editableCell", true);
736                                                        break;
737                                                }
738                                        }
739                                        if(!editApplied && !edit.isEditing() && !g.pluginMgr.isFixedCell(this.cell)){
740                                                edit.setEditCell(this.cell, this.rowIndex);
741                                        }
742                                        if(editApplied){
743                                                this.currentArea("content", true);
744                                        }else if(this.cell.editable && g.canEdit()){
745                                                this.currentArea("editableCell", true);
746                                        }
747                                }
748                                break;
749                        case dk.PAGE_UP:
750                        case dk.PAGE_DOWN:
751                                if(!isBubble && edit.isEditing()){
752                                        //prevent propagating to content area
753                                        toPropagate = false;
754                                }
755                                break;
756                        case dk.ESCAPE:
757                                if(!isBubble){
758                                        edit.cancel();
759                                        this.currentArea("content", true);
760                                }
761                }
762                return toPropagate;
763        },
764        _onEditableCellMouseEvent: function(evt){
765                if(evt.type == "click"){
766                        var cell = this.cell || evt.cell;
767                        if(cell && !cell.editable && cell.navigatable){
768                                this._initNavigatableElems();
769                                if(this._navElems.lowest || this._navElems.first){
770                                        var target = has('ie') ? evt.srcElement : evt.target;
771                                        if(target != cell.getNode(evt.rowIndex)){
772                                                this._isNavigating = true;
773                                                this.focusArea("editableCell", evt);
774                                                html.setSelectable(cell.getNode(evt.rowIndex), true);
775                                                dijitFocus.focus(target);
776                                                return false;
777                                        }
778                                }
779                        }else if(this.grid.singleClickEdit){
780                                this.currentArea("editableCell");
781                                return false;
782                        }
783                }
784                return true;
785        }
786});
787});
Note: See TracBrowser for help on using the repository browser.