text: /**
   * TODO exceptions should exit immediately from phantomjs and they DON'T
   * TODO support 404 and error code in the same way prerender.io (server) does
   * @type {number}
   */
  var EVALUATE_JAVASCRIPT_CHECK_TIMEOUT = 50;
  
  var page = require('webpage').create(),
      system = require('system'),
      URI = require('./URI'),
      fs = require('fs');
  
  var surrender = {};
  page.viewportSize = {
    width: 1024,
    height: 800
  };
  
  surrender.params = function() {
    if (system.args.length === 1) {
        throw 'Usage: prerender_rails_embedded.js <some URL>';
    }
    return { address: system.args[1]};
  };
  
  
  surrender.javascriptToExecuteOnPage = function() {
    function removeTag(tagName) {
      var element = document.getElementsByTagName(tagName);
      for (var index = element.length - 1; index >= 0; index--) {
        element[index].parentNode.removeChild(element[index]);
      }
    }
  
    var processHtml = function() {
      removeTag('script');
      removeTag('iframe');
  
      if (!window.prerenderRailsEmbedded || !window.prerenderRailsEmbedded.filterHtmlOutput || typeof(window.prerenderRailsEmbedded.filterHtmlOutput) !== "function") {
        return document.documentElement.outerHTML;
      } else {
        return window.prerenderRailsEmbedded.filterHtmlOutput.call(this);
      }
    };
  
    try {
  
      if (!document) {
        throw 'document is undefined. this function must be called inside page.evaluate ';
      }
      var html = processHtml();
      var doctype = '';
  
      if(document && document.doctype) {
        doctype = "<!DOCTYPE "
          + document.doctype.name
          + (document.doctype.publicId ? ' PUBLIC "' + document.doctype.publicId + '"' : '')
          + (!document.doctype.publicId && document.doctype.systemId ? ' SYSTEM' : '')
          + (document.doctype.systemId ? ' "' + document.doctype.systemId + '"' : '')
          + '>';
      }
  
      return {
        html: doctype + html,
        shouldWaitForPrerenderReady: typeof window.prerenderReady === 'boolean',
        prerenderReady: window.prerenderReady
      };
  
    } catch (e) {
      //console.log('ERROR: ', e);
      return  {
        html: '',
        shouldWaitForPrerenderReady: false,
        prerenderReady: window.prerenderReady
      };
    }
  };
  
  surrender.millisElapsed = function () {
    return new Date().getTime() - this.requestStarted.getTime();
  };
  
  surrender.serveHtml = function(html) {
      system.stdout.writeLine(html);
  
      this.logDebug('page returned to rails: ' + page.address + " (" + this.millisElapsed() + "ms)");
      phantom.exit(0);
  };
  
  surrender.evaluateJavascriptOnPage = function(t, msPassed) {
    var _this = t || this
        , out = page.evaluate(_this.javascriptToExecuteOnPage);
  
    if (msPassed === undefined) msPassed = 0;
  
    if(!out.shouldWaitForPrerenderReady || (out.shouldWaitForPrerenderReady && out.prerenderReady) || msPassed >= 20000) {
  
      if (msPassed >= 20000) _this.logWarning("prerenderReady=true not found after 20s for page: " + page.url);
  
      _this.serveHtml(out.html);
  
    } else {
        _this.evaluateJavascriptTimeout = setTimeout(function(){_this.evaluateJavascriptOnPage(_this, msPassed + EVALUATE_JAVASCRIPT_CHECK_TIMEOUT)}, EVALUATE_JAVASCRIPT_CHECK_TIMEOUT);
    }
    return _this;
  };
  
  surrender.run = function() {
    var _this = this;
  
    try {
  
      page.address = this.replaceHostWithLocalhost(this.removeEscapedFragmentParameter(this.params().address));
      page.settings.userAgent = 'EtaliaBotAgent';
      page.settings.resourceTimeout = 15000;
  
      _this.logDebug('requested: ' + page.address);
      surrender.requestStarted = new Date();
  
      page.open(page.address, function (st) {
        _this.logDebug('opened: ' + page.address + 'with status: ' + st);
  
        if (st !== 'success') throw 'FAIL to load the address';
  
        _this.evaluateJavascriptOnPage();
      });
  
    } catch(e) {
        _this.logError(e);
        console.log(e);
        phantom.exit(1);
    }
  
    page.onError = function(message, trace) {
      _this.logError(message);
    };
  
    page.onConsoleMessage = function(message, lineNum, sourceId) {
      _this.logInfo('CONSOLE: ' + message + '(' + sourceId + '/L#"' + lineNum + '")');
    };
  
    page.onResourceTimeout = function(request) {
      console.log('<html><head><meta name="robots" content="noindex, noarchive" /></head><body></body></html>');
      _this.logWarning('Response (#' + request.id + '): ' + JSON.stringify(request));
      setTimeout(function() { phantom.exit(1); }, 0); // workaround to avoid PhantomJS crashes
    };
  
    //page.onResourceRequested = function(requestData, networkRequest) {
    //  _this.logWarning('-------- (#' + requestData.id + '): ' + requestData.url + ' ' + JSON.stringify(requestData.headers));
    //};
  };
  surrender.removeEscapedFragmentParameter = function (addressUrl) {
    var parts = new URI(addressUrl);
  
    // Remove the _escaped_fragment_ query parameter
    if (parts.hasSearch('_escaped_fragment_')) {
      if(parts.search(true)['_escaped_fragment_']) {
        parts.hash("#!" + parts.search(true)['_escaped_fragment_']);
      }
      parts.removeSearch('_escaped_fragment_');
    }
  
    var newUrl = parts.toString();
    if(newUrl[0] === '/') newUrl = newUrl.substr(1);
  
    return newUrl;
  };
  
  surrender.replaceHostWithLocalhost = function (addressUrl) {
    this.logDebug(new URI(addressUrl).hostname('localhost'));
    return new URI(addressUrl).hostname('localhost');
  };
  
  surrender.logFilePath = function() {
    if (fs.exists("/opt/sites/frontend/current/log"))
      return "/opt/sites/frontend/current/log";
    else
      return "/tmp";
  };
  surrender.flog = function(message) {
    try {
      fs.write(this.logFilePath() + '/etalia-prerender.log', message + "\n", 'a');
    } catch(e) {
      // can't fallback on anything
    }
  };
  surrender.logDebug = function(message) {
    this.flog(this.timestamp() + ' [DEBUG] ' + message);
  };
  surrender.logInfo = function(message) {
    this.flog(this.timestamp() + ' [INFO ] ' + message);
  };
  surrender.logWarning = function(message) {
    this.flog(this.timestamp() + ' [WARN ] ' + message);
  };
  surrender.logError = function(message) {
    this.flog(this.timestamp() + ' [ERROR] ' + message);
  };
  
  surrender.timestamp = function() {
    return new Date().toString().substring(0,24);
  };
  
  surrender.run();