Something went wrong. Try again.
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313// Original path: bite-project/tools/rpf/extension/src/libs/getactioninfo.js (Google BITE, Apache 2.0, copied unmodified below this line, for reading only, not built)// Copyright 2010 Google Inc. All Rights Reserved.//// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
/** * @fileoverview This file contains content script for * recording and validation. * * @author phu@google.com (Po Hu) */
goog.provide('rpf.ContentScript.RecordHelper');
goog.require('Bite.Constants');goog.require('bite.ux.Container');goog.require('bite.rpf.BotHelper');goog.require('common.client.ElementDescriptor');goog.require('element.helper.Templates.locatorsUpdater');goog.require('goog.Timer');goog.require('goog.Uri');goog.require('goog.dom');goog.require('goog.dom.TagName');goog.require('goog.events');goog.require('goog.events.EventHandler');goog.require('goog.events.KeyCodes');goog.require('goog.net.XhrIo');goog.require('goog.style');
/** * The helper class for recording. * @constructor * @export */rpf.ContentScript.RecordHelper = function() { /** * The current element under cursor. * @type {Element} * @private */ this.elemUnderCursor_ = null;
/** * The outline style of the current selected element. * @type {string} * @private */ this.outlineOfCurrentElem_ = '';
/** * The current element with mouse down. * @type {Element} * @private */ this.curMouseDownElement_ = null;
/** * The recording mode. * @type {string} * @private */ this.recordingMode_ = '';
/** * Whether to show attributes. * @type {boolean} * @private */ this.hoverToShowAttr_ = true;
/** * Manages the UI listeners for the locator finder. * @type {goog.events.EventHandler} * @private */ this.attrEventHandler_ = new goog.events.EventHandler(this);
/** * The original contextmenu handler. * @type {function()|null} * @private */ this.originalContextMenu_ = null;
/** * The element's cursor position object. * @type {Object} */ this.currentElemCursorPos = {'x': 0, 'y': 0};
/** * The element descriptor instance. * @type {common.client.ElementDescriptor} * @private */ this.elemDescriptor_ = new common.client.ElementDescriptor();
/** * The element in locator finder. * @type {Element} * @private */ this.elemInLocatorFinder_ = null;
/** * The element and xpath mapper. * @type {!Object} * @private */ this.elementXpathMap_ = {};
/** * Whether the xpath finder dialog should be shown. * @type {boolean} * @private */ this.xpathFinderOn_ = true;
/** * The mousedown event handler. * @type {function(Event)} * @private */ this.onMouseDownHandler_ = goog.bind(this.mouseDownHandler_, this);
/** * The mouseover event handler. * @type {function(Event)} * @private */ this.onMouseOverHandler_ = goog.bind(this.mouseOverHandler_, this);
/** * The mouseout event handler. * @type {function(Event)} * @private */ this.onMouseOutHandler_ = goog.bind(this.mouseOutHandler_, this);
/** * The onchange event handler. * @type {function(Event)} * @private */ this.onChangeHandler_ = goog.bind(this.changeHandler_, this);
/** * The onsubmit event handler. * @type {function(Event)} * @private */ this.onSubmitHandler_ = goog.bind(this.submitHandler_, this);
/** * The keydown event handler. * @type {function(Event)} * @private */ this.onKeyDownHandler_ = goog.bind(this.keyDownHandler_, this);
/** * The dblclick event handler. * @type {function(Event)} * @private */ this.onDblClickHandler_ = goog.bind(this.dblClickHandler_, this);
/** * The mouseup event handler. * @type {function(Event)} * @private */ this.onMouseUpHandler_ = goog.bind(this.mouseUpHandler_, this);
/** * The on blur event handler for the contenteditable elements. * @type {function(Event)} * @private */ this.onContentEditableBlur_ = goog.bind(this.contentEditableOnBlur_, this);
/** * The xpath finder console. * @type {bite.ux.Container} * @private */ this.finderConsole_ = null;
new bite.rpf.BotHelper();
this.rootArr_ = [];};
/** * The max index. * @type {number} * @private */rpf.ContentScript.RecordHelper.MAX_ = 999;
/** * The elements that could not trigger drag events. * @type {Object} * @private */rpf.ContentScript.RecordHelper.DRAG_FILTER_ = { 'select': true};
/** * Opens the locator helper dialog. * TODO(phu): Move the bite console to common lib and call it directly. * @param {boolean=} opt_isStandalone Whether this is a standalone tool. * @private */rpf.ContentScript.RecordHelper.prototype.openLocatorDialog_ = function( opt_isStandalone) { if (goog.dom.getElement('bite-locator-console-container')) { this.finderConsole_.show(); return; } this.finderConsole_ = new bite.ux.Container( '', 'bite-locator-console-container', 'Xpath Finder', '', true, false, 'Press Shift to pause & resume Xpath changes', 'https://sites.google.com/site/rpfwiki/about/xpath-finder'); var content = this.finderConsole_.getContentElement(); content.style.overflow = 'auto'; this.finderConsole_.setContentFromHtml( element.helper.Templates.locatorsUpdater.showHelperContent( {notStandalone: !opt_isStandalone})); var size = goog.dom.getViewportSize(); this.finderConsole_.updateConsolePosition( {'position': {'x': size.width - 535, 'y': size.height - 345}, 'size': {'width': 530, 'height': 340}}); this.finderConsole_.showInfoMessage( 'Press Shift to pause & resume Xpath changes'); this.registerAncestorEvents_(opt_isStandalone); this.registerOnClose_();};
/** * Registers on close event for xpath finder. * @private */rpf.ContentScript.RecordHelper.prototype.registerOnClose_ = function() { var hideConsole = goog.dom.getElementByClass( 'bite-close-button', this.finderConsole_.getRoot()); if (hideConsole) { goog.events.listen( hideConsole, goog.events.EventType.CLICK, goog.bind(this.hideFinderConsole_, this)); }};
/** * Hides the finder console. * @private */rpf.ContentScript.RecordHelper.prototype.hideFinderConsole_ = function() { this.finderConsole_.hide();};
/** * The handler for saving a specified selector. * @private */rpf.ContentScript.RecordHelper.prototype.onSaveSelector_ = function() { var xpath = goog.dom.getElement('cssSelectorInput').value; // This xpath is the default xpath of the element, which is used as the index. var defaultXpath = this.elemDescriptor_.generateXpath( this.elemInLocatorFinder_, {'id': null}, {'id': null}); this.elementXpathMap_[defaultXpath] = xpath; this.finderConsole_.showInfoMessage('The new xpath was saved.', 2);};
/** * The handler for pinging a specified selector. * TODO(phu): This also should call a common lib function to ping. * @private */rpf.ContentScript.RecordHelper.prototype.onPingSelector_ = function() { var xpath = goog.dom.getElement('cssSelectorInput').value; var doc = goog.dom.getDocument(); try { var elems = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null); var firstR = elems.iterateNext(); firstR.style.outline = 'medium solid orange'; goog.Timer.callOnce(function() {firstR.style.outline = '';}, 1500); } catch (e) { console.log('Failed to find elements through xpath ' + xpath); }};
/** * Registers all of the attribute checkboxes of the ancestors. * @param {boolean=} opt_isStandalone Whether it's used in a standalone mode. * @private */rpf.ContentScript.RecordHelper.prototype.registerAncestorEvents_ = function( opt_isStandalone) { if (!opt_isStandalone) { goog.events.listen( goog.dom.getElement('saveSelector'), 'click', goog.bind(this.onSaveSelector_, this)); } goog.events.listen( goog.dom.getElement('pingSelector'), 'click', goog.bind(this.onPingSelector_, this)); var elems = goog.dom.getDocument().getElementsByName('ancestorLocatorCheck'); for (var i = 0, len = elems.length; i < len; ++i) { goog.events.listen( elems[i], 'click', goog.bind(this.checkboxHandler_, this)); }};
/** * Registers all of the attribute checkboxes of the element. * @private */rpf.ContentScript.RecordHelper.prototype.registerElementEvents_ = function() { var elems = goog.dom.getDocument().getElementsByName('elementLocatorCheck'); var contains = goog.dom.getDocument().getElementsByName('elementContain'); for (var i = 0, len = elems.length; i < len; ++i) { this.attrEventHandler_.listen( elems[i], 'click', goog.bind(this.checkboxHandler_, this)); this.attrEventHandler_.listen( contains[i], 'click', goog.bind(this.checkboxHandler_, this)); }};
/** * Gets the element id which is connected by '-'. * @param {!Array.<string>} parts The parts that will be connected with '-'. * @return The id in the standard format. * @private */rpf.ContentScript.RecordHelper.prototype.getElemId_ = function(parts) { return parts.join('-');};
/** * Gets the checkbox value array of a given element name. * @param {string} name The element name. * @return {Object.<string, Object>} The key is the element's attribute and * the value is an object which contains the attribute's value and whether * the value should be exact or contained. * @private */rpf.ContentScript.RecordHelper.prototype.getValueArr_ = function(name) { var elems = goog.dom.getDocument().getElementsByName(name); var results = {}; for (var i = 0, len = elems.length; i < len; ++i) { if (elems[i].checked) { var attr = elems[i].value; // For the ancestor elements, they don't have the value input boxes. if (!goog.dom.getElement(this.getElemId_(['value', name, attr]))) { results[attr] = null; } else { var value = goog.dom.getElement( this.getElemId_(['value', name, attr])).value; var isExact = !goog.dom.getElement( this.getElemId_(['contains', name, attr])).checked; results[attr] = {'value': value, 'isExact': isExact}; } } } return results;};
/** * Handlers for all of the attribute checkboxes. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.checkboxHandler_ = function(e) { goog.dom.getElement('cssSelectorInput').value = this.elemDescriptor_.generateXpath( this.elemInLocatorFinder_, this.getValueArr_('ancestorLocatorCheck'), this.getValueArr_('elementLocatorCheck'));};
/** * Shows the locator finding methods in helper dialog. * @private */rpf.ContentScript.RecordHelper.prototype.showLocatorMethods_ = function() { if (!this.hoverToShowAttr_) { return; } var table = goog.dom.getElement('locatorMethodsTable'); if (!table) { return; } this.elemInLocatorFinder_ = this.elemUnderCursor_; var attrs = common.client.ElementDescriptor.getAttributeArray( this.elemUnderCursor_); soy.renderElement( table, element.helper.Templates.locatorsUpdater.showMethodsContent, {'data': attrs});
var xpath = this.elemDescriptor_.generateXpath( this.elemInLocatorFinder_, this.getValueArr_('ancestorLocatorCheck'), this.getValueArr_('elementLocatorCheck')); if (this.elementXpathMap_[xpath]) { xpath = this.elementXpathMap_[xpath]; } goog.dom.getElement('cssSelectorInput').value = xpath; this.attrEventHandler_.removeAll(); this.registerElementEvents_();};
/** * Creates a variable name to an element. * @param {Object} elem The element. * @param {number=} opt_maxLen The maximum length. * @return {string} The generated unique variable name. * @private */rpf.ContentScript.RecordHelper.prototype.createVarName_ = function( elem, opt_maxLen) { var maxLen = opt_maxLen || 10; var url = goog.dom.getDocument().URL.replace(/\W+/g, ''); var starts = 0; if (url.length > maxLen) { starts = url.length - maxLen; } return (elem.tagName + this.getElemIndexWithSameTag_(elem) + '-' + rpf.ContentScript.RecordHelper.randomInt());};
/** * Gets the index of the element within all the elements having the same * tag name. * @param {Object} elem The input element. * @return {number} The index. * @private */rpf.ContentScript.RecordHelper.prototype.getElemIndexWithSameTag_ = function(elem) { var elemsWithSameTag = goog.dom.getElementsByTagNameAndClass(elem.tagName); for (var i = 0; i < elemsWithSameTag.length; i++) { if (elemsWithSameTag[i] == elem) { return i; } } return -1;};
/** * Callback on a get context menu event. * @param {Event} event The get context event. * @private */rpf.ContentScript.RecordHelper.prototype.getContextMenu_ = function(event) { event.stopPropagation(); event.preventDefault();};
/** * Call back function for catching the onblur event on related divs. * @param {Event} event The event object. * @private */rpf.ContentScript.RecordHelper.prototype.contentEditableOnBlur_ = function(event) { event.stopPropagation(); var elem = event.srcElement; this.sendActionBack(elem, elem.innerHTML, 'replaceHtml', event);};
/** * Add listeners to content editable divs. * @private */rpf.ContentScript.RecordHelper.prototype.addListenersToContentEditables_ = function() { var attrName = 'contenteditable'; var elems = document.querySelectorAll('*[contenteditable]'); for (var i = 0; i < elems.length; ++i) { var attr = elems[i].getAttribute(attrName); if (attr != 'false') { elems[i].onblur = this.onContentEditableBlur_; } }};
/** * Checks if the given element is within the recording area. * @param {Element} elem The element object. * @return {boolean} Wether the element is in recording area. * @private */rpf.ContentScript.RecordHelper.prototype.checkInRecordingArea_ = function( elem) { if (elem) { var biteConsole = goog.dom.getElement('bite-locator-console-container'); if (biteConsole && goog.dom.contains(biteConsole, elem)) { return false; } } return true;};
/** * Mouse click handler. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.mouseUpHandler_ = function(e) { if (e.button == 0) { var dx = e.screenX - this.currentElemCursorPos['x']; var dy = e.screenY - this.currentElemCursorPos['y']; if ((dx < 10 && dx > -10) && (dy < 10 && dy > -10)) { return; } else { var tagName = this.curMouseDownElement_.tagName.toLowerCase(); if (this.checkInRecordingArea_(this.curMouseDownElement_) && !rpf.ContentScript.RecordHelper.DRAG_FILTER_[tagName]) { this.sendActionBack( this.curMouseDownElement_, dx + 'x' + dy, 'drag', e); } } }};
/** * Mouse click handler. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.mouseDownHandler_ = function(e) { if (!this.elemUnderCursor_) { this.elemUnderCursor_ = /** @type {Element} */ (e.target); } var content = this.elemDescriptor_.getText(this.elemUnderCursor_); if (e.button == 0 && this.recordingMode_ == 'rpf') { this.currentElemCursorPos = {'x': e.screenX, 'y': e.screenY}; this.curMouseDownElement_ = this.elemUnderCursor_; if (this.elemUnderCursor_.tagName.toLowerCase() != 'select') { this.sendActionBack(this.elemUnderCursor_, content, 'click', e); } } else if (e.button == 2) { this.sendActionBack(this.elemUnderCursor_, content, 'rightclick', e); }};
/** * Mouse click handler. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.submitHandler_ = function(e) { this.sendActionBack(/** @type {Element} */ (e.target), '', 'submit', e);};
/** * Toggles to show attributes in dialog on hover over. * @private */rpf.ContentScript.RecordHelper.prototype.toggleHoverShowAttr_ = function() { this.hoverToShowAttr_ = !this.hoverToShowAttr_;};
/** * Mouse click handler. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.keyDownHandler_ = function(e) { var keynum = e.keyCode; if (this.recordingMode_ == 'rpf' && keynum == goog.events.KeyCodes.ENTER && e.target.tagName.toLowerCase() == 'input') { this.sendActionBack(/** @type {Element} */ (e.target), '', 'enter', e); } else if (keynum == goog.events.KeyCodes.SHIFT) { this.toggleHoverShowAttr_(); }};
/** * Mouse click handler. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.changeHandler_ = function(e) { console.log('onchange event caught.'); var elem = /** @type {Element} */ (e.target); if (!this.checkApplicableAsInput_(elem)) { console.log('The element is a checkbox or radio button, discard.'); return; } var eventType = 'change'; var upperTagName = elem.tagName.toUpperCase(); if (upperTagName == goog.dom.TagName.INPUT) { eventType = 'type'; } else if (upperTagName == goog.dom.TagName.SELECT) { eventType = 'select'; } this.sendActionBack(elem, elem.value, eventType, e);};
/** * Reverts the outline style back to element's original style. * @param {Element} elem The element. * @return {Element} The element whose outline is reverted. * @private */rpf.ContentScript.RecordHelper.prototype.revertOutline_ = function(elem) { goog.style.setStyle(elem, 'outline', this.outlineOfCurrentElem_); return elem;};
/** * Highlights the outline style. * @param {Element} elem The element. * @param {string=} opt_outline The optional outline. * @private */rpf.ContentScript.RecordHelper.prototype.highlightOutline_ = function( elem, opt_outline) { elem.style.outline = opt_outline || 'medium solid yellow';};
/** * Mouse click handler. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.mouseOverHandler_ = function(e) { if (this.elemUnderCursor_) { this.revertOutline_(this.elemUnderCursor_); } this.elemUnderCursor_ = /** @type {Element} */ (e.target); this.outlineOfCurrentElem_ = this.elemUnderCursor_.style.outline; // Outline elements on mouse over, if rpf Console UI is constructed. if (this.checkInRecordingArea_(this.elemUnderCursor_)) { this.showLocatorMethods_(); this.highlightOutline_(this.elemUnderCursor_); }};
/** * Mouse click handler. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.mouseOutHandler_ = function(e) { e.target.style.outline = '';};
/** * Mouse click handler. * @param {Event} e The event object. * @private */rpf.ContentScript.RecordHelper.prototype.dblClickHandler_ = function(e) { if (!this.elemUnderCursor_) { this.elemUnderCursor_ = /** @type {Element} */ (e.target); } this.sendActionBack( this.elemUnderCursor_, this.elemDescriptor_.getText(this.elemUnderCursor_), 'doubleClick', e);};
/** * The on request handler. * @param {!Object.<string, string, Object>} request Object Data sent in * the request. * @param {MessageSender} sender An object containing information about the * script context that sent the request. * @param {function(!*): void} callback Function to call when the request * completes. * @export */rpf.ContentScript.RecordHelper.prototype.onRequest = function( request, sender, callback) { switch (request['recordAction']) { case Bite.Constants.RECORD_ACTION.START_RECORDING: this.rootArr_ = request['params']['rootArr']; this.xpathFinderOn_ = request['params']['xpathFinderOn']; this.startRecording(); break; case Bite.Constants.RECORD_ACTION.OPEN_XPATH_FINDER: this.openXpathFinder_(); break; case Bite.Constants.RECORD_ACTION.STOP_RECORDING: this.rootArr_ = []; this.stopRecording(); break; case Bite.Constants.RECORD_ACTION.START_UPDATE_MODE: this.rootArr_ = request['params']['rootArr']; this.xpathFinderOn_ = request['params']['xpathFinderOn']; this.enterUpdaterMode(); break; }};
/** * Stops recording mode experiment. * @export */rpf.ContentScript.RecordHelper.prototype.stopRecording = function() { goog.events.unlisten(goog.global.document, 'mousedown', this.onMouseDownHandler_, true); goog.events.unlisten(goog.global.document, 'mouseover', this.onMouseOverHandler_, true); goog.events.unlisten(goog.global.document, 'mouseout', this.onMouseOutHandler_, true); goog.events.unlisten(goog.global.document, 'change', this.onChangeHandler_, true); goog.events.unlisten(goog.global.document, 'submit', this.onSubmitHandler_, true); goog.events.unlisten(goog.global.document, 'keydown', this.onKeyDownHandler_, true); goog.events.unlisten(goog.global.document, 'dblclick', this.onDblClickHandler_, true); goog.events.unlisten(goog.global.document, 'mouseup', this.onMouseUpHandler_, true); goog.global.document.oncontextmenu = this.originalContextMenu_; if (this.finderConsole_) { this.hideFinderConsole_(); }};
/** * Starts recording mode experiment. * @export */rpf.ContentScript.RecordHelper.prototype.startRecording = function() { this.recordingMode_ = 'rpf'; this.stopRecording(); goog.events.listen(goog.global.document, 'mousedown', this.onMouseDownHandler_, true); goog.events.listen(goog.global.document, 'mouseover', this.onMouseOverHandler_, true); goog.events.listen(goog.global.document, 'mouseout', this.onMouseOutHandler_, true); goog.events.listen(goog.global.document, 'change', this.onChangeHandler_, true); goog.events.listen(goog.global.document, 'submit', this.onSubmitHandler_, true); goog.events.listen(goog.global.document, 'keydown', this.onKeyDownHandler_, true); goog.events.listen(goog.global.document, 'dblclick', this.onDblClickHandler_, true); goog.events.listen(goog.global.document, 'mouseup', this.onMouseUpHandler_, true); this.originalContextMenu_ = goog.global.document.oncontextmenu; goog.global.document.oncontextmenu = goog.bind(this.getContextMenu_, this); this.addListenersToContentEditables_(); if (window == window.parent && this.xpathFinderOn_) { this.openLocatorDialog_(); }};
/** * Check whether this is applicable. * @param {Object} elem The element was interacted with. * @return {boolean} Whether the event triggered as input is applicable. * @private */rpf.ContentScript.RecordHelper.prototype.checkApplicableAsInput_ = function(elem) { return !(elem && elem.type && (elem.type == 'checkbox' || elem.type == 'radio'));};
/** * Gets the xpath of the given element. * @param {Element} elem The element was interacted with. * @return {string} The xpath string. * @private */rpf.ContentScript.RecordHelper.prototype.getXpathStr_ = function(elem) { // This is the default xpath which will be used to find the custom xpath. var defaultXpath = this.elemDescriptor_.generateXpath( elem, {'id': null}, {'id': null}); for (var xpath in this.elementXpathMap_) { if (xpath == defaultXpath) { return this.elementXpathMap_[xpath]; } } return defaultXpath;};
/** * Returns the class name if the element is a descendant of a root element. * @param {Element} elem The element was interacted with. * @return {string} The class name. * @private */rpf.ContentScript.RecordHelper.prototype.checkForClassName_ = function(elem) { for (var i = 0, len = this.rootArr_.length; i < len; ++i) { var xpath = this.rootArr_[i]['xpath']; var rootElem = null; try { rootElem = bot.locators.xpath.single( /** @type {string} */ (xpath), goog.dom.getDocument()); } catch (e) { continue; } if (rootElem) { if (goog.dom.contains(rootElem, elem)) { return this.rootArr_[i]['className']; } } } return '';};
/** * Sends an action related info to background page. * @param {Element} elem The target element. * @param {string} content The content in the target element. * @param {string} action The action performed. * @param {Event} event The event object. * @export */rpf.ContentScript.RecordHelper.prototype.sendActionBack = function( elem, content, action, event) { if (!this.checkInRecordingArea_(elem)) { return; } var descriptorStr = this.elemDescriptor_.generateElementDescriptor( this.revertOutline_(elem), 2, false); this.highlightOutline_(elem); var elemVarName = this.createVarName_(elem); var selectors = [this.elemDescriptor_.generateSelector(elem), this.elemDescriptor_.generateSelectorPath( /** @type {!Node} */ (elem))]; var xpaths = [this.getXpathStr_(elem)]; var tagName = elem.tagName; this.sendActionBack_( selectors, content, tagName, action, descriptorStr, elemVarName, elem, event, xpaths);};
/** * Sends an action related info to background page. * @param {Array} selectors The array of selectors. * @param {string} content The content in the target element. * @param {string} nodeType The node type of the element. * @param {string} action The action performed. * @param {string} descriptor The descriptor of the element. * @param {string=} opt_elemVarName The variable name of the element. * @param {Element=} opt_elem The element. * @param {Event=} opt_event The event object. * @param {Array=} opt_xpaths The xpath array. * @private */rpf.ContentScript.RecordHelper.prototype.sendActionBack_ = function( selectors, content, nodeType, action, descriptor, opt_elemVarName, opt_elem, opt_event, opt_xpaths) { if (!this.checkInRecordingArea_(opt_elem || this.elemUnderCursor_)) { return; } var elemVarName = opt_elemVarName || ''; var xpaths = opt_xpaths || []; console.log('Caught event: ' + action); var iframeInfo = null; // Assign the iframeInfo only if it's in an iframe window. if (window != window.parent) { iframeInfo = {'host': window.location.host, 'pathname': window.location.pathname}; } var position = null; var className = '';
if (opt_elem) { var pos = goog.style.getClientPosition(opt_elem); var size = goog.style.getSize(opt_elem); var eX = 0; var eY = 0; if (opt_event) { eX = opt_event.clientX; eY = opt_event.clientY; } position = {'x': pos.x, 'y': pos.y, 'width': size.width, 'height': size.height, 'eX': eX, 'eY': eY}; className = this.checkForClassName_(opt_elem); } chrome.extension.sendRequest({'command': 'GetActionInfo', 'selectors': selectors, 'content': escape(content), 'nodeType': nodeType, 'action': action, 'descriptor': descriptor, 'elemVarName': elemVarName, 'iframeInfo': iframeInfo, 'position': position, 'mode': this.recordingMode_, 'xpaths': xpaths, 'className': className});};
/** * Enum for the received request command. * @enum {string} * @export */rpf.ContentScript.RecordHelper.ReqCmds = { OPEN_XPATH_FINDER: 'openXpathFinder', TEST_DESCRIPTOR: 'testDescriptor', TEST_LOCATOR: 'testLocator'};
/** * Callback for the onRequest listener. * @param {Object} request The request object. * @param {Object} sender The sender object. * @param {function(Object)} sendResponse The sendResponse object used * to send back results. * @private */rpf.ContentScript.RecordHelper.prototype.callBackAddOnRequest_ = function( request, sender, sendResponse) { switch (request.command) { case rpf.ContentScript.RecordHelper.ReqCmds.TEST_DESCRIPTOR: var result = 'pass'; try { var desc = (/** @type {string} */ request['descriptor']); this.outlineElems_(desc); } catch (e) { result = e.message; } sendResponse({result: result}); break; case rpf.ContentScript.RecordHelper.ReqCmds.TEST_LOCATOR: var results = []; var locators = request['locators']; for (var i = 0, len = locators.length; i < len; ++i) { var loc = locators[i]; var passed = false; var elem = common.client.ElementDescriptor.getElemBy( loc['method'], loc['value']); if (elem && elem.style) { if (loc['show']) { elem.style.outline = 'medium solid red'; } else { elem.style.outline = ''; } passed = true; } results.push({'id': loc['id'], 'passed': passed, 'show': loc['show']}); } sendResponse({'results': results}); break; }};
/** * Opens the xpath finder in the standalone mode. * @private */rpf.ContentScript.RecordHelper.prototype.openXpathFinder_ = function() { goog.events.listen(goog.dom.getDocument(), 'mouseover', this.onMouseOverHandler_, true); goog.events.listen(goog.dom.getDocument(), 'mouseout', this.onMouseOutHandler_, true); goog.events.listen(goog.dom.getDocument(), 'keydown', this.onKeyDownHandler_, true); if (goog.global.window == goog.global.window.parent) { this.openLocatorDialog_(true); }};
/** * Enters locator updater mode. * @export */rpf.ContentScript.RecordHelper.prototype.enterUpdaterMode = function() { this.recordingMode_ = 'updater'; goog.events.listen(goog.dom.getDocument(), 'mousedown', this.onMouseDownHandler_, true); goog.events.listen(goog.dom.getDocument(), 'mouseover', this.onMouseOverHandler_, true); goog.events.listen(goog.dom.getDocument(), 'mouseout', this.onMouseOutHandler_, true); goog.events.listen(goog.dom.getDocument(), 'keydown', this.onKeyDownHandler_, true); chrome.extension.onRequest.removeListener( goog.bind(this.callBackAddOnRequest_, this)); chrome.extension.onRequest.addListener( goog.bind(this.callBackAddOnRequest_, this)); if (goog.global.window == goog.global.window.parent) { this.openLocatorDialog_(); }};
/** * Ends locator updater mode. * @export */rpf.ContentScript.RecordHelper.prototype.endUpdaterMode = function() { goog.events.unlisten(goog.dom.getDocument(), 'mousedown', this.onMouseDownHandler_, true); goog.events.unlisten(goog.dom.getDocument(), 'mouseover', this.onMouseOverHandler_, true); goog.events.unlisten(goog.dom.getDocument(), 'mouseout', this.onMouseOutHandler_, true); goog.events.unlisten(goog.dom.getDocument(), 'keydown', this.onKeyDownHandler_, true); chrome.extension.onRequest.removeListener( goog.bind(this.callBackAddOnRequest_, this)); if (this.finderConsole_) { this.hideFinderConsole_(); }};
/** * Adds the onRequest listener. * @export */rpf.ContentScript.RecordHelper.prototype.addOnRequestListener = function() { chrome.extension.onRequest.removeListener( goog.bind(this.callBackAddOnRequest_, this)); chrome.extension.onRequest.addListener( goog.bind(this.callBackAddOnRequest_, this));};
/** * Outlines the found elements. * @param {string} descriptor The descriptor object. * @private */rpf.ContentScript.RecordHelper.prototype.outlineElems_ = function( descriptor) { var result = this.elemDescriptor_.parseElementDescriptor(descriptor); if (!result['elems']) { // TODO(phu): Display info on console instead of alerts. alert('There is no matched element.'); return; } var elems = result['elems']; elems = elems.length == undefined ? [elems] : elems; for (var i = 0; i < elems.length; ++i) { var elem = elems[i]; goog.style.setStyle(elem, 'outline', 'medium solid orange'); goog.Timer.callOnce( function() {goog.style.setStyle(elem, 'outline', '');}, 1500); }};
/** * The RecordHelper instance. * @export */var recordHelper = new rpf.ContentScript.RecordHelper();
/** * Get a random int. * @return {number} A random int number. * @export */rpf.ContentScript.RecordHelper.randomInt = function() { return Math.floor(Math.random() * rpf.ContentScript.RecordHelper.MAX_);};
/** * Sent the page loaded message to background page. * @export */rpf.ContentScript.RecordHelper.run = function() { chrome.extension.sendRequest( {command: Bite.Constants.CONSOLE_CMDS.RECORD_PAGE_LOADED_COMPLETE, url: window.location.href});};
/** * Starts auto recording. * @private */rpf.ContentScript.RecordHelper.startAutoRecord_ = function() { chrome.extension.sendRequest( {command: Bite.Constants.CONTROL_CMDS.OPEN_CONSOLE_AUTO_RECORD}, rpf.ContentScript.RecordHelper.startAutoRecordCallback);};
/** * The callback for starting auto record. * @param {Object} response The response object. * @export */rpf.ContentScript.RecordHelper.startAutoRecordCallback = function(response) { if (response['result']) { chrome.extension.sendRequest( {command: Bite.Constants.CONSOLE_CMDS.START_AUTO_RECORD}); rpf.ContentScript.RecordHelper.randomActions(); }};
/** * Randomly click on a link in a page. * @export */rpf.ContentScript.RecordHelper.randomActions = function() { var aTags = document.querySelectorAll('a'); if (!aTags) { return; } var selectedElem = aTags[Math.floor(Math.random() * aTags.length)]; goog.Timer.callOnce( function() {rpf.ContentScript.RecordHelper.randomActions();}, 4000);};
/** * Sends a message to background page to start a sequence of commands. * @param {Object} params The parameters. * @private */rpf.ContentScript.RecordHelper.automateRpf_ = function(params) { chrome.extension.sendRequest({ 'command': Bite.Constants.CONSOLE_CMDS.AUTOMATE_RPF, 'params': params});};
/** * Closes the current tab. * @private */rpf.ContentScript.RecordHelper.closeCurrentTab_ = function() { chrome.extension.sendRequest({ 'command': Bite.Constants.CONSOLE_CMDS.CLOSE_CURRENT_TAB});};
/** * Groups the parameters in URL to an object. * @param {string} url The current page url. * @private */rpf.ContentScript.RecordHelper.getParamsFromUrl_ = function(url) { var uri = new goog.Uri(href); var queryData = uri.getQueryData(); var keys = queryData.getKeys(); var parameters = {}; for (var i = 0, len = keys.length; i < len; ++i) { parameters[keys[i]] = queryData.get(keys[i]); } return parameters;};
// Execute the run function only if it's in the main window.if (window == window.parent) { rpf.ContentScript.RecordHelper.run(); var href = window.location.href; var parameters = null; if (href.indexOf('localhost:7171') != -1) { parameters = rpf.ContentScript.RecordHelper.getParamsFromUrl_(href); parameters['command'] = Bite.Constants.RPF_AUTOMATION.LOAD_AND_RUN_FROM_LOCAL; rpf.ContentScript.RecordHelper.automateRpf_(parameters); } else if (href.indexOf('.com/automateRpf') != -1) { parameters = rpf.ContentScript.RecordHelper.getParamsFromUrl_(href); if (parameters['projectName'] && parameters['location']) { parameters['command'] = Bite.Constants.RPF_AUTOMATION.PLAYBACK_MULTIPLE; if (parameters['scriptName']) { parameters['command'] = Bite.Constants.RPF_AUTOMATION.AUTOMATE_SINGLE_SCRIPT; } rpf.ContentScript.RecordHelper.automateRpf_(parameters); } rpf.ContentScript.RecordHelper.closeCurrentTab_(); } goog.dom.getDocument().addEventListener('rpfLaunchEvent', function(e) { var rpfData = goog.json.parse( goog.dom.getElement('rpfLaunchData').innerHTML); rpf.ContentScript.RecordHelper.automateRpf_(rpfData); }, false);}
chrome.extension.onRequest.addListener( goog.bind(recordHelper.onRequest, recordHelper));