diff options
Diffstat (limited to 'elpa/skewer-mode-20200304.1142/skewer.js')
-rw-r--r-- | elpa/skewer-mode-20200304.1142/skewer.js | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/elpa/skewer-mode-20200304.1142/skewer.js b/elpa/skewer-mode-20200304.1142/skewer.js new file mode 100644 index 0000000..eb741c7 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer.js @@ -0,0 +1,436 @@ +/** + * @fileOverview Live browser interaction with Emacs + * @version 1.4 + */ + +/** + * Connects to Emacs and waits for a request. After handling the + * request it sends back the results and queues itself for another + * request. + * @namespace Holds all of Skewer's functionality. + */ +function skewer() { + function callback(request) { + var result = skewer.fn[request.type](request); + if (result) { + result = skewer.extend({ + id: request.id, + type: request.type, + status: 'success', + value: '' + }, result); + skewer.postJSON(skewer.host + "/skewer/post", result, callback); + } else { + skewer.getJSON(skewer.host + "/skewer/get", callback); + } + }; + skewer.getJSON(skewer.host + "/skewer/get", callback); +} + +/** + * Get a JSON-encoded object from a server. + * @param {String} url The location of the remote server + * @param {Function} [callback] The callback to receive a response object + */ +skewer.getJSON = function(url, callback) { + var XHR = window.skewerNativeXHR || XMLHttpRequest; + var xhr = new XHR(); + xhr.onreadystatechange = function() { + if (xhr.readyState === 4 && xhr.status === 200) { + callback(JSON.parse(xhr.responseText)); + } + }; + xhr.open('GET', url, true); + xhr.send(); +}; + +/** + * Send a JSON-encoded object to a server. + * @param {String} url The location of the remote server + * @param {Object} object The object to transmit to the server + * @param {Function} [callback] The callback to receive a response object + */ +skewer.postJSON = function(url, object, callback) { + var XHR = window.skewerNativeXHR || XMLHttpRequest; + var xhr = new XHR(); + xhr.onreadystatechange = function() { + if (callback && xhr.readyState === 4 && xhr.status === 200) { + callback(JSON.parse(xhr.responseText)); + } + }; + xhr.open('POST', url, true); + xhr.setRequestHeader("Content-Type", "text/plain"); // CORS + xhr.send(JSON.stringify(object)); +}; + +/** + * Add the properties other objects to a target object (jQuery.extend). + * @param {Object} target The object to receive new properties + * @param {...Object} objects Source objects for properties + * @returns The target object + */ +skewer.extend = function(target) { + for (var i = 1; i < arguments.length; i++) { + var object = arguments[i]; + for (var key in object) { + if (object.hasOwnProperty(key)) { + target[key] = object[key]; + } + } + } + return target; +}; + +/** + * Globally evaluate an expression and return the result. This + * <i>only</i> works when the implementation's indirect eval performs + * a global eval. If not, there's no alternative, since a return value + * is essential. + * + * @see http://perfectionkills.com/global-eval-what-are-the-options/ + * + * @param expression A string containing an expression to evaluate + * @returns The result of the evaluation + */ +skewer.globalEval = (function() { + var eval0 = (function(original, Object) { + try { + return [eval][0]('Object') === original; + } catch (e) { + return false; + } + }(Object, false)); + if (eval0) { + return function(expression) { + return [eval][0](expression); + }; + } else { + return function(expression) { // Safari + return eval.call(window, expression); + }; + } +}()); + +/** + * Same as Date.now(), supplied for pre-ES5 JS (<=IE8). + * @returns {number} The epoch time in milliseconds + */ +skewer.now = function() { + return new Date().valueOf(); +}; + +/** + * Handlers accept a request object from Emacs and return either a + * logical false (no response) or an object to return to Emacs. + * @namespace Request handlers. + */ +skewer.fn = {}; + +/** + * Handles an code evaluation request from Emacs. + * @param request The request object sent by Emacs + * @returns The result object to be returned to Emacs + */ +skewer.fn.eval = function(request) { + var result = { + strict: request.strict + }; + var start = skewer.now(); + try { + var prefix = request.strict ? '"use strict";\n' : ""; + var value = skewer.globalEval(prefix + request.eval); + result.value = skewer.safeStringify(value, request.verbose); + } catch (error) { + result = skewer.errorResult(error, result, request); + } + result.time = (skewer.now() - start) / 1000; + return result; +}; + +/** + * Load a hosted script named by the request. + * @param request The request object sent by Emacs + * @returns The result object to be returned to Emacs + */ +skewer.fn.script = function(request) { + var script = document.createElement('script'); + script.src = skewer.host + request.eval; + document.body.appendChild(script); + return {value: JSON.stringify(request.eval)}; +}; + +/** + * A keep-alive and connecton testing handler. + * @param request The request object sent by Emacs + * @returns The result object to be returned to Emacs + */ +skewer.fn.ping = function(request) { + return { + type: 'pong', + date: skewer.now() / 1000, + value: request.eval + }; +}; + +/** + * Establish a new stylesheet with the provided value. + */ +skewer.fn.css = function(request) { + var style = document.createElement('style'); + style.type = 'text/css'; + style.className = 'skewer'; + if (style.styleSheet) { // < IE9 + style.styleSheet.cssText = request.eval; + } else { + style.appendChild(document.createTextNode(request.eval)); + } + document.body.appendChild(style); + return {}; +}; + +/** + * Remove all of Skewer's style tags from the document. + */ +skewer.fn.cssClearAll = function(request) { + var styles = document.body.querySelectorAll('style.skewer'); + for (var i = 0; i < styles.length; i++) { + styles[i].parentNode.removeChild(styles[i]); + } + return {}; +}; + +/** + * HTML evaluator, appends or replaces a selection with given HTML. + */ +skewer.fn.html = function(request) { + function buildSelector(ancestry) { + return ancestry.map(function(tag) { + return tag[0] + ':nth-of-type(' + tag[1] + ')'; + }).join(' > '); + } + function query(ancestry) { + return document.querySelector(buildSelector(ancestry)); + } + function htmlToNode(html) { + var wrapper = document.createElement('div'); + wrapper.innerHTML = html; + return wrapper.firstChild; + } + + var target = query(request.ancestry); + + if (target == null) { + /* Determine missing part of the ancestry. */ + var path = request.ancestry.slice(0); // copy + var missing = []; + while (query(path) == null) { + missing.push(path.pop()); + } + + /* Build up the missing elements. */ + target = query(path); + while (missing.length > 0) { + var tag = missing.pop(), + name = tag[0], + nth = tag[1]; + var empty = null; + var count = target.querySelectorAll(name).length; + for (; count < nth; count++) { + empty = document.createElement(tag[0]); + target.appendChild(empty); + } + target = empty; + } + } + + target.parentNode.replaceChild(htmlToNode(request.eval), target); + return {}; +}; + +/** + * Fetch the HTML contents of selector. + */ +skewer.fn.fetchselector = function(request) { + var element = document.querySelector(request.eval); + return { value: element.innerHTML }; +}; + +/** + * Return a list of completions for an object. + */ +skewer.fn.completions = function(request) { + var object = skewer.globalEval(request.eval); + var keys = new Set(); + var regex = new RegExp(request.regexp); + for (var key in object) { + if (regex.test(key)) { + keys.add(key); + } + } + var props = object != null ? Object.getOwnPropertyNames(object) : []; + for (var i = 0; i < props.length; i++) { + if (regex.test(props[i])) { + keys.add(props[i]); + } + } + return { value: Array.from(keys).sort() }; +}; + +/** + * Host of the skewer script (CORS support). + * @type string + */ +(function() { + var script = document.querySelector('script[src$="/skewer"]'); + if (script) { + skewer.host = script.src.match(/\w+:\/\/[^/]+/)[0]; + } else { + skewer.host = ''; // default to the current host + } +}()); + +/** + * Stringify a potentially circular object without throwing an exception. + * @param object The object to be printed. + * @param {boolean} verbose Enable more verbose output. + * @returns {string} The printed object. + */ +skewer.safeStringify = function (object, verbose) { + "use strict"; + var circular = "#<Circular>"; + var seen = []; + + var stringify = function(obj) { + if (obj === true) { + return "true"; + } else if (obj === false) { + return "false"; + } else if (obj === undefined) { + return "undefined"; + } else if (obj === null) { + return "null"; + } else if (typeof obj === "number") { + return obj.toString(); + } else if (obj instanceof Array) { + if (seen.indexOf(obj) >= 0) { + return circular; + } else { + seen.push(obj); + return "[" + obj.map(function(e) { + return stringify(e); + }).join(", ") + "]"; + } + } else if (typeof obj === "string") { + return JSON.stringify(obj); + } else if (window.Node != null && obj instanceof Node) { + return obj.toString(); // DOM elements can't stringify + } else if (typeof obj === "function") { + if (verbose) + return obj.toString(); + else + return "Function"; + } else if (Object.prototype.toString.call(obj) === '[object Date]') { + if (verbose) + return JSON.stringify(obj); + else + return obj.toString(); + } else { + if (verbose) { + if (seen.indexOf(obj) >= 0) + return circular; + else + seen.push(obj); + var pairs = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var pair = JSON.stringify(key) + ":"; + pair += stringify(obj[key]); + pairs.push(pair); + } + } + return "{" + pairs.join(',') + "}"; + } else { + try { + return obj.toString(); + } catch (error) { + return ({}).toString(); + } + } + } + }; + + try { + return stringify(object); + } catch (error) { + return skewer.safeStringify(object, false); + } +}; + +/** + * Log an object to the Skewer REPL in Emacs (console.log). + * @param message The object to be logged. + */ +skewer.log = function() { + "use strict"; + for (var i = 0; i < arguments.length; i++) { + var log = { + type: "log", + value: skewer.safeStringify(arguments[i], true) + }; + skewer.postJSON(skewer.host + "/skewer/post", log); + } +}; + +/** + * Report an error event to the REPL. + * @param event An error event object. + */ +skewer.error = function(event) { + "use strict"; + var log = { + type: "error", + value: event.message, + filename: event.filename, + line: event.lineno, + column: event.column + }; + skewer.postJSON(skewer.host + "/skewer/post", log); +}; + +/** + * Prepare a result when an error occurs evaluating Javascript code. + * @param error The error object given by catch. + * @param result The resutl object to return to Emacs. + * @param request The request object from Emacs. + * @return The result object to send back to Emacs. + */ +skewer.errorResult = function(error, result, request) { + "use strict"; + return skewer.extend({}, result, { + value: error.toString(), + status: 'error', + error: { + name: error.name, + stack: error.stack, + type: error.type, + message: error.message, + eval: request.eval + } + }); +}; + +if (window.addEventListener) { + window.addEventListener('error', skewer.error); + if (document.readyState === 'complete') { + skewer(); + } else { + window.addEventListener('load', skewer); + } +} else { // < IE9 + window.attachEvent('onerror', skewer.error); + if (document.readyState === 'complete') { + skewer(); + } else { + window.attachEvent('onload', skewer); + } +} |