--- /dev/null
+var __indexOf = [].indexOf || function (item) {
+ for (var i = 0, l = this.length; i < l; i++) {
+ if (i in this && this[i] === item) return i;
+ }
+ return -1;
+ };
+
+var xpathStringLiteral = function (s) {
+ if (s.indexOf('"') === -1)
+ return '"' + s + '"';
+ if (s.indexOf("'") === -1)
+ return "'" + s + "'";
+ return 'concat("' + s.replace(/"/g, '",\'"\',"') + '")';
+};
+
+Poltergeist.Browser = (function () {
+ /**
+ * Creates the "browser" inside phantomjs
+ * @param owner
+ * @param width
+ * @param height
+ * @param jsErrors
+ * @constructor
+ */
+ function Browser(owner, width, height, jsErrors) {
+ this.owner = owner;
+ this.width = width || 1024;
+ this.height = height || 768;
+ this.pages = [];
+ this.js_errors = (typeof jsErrors === 'boolean') ? jsErrors : true;
+ this._debug = false;
+ this._counter = 0;
+ this.resetPage();
+ }
+
+ /**
+ * Resets the browser to a clean slate
+ * @return {Function}
+ */
+ Browser.prototype.resetPage = function () {
+ var _ref;
+ var self = this;
+
+ _ref = [0, []];
+ this._counter = _ref[0];
+ this.pages = _ref[1];
+
+ if (this.page != null) {
+ if (!this.page.closed) {
+ if (this.page.currentUrl() !== 'about:blank') {
+ this.page.clearLocalStorage();
+ }
+ this.page.release();
+ }
+ phantom.clearCookies();
+ }
+
+ this.page = this.currentPage = new Poltergeist.WebPage;
+ this.page.setViewportSize({
+ width: this.width,
+ height: this.height
+ });
+ this.page.handle = "" + (this._counter++);
+ this.pages.push(this.page);
+
+ return this.page.onPageCreated = function (newPage) {
+ var page;
+ page = new Poltergeist.WebPage(newPage);
+ page.handle = "" + (self._counter++);
+ return self.pages.push(page);
+ };
+ };
+
+ /**
+ * Given a page handle id, tries to get it from the browser page list
+ * @param handle
+ * @return {WebPage}
+ */
+ Browser.prototype.getPageByHandle = function (handle) {
+ var filteredPages;
+
+ //TODO: perhaps we should throw a PageNotFoundByHandle or something like that..
+ if (handle === null || typeof handle == "undefined") {
+ return null;
+ }
+
+ filteredPages = this.pages.filter(function (p) {
+ return !p.closed && p.handle === handle;
+ });
+
+ if (filteredPages.length === 1) {
+ return filteredPages[0];
+ }
+
+ return null;
+ };
+
+ /**
+ * Sends a debug message to the console
+ * @param message
+ * @return {*}
+ */
+ Browser.prototype.debug = function (message) {
+ if (this._debug) {
+ return console.log("poltergeist [" + (new Date().getTime()) + "] " + message);
+ }
+ };
+
+ /**
+ * Given a page_id and id, gets if possible the node in such page
+ * @param page_id
+ * @param id
+ * @return {Poltergeist.Node}
+ */
+ Browser.prototype.node = function (page_id, id) {
+ if (this.currentPage.id === page_id) {
+ return this.currentPage.get(id);
+ } else {
+ throw new Poltergeist.ObsoleteNode;
+ }
+ };
+
+ /**
+ * Returns the frameUrl related to the frame given by name
+ * @param frame_name
+ * @return {*}
+ */
+ Browser.prototype.frameUrl = function (frame_name) {
+ return this.currentPage.frameUrl(frame_name);
+ };
+
+ /**
+ * This method defines the rectangular area of the web page to be rasterized when render is invoked.
+ * If no clipping rectangle is set, render will process the entire web page.
+ * @param full
+ * @param selector
+ * @return {*}
+ */
+ Browser.prototype.set_clip_rect = function (full, selector) {
+ var dimensions, clipDocument, rect, clipViewport;
+
+ dimensions = this.currentPage.validatedDimensions();
+ clipDocument = dimensions.document;
+ clipViewport = dimensions.viewport;
+
+ if (full) {
+ rect = {
+ left: 0,
+ top: 0,
+ width: clipDocument.width,
+ height: clipDocument.height
+ };
+ } else {
+ if (selector != null) {
+ rect = this.currentPage.elementBounds(selector);
+ } else {
+ rect = {
+ left: 0,
+ top: 0,
+ width: clipViewport.width,
+ height: clipViewport.height
+ };
+ }
+ }
+
+ this.currentPage.setClipRect(rect);
+ return dimensions;
+ };
+
+ /**
+ * Kill the browser, i.e kill phantomjs current process
+ * @return {int}
+ */
+ Browser.prototype.exit = function () {
+ return phantom.exit(0);
+ };
+
+ /**
+ * Do nothing
+ */
+ Browser.prototype.noop = function () {
+ };
+
+ /**
+ * Throws a new Object error
+ */
+ Browser.prototype.browser_error = function () {
+ throw new Error('zomg');
+ };
+
+ /**
+ * Visits a page and load its content
+ * @param serverResponse
+ * @param url
+ * @return {*}
+ */
+ Browser.prototype.visit = function (serverResponse, url) {
+ var prevUrl;
+ var self = this;
+ this.currentPage.state = 'loading';
+ prevUrl = this.currentPage.source === null ? 'about:blank' : this.currentPage.currentUrl();
+ this.currentPage.open(url);
+ if (/#/.test(url) && prevUrl.split('#')[0] === url.split('#')[0]) {
+ this.currentPage.state = 'default';
+ return this.serverSendResponse({
+ status: 'success'
+ }, serverResponse);
+ } else {
+ return this.currentPage.waitState('default', function () {
+ if (self.currentPage.statusCode === null && self.currentPage.status === 'fail') {
+ return self.owner.serverSendError(new Poltergeist.StatusFailError, serverResponse);
+ } else {
+ return self.serverSendResponse({
+ status: self.currentPage.status
+ }, serverResponse);
+ }
+ });
+ }
+ };
+
+ /**
+ * Puts the control of the browser inside the IFRAME given by name
+ * @param serverResponse
+ * @param name
+ * @param timeout
+ * @return {*}
+ */
+ Browser.prototype.push_frame = function (serverResponse, name, timeout) {
+ var _ref;
+ var self = this;
+
+ if (timeout == null) {
+ timeout = new Date().getTime() + 2000;
+ }
+
+ //TODO: WTF, else if after a if with return COMMON
+ if (_ref = this.frameUrl(name), __indexOf.call(this.currentPage.blockedUrls(), _ref) >= 0) {
+ return this.serverSendResponse(true, serverResponse);
+ } else if (this.currentPage.pushFrame(name)) {
+ if (this.currentPage.currentUrl() === 'about:blank') {
+ this.currentPage.state = 'awaiting_frame_load';
+ return this.currentPage.waitState('default', function () {
+ return self.serverSendResponse(true, serverResponse);
+ });
+ } else {
+ return this.serverSendResponse(true, serverResponse);
+ }
+ } else {
+ if (new Date().getTime() < timeout) {
+ return setTimeout((function () {
+ return self.push_frame(serverResponse, name, timeout);
+ }), 50);
+ } else {
+ return this.owner.serverSendError(new Poltergeist.FrameNotFound(name), serverResponse);
+ }
+ }
+ };
+
+ /**
+ * Injects a javascript into the current page
+ * @param serverResponse
+ * @param extension
+ * @return {*}
+ */
+ Browser.prototype.add_extension = function (serverResponse, extension) {
+ //TODO: error control when the injection was not possible
+ this.currentPage.injectExtension(extension);
+ return this.serverSendResponse('success', serverResponse);
+ };
+
+ /**
+ * Returns the url we are currently in
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.current_url = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.currentUrl(), serverResponse);
+ };
+
+ /**
+ * Returns the current page window name
+ * @param serverResponse
+ * @returns {*}
+ */
+ Browser.prototype.window_name = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.windowName(), serverResponse);
+ };
+
+ /**
+ * Returns the status code associated to the page
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.status_code = function (serverResponse) {
+ if (this.currentPage.statusCode === undefined || this.currentPage.statusCode === null) {
+ return this.owner.serverSendError(new Poltergeist.StatusFailError("status_code_error"), serverResponse);
+ }
+ return this.serverSendResponse(this.currentPage.statusCode, serverResponse);
+ };
+
+ /**
+ * Returns the source code of the active frame, useful for when inside an IFRAME
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.body = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.content(), serverResponse);
+ };
+
+ /**
+ * Returns the source code of the page all the html
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.source = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.source, serverResponse);
+ };
+
+ /**
+ * Returns the current page title
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.title = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.title(), serverResponse);
+ };
+
+ /**
+ * Finds the elements that match a method of selection and a selector
+ * @param serverResponse
+ * @param method
+ * @param selector
+ * @return {*}
+ */
+ Browser.prototype.find = function (serverResponse, method, selector) {
+ return this.serverSendResponse({
+ page_id: this.currentPage.id,
+ ids: this.currentPage.find(method, selector)
+ }, serverResponse);
+ };
+
+ /**
+ * Find elements within a given element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param method
+ * @param selector
+ * @return {*}
+ */
+ Browser.prototype.find_within = function (serverResponse, page_id, id, method, selector) {
+ return this.serverSendResponse(this.node(page_id, id).find(method, selector), serverResponse);
+ };
+
+ /**
+ * Returns ALL the text, visible and not visible from the given element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.all_text = function (serverResponse, page_id, id) {
+ return this.serverSendResponse(this.node(page_id, id).allText(), serverResponse);
+ };
+
+ /**
+ * Returns the inner or outer html of a given id
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param type
+ * @returns Object
+ */
+ Browser.prototype.all_html = function (serverResponse, page_id, id, type) {
+ return this.serverSendResponse(this.node(page_id, id).allHTML(type), serverResponse);
+ };
+
+ /**
+ * Returns only the visible text in a given element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.visible_text = function (serverResponse, page_id, id) {
+ return this.serverSendResponse(this.node(page_id, id).visibleText(), serverResponse);
+ };
+
+ /**
+ * Deletes the text in a given element leaving it empty
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.delete_text = function (serverResponse, page_id, id) {
+ return this.serverSendResponse(this.node(page_id, id).deleteText(), serverResponse);
+ };
+
+ /**
+ * Gets the value of a given attribute in an element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param name
+ * @return {*}
+ */
+ Browser.prototype.attribute = function (serverResponse, page_id, id, name) {
+ return this.serverSendResponse(this.node(page_id, id).getAttribute(name), serverResponse);
+ };
+
+ /**
+ * Allows the possibility to set an attribute on a given element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param name
+ * @param value
+ * @returns {*}
+ */
+ Browser.prototype.set_attribute = function (serverResponse, page_id, id, name, value) {
+ return this.serverSendResponse(this.node(page_id, id).setAttribute(name, value), serverResponse);
+ };
+
+ /**
+ * Allows the possibility to remove an attribute on a given element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param name
+ * @returns {*}
+ */
+ Browser.prototype.remove_attribute = function (serverResponse, page_id, id, name) {
+ return this.serverSendResponse(this.node(page_id, id).removeAttribute(name), serverResponse);
+ };
+
+ /**
+ * Returns all the attributes of a given element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param name
+ * @return {*}
+ */
+ Browser.prototype.attributes = function (serverResponse, page_id, id, name) {
+ return this.serverSendResponse(this.node(page_id, id).getAttributes(), serverResponse);
+ };
+
+ /**
+ * Returns all the way to the document level the parents of a given element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.parents = function (serverResponse, page_id, id) {
+ return this.serverSendResponse(this.node(page_id, id).parentIds(), serverResponse);
+ };
+
+ /**
+ * Returns the element.value of an element given by its page and id
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.value = function (serverResponse, page_id, id) {
+ return this.serverSendResponse(this.node(page_id, id).value(), serverResponse);
+ };
+
+ /**
+ * Sets the element.value of an element by the given value
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param value
+ * @return {*}
+ */
+ Browser.prototype.set = function (serverResponse, page_id, id, value) {
+ this.node(page_id, id).set(value);
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Uploads a file to an input file element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param file_path
+ * @return {*}
+ */
+ Browser.prototype.select_file = function (serverResponse, page_id, id, file_path) {
+ var node = this.node(page_id, id);
+
+ this.currentPage.beforeUpload(node.id);
+ this.currentPage.uploadFile('[_poltergeist_selected]', file_path);
+ this.currentPage.afterUpload(node.id);
+
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Sets a value to the selected element (to be used in select elements)
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param value
+ * @return {*}
+ */
+ Browser.prototype.select = function (serverResponse, page_id, id, value) {
+ return this.serverSendResponse(this.node(page_id, id).select(value), serverResponse);
+ };
+
+ /**
+ * Selects an option with the given value
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param value
+ * @param multiple
+ * @return {*}
+ */
+ Browser.prototype.select_option = function (serverResponse, page_id, id, value, multiple) {
+ return this.serverSendResponse(this.node(page_id, id).select_option(value, multiple), serverResponse);
+ };
+
+ /**
+ *
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.tag_name = function (serverResponse, page_id, id) {
+ return this.serverSendResponse(this.node(page_id, id).tagName(), serverResponse);
+ };
+
+
+ /**
+ * Tells if an element is visible or not
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.visible = function (serverResponse, page_id, id) {
+ return this.serverSendResponse(this.node(page_id, id).isVisible(), serverResponse);
+ };
+
+ /**
+ * Tells if an element is disabled
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.disabled = function (serverResponse, page_id, id) {
+ return this.serverSendResponse(this.node(page_id, id).isDisabled(), serverResponse);
+ };
+
+ /**
+ * Evaluates a javascript and returns the outcome to the client
+ * This will be JSON response so your script better be returning objects that can be used
+ * in JSON.stringify
+ * @param serverResponse
+ * @param script
+ * @return {*}
+ */
+ Browser.prototype.evaluate = function (serverResponse, script) {
+ return this.serverSendResponse(this.currentPage.evaluate("function() { return " + script + " }"), serverResponse);
+ };
+
+ /**
+ * Executes a javascript and goes back to the client with true if there were no errors
+ * @param serverResponse
+ * @param script
+ * @return {*}
+ */
+ Browser.prototype.execute = function (serverResponse, script) {
+ this.currentPage.execute("function() { " + script + " }");
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * If inside a frame then we will go back to the parent
+ * Not defined behaviour if you pop and are not inside an iframe
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.pop_frame = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.popFrame(), serverResponse);
+ };
+
+ /**
+ * Gets the window handle id by a given window name
+ * @param serverResponse
+ * @param name
+ * @return {*}
+ */
+ Browser.prototype.window_handle = function (serverResponse, name) {
+ var handle, pageByWindowName;
+
+ if (name === null || typeof name == "undefined" || name.length === 0) {
+ return this.serverSendResponse(this.currentPage.handle, serverResponse);
+ }
+
+ handle = null;
+
+ //Lets search the handle by the given window name
+ var filteredPages = this.pages.filter(function (p) {
+ return !p.closed && p.windowName() === name;
+ });
+
+ //A bit of error control is always good
+ if (Array.isArray(filteredPages) && filteredPages.length >= 1) {
+ pageByWindowName = filteredPages[0];
+ } else {
+ pageByWindowName = null;
+ }
+
+ if (pageByWindowName !== null && typeof pageByWindowName != "undefined") {
+ handle = pageByWindowName.handle;
+ }
+
+ return this.serverSendResponse(handle, serverResponse);
+ };
+
+ /**
+ * Returns all the window handles of opened windows
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.window_handles = function (serverResponse) {
+ var handles, filteredPages;
+
+ filteredPages = this.pages.filter(function (p) {
+ return !p.closed;
+ });
+
+ if (filteredPages.length > 0) {
+ handles = filteredPages.map(function (p) {
+ return p.handle;
+ });
+ if (handles.length === 0) {
+ handles = null;
+ }
+ } else {
+ handles = null;
+ }
+
+ return this.serverSendResponse(handles, serverResponse);
+ };
+
+ /**
+ * Tries to switch to a window given by the handle id
+ * @param serverResponse
+ * @param handle
+ * @return {*}
+ */
+ Browser.prototype.switch_to_window = function (serverResponse, handle) {
+ var page;
+ var self = this;
+
+ page = this.getPageByHandle(handle);
+ if (page === null || typeof page == "undefined") {
+ throw new Poltergeist.NoSuchWindowError;
+ }
+
+ if (page !== this.currentPage) {
+ return page.waitState('default', function () {
+ self.currentPage = page;
+ return self.serverSendResponse(true, serverResponse);
+ });
+ }
+
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Opens a new window where we can do stuff
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.open_new_window = function (serverResponse) {
+ return this.execute(serverResponse, 'window.open()');
+ };
+
+ /**
+ * Closes the window given by handle name if possible
+ * @param serverResponse
+ * @param handle
+ * @return {*}
+ */
+ Browser.prototype.close_window = function (serverResponse, handle) {
+ var page;
+
+ page = this.getPageByHandle(handle);
+ if (page === null || typeof page == "undefined") {
+ //TODO: should we throw error since we actually could not find the window?
+ return this.serverSendResponse(false, serverResponse);
+ }
+
+ //TODO: we have to add some control here to actually asses that the release has been done
+ page.release();
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Generic mouse event on an element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param name
+ * @return {number}
+ */
+ Browser.prototype.mouse_event = function (serverResponse, page_id, id, name) {
+ var node;
+ var self = this;
+ node = this.node(page_id, id);
+ this.currentPage.state = 'mouse_event';
+ this.last_mouse_event = node.mouseEvent(name);
+ return setTimeout(function () {
+ if (self.currentPage.state === 'mouse_event') {
+ self.currentPage.state = 'default';
+ return self.serverSendResponse({
+ position: self.last_mouse_event
+ }, serverResponse);
+ } else {
+ return self.currentPage.waitState('default', function () {
+ return self.serverSendResponse({
+ position: self.last_mouse_event
+ }, serverResponse);
+ });
+ }
+ }, 5);
+ };
+
+ /**
+ * Simple click on the element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.click = function (serverResponse, page_id, id) {
+ return this.mouse_event(serverResponse, page_id, id, 'click');
+ };
+
+ /**
+ * Right click on the element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.right_click = function (serverResponse, page_id, id) {
+ return this.mouse_event(serverResponse, page_id, id, 'rightclick');
+ };
+
+ /**
+ * Double click on the element given by page and id
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.double_click = function (serverResponse, page_id, id) {
+ return this.mouse_event(serverResponse, page_id, id, 'doubleclick');
+ };
+
+ /**
+ * Executes a mousemove event on the page and given element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @return {*}
+ */
+ Browser.prototype.hover = function (serverResponse, page_id, id) {
+ return this.mouse_event(serverResponse, page_id, id, 'mousemove');
+ };
+
+ /**
+ * Triggers a mouse click event on the given coordinates
+ * @param serverResponse
+ * @param x
+ * @param y
+ * @return {*}
+ */
+ Browser.prototype.click_coordinates = function (serverResponse, x, y) {
+ var response;
+
+ this.currentPage.sendEvent('click', x, y);
+ response = {
+ click: {
+ x: x,
+ y: y
+ }
+ };
+
+ return this.serverSendResponse(response, serverResponse);
+ };
+
+ /**
+ * Drags one element into another, useful for nice javascript thingies
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param other_id
+ * @return {*}
+ */
+ Browser.prototype.drag = function (serverResponse, page_id, id, other_id) {
+ this.node(page_id, id).dragTo(this.node(page_id, other_id));
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Triggers an event on the given page and element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param event
+ * @return {*}
+ */
+ Browser.prototype.trigger = function (serverResponse, page_id, id, event) {
+ this.node(page_id, id).trigger(event);
+ return this.serverSendResponse(event, serverResponse);
+ };
+
+ /**
+ * Checks if two elements are equal on a dom level
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param other_id
+ * @return {*}
+ */
+ Browser.prototype.equals = function (serverResponse, page_id, id, other_id) {
+ return this.serverSendResponse(this.node(page_id, id).isEqual(this.node(page_id, other_id)), serverResponse);
+ };
+
+ /**
+ * Resets the current page to a clean slate
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.reset = function (serverResponse) {
+ this.resetPage();
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Scrolls to a position given by the left, top coordinates
+ * @param serverResponse
+ * @param left
+ * @param top
+ * @return {*}
+ */
+ Browser.prototype.scroll_to = function (serverResponse, left, top) {
+ this.currentPage.setScrollPosition({
+ left: left,
+ top: top
+ });
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Sends keys to an element simulating as closest as possible what a user would do
+ * when typing
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param keys
+ * @return {*}
+ */
+ Browser.prototype.send_keys = function (serverResponse, page_id, id, keys) {
+ var key, sequence, target, _i, _len;
+ target = this.node(page_id, id);
+ if (!target.containsSelection()) {
+ target.mouseEvent('click');
+ }
+ for (_i = 0, _len = keys.length; _i < _len; _i++) {
+ sequence = keys[_i];
+ key = sequence.key != null ? this.currentPage.keyCode(sequence.key) : sequence;
+ this.currentPage.sendEvent('keypress', key);
+ }
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Sends a native phantomjs key event to element
+ * @param serverResponse
+ * @param page_id
+ * @param id
+ * @param keyEvent
+ * @param key
+ * @param modifier
+ */
+ Browser.prototype.key_event = function (serverResponse, page_id, id, keyEvent, key, modifier) {
+ var keyEventModifierMap;
+ var keyEventModifier;
+ var target;
+
+ keyEventModifierMap = {
+ 'none': 0x0,
+ 'shift': 0x02000000,
+ 'ctrl': 0x04000000,
+ 'alt': 0x08000000,
+ 'meta': 0x10000000
+ };
+ keyEventModifier = keyEventModifierMap[modifier];
+
+ target = this.node(page_id, id);
+ if (!target.containsSelection()) {
+ target.mouseEvent('click');
+ }
+ target.page.sendEvent(keyEvent, key, null, null, keyEventModifier);
+
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Sends the rendered page in a base64 encoding
+ * @param serverResponse
+ * @param format
+ * @param full
+ * @param selector
+ * @return {*}
+ */
+ Browser.prototype.render_base64 = function (serverResponse, format, full, selector) {
+ var encoded_image;
+ if (selector == null) {
+ selector = null;
+ }
+ this.set_clip_rect(full, selector);
+ encoded_image = this.currentPage.renderBase64(format);
+ return this.serverSendResponse(encoded_image, serverResponse);
+ };
+
+ /**
+ * Renders the current page entirely or a given selection
+ * @param serverResponse
+ * @param path
+ * @param full
+ * @param selector
+ * @return {*}
+ */
+ Browser.prototype.render = function (serverResponse, path, full, selector) {
+ var dimensions;
+ if (selector == null) {
+ selector = null;
+ }
+ dimensions = this.set_clip_rect(full, selector);
+ this.currentPage.setScrollPosition({
+ left: 0,
+ top: 0
+ });
+ this.currentPage.render(path);
+ this.currentPage.setScrollPosition({
+ left: dimensions.left,
+ top: dimensions.top
+ });
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+
+ /**
+ * Sets the paper size, useful when printing to PDF
+ * @param serverResponse
+ * @param size
+ * @return {*}
+ */
+ Browser.prototype.set_paper_size = function (serverResponse, size) {
+ this.currentPage.setPaperSize(size);
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Sets the zoom factor on the current page
+ * @param serverResponse
+ * @param zoom_factor
+ * @return {*}
+ */
+ Browser.prototype.set_zoom_factor = function (serverResponse, zoom_factor) {
+ this.currentPage.setZoomFactor(zoom_factor);
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Resizes the browser viewport, useful when testing mobile stuff
+ * @param serverResponse
+ * @param width
+ * @param height
+ * @return {*}
+ */
+ Browser.prototype.resize = function (serverResponse, width, height) {
+ this.currentPage.setViewportSize({
+ width: width,
+ height: height
+ });
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Gets the browser viewport size
+ * Because PhantomJS is headless (nothing is shown)
+ * viewportSize effectively simulates the size of the window like in a traditional browser.
+ * @param serverResponse
+ * @param handle
+ * @return {*}
+ */
+ Browser.prototype.window_size = function (serverResponse, handle) {
+ //TODO: add support for window handles
+ return this.serverSendResponse(this.currentPage.viewportSize(), serverResponse);
+ };
+
+ /**
+ * Returns the network traffic that the current page has generated
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.network_traffic = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.networkTraffic(), serverResponse);
+ };
+
+ /**
+ * Clears the accumulated network_traffic in the current page
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.clear_network_traffic = function (serverResponse) {
+ this.currentPage.clearNetworkTraffic();
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Gets the headers of the current page
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.get_headers = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.getCustomHeaders(), serverResponse);
+ };
+
+ /**
+ * Set headers in the browser
+ * @param serverResponse
+ * @param headers
+ * @return {*}
+ */
+ Browser.prototype.set_headers = function (serverResponse, headers) {
+ if (headers['User-Agent']) {
+ this.currentPage.setUserAgent(headers['User-Agent']);
+ }
+ this.currentPage.setCustomHeaders(headers);
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Given an array of headers, adds them to the page
+ * @param serverResponse
+ * @param headers
+ * @return {*}
+ */
+ Browser.prototype.add_headers = function (serverResponse, headers) {
+ var allHeaders, name, value;
+ allHeaders = this.currentPage.getCustomHeaders();
+ for (name in headers) {
+ if (headers.hasOwnProperty(name)) {
+ value = headers[name];
+ allHeaders[name] = value;
+ }
+ }
+ return this.set_headers(serverResponse, allHeaders);
+ };
+
+ /**
+ * Adds a header to the page temporary or permanently
+ * @param serverResponse
+ * @param header
+ * @param permanent
+ * @return {*}
+ */
+ Browser.prototype.add_header = function (serverResponse, header, permanent) {
+ if (!permanent) {
+ this.currentPage.addTempHeader(header);
+ }
+ return this.add_headers(serverResponse, header);
+ };
+
+
+ /**
+ * Sends back the client the response headers sent from the browser when making
+ * the page request
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.response_headers = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.responseHeaders(), serverResponse);
+ };
+
+ /**
+ * Returns the cookies of the current page being browsed
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.cookies = function (serverResponse) {
+ return this.serverSendResponse(this.currentPage.cookies(), serverResponse);
+ };
+
+ /**
+ * Sets a cookie in the browser, the format of the cookies has to be the format it says
+ * on phantomjs documentation and as such you can set it in other domains, not on the
+ * current page
+ * @param serverResponse
+ * @param cookie
+ * @return {*}
+ */
+ Browser.prototype.set_cookie = function (serverResponse, cookie) {
+ return this.serverSendResponse(phantom.addCookie(cookie), serverResponse);
+ };
+
+ /**
+ * Remove a cookie set on the current page
+ * @param serverResponse
+ * @param name
+ * @return {*}
+ */
+ Browser.prototype.remove_cookie = function (serverResponse, name) {
+ //TODO: add error control to check if the cookie was properly deleted
+ this.currentPage.deleteCookie(name);
+ phantom.deleteCookie(name);
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Clear the cookies in the browser
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.clear_cookies = function (serverResponse) {
+ phantom.clearCookies();
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Enables / Disables the cookies on the browser
+ * @param serverResponse
+ * @param flag
+ * @return {*}
+ */
+ Browser.prototype.cookies_enabled = function (serverResponse, flag) {
+ phantom.cookiesEnabled = flag;
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * US19: DONE
+ * Sets a basic authentication credential to access a page
+ * THIS SHOULD BE USED BEFORE accessing a page
+ * @param serverResponse
+ * @param user
+ * @param password
+ * @return {*}
+ */
+ Browser.prototype.set_http_auth = function (serverResponse, user, password) {
+ this.currentPage.setHttpAuth(user, password);
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Sets the flag whether to fail on javascript errors or not.
+ * @param serverResponse
+ * @param value
+ * @return {*}
+ */
+ Browser.prototype.set_js_errors = function (serverResponse, value) {
+ this.js_errors = value;
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Sets the debug mode to boolean value
+ * @param serverResponse
+ * @param value
+ * @return {*}
+ */
+ Browser.prototype.set_debug = function (serverResponse, value) {
+ this._debug = value;
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Goes back in the history when possible
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.go_back = function (serverResponse) {
+ var self = this;
+ if (this.currentPage.canGoBack()) {
+ this.currentPage.state = 'loading';
+ this.currentPage.goBack();
+ return this.currentPage.waitState('default', function () {
+ return self.serverSendResponse(true, serverResponse);
+ });
+ } else {
+ return this.serverSendResponse(false, serverResponse);
+ }
+ };
+
+ /**
+ * Reloads the page if possible
+ * @return {*}
+ */
+ Browser.prototype.reload = function (serverResponse) {
+ var self = this;
+ this.currentPage.state = 'loading';
+ this.currentPage.reload();
+ return this.currentPage.waitState('default', function () {
+ return self.serverSendResponse(true, serverResponse);
+ });
+ };
+
+ /**
+ * Goes forward in the browser history if possible
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.go_forward = function (serverResponse) {
+ var self = this;
+ if (this.currentPage.canGoForward()) {
+ this.currentPage.state = 'loading';
+ this.currentPage.goForward();
+ return this.currentPage.waitState('default', function () {
+ return self.serverSendResponse(true, serverResponse);
+ });
+ } else {
+ return this.serverSendResponse(false, serverResponse);
+ }
+ };
+
+ /**
+ * Sets the urlBlacklist for the given urls as parameters
+ * @return {boolean}
+ */
+ Browser.prototype.set_url_blacklist = function (serverResponse, blackList) {
+ this.currentPage.urlBlacklist = Array.prototype.slice.call(blackList);
+ return this.serverSendResponse(true, serverResponse);
+ };
+
+ /**
+ * Runs a browser command and returns the response back to the client
+ * when the command has finished the execution
+ * @param command
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.serverRunCommand = function (command, serverResponse) {
+ var commandData;
+ var commandArgs;
+ var commandName;
+
+ commandName = command.name;
+ commandArgs = command.args;
+ this.currentPage.state = 'default';
+ commandData = [serverResponse].concat(commandArgs);
+
+ if (typeof this[commandName] !== "function") {
+ //We can not run such command
+ throw new Poltergeist.Error();
+ }
+
+ return this[commandName].apply(this, commandData);
+ };
+
+ /**
+ * Sends a response back to the client who made the request
+ * @param response
+ * @param serverResponse
+ * @return {*}
+ */
+ Browser.prototype.serverSendResponse = function (response, serverResponse) {
+ var errors;
+ errors = this.currentPage.errors;
+ this.currentPage.clearErrors();
+ if (errors.length > 0 && this.js_errors) {
+ return this.owner.serverSendError(new Poltergeist.JavascriptError(errors), serverResponse);
+ } else {
+ return this.owner.serverSendResponse(response, serverResponse);
+ }
+ };
+
+ return Browser;
+
+})();