summaryrefslogtreecommitdiff
path: root/services/sync/tps/extensions/mozmill/resource/modules/elementslib.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/sync/tps/extensions/mozmill/resource/modules/elementslib.js')
-rw-r--r--services/sync/tps/extensions/mozmill/resource/modules/elementslib.js444
1 files changed, 444 insertions, 0 deletions
diff --git a/services/sync/tps/extensions/mozmill/resource/modules/elementslib.js b/services/sync/tps/extensions/mozmill/resource/modules/elementslib.js
new file mode 100644
index 000000000..e59429f06
--- /dev/null
+++ b/services/sync/tps/extensions/mozmill/resource/modules/elementslib.js
@@ -0,0 +1,444 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+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);
+};