Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/**
2
 * Provides support for focusing on different nodes after the Widget is
3
 * hidden.
4
 *
5
 * If the focusOnPreviousTargetAfterHide attribute is true, then the module hooks
6
 * into the show function for that Widget to try and determine which Node
7
 * caused the Widget to be shown.
8
 *
9
 * Alternatively, the focusAfterHide attribute can be passed a Node.
10
 *
11
 * @module moodle-core-widget-focusafterhide
12
 */
13
 
14
var CAN_RECEIVE_FOCUS_SELECTOR = 'input:not([type="hidden"]), ' +
15
                                 'a[href], button, textarea, select, ' +
16
                                '[tabindex], [contenteditable="true"]';
17
 
18
/**
19
 * Provides support for focusing on different nodes after the Widget is
20
 * hidden.
21
 *
22
 * @class M.core.WidgetFocusAfterHide
23
 */
24
function WidgetFocusAfterHide() {
25
    Y.after(this._bindUIFocusAfterHide, this, 'bindUI');
26
    if (this.get('rendered')) {
27
        this._bindUIFocusAfterHide();
28
    }
29
}
30
 
31
WidgetFocusAfterHide.ATTRS = {
32
    /**
33
     * Whether to focus on the target that caused the Widget to be shown.
34
     *
35
     * <em>If this is true, and a valid Node is found, any Node specified to focusAfterHide
36
     * will be ignored.</em>
37
     *
38
     * @attribute focusOnPreviousTargetAfterHide
39
     * @default false
40
     * @type boolean
41
     */
42
    focusOnPreviousTargetAfterHide: {
43
        value: false
44
    },
45
 
46
    /**
47
     * The Node to focus on after hiding the Widget.
48
     *
49
     * <em>Note: If focusOnPreviousTargetAfterHide is true, and a valid Node is found, then this
50
     * value will be ignored. If it is true and not found, then this value will be used as
51
     * a fallback.</em>
52
     *
53
     * @attribute focusAfterHide
54
     * @default null
55
     * @type Node
56
     */
57
    focusAfterHide: {
58
        value: null,
59
        type: Y.Node
60
    }
61
};
62
 
63
WidgetFocusAfterHide.prototype = {
64
    /**
65
     * The list of Event Handles which we should cancel when the dialogue is destroyed.
66
     *
67
     * @property uiHandleFocusAfterHide
68
     * @type array
69
     * @protected
70
     */
71
    _uiHandlesFocusAfterHide: [],
72
 
73
    /**
74
     * A reference to the real show method which is being overwritten.
75
     *
76
     * @property _showFocusAfterHide
77
     * @type function
78
     * @default null
79
     * @protected
80
     */
81
    _showFocusAfterHide: null,
82
 
83
    /**
84
     * A reference to the detected previous target.
85
     *
86
     * @property _previousTargetFocusAfterHide
87
     * @type function
88
     * @default null
89
     * @protected
90
     */
91
    _previousTargetFocusAfterHide: null,
92
 
93
    initializer: function() {
94
 
95
        if (this.get('focusOnPreviousTargetAfterHide') && this.show) {
96
            // Overwrite the parent method so that we can get the focused
97
            // target.
98
            this._showFocusAfterHide = this.show;
99
            this.show = function(e) {
100
                this._showFocusAfterHide.apply(this, arguments);
101
 
102
                // We use a property rather than overriding the focusAfterHide parameter in
103
                // case the target cannot be found at hide time.
104
                this._previousTargetFocusAfterHide = null;
105
                if (e && e.currentTarget) {
106
                    Y.log("Determined a Node which caused the Widget to be shown",
107
                            'debug', 'moodle-core-widget-focusafterhide');
108
                    this._previousTargetFocusAfterHide = e.currentTarget;
109
                }
110
            };
111
        }
112
    },
113
 
114
    destructor: function() {
115
        new Y.EventHandle(this.uiHandleFocusAfterHide).detach();
116
    },
117
 
118
    /**
119
     * Set up the event handling required for this module to work.
120
     *
121
     * @method _bindUIFocusAfterHide
122
     * @private
123
     */
124
    _bindUIFocusAfterHide: function() {
125
        // Detach the old handles first.
126
        new Y.EventHandle(this.uiHandleFocusAfterHide).detach();
127
        this.uiHandleFocusAfterHide = [
128
            this.after('visibleChange', this._afterHostVisibleChangeFocusAfterHide)
129
        ];
130
    },
131
 
132
    /**
133
     * Handle the change in UI visibility.
134
     *
135
     * This method changes the focus after the hide has taken place.
136
     *
137
     * @method _afterHostVisibleChangeFocusAfterHide
138
     * @private
139
     */
140
    _afterHostVisibleChangeFocusAfterHide: function() {
141
        if (!this.get('visible')) {
142
            if (this._attemptFocus(this._previousTargetFocusAfterHide)) {
143
                Y.log("Focusing on the target automatically determined when the Widget was opened",
144
                        'debug', 'moodle-core-widget-focusafterhide');
145
 
146
            } else if (this._attemptFocus(this.get('focusAfterHide'))) {
147
                // Fall back to the focusAfterHide value if one was specified.
148
                Y.log("Focusing on the target provided to focusAfterHide",
149
                        'debug', 'moodle-core-widget-focusafterhide');
150
 
151
            } else {
152
                Y.log("No valid focus target found - not returning focus.",
153
                        'debug', 'moodle-core-widget-focusafterhide');
154
 
155
            }
156
        }
157
    },
158
 
159
    _attemptFocus: function(node) {
160
        var focusTarget = Y.one(node);
161
        if (focusTarget) {
162
            focusTarget = focusTarget.ancestor(CAN_RECEIVE_FOCUS_SELECTOR, true);
163
            if (focusTarget) {
164
                focusTarget.focus();
165
                return true;
166
            }
167
        }
168
        return false;
169
    }
170
};
171
 
172
var NS = Y.namespace('M.core');
173
NS.WidgetFocusAfterHide = WidgetFocusAfterHide;