var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, slice = [].slice, 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; }, hasProp = {}.hasOwnProperty; Poltergeist.WebPage = (function() { var command, delegate, fn1, fn2, i, j, len, len1, ref, ref1; WebPage.CALLBACKS = ['onConsoleMessage', 'onError', 'onLoadFinished', 'onInitialized', 'onLoadStarted', 'onResourceRequested', 'onResourceReceived', 'onResourceError', 'onResourceTimeout', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated', 'onClosing', 'onCallback']; WebPage.DELEGATES = ['url', 'open', 'sendEvent', 'uploadFile', 'render', 'close', 'renderBase64', 'goBack', 'goForward', 'reload']; WebPage.COMMANDS = ['find', 'nodeCall', 'documentSize', 'beforeUpload', 'afterUpload', 'clearLocalStorage']; WebPage.EXTENSIONS = []; function WebPage(_native, settings) { var callback, i, len, ref; this._native = _native; this._checkForAsyncResult = bind(this._checkForAsyncResult, this); this._native || (this._native = require('webpage').create()); this.id = 0; this.source = null; this.closed = false; this.state = 'default'; this.urlWhitelist = []; this.urlBlacklist = []; this.errors = []; this._networkTraffic = {}; this._tempHeaders = {}; this._blockedUrls = []; this._requestedResources = {}; this._responseHeaders = []; this._tempHeadersToRemoveOnRedirect = {}; this._asyncResults = {}; this._asyncEvaluationId = 0; this.setSettings(settings); ref = WebPage.CALLBACKS; for (i = 0, len = ref.length; i < len; i++) { callback = ref[i]; this.bindCallback(callback); } if (phantom.version.major < 2) { this._overrideNativeEvaluate(); } } ref = WebPage.COMMANDS; fn1 = 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, len = ref.length; i < len; i++) { command = ref[i]; fn1(command); } ref1 = WebPage.DELEGATES; fn2 = function(delegate) { return WebPage.prototype[delegate] = function() { return this._native[delegate].apply(this._native, arguments); }; }; for (j = 0, len1 = ref1.length; j < len1; j++) { delegate = ref1[j]; fn2(delegate); } WebPage.prototype.setSettings = function(settings) { var results, setting, value; if (settings == null) { settings = {}; } results = []; for (setting in settings) { value = settings[setting]; results.push(this._native.settings[setting] = value); } return results; }; WebPage.prototype.onInitializedNative = function() { this.id += 1; this.source = null; this.injectAgent(); this.removeTempHeaders(); this.removeTempHeadersForRedirect(); return this.setScrollPosition({ left: 0, top: 0 }); }; WebPage.prototype.onClosingNative = function() { this.handle = null; return this.closed = true; }; WebPage.prototype.onConsoleMessageNative = function(message) { if (message === '__DOMContentLoaded') { this.source = this._native.content; return false; } else { return console.log(message); } }; WebPage.prototype.onLoadStartedNative = function() { this.state = 'loading'; this.requestId = this.lastRequestId; return this._requestedResources = {}; }; WebPage.prototype.onLoadFinishedNative = function(status) { this.status = status; this.state = 'default'; return this.source || (this.source = this._native.content); }; 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"]; } }); this.errors.push({ message: message, stack: stackString }); return true; }; WebPage.prototype.onCallbackNative = function(data) { this._asyncResults[data['command_id']] = data['command_result']; return true; }; WebPage.prototype.onResourceRequestedNative = function(request, net) { var ref2; this._networkTraffic[request.id] = { request: request, responseParts: [], error: null }; if (this._blockRequest(request.url)) { this._networkTraffic[request.id].blocked = true; if (ref2 = request.url, indexOf.call(this._blockedUrls, ref2) < 0) { this._blockedUrls.push(request.url); } net.abort(); } else { this.lastRequestId = request.id; if (this.normalizeURL(request.url) === this.redirectURL) { this.removeTempHeadersForRedirect(); this.redirectURL = null; this.requestId = request.id; } this._requestedResources[request.id] = request.url; } return true; }; WebPage.prototype.onResourceReceivedNative = function(response) { var ref2; if ((ref2 = this._networkTraffic[response.id]) != null) { ref2.responseParts.push(response); } if (response.stage === 'end') { delete this._requestedResources[response.id]; } if (this.requestId === response.id) { if (response.redirectURL) { this.removeTempHeadersForRedirect(); this.redirectURL = this.normalizeURL(response.redirectURL); } else { this.statusCode = response.status; this._responseHeaders = response.headers; } } return true; }; WebPage.prototype.onResourceErrorNative = function(errorResponse) { var ref2; if ((ref2 = this._networkTraffic[errorResponse.id]) != null) { ref2.error = errorResponse; } delete this._requestedResources[errorResponse.id]; return true; }; WebPage.prototype.onResourceTimeoutNative = function(request) { return console.log("Resource request timed out for " + request.url); }; WebPage.prototype.injectAgent = function() { var extension, k, len2, ref2; if (this["native"]().evaluate(function() { return typeof __poltergeist; }) === "undefined") { this["native"]().injectJs(phantom.libraryPath + "/agent.js"); ref2 = WebPage.EXTENSIONS; for (k = 0, len2 = ref2.length; k < len2; k++) { extension = ref2[k]; this["native"]().injectJs(extension); } return true; } return false; }; WebPage.prototype.injectExtension = function(file) { WebPage.EXTENSIONS.push(file); return this["native"]().injectJs(file); }; WebPage.prototype["native"] = function() { if (this.closed) { throw new Poltergeist.NoSuchWindowError; } else { return this._native; } }; WebPage.prototype.windowName = function() { return this["native"]().windowName; }; WebPage.prototype.keyCode = function(name) { if (name === "Ctrl") { name = "Control"; } return this["native"]().event.key[name]; }; WebPage.prototype.keyModifierCode = function(names) { var modifiers; modifiers = this["native"]().event.modifier; return names.split(',').map(function(name) { return modifiers[name]; }).reduce(function(n1, n2) { return n1 | n2; }); }; WebPage.prototype.keyModifierKeys = function(names) { var k, len2, name, ref2, results; ref2 = names.split(','); results = []; for (k = 0, len2 = ref2.length; k < len2; k++) { name = ref2[k]; if (!(name !== 'keypad')) { continue; } name = name.charAt(0).toUpperCase() + name.substring(1); results.push(this.keyCode(name)); } return results; }; WebPage.prototype._waitState_until = function(states, callback, timeout, timeout_callback) { var ref2; if ((ref2 = this.state, indexOf.call(states, ref2) >= 0)) { return callback.call(this, this.state); } else { if (new Date().getTime() > timeout) { return timeout_callback.call(this); } else { return setTimeout(((function(_this) { return function() { return _this._waitState_until(states, callback, timeout, timeout_callback); }; })(this)), 100); } } }; WebPage.prototype.waitState = function(states, callback, max_wait, timeout_callback) { var ref2, timeout; if (max_wait == null) { max_wait = 0; } states = [].concat(states); if (ref2 = this.state, indexOf.call(states, ref2) >= 0) { return callback.call(this, this.state); } else { if (max_wait !== 0) { timeout = new Date().getTime() + (max_wait * 1000); return setTimeout(((function(_this) { return function() { return _this._waitState_until(states, callback, timeout, timeout_callback); }; })(this)), 100); } else { return setTimeout(((function(_this) { return function() { return _this.waitState(states, callback); }; })(this)), 100); } } }; WebPage.prototype.setHttpAuth = function(user, password) { this["native"]().settings.userName = user; this["native"]().settings.password = password; return true; }; WebPage.prototype.networkTraffic = function(type) { var id, ref2, ref3, ref4, request, results, results1, results2; switch (type) { case 'all': ref2 = this._networkTraffic; results = []; for (id in ref2) { if (!hasProp.call(ref2, id)) continue; request = ref2[id]; results.push(request); } return results; break; case 'blocked': ref3 = this._networkTraffic; results1 = []; for (id in ref3) { if (!hasProp.call(ref3, id)) continue; request = ref3[id]; if (request.blocked) { results1.push(request); } } return results1; break; default: ref4 = this._networkTraffic; results2 = []; for (id in ref4) { if (!hasProp.call(ref4, id)) continue; request = ref4[id]; if (!request.blocked) { results2.push(request); } } return results2; } }; WebPage.prototype.clearNetworkTraffic = function() { this._networkTraffic = {}; return true; }; WebPage.prototype.blockedUrls = function() { return this._blockedUrls; }; WebPage.prototype.clearBlockedUrls = function() { this._blockedUrls = []; return true; }; WebPage.prototype.openResourceRequests = function() { var id, ref2, results, url; ref2 = this._requestedResources; results = []; for (id in ref2) { if (!hasProp.call(ref2, id)) continue; url = ref2[id]; results.push(url); } return results; }; WebPage.prototype.content = function() { return this["native"]().frameContent; }; WebPage.prototype.title = function() { return this["native"]().title; }; WebPage.prototype.frameTitle = function() { return this["native"]().frameTitle; }; WebPage.prototype.currentUrl = function() { return this["native"]().url; }; WebPage.prototype.frameUrl = function() { if (phantom.version.major > 2 || (phantom.version.major === 2 && phantom.version.minor >= 1)) { return this["native"]().frameUrl; } else { return this.runCommand('frameUrl'); } }; WebPage.prototype.frameUrlFor = function(frameNameOrId) { var query; query = function(frameNameOrId) { var ref2; return (ref2 = document.querySelector("iframe[name='" + frameNameOrId + "'], iframe[id='" + frameNameOrId + "']")) != null ? ref2.src : void 0; }; return this.evaluate(query, frameNameOrId); }; WebPage.prototype.clearErrors = function() { this.errors = []; return true; }; WebPage.prototype.responseHeaders = function() { var headers; headers = {}; this._responseHeaders.forEach(function(item) { return headers[item.name] = item.value; }); return headers; }; WebPage.prototype.cookies = function() { return this["native"]().cookies; }; WebPage.prototype.deleteCookie = function(name) { return this["native"]().deleteCookie(name); }; WebPage.prototype.viewportSize = function() { return this["native"]().viewportSize; }; WebPage.prototype.setViewportSize = function(size) { return this["native"]().viewportSize = size; }; WebPage.prototype.setZoomFactor = function(zoom_factor) { return this["native"]().zoomFactor = zoom_factor; }; WebPage.prototype.setPaperSize = function(size) { return this["native"]().paperSize = size; }; WebPage.prototype.scrollPosition = function() { return this["native"]().scrollPosition; }; WebPage.prototype.setScrollPosition = function(pos) { return this["native"]().scrollPosition = pos; }; WebPage.prototype.clipRect = function() { return this["native"]().clipRect; }; WebPage.prototype.setClipRect = function(rect) { return this["native"]().clipRect = rect; }; WebPage.prototype.elementBounds = function(selector) { return this["native"]().evaluate(function(selector) { return document.querySelector(selector).getBoundingClientRect(); }, selector); }; WebPage.prototype.getUserAgent = function() { return this["native"]().settings.userAgent; }; WebPage.prototype.setUserAgent = function(userAgent) { return this["native"]().settings.userAgent = userAgent; }; WebPage.prototype.getCustomHeaders = function() { return this["native"]().customHeaders; }; WebPage.prototype.getPermanentCustomHeaders = function() { var allHeaders, name, ref2, ref3, value; allHeaders = this.getCustomHeaders(); ref2 = this._tempHeaders; for (name in ref2) { value = ref2[name]; delete allHeaders[name]; } ref3 = this._tempHeadersToRemoveOnRedirect; for (name in ref3) { value = ref3[name]; delete allHeaders[name]; } return allHeaders; }; WebPage.prototype.setCustomHeaders = function(headers) { return this["native"]().customHeaders = headers; }; WebPage.prototype.addTempHeader = function(header) { var name, value; for (name in header) { value = header[name]; this._tempHeaders[name] = value; } return this._tempHeaders; }; WebPage.prototype.addTempHeaderToRemoveOnRedirect = function(header) { var name, value; for (name in header) { value = header[name]; this._tempHeadersToRemoveOnRedirect[name] = value; } return this._tempHeadersToRemoveOnRedirect; }; WebPage.prototype.removeTempHeadersForRedirect = function() { var allHeaders, name, ref2, value; allHeaders = this.getCustomHeaders(); ref2 = this._tempHeadersToRemoveOnRedirect; for (name in ref2) { value = ref2[name]; delete allHeaders[name]; } return this.setCustomHeaders(allHeaders); }; WebPage.prototype.removeTempHeaders = function() { var allHeaders, name, ref2, value; allHeaders = this.getCustomHeaders(); ref2 = this._tempHeaders; for (name in ref2) { value = ref2[name]; delete allHeaders[name]; } return this.setCustomHeaders(allHeaders); }; WebPage.prototype.pushFrame = function(name) { var frame_no; if (this["native"]().switchToFrame(name)) { return true; } frame_no = this["native"]().evaluate(function(frame_name) { var f, frames, idx; frames = document.querySelectorAll("iframe, frame"); return ((function() { var k, len2, results; results = []; for (idx = k = 0, len2 = frames.length; k < len2; idx = ++k) { f = frames[idx]; if ((f != null ? f['name'] : void 0) === frame_name || (f != null ? f['id'] : void 0) === frame_name) { results.push(idx); } } return results; })())[0]; }, name); return (frame_no != null) && this["native"]().switchToFrame(frame_no); }; WebPage.prototype.popFrame = function(pop_all) { if (pop_all == null) { pop_all = false; } if (pop_all) { return this["native"]().switchToMainFrame(); } else { return this["native"]().switchToParentFrame(); } }; 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() }; }; WebPage.prototype.validatedDimensions = function() { var dimensions, document; dimensions = this.dimensions(); document = dimensions.document; if (dimensions.right > document.width) { dimensions.left = Math.max(0, dimensions.left - (dimensions.right - document.width)); dimensions.right = document.width; } if (dimensions.bottom > document.height) { dimensions.top = Math.max(0, dimensions.top - (dimensions.bottom - document.height)); dimensions.bottom = document.height; } this.setScrollPosition({ left: dimensions.left, top: dimensions.top }); return dimensions; }; WebPage.prototype.get = function(id) { return new Poltergeist.Node(this, id); }; WebPage.prototype.mouseEvent = function(name, x, y, button, modifiers) { if (button == null) { button = 'left'; } if (modifiers == null) { modifiers = 0; } this.sendEvent('mousemove', x, y); return this.sendEvent(name, x, y, button, modifiers); }; WebPage.prototype.evaluate = function() { var args, fn, ref2, result; fn = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; this.injectAgent(); result = (ref2 = this["native"]()).evaluate.apply(ref2, ["function() { var page_id = arguments[0]; var args = []; for(var i=1; i < arguments.length; i++){ if ((typeof(arguments[i]) == 'object') && (typeof(arguments[i]['ELEMENT']) == 'object')){ args.push(window.__poltergeist.get(arguments[i]['ELEMENT']['id']).element); } else { args.push(arguments[i]) } } var _result = " + (this.stringifyCall(fn, "args")) + "; return window.__poltergeist.wrapResults(_result, page_id); }", this.id].concat(slice.call(args))); return result; }; WebPage.prototype.evaluate_async = function() { var args, callback, cb, command_id, fn, ref2; fn = arguments[0], callback = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : []; command_id = ++this._asyncEvaluationId; cb = callback; this.injectAgent(); (ref2 = this["native"]()).evaluate.apply(ref2, ["function(){ var page_id = arguments[0]; var args = []; for(var i=1; i < arguments.length; i++){ if ((typeof(arguments[i]) == 'object') && (typeof(arguments[i]['ELEMENT']) == 'object')){ args.push(window.__poltergeist.get(arguments[i]['ELEMENT']['id']).element); } else { args.push(arguments[i]) } } args.push(function(result){ result = window.__poltergeist.wrapResults(result, page_id); window.callPhantom( { command_id: " + command_id + ", command_result: result } ); }); " + (this.stringifyCall(fn, "args")) + "; return}", this.id].concat(slice.call(args))); setTimeout((function(_this) { return function() { return _this._checkForAsyncResult(command_id, cb); }; })(this), 10); }; WebPage.prototype.execute = function() { var args, fn, ref2; fn = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; return (ref2 = this["native"]()).evaluate.apply(ref2, ["function() { for(var i=0; i < arguments.length; i++){ if ((typeof(arguments[i]) == 'object') && (typeof(arguments[i]['ELEMENT']) == 'object')){ arguments[i] = window.__poltergeist.get(arguments[i]['ELEMENT']['id']).element; } } " + (this.stringifyCall(fn)) + " }"].concat(slice.call(args))); }; WebPage.prototype.stringifyCall = function(fn, args_name) { if (args_name == null) { args_name = "arguments"; } return "(" + (fn.toString()) + ").apply(this, " + args_name + ")"; }; WebPage.prototype.bindCallback = function(name) { this["native"]()[name] = (function(_this) { return function() { var result; if (_this[name + 'Native'] != null) { result = _this[name + 'Native'].apply(_this, arguments); } if (result !== false && (_this[name] != null)) { return _this[name].apply(_this, arguments); } }; })(this); return true; }; WebPage.prototype.runCommand = function(name, args) { var method, result, selector; result = this.evaluate(function(name, args) { return __poltergeist.externalCall(name, args); }, name, args); if ((result != null ? result.error : void 0) != 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 != null ? result.value : void 0; } }; WebPage.prototype.canGoBack = function() { return this["native"]().canGoBack; }; WebPage.prototype.canGoForward = function() { return this["native"]().canGoForward; }; WebPage.prototype.normalizeURL = function(url) { var parser; parser = document.createElement('a'); parser.href = url; return parser.href; }; WebPage.prototype.clearMemoryCache = function() { var clearMemoryCache; clearMemoryCache = this["native"]().clearMemoryCache; if (typeof clearMemoryCache === "function") { return clearMemoryCache(); } else { throw new Poltergeist.UnsupportedFeature("clearMemoryCache is supported since PhantomJS 2.0.0"); } }; WebPage.prototype._checkForAsyncResult = function(command_id, callback) { if (this._asyncResults.hasOwnProperty(command_id)) { callback(this._asyncResults[command_id]); delete this._asyncResults[command_id]; } else { setTimeout((function(_this) { return function() { return _this._checkForAsyncResult(command_id, callback); }; })(this), 50); } }; WebPage.prototype._blockRequest = function(url) { var blacklisted, useWhitelist, whitelisted; useWhitelist = this.urlWhitelist.length > 0; whitelisted = this.urlWhitelist.some(function(whitelisted_regex) { return whitelisted_regex.test(url); }); blacklisted = this.urlBlacklist.some(function(blacklisted_regex) { return blacklisted_regex.test(url); }); if (useWhitelist && !whitelisted) { return true; } if (blacklisted) { return true; } return false; }; WebPage.prototype._overrideNativeEvaluate = function() { return this._native.evaluate = function (func, args) { function quoteString(str) { var c, i, l = str.length, o = '"'; for (i = 0; i < l; i += 1) { c = str.charAt(i); if (c >= ' ') { if (c === '\\' || c === '"') { o += '\\'; } o += c; } else { switch (c) { case '\b': o += '\\b'; break; case '\f': o += '\\f'; break; case '\n': o += '\\n'; break; case '\r': o += '\\r'; break; case '\t': o += '\\t'; break; default: c = c.charCodeAt(); o += '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16); } } } return o + '"'; } function detectType(value) { var s = typeof value; if (s === 'object') { if (value) { if (value instanceof Array) { s = 'array'; } else if (value instanceof RegExp) { s = 'regexp'; } else if (value instanceof Date) { s = 'date'; } } else { s = 'null'; } } return s; } var str, arg, argType, i, l; if (!(func instanceof Function || typeof func === 'string' || func instanceof String)) { throw "Wrong use of WebPage#evaluate"; } str = 'function() { return (' + func.toString() + ')('; for (i = 1, l = arguments.length; i < l; i++) { arg = arguments[i]; argType = detectType(arg); switch (argType) { case "object": //< for type "object" case "array": //< for type "array" str += JSON.stringify(arg) + "," break; case "date": //< for type "date" str += "new Date(" + JSON.stringify(arg) + ")," break; case "string": //< for type "string" str += quoteString(arg) + ','; break; default: // for types: "null", "number", "function", "regexp", "undefined" str += arg + ','; break; } } str = str.replace(/,$/, '') + '); }'; return this.evaluateJavaScript(str); };; }; return WebPage; })();