/services/sync/tps/extensions/mozmill/resource/modules/elementslib.js
http://github.com/zpao/v8monkey · JavaScript · 478 lines · 438 code · 3 blank · 37 comment · 0 complexity · 960ed589d1fb250dfd853a88d9aefb3c MD5 · raw file
- // ***** BEGIN LICENSE BLOCK *****// ***** BEGIN LICENSE BLOCK *****
- // Version: MPL 1.1/GPL 2.0/LGPL 2.1
- //
- // The contents of this file are subject to the Mozilla Public License Version
- // 1.1 (the "License"); you may not use this file except in compliance with
- // the License. You may obtain a copy of the License at
- // http://www.mozilla.org/MPL/
- //
- // Software distributed under the License is distributed on an "AS IS" basis,
- // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- // for the specific language governing rights and limitations under the
- // License.
- //
- // The Original Code is Mozilla Corporation Code.
- //
- // The Initial Developer of the Original Code is
- // Adam Christian.
- // Portions created by the Initial Developer are Copyright (C) 2008
- // the Initial Developer. All Rights Reserved.
- //
- // Contributor(s):
- // Adam Christian <adam.christian@gmail.com>
- // Mikeal Rogers <mikeal.rogers@gmail.com>
- //
- // Alternatively, the contents of this file may be used under the terms of
- // either the GNU General Public License Version 2 or later (the "GPL"), or
- // the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- // in which case the provisions of the GPL or the LGPL are applicable instead
- // of those above. If you wish to allow use of your version of this file only
- // under the terms of either the GPL or the LGPL, and not to allow others to
- // use your version of this file under the terms of the MPL, indicate your
- // decision by deleting the provisions above and replace them with the notice
- // and other provisions required by the GPL or the LGPL. If you do not delete
- // the provisions above, a recipient may use your version of this file under
- // the terms of any one of the MPL, the GPL or the LGPL.
- //
- // ***** END LICENSE BLOCK *****
- var EXPORTED_SYMBOLS = ["Elem", "ID", "Link", "XPath", "Selector", "Name", "Anon", "AnonXPath",
- "Lookup", "_byID", "_byName", "_byAttrib", "_byAnonAttrib",
- ];
- var utils = {}; Components.utils.import('resource://mozmill/modules/utils.js', utils);
- var strings = {}; Components.utils.import('resource://mozmill/stdlib/strings.js', strings);
- var arrays = {}; Components.utils.import('resource://mozmill/stdlib/arrays.js', arrays);
- var json2 = {}; Components.utils.import('resource://mozmill/stdlib/json2.js', json2);
- var withs = {}; Components.utils.import('resource://mozmill/stdlib/withs.js', withs);
- var dom = {}; Components.utils.import('resource://mozmill/stdlib/dom.js', dom);
- var objects = {}; Components.utils.import('resource://mozmill/stdlib/objects.js', objects);
- var countQuotes = function(str){
- var count = 0;
- var i = 0;
- while(i < str.length) {
- i = str.indexOf('"', i);
- if (i != -1) {
- count++;
- i++;
- } else {
- break;
- }
- }
- return count;
- };
- /**
- * smartSplit()
- *
- * Takes a lookup string as input and returns
- * a list of each node in the string
- */
- var smartSplit = function (str) {
- // Ensure we have an even number of quotes
- if (countQuotes(str) % 2 != 0) {
- throw new Error ("Invalid Lookup Expression");
- }
- /**
- * This regex matches a single "node" in a lookup string.
- * In otherwords, it matches the part between the two '/'s
- *
- * Regex Explanation:
- * \/ - start matching at the first forward slash
- * ([^\/"]*"[^"]*")* - match as many pairs of quotes as possible until we hit a slash (ignore slashes inside quotes)
- * [^\/]* - match the remainder of text outside of last quote but before next slash
- */
- var re = /\/([^\/"]*"[^"]*")*[^\/]*/g
- var ret = []
- var match = re.exec(str);
- while (match != null) {
- ret.push(match[0].replace(/^\//, ""));
- match = re.exec(str);
- }
- return ret;
- };
- /**
- * defaultDocuments()
- *
- * Returns a list of default documents in which to search for elements
- * if no document is provided
- */
- function defaultDocuments() {
- var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator);
- win = windowManager.getMostRecentWindow("navigator:browser");
- return [win.gBrowser.selectedBrowser.contentDocument, win.document];
- };
- /**
- * nodeSearch()
- *
- * Takes an optional document, callback and locator string
- * Returns a handle to the located element or null
- */
- function nodeSearch(doc, func, string) {
- if (doc != undefined) {
- var documents = [doc];
- } else {
- var documents = defaultDocuments();
- }
- var e = null;
- var element = null;
- //inline function to recursively find the element in the DOM, cross frame.
- var search = function(win, func, string) {
- if (win == null)
- return;
- //do the lookup in the current window
- element = func.call(win, string);
- if (!element || (element.length == 0)) {
- var frames = win.frames;
- for (var i=0; i < frames.length; i++) {
- search(frames[i], func, string);
- }
- }
- else { e = element; }
- };
- for (var i = 0; i < documents.length; ++i) {
- var win = documents[i].defaultView;
- search(win, func, string);
- if (e) break;
- }
- return e;
- };
- /**
- * Selector()
- *
- * Finds an element by selector string
- */
- function Selector(_document, selector, index) {
- if (selector == undefined) {
- throw new Error('Selector constructor did not recieve enough arguments.');
- }
- this.selector = selector;
- this.getNodeForDocument = function (s) {
- return this.document.querySelectorAll(s);
- };
- var nodes = nodeSearch(_document, this.getNodeForDocument, this.selector);
- return nodes ? nodes[index || 0] : null;
- };
- /**
- * ID()
- *
- * Finds an element by ID
- */
- function ID(_document, nodeID) {
- if (nodeID == undefined) {
- throw new Error('ID constructor did not recieve enough arguments.');
- }
- this.getNodeForDocument = function (nodeID) {
- return this.document.getElementById(nodeID);
- };
- return nodeSearch(_document, this.getNodeForDocument, nodeID);
- };
- /**
- * Link()
- *
- * Finds a link by innerHTML
- */
- function Link(_document, linkName) {
- if (linkName == undefined) {
- throw new Error('Link constructor did not recieve enough arguments.');
- }
- this.getNodeForDocument = function (linkName) {
- var getText = function(el){
- var text = "";
- if (el.nodeType == 3){ //textNode
- if (el.data != undefined){
- text = el.data;
- } else {
- text = el.innerHTML;
- }
- text = text.replace(/n|r|t/g, " ");
- }
- if (el.nodeType == 1){ //elementNode
- for (var i = 0; i < el.childNodes.length; i++) {
- var child = el.childNodes.item(i);
- text += getText(child);
- }
- if (el.tagName == "P" || el.tagName == "BR" || el.tagName == "HR" || el.tagName == "DIV") {
- text += "n";
- }
- }
- return text;
- };
- //sometimes the windows won't have this function
- try {
- var links = this.document.getElementsByTagName('a'); }
- catch(err){ // ADD LOG LINE mresults.write('Error: '+ err, 'lightred');
- }
- for (var i = 0; i < links.length; i++) {
- var el = links[i];
- //if (getText(el).indexOf(this.linkName) != -1) {
- if (el.innerHTML.indexOf(linkName) != -1){
- return el;
- }
- }
- return null;
- };
- return nodeSearch(_document, this.getNodeForDocument, linkName);
- };
- /**
- * XPath()
- *
- * Finds an element by XPath
- */
- function XPath(_document, expr) {
- if (expr == undefined) {
- throw new Error('XPath constructor did not recieve enough arguments.');
- }
- this.getNodeForDocument = function (s) {
- var aNode = this.document;
- var aExpr = s;
- var xpe = null;
- if (this.document.defaultView == null) {
- xpe = new getMethodInWindows('XPathEvaluator')();
- } else {
- xpe = new this.document.defaultView.XPathEvaluator();
- }
- var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ? aNode.documentElement : aNode.ownerDocument.documentElement);
- var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
- var found = [];
- var res;
- while (res = result.iterateNext())
- found.push(res);
- return found[0];
- };
- return nodeSearch(_document, this.getNodeForDocument, expr);
- };
- /**
- * Name()
- *
- * Finds an element by Name
- */
- function Name(_document, nName) {
- if (nName == undefined) {
- throw new Error('Name constructor did not recieve enough arguments.');
- }
- this.getNodeForDocument = function (s) {
- try{
- var els = this.document.getElementsByName(s);
- if (els.length > 0) { return els[0]; }
- }
- catch(err){};
- return null;
- };
- return nodeSearch(_document, this.getNodeForDocument, nName);
- };
- var _returnResult = function (results) {
- if (results.length == 0) {
- return null
- } else if (results.length == 1) {
- return results[0];
- } else {
- return results;
- }
- }
- var _forChildren = function (element, name, value) {
- var results = [];
- var nodes = [e for each (e in element.childNodes) if (e)]
- for (var i in nodes) {
- var n = nodes[i];
- if (n[name] == value) {
- results.push(n);
- }
- }
- return results;
- }
- var _forAnonChildren = function (_document, element, name, value) {
- var results = [];
- var nodes = [e for each (e in _document.getAnoymousNodes(element)) if (e)];
- for (var i in nodes ) {
- var n = nodes[i];
- if (n[name] == value) {
- results.push(n);
- }
- }
- return results;
- }
- var _byID = function (_document, parent, value) {
- return _returnResult(_forChildren(parent, 'id', value));
- }
- var _byName = function (_document, parent, value) {
- return _returnResult(_forChildren(parent, 'tagName', value));
- }
- var _byAttrib = function (parent, attributes) {
- var results = [];
- var nodes = parent.childNodes;
- for (var i in nodes) {
- var n = nodes[i];
- requirementPass = 0;
- requirementLength = 0;
- for (var a in attributes) {
- requirementLength++;
- try {
- if (n.getAttribute(a) == attributes[a]) {
- requirementPass++;
- }
- } catch (err) {
- // Workaround any bugs in custom attribute crap in XUL elements
- }
- }
- if (requirementPass == requirementLength) {
- results.push(n);
- }
- }
- return _returnResult(results)
- }
- var _byAnonAttrib = function (_document, parent, attributes) {
- var results = [];
- if (objects.getLength(attributes) == 1) {
- for (var i in attributes) {var k = i; var v = attributes[i]; }
- var result = _document.getAnonymousElementByAttribute(parent, k, v)
- if (result) {
- return result;
- }
- }
- var nodes = [n for each (n in _document.getAnonymousNodes(parent)) if (n.getAttribute)];
- function resultsForNodes (nodes) {
- for (var i in nodes) {
- var n = nodes[i];
- requirementPass = 0;
- requirementLength = 0;
- for (var a in attributes) {
- requirementLength++;
- if (n.getAttribute(a) == attributes[a]) {
- requirementPass++;
- }
- }
- if (requirementPass == requirementLength) {
- results.push(n);
- }
- }
- }
- resultsForNodes(nodes)
- if (results.length == 0) {
- resultsForNodes([n for each (n in parent.childNodes) if (n != undefined && n.getAttribute)])
- }
- return _returnResult(results)
- }
- var _byIndex = function (_document, parent, i) {
- if (parent instanceof Array) {
- return parent[i];
- }
- return parent.childNodes[i];
- }
- var _anonByName = function (_document, parent, value) {
- return _returnResult(_forAnonChildren(_document, parent, 'tagName', value));
- }
- var _anonByAttrib = function (_document, parent, value) {
- return _byAnonAttrib(_document, parent, value);
- }
- var _anonByIndex = function (_document, parent, i) {
- return _document.getAnonymousNodes(parent)[i];
- }
- /**
- * Lookup()
- *
- * Finds an element by Lookup expression
- */
- function Lookup (_document, expression) {
- if (expression == undefined) {
- throw new Error('Lookup constructor did not recieve enough arguments.');
- }
- var expSplit = [e for each (e in smartSplit(expression) ) if (e != '')];
- expSplit.unshift(_document)
- var nCases = {'id':_byID, 'name':_byName, 'attrib':_byAttrib, 'index':_byIndex};
- var aCases = {'name':_anonByName, 'attrib':_anonByAttrib, 'index':_anonByIndex};
- var reduceLookup = function (parent, exp) {
- // Handle case where only index is provided
- var cases = nCases;
- // Handle ending index before any of the expression gets mangled
- if (withs.endsWith(exp, ']')) {
- var expIndex = json2.JSON.parse(strings.vslice(exp, '[', ']'));
- }
- // Handle anon
- if (withs.startsWith(exp, 'anon')) {
- var exp = strings.vslice(exp, '(', ')');
- var cases = aCases;
- }
- if (withs.startsWith(exp, '[')) {
- try {
- var obj = json2.JSON.parse(strings.vslice(exp, '[', ']'));
- } catch (err) {
- throw new Error(err+'. String to be parsed was || '+strings.vslice(exp, '[', ']')+' ||');
- }
- var r = cases['index'](_document, parent, obj);
- if (r == null) {
- throw new Error('Expression "'+exp+'" returned null. Anonymous == '+(cases == aCases));
- }
- return r;
- }
- for (var c in cases) {
- if (withs.startsWith(exp, c)) {
- try {
- var obj = json2.JSON.parse(strings.vslice(exp, '(', ')'))
- } catch(err) {
- throw new Error(err+'. String to be parsed was || '+strings.vslice(exp, '(', ')')+' ||');
- }
- var result = cases[c](_document, parent, obj);
- }
- }
- if (!result) {
- if ( withs.startsWith(exp, '{') ) {
- try {
- var obj = json2.JSON.parse(exp)
- } catch(err) {
- throw new Error(err+'. String to be parsed was || '+exp+' ||');
- }
- if (cases == aCases) {
- var result = _anonByAttrib(_document, parent, obj)
- } else {
- var result = _byAttrib(parent, obj)
- }
- }
- if (!result) {
- throw new Error('Expression "'+exp+'" returned null. Anonymous == '+(cases == aCases));
- }
- }
- // Final return
- if (expIndex) {
- // TODO: Check length and raise error
- return result[expIndex];
- } else {
- // TODO: Check length and raise error
- return result;
- }
- // Maybe we should cause an exception here
- return false;
- };
- return expSplit.reduce(reduceLookup);
- };