Skip to content

Instantly share code, notes, and snippets.

@kbauer
Last active September 21, 2023 07:53
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kbauer/68db9deb88ae170a8f18a2b84de74fb6 to your computer and use it in GitHub Desktop.
Save kbauer/68db9deb88ae170a8f18a2b84de74fb6 to your computer and use it in GitHub Desktop.
Emacs-lisp command for compiling a javascript file into a bookmarklet, by stripping unnecessary characters and adding a wrapper.
;;; myjs-compile-bookmarklet.el --- strip javascript code as bookmarklet -*- lexical-binding: t; coding: utf-8; lisp-indent-offset: nil; -*-
;; Copyright (C) 2018 Klaus-Dieter Bauer
;; Author: Klaus-Dieter Bauer <kdb.devel@gmail.com>
;; Keywords: javascript, bookmarklet
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; See docstrings.
;;
;; Published as https://gist.github.com/kbauer/68db9deb88ae170a8f18a2b84de74fb6
;;; Code:
(defconst myjs-compile-bookmarklet-metachars
"(){}[];+-*/.,%>=<!\"'`|&"
"A string of operator-only characters.
Used for stripping unneeded whitespace between operators and identifiers.")
(defun myjs-compile-bookmarklet ()
"Copy current buffer as bookmarklet.
In detail this means putting a copy of `buffer-string'
into `buffer-file-name'.txt and the (clipboard), where
- Comments and syntactically irrelevant whitespace are
stripped, to reduce character count and to reduce the code
to a single-line string.
- A wrapper is added, that contains the file name as a comment,
the `javascript:' prefix, required for executing javascript from
bookmarks, and a wrapper-function, that prevents the bookmarklet
from cluttering the namespace of the function.
==== KNOWN ISSUES ====
- Some whitespace may be incorrectly judged as syntactically irrelevant,
e.g. whitespace in string-literals. Workaround: Avoid such strings,
its only a bookmarklet after all."
(interactive)
(when (buffer-file-name)
(save-buffer))
(let ((raw-string (buffer-string))
(file-name (buffer-file-name)))
(with-temp-buffer
(insert raw-string)
(goto-char (point-min))
(save-excursion
(while (search-forward-regexp (rx "/*" (*? anything) "*/") nil t)
(replace-match "")))
(save-excursion
(while (re-search-forward "//.*" nil t)
(replace-match " ")))
(save-excursion
(while (re-search-forward "[[:blank:]\n]+" nil t)
(unless (eq (save-excursion (forward-char -1) (face-at-point))
'font-lock-string-face)
(replace-match " "))))
;; Removal of whitespace separating punctuation outside strings.
(save-excursion
(while (search-forward-regexp " " nil t)
(let ((a (char-after (point)))
(b (char-before (- (point) 1)))
(meta (append myjs-compile-bookmarklet-metachars nil)))
(when (and (or (memq a meta) (memq b meta))
(not (eq (save-excursion (forward-char -1) (face-at-point))
'font-lock-string-face)))
(delete-char -1)))))
;; Add "private namespace" and "javascript:" wrappers.
(insert
"javascript:"
(if file-name
(concat "/* " (file-name-nondirectory file-name) " */")
"")
"(function(){")
(goto-char (point-max))
(while (memq (char-before) '(?\ ?\n ?\t))
(backward-delete-char 1))
(insert "})();undefined;")
;; Export to .txt file.
(when file-name
(let ((txt-file-name (concat file-name ".txt"))
(contents (buffer-string)))
(with-temp-buffer
(when (file-exists-p txt-file-name)
(insert-file-contents txt-file-name))
(if (string= contents (buffer-string))
(message "%s: Unchanged contents, not updated." txt-file-name)
(erase-buffer)
(insert contents)
(write-file txt-file-name)))
(message "Exported bookmarklet to `%s'" txt-file-name)))
;; Copy to kill-ring
(message "Copied bookmarklet to kill-ring (%d characters)" (point-max))
(kill-region (point-min) (point-max))
;; Display result
(with-selected-window (display-buffer (get-buffer-create "*myjs:bookmarklet*"))
(erase-buffer)
(yank)
(insert "\n\n"
(format "Copied bookmarklet to kill-ring (%d characters)" (point-max)))
(unless (eq major-mode 'js-mode)
(js-mode)))))
nil)
(provide 'myjs-compile-bookmarklet)
;;; myjs-compile-bookmarklet.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment