tagui
Version:
Command-line tool for digital process automation (RPA)
629 lines (546 loc) • 101 kB
JavaScript
// 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) {