From 3f4a0d5370ae6c34afe180df96add3b8522f4af1 Mon Sep 17 00:00:00 2001 From: mattkae Date: Wed, 11 May 2022 09:23:58 -0400 Subject: initial commit --- elpa/skewer-mode-20200304.1142/cache-table.el | 66 +++ elpa/skewer-mode-20200304.1142/cache-table.elc | Bin 0 -> 6281 bytes elpa/skewer-mode-20200304.1142/example.html | 9 + elpa/skewer-mode-20200304.1142/skewer-bower.el | 217 ++++++++ elpa/skewer-mode-20200304.1142/skewer-bower.elc | Bin 0 -> 7211 bytes elpa/skewer-mode-20200304.1142/skewer-css.el | 134 +++++ elpa/skewer-mode-20200304.1142/skewer-css.elc | Bin 0 -> 5346 bytes .../skewer-everything.user.js | 54 ++ elpa/skewer-mode-20200304.1142/skewer-html.el | 165 ++++++ elpa/skewer-mode-20200304.1142/skewer-html.elc | Bin 0 -> 7845 bytes .../skewer-mode-autoloads.el | 160 ++++++ elpa/skewer-mode-20200304.1142/skewer-mode-pkg.el | 12 + elpa/skewer-mode-20200304.1142/skewer-mode.el | 620 +++++++++++++++++++++ elpa/skewer-mode-20200304.1142/skewer-mode.elc | Bin 0 -> 29666 bytes elpa/skewer-mode-20200304.1142/skewer-repl.el | 210 +++++++ elpa/skewer-mode-20200304.1142/skewer-repl.elc | Bin 0 -> 8507 bytes elpa/skewer-mode-20200304.1142/skewer-setup.el | 21 + elpa/skewer-mode-20200304.1142/skewer-setup.elc | Bin 0 -> 378 bytes elpa/skewer-mode-20200304.1142/skewer.js | 436 +++++++++++++++ 19 files changed, 2104 insertions(+) create mode 100644 elpa/skewer-mode-20200304.1142/cache-table.el create mode 100644 elpa/skewer-mode-20200304.1142/cache-table.elc create mode 100644 elpa/skewer-mode-20200304.1142/example.html create mode 100644 elpa/skewer-mode-20200304.1142/skewer-bower.el create mode 100644 elpa/skewer-mode-20200304.1142/skewer-bower.elc create mode 100644 elpa/skewer-mode-20200304.1142/skewer-css.el create mode 100644 elpa/skewer-mode-20200304.1142/skewer-css.elc create mode 100644 elpa/skewer-mode-20200304.1142/skewer-everything.user.js create mode 100644 elpa/skewer-mode-20200304.1142/skewer-html.el create mode 100644 elpa/skewer-mode-20200304.1142/skewer-html.elc create mode 100644 elpa/skewer-mode-20200304.1142/skewer-mode-autoloads.el create mode 100644 elpa/skewer-mode-20200304.1142/skewer-mode-pkg.el create mode 100644 elpa/skewer-mode-20200304.1142/skewer-mode.el create mode 100644 elpa/skewer-mode-20200304.1142/skewer-mode.elc create mode 100644 elpa/skewer-mode-20200304.1142/skewer-repl.el create mode 100644 elpa/skewer-mode-20200304.1142/skewer-repl.elc create mode 100644 elpa/skewer-mode-20200304.1142/skewer-setup.el create mode 100644 elpa/skewer-mode-20200304.1142/skewer-setup.elc create mode 100644 elpa/skewer-mode-20200304.1142/skewer.js (limited to 'elpa/skewer-mode-20200304.1142') diff --git a/elpa/skewer-mode-20200304.1142/cache-table.el b/elpa/skewer-mode-20200304.1142/cache-table.el new file mode 100644 index 0000000..c3b2448 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/cache-table.el @@ -0,0 +1,66 @@ +;;; cache-table.el --- a hash table with expiring entries -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;; Author: Christopher Wellons +;; Version: 1.0 + +;;; Commentary: + +;; See the docstring of `cache-table-create'. There is no +;; `cache-table-put': use `setf' on `cache-table-get' instead. + +;;; Code: + +(require 'cl-lib) + +(cl-defstruct (cache-table (:constructor cache-table--create)) + "A cache table with expiring entries." + expire-time table) + +(defun cache-table-create (expire-time &rest keyword-args) + "Create a new cache-table with entries automatically removed +from the table after EXPIRE-TIME seconds. This function accepts +the same keyword arguments as `make-hash-table'. Entries are not +actually removed from the cache-table until an access is made to +the cache-table. + +Use `cache-table-get' to get and put (via setf) entries." + (cache-table--create :expire-time expire-time + :table (apply #'make-hash-table keyword-args))) + +(defun cache-table-clear-expired (cache-table) + "Remove all expired entries from CACHE-TABLE." + (cl-loop with expire-time = (cache-table-expire-time cache-table) + with table = (cache-table-table cache-table) + with dead-time = (- (float-time) expire-time) + for key being the hash-keys of table using (hash-value entry) + for (time . value) = entry + when (< time dead-time) do (remhash key table))) + +(defun cache-table-get (key cache-table &optional default) + "Access the value for KEY in CACHE-TABLE if it has not yet +expired. Behaves just like `gethash'." + (cache-table-clear-expired cache-table) + (cdr (gethash key (cache-table-table cache-table) (cons 0 default)))) + +(gv-define-setter cache-table-get (value key cache-table) + "Put an entry in the hash table, like (setf (gethash key table) value)." + `(progn + (cache-table-clear-expired ,cache-table) + (puthash ,key (cons (float-time) ,value) + (cache-table-table ,cache-table)))) + +(defun cache-table-map (f cache-table) + "Like `maphash', call F for all non-expired entries in CACHE-TABLE." + (cache-table-clear-expired cache-table) + (maphash (lambda (k v) (funcall f k (cdr v))) + (cache-table-table cache-table))) + +(defun cache-table-count (cache-table) + "Like `hash-table-count', count the number of non-expired entries." + (hash-table-count (cache-table-table cache-table))) + +(provide 'cache-table) + +;;; cache-table.el ends here diff --git a/elpa/skewer-mode-20200304.1142/cache-table.elc b/elpa/skewer-mode-20200304.1142/cache-table.elc new file mode 100644 index 0000000..d83791e Binary files /dev/null and b/elpa/skewer-mode-20200304.1142/cache-table.elc differ diff --git a/elpa/skewer-mode-20200304.1142/example.html b/elpa/skewer-mode-20200304.1142/example.html new file mode 100644 index 0000000..a3e301a --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/example.html @@ -0,0 +1,9 @@ + + + + Skewer + + + + + diff --git a/elpa/skewer-mode-20200304.1142/skewer-bower.el b/elpa/skewer-mode-20200304.1142/skewer-bower.el new file mode 100644 index 0000000..69bfbe6 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-bower.el @@ -0,0 +1,217 @@ +;;; skewer-bower.el --- dynamic library loading -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This package loads libraries into the current page using the bower +;; infrastructure. Note: bower is not actually used by this package +;; and so does *not* need to be installed. Only git is required (see +;; `skewer-bower-git-executable'). It will try to learn how to run git +;; from Magit if available. + +;; The interactive command for loading libraries is +;; `skewer-bower-load'. It will prompt for a library and a version, +;; automatically fetching it from the bower infrastructure if needed. +;; For example, I often find it handy to load some version of jQuery +;; when poking around at a page that doesn't already have it loaded. + +;; Caveat: unfortunately the bower infrastructure is a mess; many +;; packages are in some sort of broken state -- missing dependencies, +;; missing metadata, broken metadata, or an invalid repository URL. +;; Some of this is due to under-specification of the metadata by the +;; bower project. Broken packages are unlikely to be loadable by +;; skewer-bower. + +;;; Code: + +(require 'cl-lib) +(require 'skewer-mode) +(require 'simple-httpd) +(require 'magit nil t) ; optional + +(defcustom skewer-bower-cache-dir (locate-user-emacs-file "skewer-cache") + "Location of library cache (git repositories)." + :type 'string + :group 'skewer) + +(defcustom skewer-bower-endpoint "https://bower.herokuapp.com" + "Endpoint for accessing package information." + :type 'string + :group 'skewer) + +(defcustom skewer-bower-json '("bower.json" "package.json" "component.json") + "Files to search for package metadata." + :type 'list + :group 'skewer) + +; Try to match Magit's configuration if available +(defcustom skewer-bower-git-executable "git" + "Name of the git executable." + :type 'string + :group 'skewer) + +(defvar skewer-bower-packages nil + "Alist of all packages known to bower.") + +(defvar skewer-bower-refreshed nil + "List of packages that have been refreshed recently. This keeps +them from hitting the network frequently.") + +;;;###autoload +(defun skewer-bower-refresh () + "Update the package listing and packages synchronously." + (interactive) + (cl-declare (special url-http-end-of-headers)) + (setf skewer-bower-refreshed nil) + (with-current-buffer + (url-retrieve-synchronously (concat skewer-bower-endpoint "/packages")) + (setf (point) url-http-end-of-headers) + (setf skewer-bower-packages + (cl-sort + (cl-loop for package across (json-read) + collect (cons (cdr (assoc 'name package)) + (cdr (assoc 'url package)))) + #'string< :key #'car)))) + +;; Git functions + +(defun skewer-bower-cache (package) + "Return the cache repository directory for PACKAGE." + (unless (file-exists-p skewer-bower-cache-dir) + (make-directory skewer-bower-cache-dir t)) + (expand-file-name package skewer-bower-cache-dir)) + +(defun skewer-bower-git (package &rest args) + "Run git for PACKAGE's repository with ARGS." + (with-temp-buffer + (when (zerop (apply #'call-process skewer-bower-git-executable nil t nil + (format "--git-dir=%s" (skewer-bower-cache package)) + args)) + (buffer-string)))) + +(defun skewer-bower-git-clone (url package) + "Clone or fetch PACKAGE's repository from URL if needed." + (if (member package skewer-bower-refreshed) + t + (let* ((cache (skewer-bower-cache package)) + (status + (if (file-exists-p cache) + (when (skewer-bower-git package "fetch") + (push package skewer-bower-refreshed)) + (skewer-bower-git package "clone" "--bare" url cache)))) + (not (null status))))) + +(defun skewer-bower-git-show (package version file) + "Grab FILE from PACKAGE at version VERSION." + (when (string-match-p "^\\./" file) ; avoid relative paths + (setf file (substring file 2))) + (skewer-bower-git package "show" (format "%s:%s" version file))) + +(defun skewer-bower-git-tag (package) + "List all the tags in PACKAGE's repository." + (split-string (skewer-bower-git package "tag"))) + +;; Bower functions + +(defun skewer-bower-package-ensure (package) + "Ensure a package is installed in the cache and up to date. +Emit an error if the package could not be ensured." + (when (null skewer-bower-packages) (skewer-bower-refresh)) + (let ((url (cdr (assoc package skewer-bower-packages)))) + (when (null url) + (error "Unknown package: %s" package)) + (when (null (skewer-bower-git-clone url package)) + (error "Failed to fetch: %s" url)) + t)) + +(defun skewer-bower-package-versions (package) + "List the available versions for a package. Always returns at +least one version." + (skewer-bower-package-ensure package) + (or (sort (skewer-bower-git-tag package) #'string<) + (list "master"))) + +(defun skewer-bower-get-config (package &optional version) + "Get the configuration alist for PACKAGE at VERSION. Return nil +if no configuration could be found." + (skewer-bower-package-ensure package) + (unless version (setf version "master")) + (json-read-from-string + (cl-loop for file in skewer-bower-json + for config = (skewer-bower-git-show package version file) + when config return it + finally (return "null")))) + +;; Serving the library + +(defvar skewer-bower-history () + "Library selection history for `completing-read'.") + +(defun skewer-bowser--path (package version main) + "Return the simple-httpd hosted path for PACKAGE." + (format "/skewer/bower/%s/%s/%s" package (or version "master") main)) + +(defun skewer-bower-prompt-package () + "Prompt for a package and version from the user." + (when (null skewer-bower-packages) (skewer-bower-refresh)) + ;; ido-completing-read bug workaround: + (when (> (length skewer-bower-history) 32) + (setf skewer-bower-history (cl-subseq skewer-bower-history 0 16))) + (let* ((packages (mapcar #'car skewer-bower-packages)) + (selection (nconc skewer-bower-history packages)) + (package (completing-read "Library: " selection nil t nil + 'skewer-bower-history)) + (versions (reverse (skewer-bower-package-versions package))) + (version (completing-read "Version: " versions + nil t nil nil (car versions)))) + (list package version))) + +(defun skewer-bower--js-p (filename) + "Return non-nil if FILENAME looks like JavaScript." + (string-match "\\.js$" filename)) + +(defun skewer-bower-guess-main (package version config) + "Attempt to determine the main entrypoints from a potentially +incomplete or incorrect bower configuration. Returns nil if +guessing failed." + (let ((check (apply-partially #'skewer-bower-git-show package version)) + (main (cdr (assoc 'main config)))) + (cond ((and (vectorp main) (cl-some check main)) + (cl-coerce (cl-remove-if-not #'skewer-bower--js-p main) 'list)) + ((and (stringp main) (funcall check main)) + (list main)) + ((funcall check (concat package ".js")) + (list (concat package ".js"))) + ((funcall check package) + (list package))))) + +;;;###autoload +(defun skewer-bower-load (package &optional version) + "Dynamically load a library from bower into the current page." + (interactive (skewer-bower-prompt-package)) + (let* ((config (skewer-bower-get-config package version)) + (deps (cdr (assoc 'dependencies config))) + (main (skewer-bower-guess-main package version config))) + (when (null main) + (error "Could not load %s (%s): no \"main\" entrypoint specified" + package version)) + (cl-loop for (dep . version) in deps + do (skewer-bower-load (format "%s" dep) version)) + (cl-loop for entrypoint in main + for path = (skewer-bowser--path package version entrypoint) + do (skewer-eval path nil :type "script")))) + +(defservlet skewer/bower "application/javascript; charset=utf-8" (path) + "Serve a script from the local bower repository cache." + (cl-destructuring-bind (_ _skewer _bower package version . parts) + (split-string path "/") + (let* ((file (mapconcat #'identity parts "/")) + (contents (skewer-bower-git-show package version file))) + (if contents + (insert contents) + (httpd-error t 404))))) + +(provide 'skewer-bower) + +;;; skewer-bower.el ends here diff --git a/elpa/skewer-mode-20200304.1142/skewer-bower.elc b/elpa/skewer-mode-20200304.1142/skewer-bower.elc new file mode 100644 index 0000000..e85390c Binary files /dev/null and b/elpa/skewer-mode-20200304.1142/skewer-bower.elc differ diff --git a/elpa/skewer-mode-20200304.1142/skewer-css.el b/elpa/skewer-mode-20200304.1142/skewer-css.el new file mode 100644 index 0000000..457c742 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-css.el @@ -0,0 +1,134 @@ +;;; skewer-css.el --- skewer support for live-interaction CSS -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This minor mode provides functionality for CSS like plain Skewer +;; does for JavaScript. + +;; * C-x C-e -- `skewer-css-eval-current-declaration' +;; * C-M-x -- `skewer-css-eval-current-rule' +;; * C-c C-k -- `skewer-css-eval-buffer' + +;; These functions assume there are no comments within a CSS rule, +;; *especially* not within a declaration. In the former case, if you +;; keep the comment free of CSS syntax it should be able to manage +;; reasonably well. This may be fixed someday. + +;;; Code: + +(require 'css-mode) +(require 'skewer-mode) + +(defun skewer-css-trim (string) + "Trim and compress whitespace in the string." + (let ((cleaned (replace-regexp-in-string "[\t\n ]+" " " string))) + (replace-regexp-in-string "^[\t\n ]+\\|[\t\n ]+$" "" cleaned))) + +;; Parsing + +(defun skewer-css-beginning-of-rule () + "Move to the beginning of the current rule and return point." + (skewer-css-end-of-rule) + (re-search-backward "{") + (when (re-search-backward "[}/]" nil 'start) + (forward-char)) + (re-search-forward "[^ \t\n]") + (backward-char) + (point)) + +(defun skewer-css-end-of-rule () + "Move to the end of the current rule and return point." + (if (eql (char-before) ?}) + (point) + (re-search-forward "}"))) + +(defun skewer-css-end-of-declaration () + "Move to the end of the current declaration and return point." + (if (eql (char-before) ?\;) + (point) + (re-search-forward ";"))) + +(defun skewer-css-beginning-of-declaration () + "Move to the end of the current declaration and return point." + (skewer-css-end-of-declaration) + (re-search-backward ":") + (backward-sexp 1) + (point)) + +(defun skewer-css-selectors () + "Return the selectors for the current rule." + (save-excursion + (let ((start (skewer-css-beginning-of-rule)) + (end (1- (re-search-forward "{")))) + (skewer-css-trim + (buffer-substring-no-properties start end))))) + +(defun skewer-css-declaration () + "Return the current declaration as a pair of strings." + (save-excursion + (let ((start (skewer-css-beginning-of-declaration)) + (end (skewer-css-end-of-declaration))) + (let* ((clip (buffer-substring-no-properties start end)) + (pair (split-string clip ":"))) + (mapcar #'skewer-css-trim pair))))) + +;; Evaluation + +(defun skewer-css (rule) + "Add RULE as a new stylesheet." + (skewer-eval rule nil :type "css")) + +(defun skewer-css-eval-current-declaration () + "Evaluate the declaration at the point." + (interactive) + (save-excursion + (let ((selectors (skewer-css-selectors)) + (rule (skewer-css-declaration)) + (start (skewer-css-beginning-of-declaration)) + (end (skewer-css-end-of-declaration))) + (skewer-flash-region start end) + (skewer-css (apply #'format "%s { %s: %s }" selectors rule))))) + +(defun skewer-css-eval-current-rule () + "Evaluate the rule at the point." + (interactive) + (save-excursion + (let* ((start (skewer-css-beginning-of-rule)) + (end (skewer-css-end-of-rule)) + (rule (buffer-substring-no-properties start end))) + (skewer-flash-region start end) + (skewer-css (skewer-css-trim rule))))) + +(defun skewer-css-eval-buffer () + "Send the entire current buffer as a new stylesheet." + (interactive) + (skewer-css (buffer-substring-no-properties (point-min) (point-max)))) + +(defun skewer-css-clear-all () + "Remove *all* Skewer-added styles from the document." + (interactive) + (skewer-eval nil nil :type "cssClearAll")) + +;; Minor mode definition + +(defvar skewer-css-mode-map + (let ((map (make-sparse-keymap))) + (prog1 map + (define-key map (kbd "C-x C-e") 'skewer-css-eval-current-declaration) + (define-key map (kbd "C-M-x") 'skewer-css-eval-current-rule) + (define-key map (kbd "C-c C-k") 'skewer-css-eval-buffer) + (define-key map (kbd "C-c C-c") 'skewer-css-clear-all))) + "Keymap for skewer-css-mode.") + +;;;###autoload +(define-minor-mode skewer-css-mode + "Minor mode for interactively loading new CSS rules." + :lighter " skewer-css" + :keymap skewer-css-mode-map + :group 'skewer) + +(provide 'skewer-css) + +;;; skewer-css.el ends here diff --git a/elpa/skewer-mode-20200304.1142/skewer-css.elc b/elpa/skewer-mode-20200304.1142/skewer-css.elc new file mode 100644 index 0000000..0026a9d Binary files /dev/null and b/elpa/skewer-mode-20200304.1142/skewer-css.elc differ diff --git a/elpa/skewer-mode-20200304.1142/skewer-everything.user.js b/elpa/skewer-mode-20200304.1142/skewer-everything.user.js new file mode 100644 index 0000000..4d22273 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-everything.user.js @@ -0,0 +1,54 @@ +// ==UserScript== +// @name Skewer Everything +// @description Add a toggle button to run Skewer on the current page +// @lastupdated 2015-09-14 +// @version 1.3 +// @license Public Domain +// @include /^https?:/// +// @grant none +// @run-at document-start +// ==/UserScript== + +window.skewerNativeXHR = XMLHttpRequest; +window.skewerInject = inject; + +var host = 'http://localhost:8080'; + +var toggle = document.createElement('div'); +toggle.onclick = inject; +toggle.style.width = '0px'; +toggle.style.height = '0px'; +toggle.style.borderStyle = 'solid'; +toggle.style.borderWidth = '0 12px 12px 0'; +toggle.style.borderColor = 'transparent #F00 transparent transparent'; +toggle.style.position = 'fixed'; +toggle.style.right = 0; +toggle.style.top = 0; +toggle.style.zIndex = 214748364; + +var injected = false; + +function inject() { + if (!injected) { + var script = document.createElement('script'); + script.src = host + '/skewer'; + document.body.appendChild(script); + toggle.style.borderRightColor = '#0F0'; + } else { + /* break skewer to disable it */ + skewer.fn = null; + toggle.style.borderRightColor = '#F00'; + } + injected = !injected; + localStorage._autoskewered = JSON.stringify(injected); +} + +document.addEventListener('DOMContentLoaded', function() { + /* Don't use on iframes. */ + if (window.top === window.self) { + document.body.appendChild(toggle); + if (JSON.parse(localStorage._autoskewered || 'false')) { + inject(); + } + } +}); diff --git a/elpa/skewer-mode-20200304.1142/skewer-html.el b/elpa/skewer-mode-20200304.1142/skewer-html.el new file mode 100644 index 0000000..b21de16 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-html.el @@ -0,0 +1,165 @@ +;;; skewer-html.el --- skewer support for live-interaction HTML -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This minor mode provides functionality for HTML like plain Skewer +;; does for JavaScript. There's no clean way to replace the body and +;; head elements of a live document, so "evaluating" these elements is +;; not supported. + +;; * C-M-x -- `skewer-html-eval-tag' + +;; See also `skewer-html-fetch-selector-into-buffer' for grabbing the +;; page as it current exists. + +;;; Code: + +(require 'cl-lib) +(require 'sgml-mode) +(require 'skewer-mode) + +;; Macros + +(defmacro skewer-html--with-html-mode (&rest body) + "Evaluate BODY as if in `html-mode', using a temp buffer if necessary." + (declare (indent 0)) + (let ((orig-buffer (make-symbol "orig-buffer")) + (temp-buffer (make-symbol "temp-buffer")) + (orig-point (make-symbol "orig-point"))) + `(let ((,temp-buffer (and (not (eq major-mode 'html-mode)) + (generate-new-buffer " *skewer-html*"))) + (,orig-buffer (current-buffer)) + (,orig-point (point))) + (unwind-protect + (with-current-buffer (or ,temp-buffer ,orig-buffer) + (when ,temp-buffer + (insert-buffer-substring ,orig-buffer) + (setf (point) ,orig-point) + (html-mode)) + ,@body) + (when ,temp-buffer + (kill-buffer ,temp-buffer)))))) + +;; Selector computation + +(defun skewer-html--cleanup (tag) + "Cleanup TAG name from sgml-mode." + (skewer-html--with-html-mode + (replace-regexp-in-string "/$" "" (sgml-tag-name tag)))) + +(defun skewer-html--tag-after-point () + "Return the tag struct for the tag immediately following point." + (skewer-html--with-html-mode + (save-excursion + (forward-char 1) + (sgml-parse-tag-backward)))) + +(defun skewer-html--get-context () + "Like `sgml-get-context' but to the root, skipping close tags." + (skewer-html--with-html-mode + (save-excursion + (cl-loop for context = (sgml-get-context) + while context + nconc (nreverse context) into tags + finally return (cl-delete 'close tags :key #'sgml-tag-type))))) + +(cl-defun skewer-html-compute-tag-nth (&optional (point (point))) + "Compute the position of this tag within its parent." + (skewer-html--with-html-mode + (save-excursion + (setf (point) point) + (let ((context (skewer-html--get-context))) + (when context + (let ((tag-name (skewer-html--cleanup (car context))) + (target-depth (1- (length context)))) + (cl-loop with n = 0 + ;; If point doesn't move, we're at the root. + for point-start = (point) + do (sgml-skip-tag-backward 1) + until (= (point) point-start) + ;; If depth changed, we're done. + for current-depth = (length (skewer-html--get-context)) + until (< current-depth target-depth) + ;; Examine the sibling tag. + for current-name = (save-excursion + (forward-char) + (sgml-parse-tag-name)) + when (equal current-name tag-name) + do (cl-incf n) + finally return n))))))) + +(defun skewer-html-compute-tag-ancestry () + "Compute the ancestry chain at point." + (skewer-html--with-html-mode + (nreverse + (cl-loop for tag in (skewer-html--get-context) + for nth = (skewer-html-compute-tag-nth (1+ (sgml-tag-start tag))) + for name = (skewer-html--cleanup tag) + unless (equal name "html") + collect (list name nth))))) + +(defun skewer-html-compute-selector () + "Compute the selector for exactly the tag around point." + (let ((ancestry (skewer-html-compute-tag-ancestry))) + (mapconcat (lambda (tag) + (format "%s:nth-of-type(%d)" (cl-first tag) (cl-second tag))) + ancestry " > "))) + +;; Fetching + +(defun skewer-html-fetch-selector (selector) + "Fetch the innerHTML of a selector." + (let ((result (skewer-eval-synchronously selector :type "fetchselector"))) + (if (skewer-success-p result) + (cdr (assoc 'value result)) + ""))) + +(defun skewer-html-fetch-selector-into-buffer (selector) + "Fetch the innerHTML of a selector and insert it into the active buffer." + (interactive "sSelector: ") + (insert (skewer-html-fetch-selector selector))) + +;; Evaluation + +(defun skewer-html-eval (string ancestry &optional append) + "Load HTML into a selector, optionally appending." + (let ((ancestry* (cl-coerce ancestry 'vector))) ; for JSON + (skewer-eval string nil :type "html" :extra `((ancestry . ,ancestry*) + (append . ,append))))) + +(defun skewer-html-eval-tag () + "Load HTML from the immediately surrounding tag." + (interactive) + (let ((ancestry (skewer-html-compute-tag-ancestry))) + (save-excursion + ;; Move to beginning of opening tag + (let* ((beg (skewer-html--with-html-mode + (sgml-skip-tag-forward 1) (point))) + (end (skewer-html--with-html-mode + (sgml-skip-tag-backward 1) (point))) + (region (buffer-substring-no-properties beg end))) + (skewer-flash-region beg end) + (if (= (length ancestry) 1) + (error "Error: cannot eval body and head tags.") + (skewer-html-eval region ancestry nil)))))) + +;; Minor mode definition + +(defvar skewer-html-mode-map + (let ((map (make-sparse-keymap))) + (prog1 map + (define-key map (kbd "C-M-x") 'skewer-html-eval-tag))) + "Keymap for skewer-html-mode") + +;;;###autoload +(define-minor-mode skewer-html-mode + "Minor mode for interactively loading new HTML." + :lighter " skewer-html" + :keymap skewer-html-mode-map + :group 'skewer) + +(provide 'skewer-html) + +;;; skewer-html.el ends here diff --git a/elpa/skewer-mode-20200304.1142/skewer-html.elc b/elpa/skewer-mode-20200304.1142/skewer-html.elc new file mode 100644 index 0000000..017178f Binary files /dev/null and b/elpa/skewer-mode-20200304.1142/skewer-html.elc differ diff --git a/elpa/skewer-mode-20200304.1142/skewer-mode-autoloads.el b/elpa/skewer-mode-20200304.1142/skewer-mode-autoloads.el new file mode 100644 index 0000000..f09afb8 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-mode-autoloads.el @@ -0,0 +1,160 @@ +;;; skewer-mode-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "cache-table" "cache-table.el" (0 0 0 0)) +;;; Generated autoloads from cache-table.el + +(register-definition-prefixes "cache-table" '("cache-table-")) + +;;;*** + +;;;### (autoloads nil "skewer-bower" "skewer-bower.el" (0 0 0 0)) +;;; Generated autoloads from skewer-bower.el + +(autoload 'skewer-bower-refresh "skewer-bower" "\ +Update the package listing and packages synchronously." t nil) + +(autoload 'skewer-bower-load "skewer-bower" "\ +Dynamically load a library from bower into the current page. + +\(fn PACKAGE &optional VERSION)" t nil) + +(register-definition-prefixes "skewer-bower" '("skewer")) + +;;;*** + +;;;### (autoloads nil "skewer-css" "skewer-css.el" (0 0 0 0)) +;;; Generated autoloads from skewer-css.el + +(autoload 'skewer-css-mode "skewer-css" "\ +Minor mode for interactively loading new CSS rules. + +This is a minor mode. If called interactively, toggle the +`skewer-css mode' mode. If the prefix argument is positive, +enable the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `skewer-css-mode'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "skewer-css" '("skewer-css")) + +;;;*** + +;;;### (autoloads nil "skewer-html" "skewer-html.el" (0 0 0 0)) +;;; Generated autoloads from skewer-html.el + +(autoload 'skewer-html-mode "skewer-html" "\ +Minor mode for interactively loading new HTML. + +This is a minor mode. If called interactively, toggle the +`skewer-html mode' mode. If the prefix argument is positive, +enable the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `skewer-html-mode'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "skewer-html" '("skewer-html-")) + +;;;*** + +;;;### (autoloads nil "skewer-mode" "skewer-mode.el" (0 0 0 0)) +;;; Generated autoloads from skewer-mode.el + +(autoload 'list-skewer-clients "skewer-mode" "\ +List the attached browsers in a buffer." t nil) + +(autoload 'skewer-mode "skewer-mode" "\ +Minor mode for interacting with a browser. + +This is a minor mode. If called interactively, toggle the +`skewer mode' mode. If the prefix argument is positive, enable +the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `skewer-mode'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(autoload 'run-skewer "skewer-mode" "\ +Attach a browser to Emacs for a skewer JavaScript REPL. Uses +`browse-url' to launch a browser. + +With a prefix arugment (C-u), it will ask the filename of the +root document. With two prefix arguments (C-u C-u), it will use +the contents of the current buffer as the root document. + +\(fn &optional ARG)" t nil) + +(autoload 'skewer-run-phantomjs "skewer-mode" "\ +Connect an inferior PhantomJS process to Skewer, returning the process." t nil) + +(register-definition-prefixes "skewer-mode" '("httpd/skewer/" "phantomjs-program-name" "skewer")) + +;;;*** + +;;;### (autoloads nil "skewer-repl" "skewer-repl.el" (0 0 0 0)) +;;; Generated autoloads from skewer-repl.el + +(autoload 'skewer-repl--response-hook "skewer-repl" "\ +Catches all browser messages logging some to the REPL. + +\(fn RESPONSE)" nil nil) + +(autoload 'skewer-repl "skewer-repl" "\ +Start a JavaScript REPL to be evaluated in the visiting browser." t nil) + +(eval-after-load 'skewer-mode '(progn (add-hook 'skewer-response-hook #'skewer-repl--response-hook) (add-hook 'skewer-repl-mode-hook #'skewer-repl-mode-compilation-shell-hook) (define-key skewer-mode-map (kbd "C-c C-z") #'skewer-repl))) + +(register-definition-prefixes "skewer-repl" '("company-skewer-repl" "skewer-")) + +;;;*** + +;;;### (autoloads nil "skewer-setup" "skewer-setup.el" (0 0 0 0)) +;;; Generated autoloads from skewer-setup.el + +(autoload 'skewer-setup "skewer-setup" "\ +Fully integrate Skewer into js2-mode, css-mode, and html-mode buffers." nil nil) + +;;;*** + +;;;### (autoloads nil nil ("skewer-mode-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; skewer-mode-autoloads.el ends here diff --git a/elpa/skewer-mode-20200304.1142/skewer-mode-pkg.el b/elpa/skewer-mode-20200304.1142/skewer-mode-pkg.el new file mode 100644 index 0000000..ff0e79a --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-mode-pkg.el @@ -0,0 +1,12 @@ +(define-package "skewer-mode" "20200304.1142" "live browser JavaScript, CSS, and HTML interaction" + '((simple-httpd "1.4.0") + (js2-mode "20090723") + (emacs "24")) + :commit "e5bed351939c92a1f788f78398583c2f83f1bb3c" :authors + '(("Christopher Wellons" . "wellons@nullprogram.com")) + :maintainer + '("Christopher Wellons" . "wellons@nullprogram.com") + :url "https://github.com/skeeto/skewer-mode") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/skewer-mode-20200304.1142/skewer-mode.el b/elpa/skewer-mode-20200304.1142/skewer-mode.el new file mode 100644 index 0000000..a439a9c --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-mode.el @@ -0,0 +1,620 @@ +;;; skewer-mode.el --- live browser JavaScript, CSS, and HTML interaction -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;; Author: Christopher Wellons +;; URL: https://github.com/skeeto/skewer-mode + +;;; Commentary: + +;; Quick start (without package.el): + +;; 1. Put this directory in your `load-path' +;; 2. Load skewer-mode.el +;; 3. M-x `run-skewer' to attach a browser to Emacs +;; 4. From a `js2-mode' buffer with `skewer-mode' minor mode enabled, +;; send forms to the browser to evaluate + +;; The function `skewer-setup' can be used to configure all of mode +;; hooks (previously this was the default). This can also be done +;; manually like so, + +;; (add-hook 'js2-mode-hook 'skewer-mode) +;; (add-hook 'css-mode-hook 'skewer-css-mode) +;; (add-hook 'html-mode-hook 'skewer-html-mode) + +;; The keybindings for evaluating expressions in the browser are just +;; like the Lisp modes. These are provided by the minor mode +;; `skewer-mode'. + +;; * C-x C-e -- `skewer-eval-last-expression' +;; * C-M-x -- `skewer-eval-defun' +;; * C-c C-k -- `skewer-load-buffer' + +;; The result of the expression is echoed in the minibuffer. + +;; Additionally, `css-mode' and `html-mode' get a similar set of +;; bindings for modifying the CSS rules and updating HTML on the +;; current page. + +;; Note: `run-skewer' uses `browse-url' to launch the browser. This +;; may require further setup depending on your operating system and +;; personal preferences. + +;; Multiple browsers and browser tabs can be attached to Emacs at +;; once. JavaScript forms are sent to all attached clients +;; simultaneously, and each will echo back the result +;; individually. Use `list-skewer-clients' to see a list of all +;; currently attached clients. + +;; Sometimes Skewer's long polls from the browser will timeout after a +;; number of hours of inactivity. If you find the browser disconnected +;; from Emacs for any reason, use the browser's console to call +;; skewer() to reconnect. This avoids a page reload, which would lose +;; any fragile browser state you might care about. + +;; To skewer your own document rather than the provided blank page, + +;; 1. Load the dependencies +;; 2. Load skewer-mode.el +;; 3. Start the HTTP server (`httpd-start') +;; 4. Include "http://localhost:8080/skewer" as a script +;; (see `example.html' and check your `httpd-port') +;; 5. Visit the document from your browser + +;; Skewer fully supports CORS, so the document need not be hosted by +;; Emacs itself. A Greasemonkey userscript and a bookmarklet are +;; provided for injecting Skewer into any arbitrary page you're +;; visiting without needing to modify the page on the host. + +;; With skewer-repl.el loaded, a REPL into the browser can be created +;; with M-x `skewer-repl', or C-c C-z. This should work like a console +;; within the browser. Messages can be logged to this REPL with +;; skewer.log() (just like console.log()). + +;; Extending Skewer: + +;; Skewer is flexible and open to extension. The REPL and the CSS and +;; HTML minor modes are a partial examples of this. You can extend +;; skewer.js with your own request handlers and talk to them from +;; Emacs using `skewer-eval' (or `skewer-eval-synchronously') with +;; your own custom :type. The :type string chooses the dispatch +;; function under the skewer.fn object. To inject your own JavaScript +;; into skewer.js, use `skewer-js-hook'. + +;; You can also catch messages sent from the browser not in response +;; to an explicit request. Use `skewer-response-hook' to see all +;; incoming objects. + +;;; History: + +;; Version 1.8.0: features +;; * Work around XMLHttpRequest tampering in userscript +;; * Add Makefile "run" target for testing +;; Version 1.7.0: features and fixes +;; * Support for other major modes (including web-mode) in skewer-html-mode +;; * Opportunistic support for company-mode completions +;; * Always serve content as UTF-8 +;; * Improve skewer-everything.js portability +;; Version 1.6.2: fixes +;; * skewer.log() takes multiple arguments +;; * comint and encoding fixes +;; Version 1.6.1: fixes +;; * Add `skewer-css-clear-all' +;; * Better IE8 compatibility +;; * User interface tweaks +;; Version 1.6.0: fixes +;; * Bring up to speed with Emacs 24.3 +;; * Switch to cl-lib from cl +;; Version 1.5.3: features +;; * Add `skewer-run-phantomjs' +;; Version 1.5.2: small cleanup +;; * Add `skewer-apply' and `skewer-funall' +;; * Improved safeStringify +;; Version 1.5.1: features +;; * No more automatic hook setup (see `skewer-setup') +;; * Support for HTML interaction +;; * Support for loading Bower packages +;; * Drop jQuery dependency +;; * Many small improvements +;; Version 1.4: features +;; * Full CSS interaction +;; * Greasemonkey userscript for injection +;; * Full, working CORS support +;; * Better browser presence detection +;; Version 1.3: features and fixes +;; * Full offline support +;; * No more callback registering +;; * Fix 64-bit support +;; * Two new hooks for improved extension support +;; * More uniform keybindings with other interactive modes +;; Version 1.2: features +;; * Add a skewer-eval-print-last-expression +;; * Display evaluation time when it's long +;; * Flash the region on eval +;; * Improve JS stringification +;; Version 1.1: features and fixes +;; * Added `list-skewer-clients' +;; * Reduce the number of HTTP requests needed +;; * Fix stringification issues +;; Version 1.0: initial release + +;;; Code: + +(require 'cl-lib) +(require 'json) +(require 'url-util) +(require 'simple-httpd) +(require 'js2-mode) +(require 'cache-table) + +(defgroup skewer nil + "Live browser JavaScript interaction." + :group 'languages) + +(defvar skewer-mode-map + (let ((map (make-sparse-keymap))) + (prog1 map + (define-key map (kbd "C-x C-e") 'skewer-eval-last-expression) + (define-key map (kbd "C-M-x") 'skewer-eval-defun) + (define-key map (kbd "C-c C-k") 'skewer-load-buffer))) + "Keymap for skewer-mode.") + +(defvar skewer-data-root (file-name-directory load-file-name) + "Location of data files needed by impatient-mode.") + +(defvar skewer-js-hook () + "Hook to run when skewer.js is being served to the browser. + +When hook functions are called, the current buffer is the buffer +to be served to the client (a defservlet), with skewer.js script +already inserted. This is the chance for other packages to insert +their own JavaScript to extend skewer in the browser, such as +adding a new type handler.") + +(defvar skewer-response-hook () + "Hook to run when a response arrives from the browser. Used for +catching messages from the browser with no associated +callback. The response object is passed to the hook function.") + +(defvar skewer-timeout 3600 + "Maximum time to wait on the browser to respond, in seconds.") + +(defvar skewer-clients () + "Browsers awaiting JavaScript snippets.") + +(defvar skewer-callbacks (cache-table-create skewer-timeout :test 'equal) + "Maps evaluation IDs to local callbacks.") + +(defvar skewer-queue () + "Queued messages for the browser.") + +(defvar skewer--last-timestamp 0 + "Timestamp of the last browser response. Use +`skewer-last-seen-seconds' to access this.") + +(cl-defstruct skewer-client + "A client connection awaiting a response." + proc agent) + +(defun skewer-process-queue () + "Send all queued messages to clients." + (when (and skewer-queue skewer-clients) + (let ((message (pop skewer-queue)) + (sent nil)) + (while skewer-clients + (ignore-errors + (progn + (let ((proc (skewer-client-proc (pop skewer-clients)))) + (with-temp-buffer + (insert (json-encode message)) + (httpd-send-header proc "text/plain" 200 + :Cache-Control "no-cache" + :Access-Control-Allow-Origin "*"))) + (setq skewer--last-timestamp (float-time)) + (setq sent t)))) + (if (not sent) (push message skewer-queue))) + (skewer-process-queue))) + +(defun skewer-clients-tabulate () + "Prepare client list for tabulated-list-mode." + (cl-loop for client in skewer-clients collect + (let ((proc (skewer-client-proc client)) + (agent (skewer-client-agent client))) + (cl-destructuring-bind (host port) (process-contact proc) + `(,client [,host ,(format "%d" port) ,agent]))))) + +(define-derived-mode skewer-clients-mode tabulated-list-mode "skewer-clients" + "Mode for listing browsers attached to Emacs for skewer-mode." + (setq tabulated-list-format [("Host" 12 t) + ("Port" 5 t) + ("User Agent" 0 t)]) + (setq tabulated-list-entries #'skewer-clients-tabulate) + (tabulated-list-init-header)) + +(define-key skewer-clients-mode-map (kbd "g") + (lambda () + (interactive) + (skewer-ping) + (revert-buffer))) + +(defun skewer-update-list-buffer () + "Revert the client list, due to an update." + (save-window-excursion + (let ((list-buffer (get-buffer "*skewer-clients*"))) + (when list-buffer + (with-current-buffer list-buffer + (revert-buffer)))))) + +;;;###autoload +(defun list-skewer-clients () + "List the attached browsers in a buffer." + (interactive) + (pop-to-buffer (get-buffer-create "*skewer-clients*")) + (skewer-clients-mode) + (tabulated-list-print)) + +(defun skewer-queue-client (proc req) + "Add a client to the queue, given the HTTP header." + (let ((agent (cl-second (assoc "User-Agent" req)))) + (push (make-skewer-client :proc proc :agent agent) skewer-clients)) + (skewer-update-list-buffer) + (skewer-process-queue)) + +;; Servlets + +(defservlet skewer "text/javascript; charset=UTF-8" () + (insert-file-contents (expand-file-name "skewer.js" skewer-data-root)) + (goto-char (point-max)) + (run-hooks 'skewer-js-hook)) + +(defun httpd/skewer/get (proc _path _query req &rest _args) + (skewer-queue-client proc req)) + +(defun httpd/skewer/post (proc _path _query req &rest _args) + (let* ((result (json-read-from-string (cadr (assoc "Content" req)))) + (id (cdr (assoc 'id result))) + (callback (cache-table-get id skewer-callbacks))) + (setq skewer--last-timestamp (float-time)) + (when callback + (funcall callback result)) + (if id + (skewer-queue-client proc req) + (with-temp-buffer + (httpd-send-header proc "text/plain" 200 + :Access-Control-Allow-Origin "*"))) + (dolist (hook skewer-response-hook) + (funcall hook result)))) + +(defvar skewer-demo-source + (expand-file-name "example.html" skewer-data-root) + "Source file name or buffer for `httpd/skewer/demo' servlet.") + +(defservlet skewer/demo "text/html; charset=UTF-8" () + (cl-etypecase skewer-demo-source + (buffer (insert-buffer-substring skewer-demo-source)) + (string (insert-file-contents skewer-demo-source)))) + +;; Minibuffer display + +(defun skewer-success-p (result) + "Return T if result was a success." + (equal "success" (cdr (assoc 'status result)))) + +(define-derived-mode skewer-error-mode special-mode "skewer-error" + :group 'skewer + "Mode for displaying JavaScript errors returned by skewer-mode." + (setq truncate-lines t)) + +(defface skewer-error-face + '((((class color) (background light)) + :foreground "red" :underline t) + (((class color) (background dark)) + :foreground "red" :underline t)) + "Face for JavaScript errors." + :group 'skewer) + +(defun skewer--error (string) + "Return STRING propertized as an error message." + (propertize (or string "") 'font-lock-face 'skewer-error-face)) + +(defun skewer-post-minibuffer (result) + "Report results in the minibuffer or the error buffer." + (if (skewer-success-p result) + (let ((value (cdr (assoc 'value result))) + (time (cdr (assoc 'time result)))) + (if (and time (> time 1.0)) + (message "%s (%.3f seconds)" value time) + (message "%s" value))) + (with-current-buffer (pop-to-buffer (get-buffer-create "*skewer-error*")) + (let ((inhibit-read-only t) + (error (cdr (assoc 'error result)))) + (erase-buffer) + (skewer-error-mode) + (insert (skewer--error (cdr (assoc 'name error))) ": ") + (insert (or (cdr (assoc 'message error)) "") "\n\n") + (insert (or (cdr (assoc 'stack error)) "") "\n\n") + (insert (format "Expression: %s\n\n" + (if (cdr (assoc 'strict result)) "(strict)" "")) + (cdr (assoc 'eval error))) + (goto-char (point-min)))))) + +;; Evaluation functions + +(cl-defun skewer-eval (string &optional callback + &key verbose strict (type "eval") extra) + "Evaluate STRING in the waiting browsers, giving the result to CALLBACK. + +:VERBOSE -- if T, the return will try to be JSON encoded +:STRICT -- if T, expression is evaluated with 'use strict' +:TYPE -- chooses the JavaScript handler (default: eval) +:EXTRA -- additional alist keys to append to the request object" + (let* ((id (format "%x" (random most-positive-fixnum))) + (request `((type . ,type) + (eval . ,string) + (id . ,id) + (verbose . ,verbose) + (strict . ,strict) + ,@extra))) + (prog1 request + (setf (cache-table-get id skewer-callbacks) callback) + (setq skewer-queue (append skewer-queue (list request))) + (skewer-process-queue)))) + +(defun skewer-eval-synchronously (string &rest args) + "Just like `skewer-eval' but synchronously, so don't provide a +callback. Use with caution." + (let ((result nil)) + (apply #'skewer-eval string (lambda (v) (setq result v)) args) + (cl-loop until result + do (accept-process-output nil 0.01) + finally (return result)))) + +(defun skewer-apply (function args) + "Synchronously apply FUNCTION in the browser with the supplied +arguments, returning the result. All ARGS must be printable by +`json-encode'. For example, + + (skewer-apply \"Math.atan2\" '(1 -2)) ; => 2.677945044588987 + +Uncaught exceptions propagate to Emacs as an error." + (let ((specials '(("undefined" . nil) + ("NaN" . 0.0e+NaN) + ("Infinity" . 1.0e+INF) + ("-Infinity" . -1.0e+INF)))) + (let* ((expr (concat function "(" (mapconcat #'json-encode args ", ") ")")) + (result (skewer-eval-synchronously expr :verbose t)) + (value (cdr (assoc 'value result)))) + (if (skewer-success-p result) + (if (assoc value specials) + (cdr (assoc value specials)) + (condition-case _ + (json-read-from-string value) + (json-readtable-error value))) + (signal 'javascript + (list (cdr (assoc 'message (cdr (assoc'error result)))))))))) + +(defun skewer-funcall (function &rest args) + "Synchronously call FUNCTION with the supplied ARGS. All ARGS +must be printable by `json-read-from-string. For example, + + (skewer-funcall \"Math.sin\" 0.5) ; => 0.479425538604203 + +Uncaught exceptions propagate to Emacs as an error." + (skewer-apply function args)) + +(defun skewer--save-point (f &rest args) + "Return a function that calls F with point at the current point." + (let ((saved-point (point))) + (lambda (&rest more) + (save-excursion + (goto-char saved-point) + (apply f (append args more)))))) + +(defun skewer-ping () + "Ping the browser to test that it's still alive." + (unless (null skewer-clients) ; don't queue pings + (skewer-eval (prin1-to-string (float-time)) nil :type "ping"))) + +(defun skewer-last-seen-seconds () + "Return the number of seconds since the browser was last seen." + (skewer-ping) ; make sure it's still alive next request + (- (float-time) skewer--last-timestamp)) + +(defun skewer-mode-strict-p () + "Return T if buffer contents indicates strict mode." + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (js2-forward-sws) + (forward-char 1) + (let* ((stricts '("\"use strict\"" "'use strict'")) + (node (js2-node-at-point)) + (code (buffer-substring-no-properties (js2-node-abs-pos node) + (js2-node-abs-end node)))) + (and (member code stricts) t))))) + +(defun skewer-flash-region (start end &optional timeout) + "Temporarily highlight region from START to END." + (let ((overlay (make-overlay start end))) + (overlay-put overlay 'face 'secondary-selection) + (run-with-timer (or timeout 0.2) nil 'delete-overlay overlay))) + +(defun skewer-get-last-expression () + "Return the JavaScript expression before the point as a +list: (string start end)." + (save-excursion + (js2-backward-sws) + (backward-char) + (let ((node (js2-node-at-point nil t))) + (when (eq js2-FUNCTION (js2-node-type (js2-node-parent node))) + (setq node (js2-node-parent node))) + (when (js2-ast-root-p node) + (error "no expression found")) + (let ((start (js2-node-abs-pos node)) + (end (js2-node-abs-end node))) + (list (buffer-substring-no-properties start end) start end))))) + +(defun skewer-eval-last-expression (&optional prefix) + "Evaluate the JavaScript expression before the point in the +waiting browser. If invoked with a prefix argument, insert the +result into the current buffer." + (interactive "P") + (if prefix + (skewer-eval-print-last-expression) + (if js2-mode-buffer-dirty-p + (js2-mode-wait-for-parse + (skewer--save-point #'skewer-eval-last-expression)) + (cl-destructuring-bind (string start end) (skewer-get-last-expression) + (skewer-flash-region start end) + (skewer-eval string #'skewer-post-minibuffer))))) + +(defun skewer-get-defun () + "Return the toplevel JavaScript expression around the point as +a list: (string start end)." + (save-excursion + (js2-backward-sws) + (backward-char) + (let ((node (js2-node-at-point nil t))) + (when (js2-ast-root-p node) + (error "no expression found")) + (while (and (js2-node-parent node) + (not (js2-ast-root-p (js2-node-parent node)))) + (setf node (js2-node-parent node))) + (let ((start (js2-node-abs-pos node)) + (end (js2-node-abs-end node))) + (list (buffer-substring-no-properties start end) start end))))) + +(defun skewer-eval-defun () + "Evaluate the JavaScript expression before the point in the +waiting browser." + (interactive) + (if js2-mode-buffer-dirty-p + (js2-mode-wait-for-parse (skewer--save-point #'skewer-eval-defun)) + (cl-destructuring-bind (string start end) (skewer-get-defun) + (skewer-flash-region start end) + (skewer-eval string #'skewer-post-minibuffer)))) + +;; Print last expression + +(defvar skewer-eval-print-map (cache-table-create skewer-timeout :test 'equal) + "A mapping of evaluation IDs to insertion points.") + +(defun skewer-post-print (result) + "Insert the result after its source expression." + (if (not (skewer-success-p result)) + (skewer-post-minibuffer result) + (let* ((id (cdr (assoc 'id result))) + (pos (cache-table-get id skewer-eval-print-map))) + (when pos + (with-current-buffer (car pos) + (goto-char (cdr pos)) + (insert (cdr (assoc 'value result)) "\n")))))) + +(defun skewer-eval-print-last-expression () + "Evaluate the JavaScript expression before the point in the +waiting browser and insert the result in the buffer at point." + (interactive) + (if js2-mode-buffer-dirty-p + (js2-mode-wait-for-parse + (skewer--save-point #'skewer-eval-print-last-expression)) + (cl-destructuring-bind (string start end) (skewer-get-defun) + (skewer-flash-region start end) + (insert "\n") + (let* ((request (skewer-eval string #'skewer-post-print :verbose t)) + (id (cdr (assoc 'id request))) + (pos (cons (current-buffer) (point)))) + (setf (cache-table-get id skewer-eval-print-map) pos))))) + +;; Script loading + +(defvar skewer-hosted-scripts (cache-table-create skewer-timeout) + "Map of hosted scripts to IDs.") + +(defun skewer-host-script (string) + "Host script STRING from the script servlet, returning the script ID." + (let ((id (random most-positive-fixnum))) + (prog1 id + (setf (cache-table-get id skewer-hosted-scripts) string)))) + +(defun skewer-load-buffer () + "Load the entire current buffer into the browser. A snapshot of +the buffer is hosted so that browsers visiting late won't see an +inconsistent buffer." + (interactive) + (let ((id (skewer-host-script (buffer-string))) + (buffer-name (buffer-name))) + (skewer-eval (format "/skewer/script/%d/%s" + id (url-hexify-string buffer-name)) + (lambda (_) (message "%s loaded" buffer-name)) + :type "script"))) + +(defservlet skewer/script "text/javascript; charset=UTF-8" (path) + (let ((id (string-to-number (nth 3 (split-string path "/"))))) + (insert (cache-table-get id skewer-hosted-scripts "")))) + +;; Define the minor mode + +;;;###autoload +(define-minor-mode skewer-mode + "Minor mode for interacting with a browser." + :lighter " skewer" + :keymap skewer-mode-map + :group 'skewer) + +;;;###autoload +(defun run-skewer (&optional arg) + "Attach a browser to Emacs for a skewer JavaScript REPL. Uses +`browse-url' to launch a browser. + +With a prefix arugment (C-u), it will ask the filename of the +root document. With two prefix arguments (C-u C-u), it will use +the contents of the current buffer as the root document." + (interactive "p") + (cl-case arg + (4 (setf skewer-demo-source (read-file-name "Skewer filename: "))) + (16 (setf skewer-demo-source (current-buffer)))) + (httpd-start) + (browse-url (format "http://127.0.0.1:%d/skewer/demo" httpd-port))) + +;; PhantomJS + +(defvar phantomjs-program-name "/usr/bin/phantomjs" + "Path to the phantomjs executable.") + +(defvar skewer-phantomjs-processes () + "List of phantomjs processes connected to Skewer.") + +(defun skewer-phantomjs-sentinel (proc event) + "Cleanup after phantomjs exits." + (when (cl-some (lambda (s) (string-match-p s event)) + '("finished" "abnormal" "killed")) + (delete-file (process-get proc 'tempfile)))) + +;;;###autoload +(defun skewer-run-phantomjs () + "Connect an inferior PhantomJS process to Skewer, returning the process." + (interactive) + (httpd-start) + (let ((script (make-temp-file "phantomjs-")) + (url (format "http://0:%d/skewer/demo" httpd-port))) + (with-temp-buffer + (insert (format "require('webpage').create().open('%s')" url)) + (write-region nil nil script nil 0) + (let ((proc (start-process "phantomjs" nil + phantomjs-program-name script))) + (prog1 proc + (push proc skewer-phantomjs-processes) + (process-put proc 'tempfile script) + (set-process-sentinel proc 'skewer-phantomjs-sentinel)))))) + +(defun skewer-phantomjs-kill () + "Kill all inferior phantomjs processes connected to Skewer." + (interactive) + (mapc #'delete-process skewer-phantomjs-processes) + (setf skewer-phantomjs-processes nil)) + +(provide 'skewer-mode) + +;;; skewer-mode.el ends here diff --git a/elpa/skewer-mode-20200304.1142/skewer-mode.elc b/elpa/skewer-mode-20200304.1142/skewer-mode.elc new file mode 100644 index 0000000..61c8fda Binary files /dev/null and b/elpa/skewer-mode-20200304.1142/skewer-mode.elc differ diff --git a/elpa/skewer-mode-20200304.1142/skewer-repl.el b/elpa/skewer-mode-20200304.1142/skewer-repl.el new file mode 100644 index 0000000..6a55d13 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-repl.el @@ -0,0 +1,210 @@ +;;; skewer-repl.el --- create a REPL in a visiting browser -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This is largely based on of IELM's code. Run `skewer-repl' to +;; switch to the REPL buffer and evaluate code. Use +;; `skewer-repl-toggle-strict-mode' to turn strict mode on and off. + +;; If `compilation-search-path' is set up properly, along with +;; `skewer-path-strip-level', asynchronous errors will provide +;; clickable error messages that will take you to the source file of +;; the error. This is done using `compilation-shell-minor-mode'. + +;;; Code: + +(require 'comint) +(require 'compile) +(require 'skewer-mode) + +(defcustom skewer-repl-strict-p nil + "When non-NIL, all REPL evaluations are done in strict mode." + :type 'boolean + :group 'skewer) + +(defcustom skewer-repl-prompt "js> " + "Prompt string for JavaScript REPL." + :type 'string + :group 'skewer) + +(defvar skewer-repl-welcome + (propertize "*** Welcome to Skewer ***\n" + 'font-lock-face 'font-lock-comment-face) + "Header line to show at the top of the REPL buffer. Hack +notice: this allows log messages to appear before anything is +evaluated because it provides insertable space at the top of the +buffer.") + +(defun skewer-repl-process () + "Return the process for the skewer REPL." + (get-buffer-process (current-buffer))) + +(defface skewer-repl-log-face + '((((class color) (background light)) + :foreground "#77F") + (((class color) (background dark)) + :foreground "#77F")) + "Face for skewer.log() messages." + :group 'skewer) + +(define-derived-mode skewer-repl-mode comint-mode "js-REPL" + "Provide a REPL into the visiting browser." + :group 'skewer + :syntax-table emacs-lisp-mode-syntax-table + (setq comint-prompt-regexp (concat "^" (regexp-quote skewer-repl-prompt)) + comint-input-sender 'skewer-input-sender + comint-process-echoes nil) + ;; Make opportunistic use of company-mode, but don't require it. + ;; This means company-backends may be undeclared, so don't emit a + ;; warning about it. + (with-no-warnings + (setq-local company-backends '(company-skewer-repl))) + (unless (comint-check-proc (current-buffer)) + (insert skewer-repl-welcome) + (start-process "skewer-repl" (current-buffer) nil) + (set-process-query-on-exit-flag (skewer-repl-process) nil) + (goto-char (point-max)) + (set (make-local-variable 'comint-inhibit-carriage-motion) t) + (comint-output-filter (skewer-repl-process) skewer-repl-prompt) + (set-process-filter (skewer-repl-process) 'comint-output-filter))) + +(defun skewer-repl-toggle-strict-mode () + "Toggle strict mode for expressions evaluated by the REPL." + (interactive) + (setq skewer-repl-strict-p (not skewer-repl-strict-p)) + (message "REPL strict mode %s" + (if skewer-repl-strict-p "enabled" "disabled"))) + +(defun skewer-input-sender (_ input) + "REPL comint handler." + (skewer-eval input 'skewer-post-repl + :verbose t :strict skewer-repl-strict-p)) + +(defun skewer-post-repl (result) + "Callback for reporting results in the REPL." + (let ((buffer (get-buffer "*skewer-repl*")) + (output (cdr (assoc 'value result)))) + (when buffer + (with-current-buffer buffer + (comint-output-filter (skewer-repl-process) + (concat output "\n" skewer-repl-prompt)))))) + +(defvar skewer-repl-types + '(("log" . skewer-repl-log-face) + ("error" . skewer-error-face)) + "Faces to use for different types of log messages.") + +(defun skewer-log-filename (log) + "Create a log string for the source file in LOG if present." + (let ((name (cdr (assoc 'filename log))) + (line (cdr (assoc 'line log))) + (column (cdr (assoc 'column log)))) + (when name + (concat (format "\n at %s:%s" name line) + (if column (format ":%s" column)))))) + +(defun skewer-post-log (log) + "Callback for logging messages to the REPL." + (let* ((buffer (get-buffer "*skewer-repl*")) + (face (cdr (assoc (cdr (assoc 'type log)) skewer-repl-types))) + (value (or (cdr (assoc 'value log)) "")) + (output (propertize value 'font-lock-face face))) + (when buffer + (with-current-buffer buffer + (save-excursion + (goto-char (point-max)) + (forward-line 0) + (if (bobp) + (insert (concat output (skewer-log-filename log) "\n")) + (backward-char) + (insert (concat "\n" output (skewer-log-filename log))))))))) + +(defcustom skewer-path-strip-level 1 + "Number of folders which will be stripped from url when discovering paths. +Use this to limit path matching to files in your filesystem. You +may want to add some folders to `compilation-search-path', so +matched files can be found." + :type 'number + :group 'skewer) + +(defun skewer-repl-mode-compilation-shell-hook () + "Setup compilation shell minor mode for highlighting files" + (let ((error-re (format "^[ ]*at https?://[^/]+/\\(?:[^/]+/\\)\\{%d\\}\\([^:?#]+\\)\\(?:[?#][^:]*\\)?:\\([[:digit:]]+\\)\\(?::\\([[:digit:]]+\\)\\)?$" skewer-path-strip-level))) + (setq-local compilation-error-regexp-alist `((,error-re 1 2 3 2)))) + (compilation-shell-minor-mode 1)) + +;;;###autoload +(defun skewer-repl--response-hook (response) + "Catches all browser messages logging some to the REPL." + (let ((type (cdr (assoc 'type response)))) + (when (member type '("log" "error")) + (skewer-post-log response)))) + +;;;###autoload +(defun skewer-repl () + "Start a JavaScript REPL to be evaluated in the visiting browser." + (interactive) + (when (not (get-buffer "*skewer-repl*")) + (with-current-buffer (get-buffer-create "*skewer-repl*") + (skewer-repl-mode))) + (pop-to-buffer (get-buffer "*skewer-repl*"))) + +(defun company-skewer-repl (command &optional arg &rest _args) + "Skewerl REPL backend for company-mode. +See `company-backends' for more info about COMMAND and ARG." + (interactive (list 'interactive)) + (cl-case command + (interactive + (with-no-warnings ;; opportunistic use of company-mode + (company-begin-backend 'company-skewer-repl))) + (prefix (skewer-repl-company-prefix)) + (ignore-case t) + (sorted t) + (candidates (cons :async + (lambda (callback) + (skewer-repl-get-completions arg callback)))))) + +(defun skewer-repl-get-completions (arg callback) + "Get the completion list matching the prefix ARG. +Evaluate CALLBACK with the completion candidates." + (let* ((expression (skewer-repl--get-completion-expression arg)) + (pattern (if expression + (substring arg (1+ (length expression))) + arg))) + (skewer-eval (or expression "window") + (lambda (result) + (cl-loop with value = (cdr (assoc 'value result)) + for key being the elements of value + when expression + collect (concat expression "." key) into results + else + collect key into results + finally (funcall callback results))) + :type "completions" + :extra `((regexp . ,pattern))))) + +(defun skewer-repl--get-completion-expression (arg) + "Get completion expression from ARG." + (let ((components (split-string arg "\\."))) + (when (> (length components) 1) + (mapconcat #'identity (cl-subseq components 0 -1) ".")))) + +(defun skewer-repl-company-prefix () + "Prefix for company." + (and (eq major-mode 'skewer-repl-mode) + (or (with-no-warnings ;; opportunistic use of company-mode + (company-grab-symbol-cons "\\." 1)) + 'stop))) + +;;;###autoload +(eval-after-load 'skewer-mode + '(progn + (add-hook 'skewer-response-hook #'skewer-repl--response-hook) + (add-hook 'skewer-repl-mode-hook #'skewer-repl-mode-compilation-shell-hook) + (define-key skewer-mode-map (kbd "C-c C-z") #'skewer-repl))) + +(provide 'skewer-repl) + +;;; skewer-repl.el ends here diff --git a/elpa/skewer-mode-20200304.1142/skewer-repl.elc b/elpa/skewer-mode-20200304.1142/skewer-repl.elc new file mode 100644 index 0000000..0f0560b Binary files /dev/null and b/elpa/skewer-mode-20200304.1142/skewer-repl.elc differ diff --git a/elpa/skewer-mode-20200304.1142/skewer-setup.el b/elpa/skewer-mode-20200304.1142/skewer-setup.el new file mode 100644 index 0000000..77aea1b --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-setup.el @@ -0,0 +1,21 @@ +;;; skewer-setup.el --- automatic setup for the Skewer minor modes -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This exists as a separate file so that Skewer need not be fully +;; loaded just to use this setup function. + +;;; Code: + +;;;###autoload +(defun skewer-setup () + "Fully integrate Skewer into js2-mode, css-mode, and html-mode buffers." + (add-hook 'js2-mode-hook 'skewer-mode) + (add-hook 'css-mode-hook 'skewer-css-mode) + (add-hook 'html-mode-hook 'skewer-html-mode)) + +(provide 'skewer-setup) + +;;; skewer-setup.el ends here diff --git a/elpa/skewer-mode-20200304.1142/skewer-setup.elc b/elpa/skewer-mode-20200304.1142/skewer-setup.elc new file mode 100644 index 0000000..10c79d0 Binary files /dev/null and b/elpa/skewer-mode-20200304.1142/skewer-setup.elc differ 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 + * only 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 = "#"; + 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); + } +} -- cgit v1.2.1