// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617goog.provide('bot.locators.id');1819goog.require('bot.dom');20goog.require('goog.array');21goog.require('goog.dom');222324/**25* Tests whether the standardized W3C Selectors API are available on an26* element and the target locator meets CSS requirements.27* @param {!(Document|Element)} root The document or element to test for CSS28* selector support.29* @param {string} target The id to search for.30* @return {boolean} Whether or not the root supports query selector APIs.31* @see http://www.w3.org/TR/selectors-api/32* @private33*/34bot.locators.id.canUseQuerySelector_ = function (root, target) {35return !!(root.querySelectorAll && root.querySelector) && !/^\d.*/.test(target);36};373839/**40* Find an element by using the value of the ID attribute.41* @param {string} target The id to search for.42* @param {!(Document|Element)} root The document or element to perform the43* search under.44* @return {Element} The first matching element found in the DOM, or null if no45* such element could be found.46*/47bot.locators.id.single = function (target, root) {48var dom = goog.dom.getDomHelper(root);4950var e = dom.getElement(target);51if (!e) {52return null;53}5455// On IE getting by ID returns the first match by id _or_ name.56if (bot.dom.getAttribute(e, 'id') == target &&57root != e && goog.dom.contains(root, e)) {58return e;59}6061var elements = dom.getElementsByTagNameAndClass('*');62var element = goog.array.find(elements, function (element) {63return bot.dom.getAttribute(element, 'id') == target &&64root != element && goog.dom.contains(root, element);65});66return /**@type{Element}*/ (element);67};686970/**71* Find many elements by using the value of the ID attribute.72* @param {string} target The id to search for.73* @param {!(Document|Element)} root The document or element to perform the74* search under.75* @return {!IArrayLike} All matching elements, or an empty list.76*/77bot.locators.id.many = function (target, root) {78if (!target) {79return [];80}81if (bot.locators.id.canUseQuerySelector_(root, target)) {82try {83// Need to escape the ID for use in a CSS selector.84return root.querySelectorAll('#' + bot.locators.id.cssEscape_(target));85} catch (e) {86return [];87}88}89var dom = goog.dom.getDomHelper(root);90var elements = dom.getElementsByTagNameAndClass('*', null, root);91return goog.array.filter(elements, function (e) {92return bot.dom.getAttribute(e, 'id') == target;93});94};9596/**97* Given a string, escapes all the characters that have special meaning in CSS.98* https://mathiasbynens.be/notes/css-escapes99*100* An ID can contain anything but spaces, but we also escape whitespace because101* some webpages use spaces, and getElementById allows spaces in every browser.102* http://www.w3.org/TR/html5/dom.html#the-id-attribute103*104* This could be further improved, perhaps by using105* http://dev.w3.org/csswg/cssom/#the-css.escape()-method , where implemented,106* or a polyfill such as https://github.com/mathiasbynens/CSS.escape.107*108* @param {!string} s String to escape CSS meaningful characters in.109* @return {!string} Escaped string.110* @private111*/112bot.locators.id.cssEscape_ = function (s) {113// One backslash escapes things in a regex statement; we need two in a string.114return s.replace(/([\s'"\\#.:;,!?+<>=~*^$|%&@`{}\-\/\[\]\(\)])/g, '\\$1');115};116117118