Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(467)

Side by Side Diff: chrome/browser/resources/access_chromevox/common/dom_util.js

Issue 6254007: Adding ChromeVox as a component extensions (enabled only for ChromeOS, for no... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:executable
+ *
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview A collection of JavaScript utilities used to simplify working
7 * with the DOM.
8 */
9
10
11 goog.provide('cvox.DomUtil');
12
13 goog.require('cvox.AriaUtil');
14 goog.require('cvox.XpathUtil');
15
16 /**
17 * Create the namespace
18 * @constructor
19 */
20 cvox.DomUtil = function() {
21 };
22
23
24 /**
25 * @type {Object}
26 */
27 cvox.DomUtil.INPUT_TYPE_TO_INFORMATION_TABLE = {
28 'button' : 'Button',
29 'checkbox' : 'Check box',
30 'color' : 'Color picker',
31 'datetime' : 'Date time control',
32 'datetime-local' : 'Date time control',
33 'date' : 'Date control',
34 'email' : 'Edit text for email',
35 'file' : 'File selection',
36 'hidden' : '',
37 'image' : 'Button',
38 'month' : 'Month control',
39 'number' : 'Edit text numeric only',
40 'password' : 'Password edit text',
41 'radio' : 'Radio button',
42 'range' : 'Slider',
43 'reset' : 'Reset',
44 'search' : 'Edit text for search',
45 'submit' : 'Button',
46 'tel' : 'Edit text for telephone number',
47 'text' : 'Edit text',
48 'url' : 'Edit text for URL',
49 'week' : 'Week of the year control'
50 };
51 /**
52 * @type {Object}
53 */
54 cvox.DomUtil.TAG_TO_INFORMATION_TABLE = {
55 'A' : 'Link',
56 'H1' : 'Heading 1',
57 'H2' : 'Heading 2',
58 'H3' : 'Heading 3',
59 'H4' : 'Heading 4',
60 'H5' : 'Heading 5',
61 'H6' : 'Heading 6',
62 'BUTTON' : 'Button',
63 'SELECT' : 'Combo box',
64 'TEXTAREA' : 'Text area'
65 };
66
67
68 /**
69 * Determines whether or not a style is invisible according to any CSS
70 * criteria that can hide a node.
71 *
72 * @param {Object} style An object's style.
73 * @return {boolean} True if the style is invisible.
74 */
75 cvox.DomUtil.isInvisibleStyle = function(style) {
76 if (!style) {
77 return false;
78 }
79 if (style.display == 'none') {
80 return true;
81 }
82 if (style.visibility == 'hidden') {
83 return true;
84 }
85 if (style.opacity == 0) {
86 return true;
87 }
88 return false;
89 };
90
91
92 /**
93 * Determines whether or not a node is a leaf node.
94 *
95 * @param {Node} node The node to be checked.
96 * @return {boolean} True if the node is a leaf node.
97 */
98 cvox.DomUtil.isLeafNode = function(node) {
99 // TODO (clchen): account for widgets that should be treated as leaf nodes.
100 if (!node.firstChild) {
101 return true;
102 }
103 // Think of hidden nodes as spacer nodes; leaf node with no content.
104 if (node.nodeType == 1) { // nodeType:1 == ELEMENT_NODE
105 var style = document.defaultView.getComputedStyle(node, null);
106 if (cvox.DomUtil.isInvisibleStyle(style)) {
107 return true;
108 }
109 }
110 if (cvox.AriaUtil.isHidden(node)) {
111 return true;
112 }
113 if (node.tagName) {
114 if (node.tagName == 'SELECT') {
115 return true;
116 }
117 if (node.tagName == 'TEXTAREA') {
118 return true;
119 }
120 if (node.tagName == 'LABEL') {
121 return true;
122 }
123 }
124 if (cvox.DomUtil.getTitle(node)) {
125 return true;
126 }
127 return false;
128 };
129
130
131 /**
132 * Determines whether or not a node is or is the descendant of a node
133 * with a particular tag or class name.
134 *
135 * @param {Node} node The node to be checked.
136 * @param {?string} tagName The tag to check for, or null if the tag
137 * doesn't matter.
138 * @param {?string=} className The class to check for, or null if the class
139 * doesn't matter.
140 * @return {boolean} True if the node or one of its ancestor has the specified
141 * tag.
142 */
143 cvox.DomUtil.isDescendantOf = function(node, tagName, className) {
144 while (node) {
145
146 if (tagName && className &&
147 (node.tagName && (node.tagName == tagName)) &&
148 (node.className && (node.className == className))) {
149 return true;
150 } else if (tagName && !className &&
151 (node.tagName && (node.tagName == tagName))) {
152 return true;
153 } else if (!tagName && className &&
154 (node.className && (node.className == className))) {
155 return true;
156 }
157 node = node.parentNode;
158 }
159 return false;
160 };
161
162
163 /**
164 * Determines whether or not a node is or is the descendant of another node.
165 *
166 * @param {Object} node The node to be checked.
167 * @param {Object} ancestor The node to see if it's a descendant of.
168 * @return {boolean} True if the node is ancestor or is a descendant of it.
169 */
170 cvox.DomUtil.isDescendantOfNode = function(node, ancestor) {
171 while (node && ancestor) {
172 if (node == ancestor) {
173 return true;
174 }
175 node = node.parentNode;
176 }
177 return false;
178 };
179
180 /**
181 * Get the label of a node.
182 *
183 * Not recursive.
184 *
185 * @param {Node} node The node to get the title from.
186 * @param {boolean} useHeuristics Whether or not to use heuristics to guess at
187 * the label if one is not explicitly set in the DOM.
188 * @return {string} The label of the node.
189 */
190 cvox.DomUtil.getLabel = function(node, useHeuristics) {
191 var label = '';
192 if (!node) {
193 return '';
194 }
195 // Find any labels that are associated with this text control.
196 // aria-labelledby takes precedence and overrides any label for= elements.
197 if (node.hasAttribute && node.hasAttribute('aria-labelledby')) {
198 var labelNodeIds = node.getAttribute('aria-labelledby').split(' ');
199 for (var labelNodeId, i = 0; labelNodeId = labelNodeIds[i]; i++) {
200 var labelNode = document.getElementById(labelNodeId);
201 label += cvox.DomUtil.getText(labelNode) + ' ';
202 }
203 } else if (node && node.id) {
204 var labels = cvox.XpathUtil.evalXPath('//label[@for="' +
205 node.id + '"]', document.body);
206 if (labels.length > 0) {
207 label += cvox.DomUtil.getText(labels[0]) + ' ';
208 }
209 }
210
211 // If no description has been found yet and heuristics are enabled,
212 // then try getting the content from the previous node.
213 if (useHeuristics && (label.length < 1)) {
214 var tempNode = cvox.DomUtil.previousLeafNode(node);
215 while (tempNode && !cvox.DomUtil.hasContent(tempNode)) {
216 tempNode = cvox.DomUtil.previousLeafNode(tempNode);
217 }
218 if (tempNode) {
219 if (!cvox.DomUtil.isControl(tempNode)) {
220 label += cvox.DomUtil.getText(tempNode) + ' ';
221 }
222 }
223 }
224
225 return label;
226 };
227
228 /**
229 * Get the title of a node. In many cases this is equivalent to the
230 * text of the node, but it can be overridden by a title tag or alt tag,
231 * and for some form controls (like submit buttons) the title is actually
232 * the value.
233 *
234 * Not recursive.
235 *
236 * @param {Node} node The node to get the title from.
237 * @return {string} The title of the node.
238 */
239 cvox.DomUtil.getTitle = function(node) {
240 if (node.constructor == Text) {
241 return node.data;
242 } else if (node.constructor == HTMLImageElement) {
243 return cvox.DomUtil.getImageTitle(node);
244 } else if (node.hasAttribute && node.hasAttribute('title')) {
245 return node.getAttribute('title');
246 } else if (node.constructor == HTMLInputElement) {
247 if (node.type == 'image') {
248 return cvox.DomUtil.getImageTitle(node);
249 } else if (node.type == 'submit') {
250 if (node.hasAttribute && node.hasAttribute('value')) {
251 return node.getAttribute('value');
252 } else {
253 return 'Submit';
254 }
255 } else if (node.type == 'reset') {
256 if (node.hasAttribute && node.hasAttribute('value')) {
257 return node.getAttribute('value');
258 } else {
259 return 'Reset';
260 }
261 }
262 }
263 return '';
264 };
265
266 /**
267 * Get the text value of a node: the selected value of a select control or the
268 * current text of a text control. Does not return the state of a checkbox
269 * or radio button.
270 *
271 * Not recursive.
272 *
273 * @param {Node} node The node to get the value from.
274 * @return {String} The value of the node.
275 */
276 cvox.DomUtil.getValue = function(node) {
277 if (node.constructor == HTMLSelectElement) {
278 if (node.selectedIndex >= 0 &&
279 node.selectedIndex < node.options.length) {
280 return node.options[node.selectedIndex].text + '';
281 } else {
282 return '';
283 }
284 }
285
286 if (node.constructor == HTMLTextAreaElement) {
287 return node.value;
288 }
289
290 if (node.constructor == HTMLInputElement) {
291 switch (node.type) {
292 // Returning '' for the submit button since it is covered by getText.
293 case 'hidden':
294 case 'image':
295 case 'submit':
296 case 'reset':
297 case 'checkbox':
298 case 'radio':
299 return '';
300 case 'password':
301 return node.value.replace(/./g, '*');
302 default:
303 return node.value;
304 }
305 }
306
307 return '';
308 };
309
310
311 /**
312 * Given a node, return its complete text as a string. This is recursive;
313 * it will extract the text from all child nodes and concatenate it,
314 * removing extraneous whitespace.
315 *
316 * This is similar to accessing the textContent property of a node, but
317 * that doesn't handle nodes that override the text with a title attribute,
318 * or nodes that can have both a title and a value.
319 *
320 * This function concatenates the value and title of a node (value first,
321 * if both are present) and recursively concatenates the text of children
322 * of any node that doesn't have either a value or title.
323
324 * @param {Node} node The node to extract the text from.
325 * @return {string} The text of the node.
326 */
327 cvox.DomUtil.getText = function(node) {
328 var title = cvox.DomUtil.getTitle(node);
329 var value = cvox.DomUtil.getValue(node);
330 var text = '';
331 if (title && value) {
332 text = value + ' ' + title;
333 } else if (title) {
334 text = title;
335 } else if (value) {
336 text = value;
337 } else if (!cvox.DomUtil.isControl(node)) {
338 for (var i = 0; i < node.childNodes.length; i++) {
339 var child = node.childNodes[i];
340 var childStyle = window.getComputedStyle(child, null);
341 if (!cvox.DomUtil.isInvisibleStyle(childStyle) &&
342 !cvox.AriaUtil.isHidden(node)) {
343 text += ' ' + cvox.DomUtil.getText(child);
344 }
345 }
346 }
347 // Remove all whitespace from the beginning and end, and collapse all
348 // inner strings of whitespace to a single space.
349 text = text.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '');
350
351 return text;
352 };
353
354
355 /**
356 * Given an image node, return its title as a string. The preferred title
357 * is always the alt text, and if that's not available, then the title
358 * attribute. If neither of those are available, it attempts to construct
359 * a title from the filename, and if all else fails returns the word Image.
360 * @param {Node} node The image node.
361 * @return {string} The title of the image.
362 */
363 cvox.DomUtil.getImageTitle = function(node) {
364 var text;
365 if (node.hasAttribute('alt')) {
366 text = node.alt;
367 } else if (node.hasAttribute('title')) {
368 text = node.title;
369 } else {
370 var url = node.src;
371 if (url.substring(0, 4) != 'data') {
372 var filename = url.substring(
373 url.lastIndexOf('/') + 1, url.lastIndexOf('.'));
374
375 // Hack to not speak the filename if it's ridiculously long.
376 if (filename.length >= 1 && filename.length <= 16) {
377 text = filename + ' Image';
378 } else {
379 text = 'Image';
380 }
381 } else {
382 text = 'Image';
383 }
384 }
385 return text;
386 };
387
388
389 /**
390 * Determines whether or not a node has content.
391 *
392 * @param {Node} node The node to be checked.
393 * @return {boolean} True if the node has content.
394 */
395 cvox.DomUtil.hasContent = function(node) {
396 // nodeType:8 == COMMENT_NODE
397 if (node.nodeType == 8) {
398 return false;
399 }
400
401 // Exclude anything in the head
402 if (cvox.DomUtil.isDescendantOf(node, 'HEAD')) {
403 return false;
404 }
405
406 // Exclude script nodes
407 if (cvox.DomUtil.isDescendantOf(node, 'SCRIPT')) {
408 return false;
409 }
410
411 // Exclude noscript nodes
412 if (cvox.DomUtil.isDescendantOf(node, 'NOSCRIPT')) {
413 return false;
414 }
415
416 // Exclude style nodes that have been dumped into the body
417 if (cvox.DomUtil.isDescendantOf(node, 'STYLE')) {
418 return false;
419 }
420
421 // Check the style to exclude undisplayed/hidden nodes
422 var closestStyledParent = node;
423 // nodeType:3 == TEXT_NODE
424 while (closestStyledParent && (closestStyledParent.nodeType == 3)) {
425 closestStyledParent = closestStyledParent.parentNode;
426 }
427 if (closestStyledParent) {
428 var style =
429 document.defaultView.getComputedStyle(closestStyledParent, null);
430 if (cvox.DomUtil.isInvisibleStyle(style)) {
431 return false;
432 }
433 // TODO (clchen, raman): Look into why WebKit has a problem here.
434 // The issue is that getComputedStyle does not always return the correct
435 // result; manually going up the parent chain sometimes produces a different
436 // result than just using getComputedStyle.
437 var tempNode = closestStyledParent;
438 while (tempNode && tempNode.tagName != 'BODY') {
439 style = document.defaultView.getComputedStyle(tempNode, null);
440 if (cvox.DomUtil.isInvisibleStyle(style)) {
441 return false;
442 }
443 tempNode = tempNode.parentNode;
444 }
445 }
446
447 // Ignore anything that is hidden by ARIA
448 if (cvox.AriaUtil.isHidden(node)) {
449 return false;
450 }
451
452 // We need to speak controls, including those with no value entered. We
453 // therefore treat visible controls as if they had content, and return true
454 // below.
455 if (cvox.DomUtil.isControl(node)) {
456 return true;
457 }
458
459 var text = cvox.DomUtil.getText(node);
460 if (text === '') {
461 // Text only contains whitespace
462 return false;
463 }
464
465 return true;
466 };
467
468 /**
469 * Returns a list of all the ancestors of a given node.
470 *
471 * @param {Object} targetNode The node to get ancestors for.
472 * @return {Object} An array of ancestors for the targetNode.
473 */
474 cvox.DomUtil.getAncestors = function(targetNode) {
475 var ancestors = new Array();
476 while (targetNode) {
477 ancestors.push(targetNode);
478 targetNode = targetNode.parentNode;
479 }
480 ancestors.reverse();
481 while (ancestors.length && !ancestors[0].tagName && !ancestors[0].nodeValue) {
482 ancestors.shift();
483 }
484 return ancestors;
485 };
486
487 /**
488 * Compares Ancestors of A with Ancestors of B and returns
489 * the index value in B at which B diverges from A.
490 * If there is no divergence, the result will be -1.
491 * Note that if B is the same as A except B has more nodes
492 * even after A has ended, that is considered a divergence.
493 * The first node that B has which A does not have will
494 * be treated as the divergence point.
495 *
496 * @param {Object} ancestorsA The array of ancestors for Node A.
497 * @param {Object} ancestorsB The array of ancestors for Node B.
498 * @return {number} The index of the divergence point (the first node that B has
499 * which A does not have in B's list of ancestors).
500 */
501 cvox.DomUtil.compareAncestors = function(ancestorsA, ancestorsB) {
502 var i = 0;
503 while (ancestorsA[i] && ancestorsB[i] && (ancestorsA[i] == ancestorsB[i])) {
504 i++;
505 }
506 if (!ancestorsA[i] && !ancestorsB[i]) {
507 i = -1;
508 }
509 return i;
510 };
511
512 /**
513 * Returns an array of ancestors that are unique for the currentNode when
514 * compared to the previousNode. Having such an array is useful in generating
515 * the node information (identifying when interesting node boundaries have been
516 * crossed, etc.).
517 *
518 * @param {Object} previousNode The previous node.
519 * @param {Object} currentNode The current node.
520 * @return {Array.<Node>} An array of unique ancestors for the current node.
521 */
522 cvox.DomUtil.getUniqueAncestors = function(previousNode, currentNode) {
523 var prevAncestors = cvox.DomUtil.getAncestors(previousNode);
524 var currentAncestors = cvox.DomUtil.getAncestors(currentNode);
525 var divergence = cvox.DomUtil.compareAncestors(prevAncestors,
526 currentAncestors);
527 return currentAncestors.slice(divergence);
528 };
529
530 /**
531 * Returns a string of basic information about the target node.
532 * This information is only about the node itself and does not take into
533 * account any of the node's ancestors.
534 *
535 * @param {Object} targetNode The node to get information about.
536 * @return {string} A string of basic information about the current node.
537 */
538 cvox.DomUtil.getBasicNodeInformation = function(targetNode) {
539 var info = cvox.DomUtil.getBasicNodeRole(targetNode);
540 if (info.length > 0) {
541 info = info + ' ' + cvox.DomUtil.getBasicNodeState(targetNode);
542 }
543 return info;
544 };
545
546 /**
547 * Returns a string to be presented to the user that identifies what the
548 * targetNode's role is.
549 * ARIA roles are given priority; if there is no ARIA role set, the role
550 * will be determined by the HTML tag for the node.
551 *
552 * @param {Object} targetNode The node to get the role name for.
553 * @return {string} The role name for the targetNode.
554 */
555 cvox.DomUtil.getBasicNodeRole = function(targetNode) {
556 var info;
557 info = cvox.AriaUtil.getRoleName(targetNode);
558 if (!info) {
559 if (targetNode.tagName == 'INPUT') {
560 info = cvox.DomUtil.INPUT_TYPE_TO_INFORMATION_TABLE[targetNode.type];
561 } else {
562 info = cvox.DomUtil.TAG_TO_INFORMATION_TABLE[targetNode.tagName];
563 }
564 }
565 if (!info) {
566 info = '';
567 }
568 return info;
569 };
570
571 /**
572 * Returns a string that gives information about the state of the targetNode.
573 *
574 * @param {Object} targetNode The node to get the state information for.
575 * @return {string} The status information about the node.
576 */
577 cvox.DomUtil.getBasicNodeState = function(targetNode) {
578 var info;
579 info = cvox.AriaUtil.getState(targetNode);
580 if (!info) {
581 info = '';
582 } else {
583 info = info + ' ';
584 }
585
586 if (targetNode.tagName == 'INPUT') {
587 if (targetNode.type == 'checkbox' || targetNode.type == 'radio') {
588 if (targetNode.checked) {
589 info = info + ' checked';
590 } else {
591 info = info + ' not checked';
592 }
593 }
594 } else if (targetNode.tagName == 'SELECT') {
595 info = info + ' ' + (targetNode.selectedIndex + 1) + ' of ' +
596 targetNode.options.length;
597 }
598
599 return info;
600 };
601
602
603 /**
604 * Returns a string of detailed information given an array of
605 * ancestor nodes.
606 *
607 * @param {Object} ancestorsArray An array of ancestor nodes.
608 * @return {string} A string of detailed information given the
609 * array of ancestor nodes.
610 */
611 cvox.DomUtil.getInformationFromAncestors = function(ancestorsArray) {
612 var info = '';
613 for (var i = 0, node; node = ancestorsArray[i]; i++) {
614 var nodeInfo = cvox.DomUtil.getBasicNodeInformation(node);
615 if (nodeInfo.length > 0) {
616 info = info + ' ' + nodeInfo;
617 }
618 }
619 return info;
620 };
621
622
623 /**
624 * Sets the browser focus to the targetNode or its closest ancestor that is
625 * able to get focus.
626 *
627 * @param {Object} targetNode The node to move the browser focus to.
628 */
629 cvox.DomUtil.setFocus = function(targetNode) {
630 while (targetNode && ((typeof(targetNode.tabIndex) == 'undefined') ||
631 (targetNode.tabIndex == -1))) {
632 // If the target is a label for a control, focus the control.
633 if (targetNode.tagName && (targetNode.tagName == 'LABEL')) {
634 if (targetNode.htmlFor && document.getElementById(targetNode.htmlFor)) {
635 targetNode = document.getElementById(targetNode.htmlFor);
636 } else {
637 // Handle the case if a label is wrapping a control
638 var inputElems = targetNode.getElementsByTagName('INPUT');
639 if (inputElems && (inputElems.length > 0)) {
640 // In case there are multiple controls, focus on the first one.
641 // The user can always read through to the next control.
642 targetNode = inputElems[0];
643 } else {
644 // No wrapped controls found. In this case, keep moving
645 // because it means the page author was misusing label
646 // and failed to associate it with anything.
647 targetNode = targetNode.parentNode;
648 }
649 }
650 } else {
651 targetNode = targetNode.parentNode;
652 }
653
654 }
655 if (targetNode && (typeof(targetNode.tabIndex) != 'undefined') &&
656 (targetNode.tabIndex != -1)) {
657 targetNode.focus();
658 } else {
659 if (document.activeElement && (document.activeElement.tagName != 'BODY')) {
660 // Chrome will lose the selection if there is a blur, even if the blur
661 // does not touch the selection. To work around this, backup the
662 // selection, do the blur, then restore the selection.
663 var sel = window.getSelection();
664 if (sel.rangeCount > 0) {
665 var range = sel.getRangeAt(0);
666 document.activeElement.blur();
667 sel.removeAllRanges();
668 sel.addRange(range);
669 }
670 }
671 }
672 };
673
674 /**
675 * Checks if the targetNode is still attached to the document.
676 * A node can become detached because of AJAX changes.
677 *
678 * @param {Object} targetNode The node to check.
679 * @return {boolean} True if the targetNode is still attached.
680 */
681 cvox.DomUtil.isAttachedToDocument = function(targetNode) {
682 while (targetNode) {
683 if (targetNode.tagName && (targetNode.tagName == 'HTML')) {
684 return true;
685 }
686 targetNode = targetNode.parentNode;
687 }
688 return false;
689 };
690
691
692 /**
693 * Dispatches a left click event on the element that is the targetNode.
694 * Clicks go in the sequence of mousedown, mouseup, and click.
695 * @param {Node} targetNode The target node of this operation.
696 * @param {boolean} shiftKey Specifies if shift is held down.
697 */
698 cvox.DomUtil.clickElem = function(targetNode, shiftKey) {
699 //Send a mousedown
700 var evt = document.createEvent('MouseEvents');
701 evt.initMouseEvent('mousedown', true, true, document.defaultView,
702 1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null);
703 //Use a try block here so that if the AJAX fails and it is a link,
704 //it can still fall through and retry by setting the document.location.
705 try {
706 targetNode.dispatchEvent(evt);
707 } catch (e) {}
708 //Send a mouse up
709 evt = document.createEvent('MouseEvents');
710 evt.initMouseEvent('mouseup', true, true, document.defaultView,
711 1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null);
712 //Use a try block here so that if the AJAX fails and it is a link,
713 //it can still fall through and retry by setting the document.location.
714 try {
715 targetNode.dispatchEvent(evt);
716 } catch (e) {}
717 //Send a click
718 evt = document.createEvent('MouseEvents');
719 evt.initMouseEvent('click', true, true, document.defaultView,
720 1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null);
721 //Use a try block here so that if the AJAX fails and it is a link,
722 //it can still fall through and retry by setting the document.location.
723 try {
724 targetNode.dispatchEvent(evt);
725 } catch (e) {}
726 //Clicking on a link does not cause traversal because of script
727 //privilege limitations. The traversal has to be done by setting
728 //document.location.
729 var href = targetNode.getAttribute('href');
730 if ((targetNode.tagName == 'A') &&
731 href &&
732 (href != '#')) {
733 if (shiftKey) {
734 window.open(targetNode.href);
735 } else {
736 document.location = targetNode.href;
737 }
738 }
739
740 };
741
742
743 /**
744 * Given an HTMLInputElement, returns true if it's an editable text type.
745 * This includes input type='text' and input type='password' and a few
746 * others.
747 *
748 * @param {Node} node The node to check.
749 * @return {boolean} True if the node is an INPUT with an editable text type.
750 */
751 cvox.DomUtil.isInputTypeText = function(node) {
752 if (node.constructor != HTMLInputElement) {
753 return false;
754 }
755
756 switch (node.type) {
757 case 'email':
758 case 'number':
759 case 'password':
760 case 'search':
761 case 'text':
762 case 'tel':
763 case 'url':
764 case '':
765 return true;
766 default:
767 return false;
768 }
769 };
770
771 /**
772 * Given a node, returns true if it's a control.
773 * Note that controls are all leaf level widgets; they
774 * are NOT containers.
775 *
776 * @param {Node} node The node to check.
777 * @return {boolean} True if the node is a control.
778 */
779 cvox.DomUtil.isControl = function(node) {
780 if (cvox.AriaUtil.isControlWidget(node)) {
781 return true;
782 }
783 if (node.tagName) {
784 switch (node.tagName) {
785 case 'BUTTON':
786 case 'INPUT':
787 case 'TEXTAREA':
788 case 'SELECT':
789 return true;
790 }
791 }
792 return false;
793 };
794
795 /**
796 * Given a node, returns the next leaf node.
797 *
798 * @param {Node} node The node from which to start looking
799 * for the next leaf node.
800 * @return {Node} The next leaf node.
801 * Null if there is no next leaf node.
802 */
803 cvox.DomUtil.nextLeafNode = function(node) {
804 var tempNode = node;
805 while (tempNode && (!tempNode.nextSibling)) {
806 tempNode = tempNode.parentNode;
807 }
808 if (tempNode && tempNode.nextSibling) {
809 tempNode = tempNode.nextSibling;
810 while (!cvox.DomUtil.isLeafNode(tempNode)) {
811 tempNode = tempNode.firstChild;
812 }
813 }
814 return tempNode;
815 };
816
817 /**
818 * Given a node, returns the previous leaf node.
819 *
820 * @param {Node} node The node from which to start looking
821 * for the previous leaf node.
822 * @return {Node} The previous leaf node.
823 * Null if there is no previous leaf node.
824 */
825 cvox.DomUtil.previousLeafNode = function(node) {
826 var tempNode = node;
827 while (tempNode && (!tempNode.previousSibling)) {
828 tempNode = tempNode.parentNode;
829 }
830 if (tempNode && tempNode.previousSibling) {
831 tempNode = tempNode.previousSibling;
832 while (!cvox.DomUtil.isLeafNode(tempNode)) {
833 tempNode = tempNode.lastChild;
834 }
835 }
836 return tempNode;
837 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698