UNPKG

tagui

Version:

Command-line tool for digital process automation (RPA)

629 lines (546 loc) 101 kB
// xpath for object identification var xps666 = require('casper').selectXPath; // assign parameters to p1-p9 variables var p1 = casper.cli.raw.get(0); var p2 = casper.cli.raw.get(1); var p3 = casper.cli.raw.get(2); var p4 = casper.cli.raw.get(3); var p5 = casper.cli.raw.get(4); var p6 = casper.cli.raw.get(5); var p7 = casper.cli.raw.get(6); var p8 = casper.cli.raw.get(7); var p9 = casper.cli.raw.get(8); // save start time to measure execution time var automation_start_time = Date.now(); casper.echo('\nSTART - automation started - ' + Date().toLocaleString()); // initialise time for timer() function var timer_start_time = Date.now(); // infinity constant for use in for loops var infinity = 1024; // initialise default global variables var quiet_mode = false; var save_text_count = 0; var snap_image_count = 0; // counters for tracking messages in r, python, sikuli, chrome integrations var r_count = 0; var py_count = 0; var sikuli_count = 0; var chrome_id = 0; // chrome context for frame handling and targetid for popup handling var chrome_context = 'document'; var chrome_targetid = ''; // variable for ask step to accept user input var ask_result = ''; // JSON variable to pass variables into browser DOM var dom_json = {}; var dom_result = ''; // variable for advance usage of api step var api_config = {method:'GET', header:[], body:{}}; // variables for api and run steps execution result var api_result = ''; var api_json = {}; var run_result = ''; var run_json = {}; // variables for R and Python integration execution result var r_result = ''; var r_json = {}; var py_result = ''; var py_json = {}; // track begin-finish blocks for integrations eg - py, r, run, vision, js, dom var inside_py_block = 0; var inside_r_block = 0; var inside_run_block = 0; var inside_vision_block = 0; var inside_js_block = 0; var inside_dom_block = 0; // determine how many casper.then steps to skip function teleport_distance(teleport_marker) {number_to_hop = 0; if (teleport_marker.indexOf('[BREAK_SIGNAL]') > -1) {for (s = casper.steps.length-1; s >= 0; s--) { if (casper.steps[s].toString() == ("function () {for_loop_signal = '"+teleport_marker+"';}")) {number_to_hop = s; break;}};} // search backward direction for break step else if (teleport_marker.indexOf('[CONTINUE_SIGNAL]') > -1) {for (s = casper.step; s <= casper.steps.length-1; s++) { if (casper.steps[s].toString() == ("function () {for_loop_signal = '"+teleport_marker+"';}")) {number_to_hop = s; break;}}; // search forward direction for continue step if (number_to_hop == 0) {for (s = casper.steps.length-1; s >= 0; s--) {if (casper.steps[s].toString() == ("function () {for_loop_signal = '"+teleport_marker.replace('[CONTINUE_SIGNAL]','[BREAK_SIGNAL]')+"';}")) {number_to_hop = s; break;}};}} // handle as break if no step left to continue else return 0; if ((number_to_hop - casper.step) > 0) return (number_to_hop - casper.step); else return 0;} // techo function for handling quiet option function techo(echo_string) {if (!quiet_mode) { // mute about:blank, eg for desktop automation if ((echo_string == 'about:blank - \n') || (echo_string == '\nabout:blank - ')) casper.echo(''); else if (tagui_language.toLowerCase() == 'english') casper.echo(echo_string); else {var translated_string = translate(echo_string,'to',tagui_language.toLowerCase()); casper.echo(translated_string); if (translated_string.indexOf('ERROR - translation engine') !== -1) casper.exit();}} return;} // for muting echo in test automation scripts function dummy_echo(muted_string) {return;} // for saving text information to file function save_text(file_name,info_text) {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; if (!file_name) {save_text_count++; file_name = flow_path + ds + 'text' + save_text_count.toString() + '.txt';} var fs = require('fs'); fs.write(file_name, info_text, 'w');} // for appending text information to file function append_text(file_name,info_text) {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; if (!file_name) {if (save_text_count==0) save_text_count++; // increment if 0, else use same count to append file_name = flow_path + ds + 'text' + save_text_count.toString() + '.txt';} var fs = require('fs'); fs.write(file_name, info_text + '\r\n', 'a');} // for saving snapshots of website to file function snap_image() {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; snap_image_count++; return (flow_path + ds + 'snap' + snap_image_count.toString() + '.png');} // for saving table from website to file function save_table(file_name,selector) {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; if (!file_name) {save_text_count++; file_name = flow_path + ds + 'table' + save_text_count.toString() + '.csv';} var row_data = ""; var table_cell = ""; var fs = require('fs'); fs.write(file_name, '', 'w'); // always reset file if (!casper.exists(selector) || (selector.toString().indexOf('xpath selector: ')==-1)) return false; // exit if invalid if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); // get xpath for (table_row=1; table_row<=1024; table_row++) {row_data = ""; for (table_col=1; table_col<=1024; table_col++) { table_cell = '(((' + selector + '//tr)[' + table_row + ']//th)' + '|'; // build cell xpath selector to include table_cell += '((' + selector + '//tr)[' + table_row + ']//td))[' + table_col + ']'; // both td and td elements if (casper.exists(xps666(table_cell))) row_data = row_data + '","' + casper.fetchText(xps666(table_cell)).trim(); else break;} // if searching for table cells (th and td) is not successful, means end of row is reached if (row_data.substr(0,2) == '",') {row_data = row_data.substr(2); row_data += '"'; append_text(file_name,row_data);} else return true;}} // if '",' is not found, means end of table is reached as there is no cell found in row // for translating multi-language flows (comments in translate.php) function translate(script_line,direction,language) {var start_keywords = '|click|tap|move|hover|type|enter|select|choose|read|fetch|show|print|save|echo|dump|write|snap|table|mouse|keyboard|'+ 'wait|live|download|upload|load|receive|frame|popup|timeout|api|dom|js|vision|else if|else|if|for|while|check|'; var to_separator_keywords = '|read|fetch|save|load|dump|write|snap|table|download|receive|for|' var as_separator_keywords = '|type|enter|select|choose|upload|'; var forloop_keywords = '|from|'; var start_conditions_keywords = '|else if|if|for|while|check|'; var start_helper_keywords = '|echo|dump|write|'; var conditions_keywords = '|more than or equals to|more than or equal to|greater than or equals to|greater than or equal to|higher than or equals to|higher than or equal to|less than or equals to|less than or equal to|lesser than or equals to|lesser than or equal to|lower than or equals to|lower than or equal to|more than|greater than|higher than|less than|lesser than|lower than|not equals to|not equal to|equals to|equal to|not contains|not contain|contains|contain|and|or|'; var helper_keywords = '|title()|url()|text()|timer()|count()|present()|visible()|mouse_'+'xy()|mouse_'+'x()|mouse_'+'y()|'; // break up mouse_ helper functions to avoid mistaken triggering by tagui_parse.php as sikuli process needed var seconds_keywords = '|seconds|second|'; var start_seconds_keywords = '|wait|timeout|'; if (!script_line || script_line == '') return ''; if (!direction || direction == '') return 'ERROR - translation engine direction parameter missing'; if (!language || language == '') return 'ERROR - translation engine language parameter missing'; if (script_line == '' || script_line == '\r\n' || script_line == '\n') return script_line; var front_script_line_return = ''; if (script_line.charAt(0) == '\n') front_script_line_return = '\n'; var back_script_line_return = ''; if (script_line.substr(-1) == '\n') back_script_line_return = '\n'; direction = direction.toLowerCase(); if (direction !== 'to' && direction !== 'from') return 'ERROR - translation engine direction must be to or from'; if (direction == 'from') {var column_from = 1; var column_to = 0;} else {var column_from = 0; var column_to = 1;} language = language.toLowerCase(); var language_count = 0; var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var language_file = 'languages' + ds + language + '.csv'; var fs = require('fs'); if (!fs.exists(language_file)) return 'ERROR - translation engine ' + language + '.csv file missing'; else { var language_raw = fs.read(language_file); if (language_raw.indexOf('\r\n') !== -1) var language_data = language_raw.split(/\r\n/).map(function(line) {return line.split(',');}); else var language_data = language_raw.split(/\n/).map(function(line) {return line.split(',');}); language_count = language_data.length-1; if (!language_data[language_count][0] || language_data[language_count][0] == '') language_count--;} script_line = '[START_OF_LINE]'+script_line.trim()+'[END_OF_LINE]'; var start_word = '[NOT_ASSIGNED]'; for (language_check = 1; language_check <= language_count; language_check++) { if (start_keywords.indexOf('|'+language_data[language_check][0]+'|') !== -1) { if (start_word !== '[NOT_ASSIGNED]') continue; if ((script_line.indexOf('[START_OF_LINE]'+language_data[language_check][column_from]+' ') !== -1) || (script_line.indexOf('[START_OF_LINE]'+language_data[language_check][column_from]+'[END_OF_LINE]') !== -1)) start_word = language_data[language_check][0]; var regex = new RegExp('\\[START_OF_LINE\\]'+language_data[language_check][column_from]+' ','g'); script_line = script_line.replace(regex,'[START_OF_LINE]'+language_data[language_check][column_to]+' '); var regex = new RegExp('\\[START_OF_LINE\\]'+language_data[language_check][column_from]+'\\[END_OF_LINE\\]','g'); script_line = script_line.replace(regex,'[START_OF_LINE]'+language_data[language_check][column_to]+'[END_OF_LINE]');} else if (conditions_keywords.indexOf('|'+language_data[language_check][0]+'|') !== -1) { if (start_word == 'check') {var array_script_line = script_line.split('|'); var regex = new RegExp(' '+language_data[language_check][column_from]+' ','g'); array_script_line[0] = array_script_line[0].replace(regex,' '+language_data[language_check][column_to]+' '); script_line = array_script_line.join('|');} else if ((start_word !== '[NOT_ASSIGNED]') && (start_conditions_keywords.indexOf('|'+start_word+'|') !== -1)) {var regex = new RegExp(' '+language_data[language_check][column_from]+' ','g'); script_line = script_line.replace(regex,' '+language_data[language_check][column_to]+' ');}} else if (seconds_keywords.indexOf('|'+language_data[language_check][0]+'|') !== -1) { if ((start_word !== '[NOT_ASSIGNED]') && (start_seconds_keywords.indexOf('|'+start_word+'|') !== -1)) {var regex = new RegExp(' '+language_data[language_check][column_from]+'\\[END_OF_LINE\\]','g'); script_line = script_line.replace(regex,' '+language_data[language_check][column_to]+'[END_OF_LINE]');}} else if (forloop_keywords.indexOf('|'+language_data[language_check][0]+'|') !== -1) { if (start_word == 'for') {var regex = new RegExp(' '+language_data[language_check][column_from]+' ','g'); script_line = script_line.replace(regex,' '+language_data[language_check][column_to]+' ');}} else if (language_data[language_check][0] == 'to') { if ((start_word !== '[NOT_ASSIGNED]') && (to_separator_keywords.indexOf('|'+start_word+'|') !== -1)) {var regex = new RegExp(' '+language_data[language_check][column_from]+' ','g'); script_line = script_line.replace(regex,' '+language_data[language_check][column_to]+' ');}} else if (language_data[language_check][0] == 'as') { if ((start_word !== '[NOT_ASSIGNED]') && (as_separator_keywords.indexOf('|'+start_word+'|') !== -1)) {var regex = new RegExp(' '+language_data[language_check][column_from]+' ','g'); script_line = script_line.replace(regex,' '+language_data[language_check][column_to]+' ');}} else if (helper_keywords.indexOf('|'+language_data[language_check][0]+'|') !== -1) { if (((start_word !== '[NOT_ASSIGNED]') && (start_conditions_keywords.indexOf('|'+start_word+'|') !== -1)) || ((start_word !== '[NOT_ASSIGNED]') && (start_helper_keywords.indexOf('|'+start_word+'|') !== -1)) || (script_line.indexOf('=') !== -1)) {var regex = new RegExp((' '+language_data[language_check][column_from]).replace('(','\\(').replace(')',''),'g'); script_line = script_line.replace(regex,(' '+language_data[language_check][column_to]).replace(')',''));}}} script_line = script_line.replace('[START_OF_LINE]','').replace('[END_OF_LINE]',''); return front_script_line_return+script_line.trim()+back_script_line_return;} // for checking if selector is xpath selector function is_xpath_selector(selector) {if (selector.length == 0) return false; if ((selector.indexOf('/') == 0) || (selector.indexOf('(') == 0)) return true; return false;} // for finding best match for given locator function tx(locator) {if (is_xpath_selector(locator)) return xps666(locator); if (casper.exists(locator)) return locator; // check for css locator // first check for exact match then check for containing string if (casper.exists(xps666('//*[@id="'+locator+'"]'))) return xps666('//*[@id="'+locator+'"]'); if (casper.exists(xps666('//*[contains(@id,"'+locator+'")]'))) return xps666('//*[contains(@id,"'+locator+'")]'); if (casper.exists(xps666('//*[@name="'+locator+'"]'))) return xps666('//*[@name="'+locator+'"]'); if (casper.exists(xps666('//*[contains(@name,"'+locator+'")]'))) return xps666('//*[contains(@name,"'+locator+'")]'); if (casper.exists(xps666('//*[@class="'+locator+'"]'))) return xps666('//*[@class="'+locator+'"]'); if (casper.exists(xps666('//*[contains(@class,"'+locator+'")]'))) return xps666('//*[contains(@class,"'+locator+'")]'); if (casper.exists(xps666('//*[@title="'+locator+'"]'))) return xps666('//*[@title="'+locator+'"]'); if (casper.exists(xps666('//*[contains(@title,"'+locator+'")]'))) return xps666('//*[contains(@title,"'+locator+'")]'); if (casper.exists(xps666('//*[@aria-label="'+locator+'"]'))) return xps666('//*[@aria-label="'+locator+'"]'); if (casper.exists(xps666('//*[contains(@aria-label,"'+locator+'")]'))) return xps666('//*[contains(@aria-label,"'+locator+'")]'); if (casper.exists(xps666('//*[text()="'+locator+'"]'))) return xps666('//*[text()="'+locator+'"]'); if (casper.exists(xps666('//*[contains(text(),"'+locator+'")]'))) return xps666('//*[contains(text(),"'+locator+'")]'); if (casper.exists(xps666('//*[@href="'+locator+'"]'))) return xps666('//*[@href="'+locator+'"]'); if (casper.exists(xps666('//*[contains(@href,"'+locator+'")]'))) return xps666('//*[contains(@href,"'+locator+'")]'); return xps666('/html');} // for checking if given locator is found function check_tx(locator) {if (is_xpath_selector(locator)) {if (casper.exists(xps666(locator))) return true; else return false;} if (casper.exists(locator)) return true; // check for css locator // first check for exact match then check for containing string if (casper.exists(xps666('//*[@id="'+locator+'"]'))) return true; if (casper.exists(xps666('//*[contains(@id,"'+locator+'")]'))) return true; if (casper.exists(xps666('//*[@name="'+locator+'"]'))) return true; if (casper.exists(xps666('//*[contains(@name,"'+locator+'")]'))) return true; if (casper.exists(xps666('//*[@class="'+locator+'"]'))) return true; if (casper.exists(xps666('//*[contains(@class,"'+locator+'")]'))) return true; if (casper.exists(xps666('//*[@title="'+locator+'"]'))) return true; if (casper.exists(xps666('//*[contains(@title,"'+locator+'")]'))) return true; if (casper.exists(xps666('//*[@aria-label="'+locator+'"]'))) return true; if (casper.exists(xps666('//*[contains(@aria-label,"'+locator+'")]'))) return true; if (casper.exists(xps666('//*[text()="'+locator+'"]'))) return true; if (casper.exists(xps666('//*[contains(text(),"'+locator+'")]'))) return true; if (casper.exists(xps666('//*[@href="'+locator+'"]'))) return true; if (casper.exists(xps666('//*[contains(@href,"'+locator+'")]'))) return true; return false;} /** * Extra TagUI helper methods */ // friendlier name to use check_tx() in if condition in flow function present(element_locator) {if (!element_locator) return false; if (is_sikuli(element_locator)) {var abs_param = abs_file(element_locator); var fs = require('fs'); if (!fs.exists(abs_param)) {this.echo('ERROR - cannot find image file for present step').exit();} if (sikuli_step("present " + abs_param)) return true; else return false;} else return check_tx(element_locator);} // friendlier name to check element visibility using elementVisible() function visible(element_locator) {if (!element_locator) return false; if (is_sikuli(element_locator)) {var abs_param = abs_file(element_locator); var fs = require('fs'); if (!fs.exists(abs_param)) {this.echo('ERROR - cannot find image file for visible step').exit();} if (sikuli_step("visible " + abs_param)) return true; else return false;} else {var element_located = tx(element_locator); var element_visible = casper.elementVisible(element_located); // if tx() returns xps666('/html') means that the element is not found, so set element_visible to false if (element_located.toString() == xps666('/html').toString()) element_visible = false; return element_visible;}} // friendlier name to count elements using countElements() function count(element_locator) {if (!element_locator) return 0; var element_located = tx(element_locator); var element_count = casper.countElements(element_located); // if tx() returns xps666('/html') means that the element is not found, so set element_count to 0 if (element_located.toString() == xps666('/html').toString()) element_count = 0; return element_count;} // friendlier name to get web page title using getTitle() function title() {return casper.getTitle();} // friendlier name to get web page url using getCurrentUrl() function url() {return casper.getCurrentUrl();} // friendlier name to get web page text content using evaluate() function text() {return casper.evaluate(function() {return document.body.innerText || document.body.textContent;});} function timer() { // return time elapsed in seconds between calls var time_elapsed = ((Date.now()-timer_start_time)/1000); timer_start_time = Date.now(); return time_elapsed;} function sleep(ms) { // helper to add delay during loops var time_now = new Date().getTime(); var time_end = time_now + ms; while(time_now < time_end) {time_now = new Date().getTime();}} // return x,y coordinates of mouse cursor as string '(x,y)' mouse_xy = function() { // use this function declaration style for sikuli detection in tagui_parse.php sikuli_step('vision xy_mouseLocation = Env.getMouseLocation(); ' + 'xy_x = xy_mouseLocation.getX(); xy_y = xy_mouseLocation.getY(); ' + "output_sikuli_text('(' + str(xy_x) + ',' + str(xy_y) + ')');"); var xy_result = fetch_sikuli_text(); clear_sikuli_text(); return xy_result;} // return x coordinate of mouse cursor as integer number mouse_x = function() { // use this function declaration style for sikuli detection in tagui_parse.php sikuli_step('vision xy_mouseLocation = Env.getMouseLocation(); output_sikuli_text(str(xy_mouseLocation.getX()));'); var x_result = parseInt(fetch_sikuli_text()); clear_sikuli_text(); return x_result;} // return y coordinate of mouse cursor as integer number mouse_y = function() { // use this function declaration style for sikuli detection in tagui_parse.php sikuli_step('vision xy_mouseLocation = Env.getMouseLocation(); output_sikuli_text(str(xy_mouseLocation.getY()));'); var y_result = parseInt(fetch_sikuli_text()); clear_sikuli_text(); return y_result;} /** * string cell data sanitiser, returns a CSV formatted string * @param {string} cell_data */ function sanitise_csv_cell(cell_data) { // Replace all double quotes with 2 double quotes cell_data = cell_data.replace(/"/g, '\"\"') var whitespaceCheckRegex = /\s/ // if cell_data has a comma, or new line, or its first or last character is a // whitespace, then wrap the entire expression in double quotes if ( cell_data.indexOf(',') >= 0 || cell_data.indexOf('\n') >= 0 || whitespaceCheckRegex.test(cell_data.charAt(0)) || whitespaceCheckRegex.test(cell_data.charAt(cell_data.length - 1)) ) { cell_data = '\"' + cell_data + '\"' } return cell_data } /** * Returns a CSV-formatted string that denotes a row in a CSV file * @param {string[]} row_data a 1-D array of strings denoting data to * encode as a CSV row */ function csv_row(row_data) { // if row_data has at least 1 element, extract and sanitise first element // else start_element is empty string var start_element = (row_data.length > 0) ? sanitise_csv_cell(row_data.shift()) : '' // concat each row_data with a comma return row_data.reduce(function(accumulator, currentValue) { return accumulator + ',' + sanitise_csv_cell(currentValue) }, start_element) } // for initialising integration with sikuli visual automation function sikuli_handshake() { // techo('[connecting to sikuli process]'); var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; clear_sikuli_text(); var fs = require('fs'); fs.write('tagui.sikuli'+ds+'tagui_sikuli.in','','w'); var sikuli_handshake = ''; if (!fs.exists('tagui.sikuli'+ds+'tagui_sikuli.out')) fs.write('tagui.sikuli'+ds+'tagui_sikuli.out','','w'); do {sleep(500); sikuli_handshake = fs.read('tagui.sikuli'+ds+'tagui_sikuli.out').trim();} while (sikuli_handshake !== '[0] START'); // techo('[connected to sikuli process]'); } // for passing dynamic inputs to sikuli visual automation function vision_step(vision_intent) {if (vision_intent.indexOf('vision ') !== 0) vision_intent = 'vision ' + vision_intent; sikuli_step(vision_intent);} // for using sikuli visual automation instead of casperjs function sikuli_step(sikuli_intent) {sikuli_count++; if (sikuli_count == 1) sikuli_handshake(); // handshake on first call if (sikuli_intent.indexOf('snap_image()') > -1) {sikuli_intent = sikuli_intent.replace('snap_image()',snap_image());} var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); fs.write('tagui.sikuli'+ds+'tagui_sikuli.in','['+sikuli_count.toString()+'] '+sikuli_intent,'w'); var sikuli_result = ''; do {sleep(500); sikuli_result = fs.read('tagui.sikuli'+ds+'tagui_sikuli.out').trim();} while (sikuli_result.indexOf('['+sikuli_count.toString()+'] ') == -1); if (sikuli_result.indexOf('SUCCESS') !== -1) return true; else return false;} // for fetching text from sikuli optical character recognition function fetch_sikuli_text() {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); if (fs.exists('tagui.sikuli'+ds+'tagui_sikuli.txt')) return fs.read('tagui.sikuli'+ds+'tagui_sikuli.txt').trim(); else return '';} // for clearing text from sikuli optical character recognition function clear_sikuli_text() {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); fs.write('tagui.sikuli'+ds+'tagui_sikuli.txt','','w');} // for initialising integration with R function r_handshake() { // techo('[connecting to R process]'); var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; clear_r_text(); var fs = require('fs'); fs.write('tagui_r'+ds+'tagui_r.in','','w'); var r_handshake = ''; if (!fs.exists('tagui_r'+ds+'tagui_r.out')) fs.write('tagui_r'+ds+'tagui_r.out','','w'); do {sleep(100); r_handshake = fs.read('tagui_r'+ds+'tagui_r.out').trim();} while (r_handshake !== '[0] START'); // techo('[connected to R process]'); } // R integration for data analytics and machine learning function r_step(r_intent) {if (r_intent.indexOf('r ') !== 0) r_intent = 'r ' + r_intent; r_count++; if (r_count == 1) r_handshake(); // handshake on first call var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); fs.write('tagui_r'+ds+'tagui_r.in','['+r_count.toString()+'] '+r_intent,'w'); var r_step_result = ''; do {sleep(100); r_step_result = fs.read('tagui_r'+ds+'tagui_r.out').trim();} while (r_step_result.indexOf('['+r_count.toString()+'] ') == -1); if (r_step_result.indexOf('SUCCESS') !== -1) return true; else return false;} // for fetching text from R integration execution result function fetch_r_text() {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); if (fs.exists('tagui_r'+ds+'tagui_r.txt')) return fs.read('tagui_r'+ds+'tagui_r.txt').trim(); else return '';} // for clearing text from R integration execution result function clear_r_text() {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); fs.write('tagui_r'+ds+'tagui_r.txt','','w');} // for initialising integration with Python function py_handshake() { // techo('[connecting to Python process]'); var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; clear_py_text(); var fs = require('fs'); fs.write('tagui_py'+ds+'tagui_py.in','','w'); var py_handshake = ''; if (!fs.exists('tagui_py'+ds+'tagui_py.out')) fs.write('tagui_py'+ds+'tagui_py.out','','w'); do {sleep(100); py_handshake = fs.read('tagui_py'+ds+'tagui_py.out').trim();} while (py_handshake !== '[0] START'); // techo('[connected to Python process]'); } // Python integration for data analytics and machine learning function py_step(py_intent) {if (py_intent.indexOf('py ') !== 0) py_intent = 'py ' + py_intent; py_count++; if (py_count == 1) py_handshake(); // handshake on first call var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); fs.write('tagui_py'+ds+'tagui_py.in','['+py_count.toString()+'] '+py_intent,'w'); var py_step_result = ''; do {sleep(100); py_step_result = fs.read('tagui_py'+ds+'tagui_py.out').trim();} while (py_step_result.indexOf('['+py_count.toString()+'] ') == -1); if (py_step_result.indexOf('SUCCESS') !== -1) return true; else return false;} // for fetching text from Python integration execution result function fetch_py_text() {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); if (fs.exists('tagui_py'+ds+'tagui_py.txt')) return fs.read('tagui_py'+ds+'tagui_py.txt').trim(); else return '';} // for clearing text from Python integration execution result function clear_py_text() {var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\'; var fs = require('fs'); fs.write('tagui_py'+ds+'tagui_py.txt','','w');} if (chrome_id > 0) { // super large if block to load chrome related functions if chrome or headless option is used chrome_id = 0; // reset chrome_id from 1 back to 0 to prepare for initial call of chrome_step // for initialising integration with chrome web browser function chrome_handshake() { // techo('[connecting to chrome websocket]'); var fs = require('fs'); fs.write('tagui_chrome.in','','w'); var chrome_handshake = ''; if (!fs.exists('tagui_chrome.out')) fs.write('tagui_chrome.out','','w'); do {sleep(100); chrome_handshake = fs.read('tagui_chrome.out').trim();} while (chrome_handshake !== '[0] START'); // techo('[connected to chrome websocket]'); } // send websocket message to chrome browser using chrome devtools protocol // php helper process tagui_chrome.php running to handle this concurrently function chrome_step(method,params) {chrome_id++; if (chrome_id == 1) chrome_handshake(); // handshake on first call var chrome_intent = JSON.stringify({'id': chrome_id, 'method': method, 'params': params}); if (chrome_targetid !== '') chrome_intent = JSON.stringify({'id': chrome_id, 'method': 'Target.sendMessageToTarget', 'params': {'sessionId': chrome_targetid, 'message': chrome_intent}}); // send as message to target if context is popup var fs = require('fs'); fs.write('tagui_chrome.in','['+chrome_id.toString()+'] '+chrome_intent,'w'); var chrome_result = ''; do {sleep(100); chrome_result = fs.read('tagui_chrome.out').trim();} while (chrome_result.indexOf('['+chrome_id.toString()+'] ') == -1); if (chrome_targetid == '') return chrome_result.substring(chrome_result.indexOf('] ')+2); // below for handling popup else {try {var raw_json_string = JSON.stringify(JSON.parse(chrome_result.substring(chrome_result.indexOf('] ')+2)).params.message); return raw_json_string.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\n/g,"\\n");} catch(e) {return '';}}} // chrome object for handling integration with chrome or headless chrome var chrome = new Object(); chrome.mouse = new Object(); // chrome methods as casper methods replacement for chrome integration chrome.exists = function(selector) { // different handling for xpath and css to support both if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); var ws_message = chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotLength'});} else var ws_message = chrome_step('Runtime.evaluate',{expression: chrome_context+'.querySelectorAll(\''+selector+'\').length'}); try {var ws_json = JSON.parse(ws_message); if (ws_json.result.result.value > 0) return true; else return false;} catch(e) {return false;}}; chrome.elementVisible = function(selector) { // same as chrome.exists, except for checking visibility if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); var ws_message = chrome_step('Runtime.evaluate',{expression: 'var e = document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0); var visible = false; if (!e) visible = false; else {var style = window.getComputedStyle(e); visible = style && style.display !== \'none\' && style.visibility !== \'hidden\' && style.opacity !== \'0\';}; visible'});} else var ws_message = chrome_step('Runtime.evaluate',{expression: 'var e = '+chrome_context+'.querySelector(\''+selector+'\'); var visible = false; if (!e) visible = false; else {var style = window.getComputedStyle(e); visible = style && style.display !== \'none\' && style.visibility !== \'hidden\' && style.opacity !== \'0\';}; visible'}); try {var ws_json = JSON.parse(ws_message); if (ws_json.result.result.value == true) return ws_json.result.result.value; else return false;} catch(e) {return false;}}; chrome.countElements = function(selector) { // same as chrome.exists, except element count is returned if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); var ws_message = chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotLength'});} else var ws_message = chrome_step('Runtime.evaluate',{expression: chrome_context+'.querySelectorAll(\''+selector+'\').length'}); try {var ws_json = JSON.parse(ws_message); if (ws_json.result.result.value > 0) return ws_json.result.result.value; else return 0;} catch(e) {return 0;}}; /* // backup of previous click implementation to experiment with Puppeteer's version chrome.click = function(selector) { // click by sending click event instead of mouse down/up/click, then focus on element if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).click()'}); chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).focus()'});} else {chrome_step('Runtime.evaluate',{expression: chrome_context+'.querySelector(\''+selector+'\').click()'}); chrome_step('Runtime.evaluate',{expression: chrome_context+'.querySelector(\''+selector+'\').focus()'});}}; */ chrome.click = function(selector) { // click using Puppeteer's implementation - see TagUI issue #212 chrome.scrollIntoViewIfNeeded(selector); var xy = chrome.mouse.getXY(selector); chrome.mouse.action('mouseMoved',xy.x,xy.y,'none',0); chrome.mouse.action('mousePressed',xy.x,xy.y,'left',1); chrome.mouse.action('mouseReleased',xy.x,xy.y,'left',1);} chrome.scrollIntoViewIfNeeded = function(selector) { // helper function to scroll element into view if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).scrollIntoViewIfNeeded()'});} else {chrome_step('Runtime.evaluate',{expression: chrome_context+'.querySelector(\''+selector+'\').scrollIntoViewIfNeeded()'});}} chrome.mouse.action = function(type,x,y,button,clickCount) { // helper function to send various mouse events chrome_step('Input.dispatchMouseEvent',{type: type, x: x, y: y, button: button, clickCount: clickCount});}; chrome.mouse.getXY = function(selector) { // helper function to get xy center coordinates of selector if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); var ws_message = chrome_step('Runtime.evaluate',{expression: 'var result_bounds = document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).getBoundingClientRect(); var result_xy = {x: Math.round(result_bounds.left + result_bounds.width / 2), y: Math.round(result_bounds.top + result_bounds.height / 2)}; result_xy', returnByValue: true});} else var ws_message = chrome_step('Runtime.evaluate',{expression: 'var result_bounds = '+chrome_context+'.querySelector(\''+selector+'\').getBoundingClientRect(); var result_xy = {x: Math.round(result_bounds.left + result_bounds.width / 2), y: Math.round(result_bounds.top + result_bounds.height / 2)}; result_xy', returnByValue: true}); try {var ws_json = JSON.parse(ws_message); if (ws_json.result.result.value.x > 0 && ws_json.result.result.value.y > 0) return ws_json.result.result.value; else return {x: 0, y: 0};} catch(e) {return {x: 0, y: 0};}}; chrome.getRect = function(selector) { // helper function to get rectangle boundary coordinates of selector if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); var ws_message = chrome_step('Runtime.evaluate',{expression: 'var result_bounds = document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).getBoundingClientRect(); var result_rect = {top: Math.round(result_bounds.top), left: Math.round(result_bounds.left), width: Math.round(result_bounds.width), height: Math.round(result_bounds.height)}; result_rect', returnByValue: true});} else var ws_message = chrome_step('Runtime.evaluate',{expression: 'var result_bounds = '+chrome_context+'.querySelector(\''+selector+'\').getBoundingClientRect(); var result_rect = {top: Math.round(result_bounds.top), left: Math.round(result_bounds.left), width: Math.round(result_bounds.width), height: Math.round(result_bounds.height)}; result_rect', returnByValue: true}); try {var ws_json = JSON.parse(ws_message); // check if width and height are valid before returning coordinates if (ws_json.result.result.value.width > 0 && ws_json.result.result.value.height > 0) return ws_json.result.result.value; else return {left: 0, top: 0, width: 0, height: 0};} catch(e) {return {left: 0, top: 0, width: 0, height: 0};}}; chrome.mouse.move = function(selector,y) { // move mouse pointer to center of specified selector or point if (!y) {chrome.scrollIntoViewIfNeeded(selector); var xy = chrome.mouse.getXY(selector);} else var xy = {x: selector, y: y}; // get coordinates accordingly chrome.mouse.action('mouseMoved',xy.x,xy.y,'none',0);}; chrome.mouse.click = function(selector,y) { // press and release on center of specfied selector or point if (!y) {chrome.scrollIntoViewIfNeeded(selector); var xy = chrome.mouse.getXY(selector);} else var xy = {x: selector, y: y}; // get coordinates accordingly chrome.mouse.action('mousePressed',xy.x,xy.y,'left',1); chrome.mouse.action('mouseReleased',xy.x,xy.y,'left',1);}; chrome.mouse.doubleclick = function(selector,y) { // double press and release on center of selector or point if (!y) {chrome.scrollIntoViewIfNeeded(selector); var xy = chrome.mouse.getXY(selector);} else var xy = {x: selector, y: y}; // get coordinates accordingly chrome.mouse.action('mousePressed',xy.x,xy.y,'left',2); chrome.mouse.action('mouseReleased',xy.x,xy.y,'left',2);}; chrome.mouse.rightclick = function(selector,y) { // right click press and release on center of selector or point if (!y) {chrome.scrollIntoViewIfNeeded(selector); var xy = chrome.mouse.getXY(selector);} else var xy = {x: selector, y: y}; // get coordinates accordingly chrome.mouse.action('mousePressed',xy.x,xy.y,'right',1); chrome.mouse.action('mouseReleased',xy.x,xy.y,'right',1);}; chrome.mouse.down = function(selector,y) { // left press on center of specified selector or point if (!y) {chrome.scrollIntoViewIfNeeded(selector); var xy = chrome.mouse.getXY(selector);} else var xy = {x: selector, y: y}; // get coordinates accordingly chrome.mouse.action('mousePressed',xy.x,xy.y,'left',1);}; chrome.mouse.up = function(selector,y) { // left release on center of specified selector or point if (!y) {chrome.scrollIntoViewIfNeeded(selector); var xy = chrome.mouse.getXY(selector);} else var xy = {x: selector, y: y}; // get coordinates accordingly chrome.mouse.action('mouseReleased',xy.x,xy.y,'left',1);}; chrome.sendKeys = function(selector,value,options) { // send key strokes to selector, options not implemented if (value == casper.page.event.key.Enter) value = '\r'; if (value) {value = value.replace(/\[enter\]/g,'\r'); // to cater for [enter] passed in as part of a variable value = value.replace(/\r\n/g,'\r'); // change \r\n to \r which is the enter key for chrome browser value = value.replace(/\n/g,'\r');} // change \n to \r which is the enter key for chrome browser if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).focus()'});} else chrome_step('Runtime.evaluate',{expression: chrome_context+'.querySelector(\''+selector+'\').focus()'}); if (options && options.reset == true) // handling for clearing field by checking options.reset {if ((selector.indexOf('/') == 0) || (selector.indexOf('(') == 0)) // check for xpath selector and handle accordingly {chrome_step('Runtime.evaluate',{expression: 'var sendKeys_field = document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0); sendKeys_field.value = \'\'; var evt = document.createEvent(\'UIEvents\'); evt.initUIEvent(\'change\', true, true); sendKeys_field.dispatchEvent(evt);'});} else chrome_step('Runtime.evaluate',{expression: 'var sendKeys_field = '+chrome_context+'.querySelector(\''+selector+'\'); sendKeys_field.value = \'\'; var evt = document.createEvent(\'UIEvents\'); evt.initUIEvent(\'change\', true, true); sendKeys_field.dispatchEvent(evt);'});} for (var character = 0, length = value.length; character < length; character++) { chrome_step('Input.dispatchKeyEvent',{type: 'char', text: value[character]});}}; chrome.selectOptionByValue = function(selector,valueToMatch) { // select dropdown option (base on casperjs issue #1390) chrome.evaluate('function() {var selector = \''+selector+'\'; var valueToMatch = \''+valueToMatch+'\'; var found = false; if ((selector.indexOf(\'/\') == 0) || (selector.indexOf(\'(\') == 0)) var select = document.evaluate(selector,'+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0); else var select = '+chrome_context+'.querySelector(selector); if (valueToMatch == \'[clear]\') valueToMatch = \'\'; Array.prototype.forEach.call(select.children, function(opt, i) {if (!found && opt.value.indexOf(valueToMatch) !== -1) {select.selectedIndex = i; found = true;}}); var evt = document.createEvent("UIEvents"); evt.initUIEvent("change", true, true); select.dispatchEvent(evt);}');}; chrome.fetchText = function(selector) { // grab text from selector following casperjs logic, but grab only first match if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16); var ws_message = chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).textContent || document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).innerText || document.evaluate(\''+selector+'\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).value || \'\''});} else var ws_message = chrome_step('Runtime.evaluate',{expression: chrome_context+'.querySelector(\''+selector+'\').textContent || '+chrome_context+'.querySelector(\''+selector+'\').innerText || '+chrome_context+'.querySelector(\''+selector+'\').value || \'\''}); try {var ws_json = JSON.parse(ws_message); if (ws_json.result.result.value) return ws_json.result.result.value; else return '';} catch(e) {return '';}}; chrome.decode = function(str) { // funtion to convert base64 data to binary string // used in https://github.com/casperjs/casperjs/blob/master/modules/clientutils.js if (!str) return ''; // return empty string if somehow null value is passed in var BASE64_DECODE_CHARS = [ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1]; var c1, c2, c3, c4, i = 0, len = str.length, out = ""; while (i < len) { do {c1 = BASE64_DECODE_CHARS[str.charCodeAt(i++) & 0xff];} while (i < len && c1 === -1); if (c1 === -1) {break;} do {c2 = BASE64_DECODE_CHARS[str.charCodeAt(i++) & 0xff];} while (i < len && c2 === -1); if (c2 === -1) {break;} out += String.fromCharCode(c1 << 2 | (c2 & 0x30) >> 4); do {c3 = str.charCodeAt(i++) & 0xff; if (c3 === 61) {return out;} c3 = BASE64_DECODE_CHARS[c3];} while (i < len && c3 === -1); if (c3 === -1) {break;} out += String.fromCharCode((c2 & 0XF) << 4 | (c3 & 0x3C) >> 2); do {c4 = str.charCodeAt(i++) & 0xff; if (c4 === 61) {return out;} c4 = BASE64_DECODE_CHARS[c4];} while (i < len && c4 === -1); if (c4 === -1) {break;} out += String.fromCharCode((c3 & 0x03) << 6 | c4);} return out;}; chrome.capture = function(filename) { // capture screenshot of webpage to file in png/jpg/jpeg format // having pdf extension saves to a pdf file instead. only works in headless mode, visible mode errors out var format = 'png'; var quality = 80; var fromSurface = true; var screenshot_data = ''; // options not implemented if ((filename.substr(-3).toLowerCase() == 'jpg') || (filename.substr(-4).toLowerCase() == 'jpeg')) format = 'jpeg'; if (filename.substr(-3).toLowerCase() == 'pdf') var ws_message = chrome_step('Page.printToPDF',{printBackground: true}); else var ws_message = chrome_step('Page.captureScreenshot',{format: format, quality: quality, fromSurface: fromSurface}); try {var ws_json = JSON.parse(ws_message); screenshot_data = ws_json.result.data;} catch(e) {screenshot_data = '';} var fs = require('fs'); fs.write(filename,chrome.decode(screenshot_data),'wb');}; chrome.captureSelector = function(filename,selector) { // capture screenshot of selector to png/jpg/jpeg format // first capture entire screen, then use casperjs / phantomjs browser to crop image base on selector dimensions chrome.capture(filename); var selector_rect = chrome.getRect(selector); // so that there is no extra dependency if (selector_rect.width > 0 && selector_rect.height > 0) // from using other libraries or creating html canvas casper.thenOpen(file_url(filename), function() {casper . capture(filename, // spaces around . to avoid replacing {top: selector_rect.top, left: selector_rect.left, width: selector_rect.width, height: selector_rect.height}); casper.thenOpen('about:blank');});}; // reset phantomjs browser state chrome.upload = function(selector,filename) { // upload function to upload file to provided selector if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: ')) {casper.echo('ERROR - upload step is only implemented for CSS selector and not XPath selector'); casper.echo('ERROR - for consistency with PhantomJS as it only supports upload with CSS selector');} else try {var ws_message = ""; var ws_json = {}; ws_message = chrome_step('DOM.getDocument',{}); ws_json = JSON.parse(ws_message); ws_message = chrome_step('DOM.querySelector',{nodeId: ws_json.result.root.nodeId, selector: selector}); ws_json = JSON.parse(ws_message); ws_message = chrome_step('DOM.setFileInputFiles',{files: [filename], nodeId: ws_json.result.nodeId}); ws_json = JSON.parse(ws_message); ws_message = chrome_step('DOM.disable'); // disable invoked DOM agent from running and firing events } catch(e) {casper.echo('ERROR - unable to upload ' + selector + ' as ' + filename);}}; chrome.download = function(url,filename) { // download function for downloading url resource to file // casper download cannot be used for urls which requires login as casperjs engine can't access chrome // the chromium issue 696481 is moving well, else an alternative may be to inject casper clientutils.js // TagUI by default auto-sets to allow downloads for headless Chrome (otherwise it prevents downloads) casper.echo('ERROR - for headless and visible Chrome, download file using normal webpage interaction');}; chrome.evaluate = function(fn_statement,eval_json) { // evaluate expression in browser dom context // chrome runtime.evaluate is different from casperjs evaluate, do some processing to reduce gap var statement = fn_statement.toString(); if (!eval_json) {statement = statement.slice(statement.indexOf('{')+1,statement.lastIndexOf('}')); statement = statement.replace(/return /g,'');} // defining function() with return keyword is invalid for chrome else statement = '(' + statement + ')' + '(' + JSON.stringify(eval_json) + ')'; // unless variable is passed into fx var ws_message = chrome_step('Runtime.evaluate',{expression: statement}); // statements can be separated by ; try {var ws_json = JSON.parse(ws_message); if (ws_json.result.result.value) return ws_json.result.result.value; else return null;} catch(e) {return null;}}; chrome.withFrame = function(frameInfo,then) { // replace casperjs frame for switching frame context var new_context = ''; if (chrome_context == 'document') new_context = 'mainframe_context'; else if (chrome_context == 'mainframe_context') new_context = 'subframe_context'; casper.then(function _step() {chrome_step('Runtime.evaluate',{expression: new_context+' = document.evaluate(\'(//frame|//iframe)[@name="'+frameInfo+'" or @id="'+frameInfo+'"]\','+chrome_context+',null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).contentDocument'}); chrome_context = new_context;}); // set mainframe_context/subframe_context in dom casper.then(then); casper.then(function _step() {if (chrome_context == 'subframe_context') {chrome_step('Runtime.evaluate',{expression: 'subframe_context = null'}); chrome_context = 'mainframe_context';} else if (chrome_context == 'mainframe_context') {chrome_step('Runtime.evaluate',{expression: 'mainframe_context = null'}); chrome_context = 'document';}});}; chrome.waitForPopup = function(popupInfo,then,onTimeout) { // replace casperjs waitforpopup for checking popup window casper.waitFor(function check() { // use similar logic as chrome withpopup to scan through list of browser targets var found_popup = false; var chrome_targets = []; var ws_message = chrome_step('Target.getTargets',{}); try {var ws_json = JSON.parse(ws_message); if (ws_json.result.targetInfos) chrome_targets = ws_json.result.targetInfos; else chrome_targets = [];} catch(e) {chrome_targets = [];} // following line scan through targets to find match chrome_targets.forEach(function(target) {if (target.url.search(popupInfo) !== -1) found_popup = true;}); return found_popup;},then,onTimeout);}; chrome.withPopup = function(popupInfo,then) { // replace casperjs withpopup for handling popup window casper.then(function _step() { // get list of targets, find a match, attach to the target and set chrome_targetid var found_targetid = ''; var chrome_targets = []; var ws_message = chrome_step('Target.getTargets',{}); try {var ws_json = JSON.parse(ws_message); if (ws_json.result.targetInfos) chrome_targets = ws_json.result.targetInfos; else chrome_targets = [];} catch(e) {