Mercurial > hg > isophonics-drupal-site
view vendor/jcalderonzumba/gastonjs/src/Client/agent.js @ 9:1fc0ff908d1f
Add another data file
author | Chris Cannam |
---|---|
date | Mon, 05 Feb 2018 12:34:32 +0000 |
parents | 4c8ae668cc8c |
children |
line wrap: on
line source
var PoltergeistAgent; PoltergeistAgent = (function () { function PoltergeistAgent() { this.elements = []; this.nodes = {}; } /** * Executes an external call done from the web page class * @param name * @param args * @return {*} */ PoltergeistAgent.prototype.externalCall = function (name, args) { var error; try { return { value: this[name].apply(this, args) }; } catch (_error) { error = _error; return { error: { message: error.toString(), stack: error.stack } }; } }; /** * Object stringifycation * @param object * @return {*} */ PoltergeistAgent.stringify = function (object) { var error; try { return JSON.stringify(object, function (key, value) { if (Array.isArray(this[key])) { return this[key]; } else { return value; } }); } catch (_error) { error = _error; if (error instanceof TypeError) { return '"(cyclic structure)"'; } else { throw error; } } }; /** * Name speaks for itself * @return {string} */ PoltergeistAgent.prototype.currentUrl = function () { return encodeURI(decodeURI(window.location.href)); }; /** * Given a method of selection (xpath or css), a selector and a possible element to search * tries to find the elements that matches such selection * @param method * @param selector * @param within * @return {Array} */ PoltergeistAgent.prototype.find = function (method, selector, within) { var elementForXpath, error, i, results, xpath, _i, _len, _results; if (within == null) { within = document; } try { if (method === "xpath") { xpath = document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); results = (function () { var _i, _ref, _results; _results = []; for (i = _i = 0, _ref = xpath.snapshotLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { _results.push(xpath.snapshotItem(i)); } return _results; })(); } else { results = within.querySelectorAll(selector); } _results = []; for (_i = 0, _len = results.length; _i < _len; _i++) { elementForXpath = results[_i]; _results.push(this.register(elementForXpath)); } return _results; } catch (_error) { error = _error; if (error.code === DOMException.SYNTAX_ERR || error.code === 51) { throw new PoltergeistAgent.InvalidSelector; } else { throw error; } } }; /** * Register the element in the agent * @param element * @return {number} */ PoltergeistAgent.prototype.register = function (element) { this.elements.push(element); return this.elements.length - 1; }; /** * Gets the size of the document * @return {{height: number, width: number}} */ PoltergeistAgent.prototype.documentSize = function () { return { height: document.documentElement.scrollHeight || document.documentElement.clientHeight, width: document.documentElement.scrollWidth || document.documentElement.clientWidth }; }; /** * Gets a Node by a given id * @param id * @return {PoltergeistAgent.Node} */ PoltergeistAgent.prototype.get = function (id) { if (typeof this.nodes[id] == "undefined" || this.nodes[id] === null) { //Let's try now the elements approach if (typeof this.elements[id] == "undefined" || this.elements[id] === null) { throw new PoltergeistAgent.ObsoleteNode; } return new PoltergeistAgent.Node(this, this.elements[id]); } return this.nodes[id]; }; /** * Calls a Node agent function from the Node caller via delegates * @param id * @param name * @param args * @return {*} */ PoltergeistAgent.prototype.nodeCall = function (id, name, args) { var node; node = this.get(id); if (node.isObsolete()) { throw new PoltergeistAgent.ObsoleteNode; } //TODO: add some error control here, we might not be able to call name function return node[name].apply(node, args); }; PoltergeistAgent.prototype.beforeUpload = function (id) { return this.get(id).setAttribute('_poltergeist_selected', ''); }; PoltergeistAgent.prototype.afterUpload = function (id) { return this.get(id).removeAttribute('_poltergeist_selected'); }; PoltergeistAgent.prototype.clearLocalStorage = function () { //TODO: WTF where is variable... return localStorage.clear(); }; return PoltergeistAgent; })(); PoltergeistAgent.ObsoleteNode = (function () { function ObsoleteNode() { } ObsoleteNode.prototype.toString = function () { return "PoltergeistAgent.ObsoleteNode"; }; return ObsoleteNode; })(); PoltergeistAgent.InvalidSelector = (function () { function InvalidSelector() { } InvalidSelector.prototype.toString = function () { return "PoltergeistAgent.InvalidSelector"; }; return InvalidSelector; })(); PoltergeistAgent.Node = (function () { Node.EVENTS = { FOCUS: ['blur', 'focus', 'focusin', 'focusout'], MOUSE: ['click', 'dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseover', 'mouseout', 'mouseup', 'contextmenu'], FORM: ['submit'] }; function Node(agent, element) { this.agent = agent; this.element = element; } /** * Give me the node id of the parent of this node * @return {number} */ Node.prototype.parentId = function () { return this.agent.register(this.element.parentNode); }; /** * Returns all the node parents ids up to first child of the dom * @return {Array} */ Node.prototype.parentIds = function () { var ids, parent; ids = []; parent = this.element.parentNode; while (parent !== document) { ids.push(this.agent.register(parent)); parent = parent.parentNode; } return ids; }; /** * Finds and returns the node ids that matches the selector within this node * @param method * @param selector * @return {Array} */ Node.prototype.find = function (method, selector) { return this.agent.find(method, selector, this.element); }; /** * Checks whether the node is obsolete or not * @return boolean */ Node.prototype.isObsolete = function () { var obsolete; obsolete = function (element) { if (element.parentNode != null) { if (element.parentNode === document) { return false; } else { return obsolete(element.parentNode); } } else { return true; } }; return obsolete(this.element); }; Node.prototype.changed = function () { var event; event = document.createEvent('HTMLEvents'); event.initEvent('change', true, false); return this.element.dispatchEvent(event); }; Node.prototype.input = function () { var event; event = document.createEvent('HTMLEvents'); event.initEvent('input', true, false); return this.element.dispatchEvent(event); }; Node.prototype.keyupdowned = function (eventName, keyCode) { var event; event = document.createEvent('UIEvents'); event.initEvent(eventName, true, true); event.keyCode = keyCode; event.which = keyCode; event.charCode = 0; return this.element.dispatchEvent(event); }; Node.prototype.keypressed = function (altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) { var event; event = document.createEvent('UIEvents'); event.initEvent('keypress', true, true); event.window = this.agent.window; event.altKey = altKey; event.ctrlKey = ctrlKey; event.shiftKey = shiftKey; event.metaKey = metaKey; event.keyCode = keyCode; event.charCode = charCode; event.which = keyCode; return this.element.dispatchEvent(event); }; /** * Tells if the node is inside the body of the document and not somewhere else * @return {boolean} */ Node.prototype.insideBody = function () { return this.element === document.body || document.evaluate('ancestor::body', this.element, null, XPathResult.BOOLEAN_TYPE, null).booleanValue; }; /** * Returns all text visible or not of the node * @return {string} */ Node.prototype.allText = function () { return this.element.textContent; }; /** * Returns the inner html our outer * @returns {string} */ Node.prototype.allHTML = function (type) { var returnType = type || 'inner'; if (returnType === "inner") { return this.element.innerHTML; } if (returnType === "outer") { if (this.element.outerHTML) { return this.element.outerHTML; } // polyfill: var wrapper = document.createElement('div'); wrapper.appendChild(this.element.cloneNode(true)); return wrapper.innerHTML; } return ''; }; /** * If the element is visible then we return the text * @return {string} */ Node.prototype.visibleText = function () { if (!this.isVisible(null)) { return null; } if (this.element.nodeName === "TEXTAREA") { return this.element.textContent; } return this.element.innerText; }; /** * Deletes the actual text being represented by a selection object from the node's element DOM. * @return {*} */ Node.prototype.deleteText = function () { var range; range = document.createRange(); range.selectNodeContents(this.element); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); return window.getSelection().deleteFromDocument(); }; /** * Returns all the attributes {name:value} in the element * @return {{}} */ Node.prototype.getAttributes = function () { var attributes, i, elementAttributes; elementAttributes = this.element.attributes; attributes = {}; for (i = 0; i < elementAttributes.length; i++) { attributes[elementAttributes[i].name] = elementAttributes[i].value.replace("\n", "\\n"); } return attributes; }; /** * Name speaks for it self, returns the value of a given attribute by name * @param name * @return {string} */ Node.prototype.getAttribute = function (name) { if (name === 'checked' || name === 'selected' || name === 'multiple') { return this.element[name]; } return this.element.getAttribute(name); }; /** * Scrolls the current element into the visible area of the browser window * @return {*} */ Node.prototype.scrollIntoView = function () { return this.element.scrollIntoViewIfNeeded(); }; /** * Returns the element.value property with special treatment if the element is a select * @return {*} */ Node.prototype.value = function () { var options, i, values; if (this.element.tagName.toLowerCase() === 'select' && this.element.multiple) { values = []; options = this.element.children; for (i = 0; i < options.length; i++) { if (options[i].selected) { values.push(options[i].value); } } return values; } return this.element.value; }; /** * Sets a given value in the element value property by simulation key interaction * @param value * @return {*} */ Node.prototype.set = function (value) { var char, keyCode, i, len; if (this.element.readOnly) { return null; } //respect the maxLength property if present if (this.element.maxLength >= 0) { value = value.substr(0, this.element.maxLength); } this.element.value = ''; this.trigger('focus'); if (this.element.type === 'number') { this.element.value = value; } else { for (i = 0, len = value.length; i < len; i++) { char = value[i]; keyCode = this.characterToKeyCode(char); this.keyupdowned('keydown', keyCode); this.element.value += char; this.keypressed(false, false, false, false, char.charCodeAt(0), char.charCodeAt(0)); this.keyupdowned('keyup', keyCode); } } this.changed(); this.input(); return this.trigger('blur'); }; /** * Is the node multiple * @return {boolean} */ Node.prototype.isMultiple = function () { return this.element.multiple; }; /** * Sets the value of an attribute given by name * @param name * @param value * @return {boolean} */ Node.prototype.setAttribute = function (name, value) { if (value === null) { return this.removeAttribute(name); } this.element.setAttribute(name, value); return true; }; /** * Removes and attribute by name * @param name * @return {boolean} */ Node.prototype.removeAttribute = function (name) { this.element.removeAttribute(name); return true; }; /** * Selects the current node * @param value * @return {boolean} */ Node.prototype.select = function (value) { if (value === false && !this.element.parentNode.multiple) { return false; } this.element.selected = value; this.changed(); return true; }; /** * Selects the radio button that has the defined value * @param value * @return {boolean} */ Node.prototype.selectRadioValue = function (value) { if (this.element.value == value) { this.element.checked = true; this.trigger('focus'); this.trigger('click'); this.changed(); return true; } var formElements = this.element.form.elements; var name = this.element.getAttribute('name'); var element, i; var deselectAllRadios = function (elements, radioName) { var inputRadioElement; for (i = 0; i < elements.length; i++) { inputRadioElement = elements[i]; if (inputRadioElement.tagName.toLowerCase() == 'input' && inputRadioElement.type.toLowerCase() == 'radio' && inputRadioElement.name == radioName) { inputRadioElement.checked = false; } } }; var radioChange = function (radioElement) { var radioEvent; radioEvent = document.createEvent('HTMLEvents'); radioEvent.initEvent('change', true, false); return radioElement.dispatchEvent(radioEvent); }; var radioClickEvent = function (radioElement, name) { var radioEvent; radioEvent = document.createEvent('MouseEvent'); radioEvent.initMouseEvent(name, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); return radioElement.dispatchEvent(radioEvent); }; if (!name) { throw new Poltergeist.BrowserError('The radio button does not have the value "' + value + '"'); } for (i = 0; i < formElements.length; i++) { element = formElements[i]; if (element.tagName.toLowerCase() == 'input' && element.type.toLowerCase() == 'radio' && element.name === name) { if (value === element.value) { deselectAllRadios(formElements, name); element.checked = true; radioClickEvent(element, 'click'); radioChange(element); return true; } } } throw new Poltergeist.BrowserError('The radio group "' + name + '" does not have an option "' + value + '"'); }; /** * Checks or uncheck a radio option * @param value * @return {boolean} */ Node.prototype.checked = function (value) { //TODO: add error control for the checked stuff this.element.checked = value; return true; }; /** * Returns the element tag name as is, no transformations done * @return {string} */ Node.prototype.tagName = function () { return this.element.tagName; }; /** * Checks if the element is visible either by itself of because the parents are visible * @param element * @return {boolean} */ Node.prototype.isVisible = function (element) { var nodeElement = element || this.element; if (window.getComputedStyle(nodeElement).display === 'none') { return false; } else if (nodeElement.parentElement) { return this.isVisible(nodeElement.parentElement); } else { return true; } }; /** * Is the node disabled for operations with it? * @return {boolean} */ Node.prototype.isDisabled = function () { return this.element.disabled || this.element.tagName === 'OPTION' && this.element.parentNode.disabled; }; /** * Does the node contains the selections * @return {boolean} */ Node.prototype.containsSelection = function () { var selectedNode; selectedNode = document.getSelection().focusNode; if (!selectedNode) { return false; } //this magic number is NODE.TEXT_NODE if (selectedNode.nodeType === 3) { selectedNode = selectedNode.parentNode; } return this.element.contains(selectedNode); }; /** * Returns the offset of the node in relation to the current frame * @return {{top: number, left: number}} */ Node.prototype.frameOffset = function () { var offset, rect, style, win; win = window; offset = { top: 0, left: 0 }; while (win.frameElement) { rect = win.frameElement.getClientRects()[0]; style = win.getComputedStyle(win.frameElement); win = win.parent; offset.top += rect.top + parseInt(style.getPropertyValue("padding-top"), 10); offset.left += rect.left + parseInt(style.getPropertyValue("padding-left"), 10); } return offset; }; /** * Returns the object position in relation to the window * @return {{top: *, right: *, left: *, bottom: *, width: *, height: *}} */ Node.prototype.position = function () { var frameOffset, pos, rect; rect = this.element.getClientRects()[0]; if (!rect) { throw new PoltergeistAgent.ObsoleteNode; } frameOffset = this.frameOffset(); pos = { top: rect.top + frameOffset.top, right: rect.right + frameOffset.left, left: rect.left + frameOffset.left, bottom: rect.bottom + frameOffset.top, width: rect.width, height: rect.height }; return pos; }; /** * Triggers a DOM event related to the node element * @param name * @return {boolean} */ Node.prototype.trigger = function (name) { var event; if (Node.EVENTS.MOUSE.indexOf(name) !== -1) { event = document.createEvent('MouseEvent'); event.initMouseEvent(name, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); } else if (Node.EVENTS.FOCUS.indexOf(name) !== -1) { event = this.obtainEvent(name); } else if (Node.EVENTS.FORM.indexOf(name) !== -1) { event = this.obtainEvent(name); } else { throw "Unknown event"; } return this.element.dispatchEvent(event); }; /** * Creates a generic HTMLEvent to be use in the node element * @param name * @return {Event} */ Node.prototype.obtainEvent = function (name) { var event; event = document.createEvent('HTMLEvents'); event.initEvent(name, true, true); return event; }; /** * Does a check to see if the coordinates given * match the node element or some of the parents chain * @param x * @param y * @return {*} */ Node.prototype.mouseEventTest = function (x, y) { var elementForXpath, frameOffset, origEl; frameOffset = this.frameOffset(); x -= frameOffset.left; y -= frameOffset.top; elementForXpath = origEl = document.elementFromPoint(x, y); while (elementForXpath) { if (elementForXpath === this.element) { return { status: 'success' }; } else { elementForXpath = elementForXpath.parentNode; } } return { status: 'failure', selector: origEl && this.getSelector(origEl) }; }; /** * Returns the node selector in CSS style (NO xpath) * @param elementForXpath * @return {string} */ Node.prototype.getSelector = function (elementForXpath) { var className, selector, i, len, classNames; selector = elementForXpath.tagName !== 'HTML' ? this.getSelector(elementForXpath.parentNode) + ' ' : ''; selector += elementForXpath.tagName.toLowerCase(); if (elementForXpath.id) { selector += "#" + elementForXpath.id; } classNames = elementForXpath.classList; for (i = 0, len = classNames.length; i < len; i++) { className = classNames[i]; selector += "." + className; } return selector; }; /** * Returns the key code that represents the character * @param character * @return {number} */ Node.prototype.characterToKeyCode = function (character) { var code, specialKeys; code = character.toUpperCase().charCodeAt(0); specialKeys = { 96: 192, 45: 189, 61: 187, 91: 219, 93: 221, 92: 220, 59: 186, 39: 222, 44: 188, 46: 190, 47: 191, 127: 46, 126: 192, 33: 49, 64: 50, 35: 51, 36: 52, 37: 53, 94: 54, 38: 55, 42: 56, 40: 57, 41: 48, 95: 189, 43: 187, 123: 219, 125: 221, 124: 220, 58: 186, 34: 222, 60: 188, 62: 190, 63: 191 }; return specialKeys[code] || code; }; /** * Checks if one element is equal to other given by its node id * @param other_id * @return {boolean} */ Node.prototype.isDOMEqual = function (other_id) { return this.element === this.agent.get(other_id).element; }; /** * The following function allows one to pass an element and an XML document to find a unique string XPath expression leading back to that element. * @param element * @return {string} */ Node.prototype.getXPathForElement = function (element) { var elementForXpath = element || this.element; var xpath = ''; var pos, tempitem2; while (elementForXpath !== document.documentElement) { pos = 0; tempitem2 = elementForXpath; while (tempitem2) { if (tempitem2.nodeType === 1 && tempitem2.nodeName === elementForXpath.nodeName) { // If it is ELEMENT_NODE of the same name pos += 1; } tempitem2 = tempitem2.previousSibling; } xpath = "*[name()='" + elementForXpath.nodeName + "' and namespace-uri()='" + (elementForXpath.namespaceURI === null ? '' : elementForXpath.namespaceURI) + "'][" + pos + ']' + '/' + xpath; elementForXpath = elementForXpath.parentNode; } xpath = '/*' + "[name()='" + document.documentElement.nodeName + "' and namespace-uri()='" + (elementForXpath.namespaceURI === null ? '' : elementForXpath.namespaceURI) + "']" + '/' + xpath; xpath = xpath.replace(/\/$/, ''); return xpath; }; /** * Deselect all the options for this element */ Node.prototype.deselectAllOptions = function () { //TODO: error control when the node is not a select node var i, l = this.element.options.length; for (i = 0; i < l; i++) { this.element.options[i].selected = false; } }; return Node; })(); window.__poltergeist = new PoltergeistAgent; document.addEventListener('DOMContentLoaded', function () { return console.log('__DOMContentLoaded'); }); window.confirm = function (message) { return true; }; window.prompt = function (message, _default) { return _default || null; };