Version 1
[yaffs-website] / vendor / jcalderonzumba / gastonjs / src / Client / web_page.js
diff --git a/vendor/jcalderonzumba/gastonjs/src/Client/web_page.js b/vendor/jcalderonzumba/gastonjs/src/Client/web_page.js
new file mode 100644 (file)
index 0000000..c275b03
--- /dev/null
@@ -0,0 +1,829 @@
+var __slice = [].slice;
+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;
+  };
+
+Poltergeist.WebPage = (function () {
+  var command, delegate, commandFunctionBind, delegateFunctionBind, i, j, commandsLength, delegatesRefLength, commandsRef, delegatesRef,
+    _this = this;
+
+  //Native or not webpage callbacks
+  WebPage.CALLBACKS = ['onAlert', 'onConsoleMessage', 'onLoadFinished', 'onInitialized', 'onLoadStarted', 'onResourceRequested',
+    'onResourceReceived', 'onError', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated', 'onClosing'];
+
+  // Delegates the execution to the phantomjs page native functions but directly available in the WebPage object
+  WebPage.DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render', 'renderBase64', 'goBack', 'goForward', 'reload'];
+
+  //Commands to execute on behalf of the browser but on the current page
+  WebPage.COMMANDS = ['currentUrl', 'find', 'nodeCall', 'documentSize', 'beforeUpload', 'afterUpload', 'clearLocalStorage'];
+
+  WebPage.EXTENSIONS = [];
+
+  function WebPage(nativeWebPage) {
+    var callback, i, callBacksLength, callBacksRef;
+
+    //Lets create the native phantomjs webpage
+    if (nativeWebPage === null || typeof nativeWebPage == "undefined") {
+      this._native = require('webpage').create();
+    } else {
+      this._native = nativeWebPage;
+    }
+
+    this.id = 0;
+    this.source = null;
+    this.closed = false;
+    this.state = 'default';
+    this.urlBlacklist = [];
+    this.frames = [];
+    this.errors = [];
+    this._networkTraffic = {};
+    this._tempHeaders = {};
+    this._blockedUrls = [];
+
+    callBacksRef = WebPage.CALLBACKS;
+    for (i = 0, callBacksLength = callBacksRef.length; i < callBacksLength; i++) {
+      callback = callBacksRef[i];
+      this.bindCallback(callback);
+    }
+  }
+
+  //Bind the commands we can run from the browser to the current page
+  commandsRef = WebPage.COMMANDS;
+  commandFunctionBind = function (command) {
+    return WebPage.prototype[command] = function () {
+      var args;
+      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+      return this.runCommand(command, args);
+    };
+  };
+  for (i = 0, commandsLength = commandsRef.length; i < commandsLength; i++) {
+    command = commandsRef[i];
+    commandFunctionBind(command);
+  }
+
+  //Delegates bind applications
+  delegatesRef = WebPage.DELEGATES;
+  delegateFunctionBind = function (delegate) {
+    return WebPage.prototype[delegate] = function () {
+      return this._native[delegate].apply(this._native, arguments);
+    };
+  };
+  for (j = 0, delegatesRefLength = delegatesRef.length; j < delegatesRefLength; j++) {
+    delegate = delegatesRef[j];
+    delegateFunctionBind(delegate);
+  }
+
+  /**
+   * This callback is invoked after the web page is created but before a URL is loaded.
+   * The callback may be used to change global objects.
+   * @return {*}
+   */
+  WebPage.prototype.onInitializedNative = function () {
+    this.id += 1;
+    this.source = null;
+    this.injectAgent();
+    this.removeTempHeaders();
+    return this.setScrollPosition({
+      left: 0,
+      top: 0
+    });
+  };
+
+  /**
+   * This callback is invoked when the WebPage object is being closed,
+   * either via page.close in the PhantomJS outer space or via window.close in the page's client-side.
+   * @return {boolean}
+   */
+  WebPage.prototype.onClosingNative = function () {
+    this.handle = null;
+    return this.closed = true;
+  };
+
+  /**
+   * This callback is invoked when there is a JavaScript console message on the web page.
+   * The callback may accept up to three arguments: the string for the message, the line number, and the source identifier.
+   * @param message
+   * @param line
+   * @param sourceId
+   * @return {boolean}
+   */
+  WebPage.prototype.onConsoleMessageNative = function (message, line, sourceId) {
+    if (message === '__DOMContentLoaded') {
+      this.source = this._native.content;
+      return false;
+    }
+    console.log(message);
+    return true;
+  };
+
+  /**
+   * This callback is invoked when the page starts the loading. There is no argument passed to the callback.
+   * @return {number}
+   */
+  WebPage.prototype.onLoadStartedNative = function () {
+    this.state = 'loading';
+    return this.requestId = this.lastRequestId;
+  };
+
+  /**
+   * This callback is invoked when the page finishes the loading.
+   * It may accept a single argument indicating the page's status: 'success' if no network errors occurred, otherwise 'fail'.
+   * @param status
+   * @return {string}
+   */
+  WebPage.prototype.onLoadFinishedNative = function (status) {
+    this.status = status;
+    this.state = 'default';
+
+    if (this.source === null || typeof this.source == "undefined") {
+      this.source = this._native.content;
+    } else {
+      this.source = this._native.content;
+    }
+
+    return this.source;
+  };
+
+  /**
+   * This callback is invoked when there is a JavaScript execution error.
+   * It is a good way to catch problems when evaluating a script in the web page context.
+   * The arguments passed to the callback are the error message and the stack trace [as an Array].
+   * @param message
+   * @param stack
+   * @return {Number}
+   */
+  WebPage.prototype.onErrorNative = function (message, stack) {
+    var stackString;
+
+    stackString = message;
+    stack.forEach(function (frame) {
+      stackString += "\n";
+      stackString += "    at " + frame.file + ":" + frame.line;
+      if (frame["function"] && frame["function"] !== '') {
+        return stackString += " in " + frame["function"];
+      }
+    });
+
+    return this.errors.push({
+      message: message,
+      stack: stackString
+    });
+  };
+
+  /**
+   * This callback is invoked when the page requests a resource.
+   * The first argument to the callback is the requestData metadata object.
+   * The second argument is the networkRequest object itself.
+   * @param requestData
+   * @param networkRequest
+   * @return {*}
+   */
+  WebPage.prototype.onResourceRequestedNative = function (requestData, networkRequest) {
+    var abort;
+
+    abort = this.urlBlacklist.some(function (blacklistedUrl) {
+      return requestData.url.indexOf(blacklistedUrl) !== -1;
+    });
+
+    if (abort) {
+      if (this._blockedUrls.indexOf(requestData.url) === -1) {
+        this._blockedUrls.push(requestData.url);
+      }
+      //TODO: check this, as it raises onResourceError
+      return networkRequest.abort();
+    }
+
+    this.lastRequestId = requestData.id;
+    if (requestData.url === this.redirectURL) {
+      this.redirectURL = null;
+      this.requestId = requestData.id;
+    }
+
+    return this._networkTraffic[requestData.id] = {
+      request: requestData,
+      responseParts: []
+    };
+  };
+
+  /**
+   * This callback is invoked when a resource requested by the page is received.
+   * The only argument to the callback is the response metadata object.
+   * @param response
+   * @return {*}
+   */
+  WebPage.prototype.onResourceReceivedNative = function (response) {
+    var networkTrafficElement;
+
+    if ((networkTrafficElement = this._networkTraffic[response.id]) != null) {
+      networkTrafficElement.responseParts.push(response);
+    }
+
+    if (this.requestId === response.id) {
+      if (response.redirectURL) {
+        return this.redirectURL = response.redirectURL;
+      }
+
+      this.statusCode = response.status;
+      return this._responseHeaders = response.headers;
+    }
+  };
+
+  /**
+   * Inject the poltergeist agent into the webpage
+   * @return {Array}
+   */
+  WebPage.prototype.injectAgent = function () {
+    var extension, isAgentInjected, i, extensionsRefLength, extensionsRef, injectionResults;
+
+    isAgentInjected = this["native"]().evaluate(function () {
+      return typeof window.__poltergeist;
+    });
+
+    if (isAgentInjected === "undefined") {
+      this["native"]().injectJs("" + phantom.libraryPath + "/agent.js");
+      extensionsRef = WebPage.EXTENSIONS;
+      injectionResults = [];
+      for (i = 0, extensionsRefLength = extensionsRef.length; i < extensionsRefLength; i++) {
+        extension = extensionsRef[i];
+        injectionResults.push(this["native"]().injectJs(extension));
+      }
+      return injectionResults;
+    }
+  };
+
+  /**
+   * Injects a Javascript file extension into the
+   * @param file
+   * @return {*}
+   */
+  WebPage.prototype.injectExtension = function (file) {
+    //TODO: add error control, for example, check if file already in the extensions array, check if the file exists, etc.
+    WebPage.EXTENSIONS.push(file);
+    return this["native"]().injectJs(file);
+  };
+
+  /**
+   * Returns the native phantomjs webpage object
+   * @return {*}
+   */
+  WebPage.prototype["native"] = function () {
+    if (this.closed) {
+      throw new Poltergeist.NoSuchWindowError;
+    }
+
+    return this._native;
+  };
+
+  /**
+   * Returns the current page window name
+   * @return {*}
+   */
+  WebPage.prototype.windowName = function () {
+    return this["native"]().windowName;
+  };
+
+  /**
+   * Returns the keyCode of a given key as set in the phantomjs values
+   * @param name
+   * @return {number}
+   */
+  WebPage.prototype.keyCode = function (name) {
+    return this["native"]().event.key[name];
+  };
+
+  /**
+   * Waits for the page to reach a certain state
+   * @param state
+   * @param callback
+   * @return {*}
+   */
+  WebPage.prototype.waitState = function (state, callback) {
+    var self = this;
+    if (this.state === state) {
+      return callback.call();
+    } else {
+      return setTimeout((function () {
+        return self.waitState(state, callback);
+      }), 100);
+    }
+  };
+
+  /**
+   * Sets the browser header related to basic authentication protocol
+   * @param user
+   * @param password
+   * @return {boolean}
+   */
+  WebPage.prototype.setHttpAuth = function (user, password) {
+    var allHeaders = this.getCustomHeaders();
+
+    if (user === false || password === false) {
+      if (allHeaders.hasOwnProperty("Authorization")) {
+        delete allHeaders["Authorization"];
+      }
+      this.setCustomHeaders(allHeaders);
+      return true;
+    }
+
+    var userName = user || "";
+    var userPassword = password || "";
+
+    allHeaders["Authorization"] = "Basic " + btoa(userName + ":" + userPassword);
+    this.setCustomHeaders(allHeaders);
+    return true;
+  };
+
+  /**
+   * Returns all the network traffic associated to the rendering of this page
+   * @return {{}}
+   */
+  WebPage.prototype.networkTraffic = function () {
+    return this._networkTraffic;
+  };
+
+  /**
+   * Clears all the recorded network traffic related to the current page
+   * @return {{}}
+   */
+  WebPage.prototype.clearNetworkTraffic = function () {
+    return this._networkTraffic = {};
+  };
+
+  /**
+   * Returns the blocked urls that the page will not load
+   * @return {Array}
+   */
+  WebPage.prototype.blockedUrls = function () {
+    return this._blockedUrls;
+  };
+
+  /**
+   * Clean all the urls that should not be loaded
+   * @return {Array}
+   */
+  WebPage.prototype.clearBlockedUrls = function () {
+    return this._blockedUrls = [];
+  };
+
+  /**
+   * This property stores the content of the web page's currently active frame
+   * (which may or may not be the main frame), enclosed in an HTML/XML element.
+   * @return {string}
+   */
+  WebPage.prototype.content = function () {
+    return this["native"]().frameContent;
+  };
+
+  /**
+   * Returns the current active frame title
+   * @return {string}
+   */
+  WebPage.prototype.title = function () {
+    return this["native"]().frameTitle;
+  };
+
+  /**
+   * Returns if possible the frame url of the frame given by name
+   * @param frameName
+   * @return {string}
+   */
+  WebPage.prototype.frameUrl = function (frameName) {
+    var query;
+
+    query = function (frameName) {
+      var iframeReference;
+      if ((iframeReference = document.querySelector("iframe[name='" + frameName + "']")) != null) {
+        return iframeReference.src;
+      }
+      return void 0;
+    };
+
+    return this.evaluate(query, frameName);
+  };
+
+  /**
+   * Remove the errors caught on the page
+   * @return {Array}
+   */
+  WebPage.prototype.clearErrors = function () {
+    return this.errors = [];
+  };
+
+  /**
+   * Returns the response headers associated to this page
+   * @return {{}}
+   */
+  WebPage.prototype.responseHeaders = function () {
+    var headers;
+    headers = {};
+    this._responseHeaders.forEach(function (item) {
+      return headers[item.name] = item.value;
+    });
+    return headers;
+  };
+
+  /**
+   * Get Cookies visible to the current URL (though, for setting, use of page.addCookie is preferred).
+   * This array will be pre-populated by any existing Cookie data visible to this URL that is stored in the CookieJar, if any.
+   * @return {*}
+   */
+  WebPage.prototype.cookies = function () {
+    return this["native"]().cookies;
+  };
+
+  /**
+   * Delete any Cookies visible to the current URL with a 'name' property matching cookieName.
+   * Returns true if successfully deleted, otherwise false.
+   * @param name
+   * @return {*}
+   */
+  WebPage.prototype.deleteCookie = function (name) {
+    return this["native"]().deleteCookie(name);
+  };
+
+  /**
+   * This property gets the size of the viewport for the layout process.
+   * @return {*}
+   */
+  WebPage.prototype.viewportSize = function () {
+    return this["native"]().viewportSize;
+  };
+
+  /**
+   * This property sets the size of the viewport for the layout process.
+   * @param size
+   * @return {*}
+   */
+  WebPage.prototype.setViewportSize = function (size) {
+    return this["native"]().viewportSize = size;
+  };
+
+  /**
+   * This property specifies the scaling factor for the page.render and page.renderBase64 functions.
+   * @param zoomFactor
+   * @return {*}
+   */
+  WebPage.prototype.setZoomFactor = function (zoomFactor) {
+    return this["native"]().zoomFactor = zoomFactor;
+  };
+
+  /**
+   * This property defines the size of the web page when rendered as a PDF.
+   * See: http://phantomjs.org/api/webpage/property/paper-size.html
+   * @param size
+   * @return {*}
+   */
+  WebPage.prototype.setPaperSize = function (size) {
+    return this["native"]().paperSize = size;
+  };
+
+  /**
+   * This property gets the scroll position of the web page.
+   * @return {*}
+   */
+  WebPage.prototype.scrollPosition = function () {
+    return this["native"]().scrollPosition;
+  };
+
+  /**
+   * This property defines the scroll position of the web page.
+   * @param pos
+   * @return {*}
+   */
+  WebPage.prototype.setScrollPosition = function (pos) {
+    return this["native"]().scrollPosition = pos;
+  };
+
+
+  /**
+   * This property defines the rectangular area of the web page to be rasterized when page.render is invoked.
+   * If no clipping rectangle is set, page.render will process the entire web page.
+   * @return {*}
+   */
+  WebPage.prototype.clipRect = function () {
+    return this["native"]().clipRect;
+  };
+
+  /**
+   * This property defines the rectangular area of the web page to be rasterized when page.render is invoked.
+   * If no clipping rectangle is set, page.render will process the entire web page.
+   * @param rect
+   * @return {*}
+   */
+  WebPage.prototype.setClipRect = function (rect) {
+    return this["native"]().clipRect = rect;
+  };
+
+  /**
+   * Returns the size of an element given by a selector and its position relative to the viewport.
+   * @param selector
+   * @return {Object}
+   */
+  WebPage.prototype.elementBounds = function (selector) {
+    return this["native"]().evaluate(function (selector) {
+      return document.querySelector(selector).getBoundingClientRect();
+    }, selector);
+  };
+
+  /**
+   * Defines the user agent sent to server when the web page requests resources.
+   * @param userAgent
+   * @return {*}
+   */
+  WebPage.prototype.setUserAgent = function (userAgent) {
+    return this["native"]().settings.userAgent = userAgent;
+  };
+
+  /**
+   * Returns the additional HTTP request headers that will be sent to the server for EVERY request.
+   * @return {{}}
+   */
+  WebPage.prototype.getCustomHeaders = function () {
+    return this["native"]().customHeaders;
+  };
+
+  /**
+   * Gets the additional HTTP request headers that will be sent to the server for EVERY request.
+   * @param headers
+   * @return {*}
+   */
+  WebPage.prototype.setCustomHeaders = function (headers) {
+    return this["native"]().customHeaders = headers;
+  };
+
+  /**
+   * Adds a one time only request header, after being used it will be deleted
+   * @param header
+   * @return {Array}
+   */
+  WebPage.prototype.addTempHeader = function (header) {
+    var name, value, tempHeaderResult;
+    tempHeaderResult = [];
+    for (name in header) {
+      if (header.hasOwnProperty(name)) {
+        value = header[name];
+        tempHeaderResult.push(this._tempHeaders[name] = value);
+      }
+    }
+    return tempHeaderResult;
+  };
+
+  /**
+   * Remove the temporary headers we have set via addTempHeader
+   * @return {*}
+   */
+  WebPage.prototype.removeTempHeaders = function () {
+    var allHeaders, name, value, tempHeadersRef;
+    allHeaders = this.getCustomHeaders();
+    tempHeadersRef = this._tempHeaders;
+    for (name in tempHeadersRef) {
+      if (tempHeadersRef.hasOwnProperty(name)) {
+        value = tempHeadersRef[name];
+        delete allHeaders[name];
+      }
+    }
+
+    return this.setCustomHeaders(allHeaders);
+  };
+
+  /**
+   * If possible switch to the frame given by name
+   * @param name
+   * @return {boolean}
+   */
+  WebPage.prototype.pushFrame = function (name) {
+    if (this["native"]().switchToFrame(name)) {
+      this.frames.push(name);
+      return true;
+    }
+    return false;
+  };
+
+  /**
+   * Switch to parent frame, use with caution:
+   * popFrame assumes you are in frame, pop frame not being in a frame
+   * leaves unexpected behaviour
+   * @return {*}
+   */
+  WebPage.prototype.popFrame = function () {
+    //TODO: add some error control here, some way to check we are in a frame or not
+    this.frames.pop();
+    return this["native"]().switchToParentFrame();
+  };
+
+  /**
+   * Returns the webpage dimensions
+   * @return {{top: *, bottom: *, left: *, right: *, viewport: *, document: {height: number, width: number}}}
+   */
+  WebPage.prototype.dimensions = function () {
+    var scroll, viewport;
+    scroll = this.scrollPosition();
+    viewport = this.viewportSize();
+    return {
+      top: scroll.top,
+      bottom: scroll.top + viewport.height,
+      left: scroll.left,
+      right: scroll.left + viewport.width,
+      viewport: viewport,
+      document: this.documentSize()
+    };
+  };
+
+  /**
+   * Returns webpage dimensions that are valid
+   * @return {{top: *, bottom: *, left: *, right: *, viewport: *, document: {height: number, width: number}}}
+   */
+  WebPage.prototype.validatedDimensions = function () {
+    var dimensions, documentDimensions;
+
+    dimensions = this.dimensions();
+    documentDimensions = dimensions.document;
+
+    if (dimensions.right > documentDimensions.width) {
+      dimensions.left = Math.max(0, dimensions.left - (dimensions.right - documentDimensions.width));
+      dimensions.right = documentDimensions.width;
+    }
+
+    if (dimensions.bottom > documentDimensions.height) {
+      dimensions.top = Math.max(0, dimensions.top - (dimensions.bottom - documentDimensions.height));
+      dimensions.bottom = documentDimensions.height;
+    }
+
+    this.setScrollPosition({
+      left: dimensions.left,
+      top: dimensions.top
+    });
+
+    return dimensions;
+  };
+
+  /**
+   * Returns a Poltergeist.Node given by an id
+   * @param id
+   * @return {Poltergeist.Node}
+   */
+  WebPage.prototype.get = function (id) {
+    return new Poltergeist.Node(this, id);
+  };
+
+  /**
+   * Executes a phantomjs mouse event, for more info check: http://phantomjs.org/api/webpage/method/send-event.html
+   * @param name
+   * @param x
+   * @param y
+   * @param button
+   * @return {*}
+   */
+  WebPage.prototype.mouseEvent = function (name, x, y, button) {
+    if (button == null) {
+      button = 'left';
+    }
+    this.sendEvent('mousemove', x, y);
+    return this.sendEvent(name, x, y, button);
+  };
+
+  /**
+   * Evaluates a javascript and returns the evaluation of such script
+   * @return {*}
+   */
+  WebPage.prototype.evaluate = function () {
+    var args, fn;
+    fn = arguments[0];
+    args = [];
+
+    if (2 <= arguments.length) {
+      args = __slice.call(arguments, 1);
+    }
+
+    this.injectAgent();
+    return JSON.parse(this.sanitize(this["native"]().evaluate("function() { return PoltergeistAgent.stringify(" + (this.stringifyCall(fn, args)) + ") }")));
+  };
+
+  /**
+   * Does some string sanitation prior parsing
+   * @param potentialString
+   * @return {*}
+   */
+  WebPage.prototype.sanitize = function (potentialString) {
+    if (typeof potentialString === "string") {
+      return potentialString.replace("\n", "\\n").replace("\r", "\\r");
+    }
+
+    return potentialString;
+  };
+
+  /**
+   * Executes a script into the current page scope
+   * @param script
+   * @return {*}
+   */
+  WebPage.prototype.executeScript = function (script) {
+    return this["native"]().evaluateJavaScript(script);
+  };
+
+  /**
+   * Executes a script via phantomjs evaluation
+   * @return {*}
+   */
+  WebPage.prototype.execute = function () {
+    var args, fn;
+
+    fn = arguments[0];
+    args = [];
+
+    if (2 <= arguments.length) {
+      args = __slice.call(arguments, 1);
+    }
+
+    return this["native"]().evaluate("function() { " + (this.stringifyCall(fn, args)) + " }");
+  };
+
+  /**
+   * Helper methods to do script evaluation and execution
+   * @param fn
+   * @param args
+   * @return {string}
+   */
+  WebPage.prototype.stringifyCall = function (fn, args) {
+    if (args.length === 0) {
+      return "(" + (fn.toString()) + ")()";
+    }
+
+    return "(" + (fn.toString()) + ").apply(this, JSON.parse(" + (JSON.stringify(JSON.stringify(args))) + "))";
+  };
+
+  /**
+   * Binds callbacks to their respective Native implementations
+   * @param name
+   * @return {Function}
+   */
+  WebPage.prototype.bindCallback = function (name) {
+    var self;
+    self = this;
+
+    return this["native"]()[name] = function () {
+      var result;
+      if (self[name + 'Native'] != null) {
+        result = self[name + 'Native'].apply(self, arguments);
+      }
+      if (result !== false && (self[name] != null)) {
+        return self[name].apply(self, arguments);
+      }
+    };
+  };
+
+  /**
+   * Runs a command delegating to the PoltergeistAgent
+   * @param name
+   * @param args
+   * @return {*}
+   */
+  WebPage.prototype.runCommand = function (name, args) {
+    var method, result, selector;
+
+    result = this.evaluate(function (name, args) {
+      return window.__poltergeist.externalCall(name, args);
+    }, name, args);
+
+    if (result !== null) {
+      if (result.error != null) {
+        switch (result.error.message) {
+          case 'PoltergeistAgent.ObsoleteNode':
+            throw new Poltergeist.ObsoleteNode;
+            break;
+          case 'PoltergeistAgent.InvalidSelector':
+            method = args[0];
+            selector = args[1];
+            throw new Poltergeist.InvalidSelector(method, selector);
+            break;
+          default:
+            throw new Poltergeist.BrowserError(result.error.message, result.error.stack);
+        }
+      } else {
+        return result.value;
+      }
+    }
+  };
+
+  /**
+   * Tells if we can go back or not
+   * @return {boolean}
+   */
+  WebPage.prototype.canGoBack = function () {
+    return this["native"]().canGoBack;
+  };
+
+  /**
+   * Tells if we can go forward or not in the browser history
+   * @return {boolean}
+   */
+  WebPage.prototype.canGoForward = function () {
+    return this["native"]().canGoForward;
+  };
+
+  return WebPage;
+
+}).call(this);