1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
// Copyright 2012 Google Inc. All Rights Reserved.
/**
* @fileoverview Helper functions.
*/
goog.provide('cvox.SearchUtil');
/** Utility functions. */
cvox.SearchUtil = function() {
};
/**
* Extracts the first URL from an element.
* @param {Node} node DOM element to extract from.
* @return {?string} URL.
*/
cvox.SearchUtil.extractURL = function(node) {
if (node) {
if (node.tagName === 'A') {
return node.href;
}
var anchor = node.querySelector('a');
if (anchor) {
return anchor.href;
}
}
return null;
};
/**
* Indicates whether or not the search widget has been activated.
* @return {boolean} Whether or not the search widget is active.
*/
cvox.SearchUtil.isSearchWidgetActive = function() {
var SEARCH_WIDGET_SELECT = '#cvox-search';
return document.querySelector(SEARCH_WIDGET_SELECT) !== null;
};
/**
* Adds one to and index with wrapping.
* @param {number} index Index to add to.
* @param {number} length Length to wrap at.
* @return {number} The new index++, wrapped if exceeding length.
*/
cvox.SearchUtil.addOneWrap = function(index, length) {
return (index + 1) % length;
};
/**
* Subtracts one to and index with wrapping.
* @param {number} index Index to subtract from.
* @param {number} length Length to wrap at.
* @return {number} The new index--, wrapped if below 0.
*/
cvox.SearchUtil.subOneWrap = function(index, length) {
return (index - 1 + length) % length;
};
/**
* Returns the id of a node's active descendant
* @param {Node} targetNode The node.
* @return {?string} The id of the active descendant.
* @private
*/
var getActiveDescendantId_ = function(targetNode) {
if (!targetNode.getAttribute) {
return null;
}
var activeId = targetNode.getAttribute('aria-activedescendant');
if (!activeId) {
return null;
}
return activeId;
};
/**
* If the node is an object with an active descendant, returns the
* descendant node.
*
* This function will fully resolve an active descendant chain. If a circular
* chain is detected, it will return null.
*
* @param {Node} targetNode The node to get descendant information for.
* @return {Node} The descendant node or null if no node exists.
*/
var getActiveDescendant = function(targetNode) {
var seenIds = {};
var node = targetNode;
while (node) {
var activeId = getActiveDescendantId_(node);
if (!activeId) {
break;
}
if (activeId in seenIds) {
// A circlar activeDescendant is an error, so return null.
return null;
}
seenIds[activeId] = true;
node = document.getElementById(activeId);
}
if (node == targetNode) {
return null;
}
return node;
};
/**
* Dispatches a left click event on the element that is the targetNode.
* Clicks go in the sequence of mousedown, mouseup, and click.
* @param {Node} targetNode The target node of this operation.
* @param {boolean=} shiftKey Specifies if shift is held down.
* @param {boolean=} callOnClickDirectly Specifies whether or not to directly
* invoke the onclick method if there is one.
* @param {boolean=} opt_double True to issue a double click.
*/
cvox.SearchUtil.clickElem = function(
targetNode, shiftKey, callOnClickDirectly, opt_double) {
// If there is an activeDescendant of the targetNode, then that is where the
// click should actually be targeted.
var activeDescendant = getActiveDescendant(targetNode);
if (activeDescendant) {
targetNode = activeDescendant;
}
if (callOnClickDirectly) {
var onClickFunction = null;
if (targetNode.onclick) {
onClickFunction = targetNode.onclick;
}
if (!onClickFunction && (targetNode.nodeType != 1) &&
targetNode.parentNode && targetNode.parentNode.onclick) {
onClickFunction = targetNode.parentNode.onclick;
}
var keepGoing = true;
if (onClickFunction) {
try {
keepGoing = onClickFunction();
} catch (exception) {
// Something went very wrong with the onclick method; we'll ignore it
// and just dispatch a click event normally.
}
}
if (!keepGoing) {
// The onclick method ran successfully and returned false, meaning the
// event should not bubble up, so we will return here.
return;
}
}
// Send a mousedown (or simply a double click if requested).
var evt = document.createEvent('MouseEvents');
var evtType = opt_double ? 'dblclick' : 'mousedown';
evt.initMouseEvent(evtType, true, true, document.defaultView,
1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null);
// Mark any events we generate so we don't try to process our own events.
evt.fromCvox = true;
try {
targetNode.dispatchEvent(evt);
} catch (e) {}
//Send a mouse up
evt = document.createEvent('MouseEvents');
evt.initMouseEvent('mouseup', true, true, document.defaultView,
1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null);
// Mark any events we generate so we don't try to process our own events.
evt.fromCvox = true;
try {
targetNode.dispatchEvent(evt);
} catch (e) {}
//Send a click
evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, document.defaultView,
1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null);
// Mark any events we generate so we don't try to process our own events.
evt.fromCvox = true;
try {
targetNode.dispatchEvent(evt);
} catch (e) {}
};
|