]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
update atomic-chrome
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Thu, 23 Nov 2023 18:58:35 +0000 (19:58 +0100)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Thu, 23 Nov 2023 19:08:41 +0000 (20:08 +0100)
Signed-off-by: Andreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
elpa/atomic-chrome-20220723.113/atomic-chrome-autoloads.el [deleted file]
elpa/atomic-chrome-20220723.113/atomic-chrome-pkg.el [deleted file]
elpa/atomic-chrome-20220723.113/atomic-chrome.el [deleted file]
elpa/atomic-chrome-20230304.112/atomic-chrome-autoloads.el [new file with mode: 0644]
elpa/atomic-chrome-20230304.112/atomic-chrome-pkg.el [new file with mode: 0644]
elpa/atomic-chrome-20230304.112/atomic-chrome.el [new file with mode: 0644]

diff --git a/elpa/atomic-chrome-20220723.113/atomic-chrome-autoloads.el b/elpa/atomic-chrome-20220723.113/atomic-chrome-autoloads.el
deleted file mode 100644 (file)
index 9dcc5de..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-;;; atomic-chrome-autoloads.el --- automatically extracted autoloads  -*- lexical-binding: t -*-
-;;
-;;; Code:
-
-(add-to-list 'load-path (directory-file-name
-                         (or (file-name-directory #$) (car load-path))))
-
-\f
-;;;### (autoloads nil "atomic-chrome" "atomic-chrome.el" (0 0 0 0))
-;;; Generated autoloads from atomic-chrome.el
-
-(autoload 'atomic-chrome-start-server "atomic-chrome" "\
-Start websocket server for atomic-chrome.
-Fails silently if a server is already running." t nil)
-
-(autoload 'atomic-chrome-stop-server "atomic-chrome" "\
-Stop websocket server for atomic-chrome." t nil)
-
-(register-definition-prefixes "atomic-chrome" '("atomic-chrome-" "global-atomic-chrome-edit-mode"))
-
-;;;***
-\f
-;; Local Variables:
-;; version-control: never
-;; no-byte-compile: t
-;; no-update-autoloads: t
-;; coding: utf-8
-;; End:
-;;; atomic-chrome-autoloads.el ends here
diff --git a/elpa/atomic-chrome-20220723.113/atomic-chrome-pkg.el b/elpa/atomic-chrome-20220723.113/atomic-chrome-pkg.el
deleted file mode 100644 (file)
index ec26107..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-;;; Generated package description from atomic-chrome.el  -*- no-byte-compile: t -*-
-(define-package "atomic-chrome" "20220723.113" "Edit Chrome text area with Emacs using Atomic Chrome" '((emacs "24.4") (let-alist "1.0.4") (websocket "1.4")) :commit "061958ab96c31085b5daf449b1d826b052777b59" :authors '(("alpha22jp" . "alpha22jpNOSPAMgmail.com")) :maintainer '("alpha22jp" . "alpha22jpNOSPAMgmail.com") :keywords '("chrome" "edit" "textarea") :url "https://github.com/alpha22jp/atomic-chrome")
diff --git a/elpa/atomic-chrome-20220723.113/atomic-chrome.el b/elpa/atomic-chrome-20220723.113/atomic-chrome.el
deleted file mode 100644 (file)
index 025428c..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-;;; atomic-chrome.el --- Edit Chrome text area with Emacs using Atomic Chrome
-
-;; Copyright (C) 2016 alpha22jp <alpha22jpNOSPAMgmail.com>
-
-;; Author: alpha22jp <alpha22jpNOSPAMgmail.com>
-;; Package-Requires: ((emacs "24.4") (let-alist "1.0.4") (websocket "1.4"))
-;; Package-Version: 20220723.113
-;; Package-Commit: 061958ab96c31085b5daf449b1d826b052777b59
-;; Keywords: chrome edit textarea
-;; URL: https://github.com/alpha22jp/atomic-chrome
-;; Version: 2.0.0
-
-;; 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 2 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, write to the Free Software Foundation, Inc., 51
-;; Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-;;; Commentary:
-
-;; This is the Emacs version of Atomic Chrome which is an extension for Google
-;; Chrome browser that allows you to edit text areas of the browser in Emacs.
-;;
-;; It's similar to Edit with Emacs, but has some advantages as below with the
-;; help of websocket.
-;;
-;; * Live update
-;;   The input on Emacs is reflected to the browser instantly and continuously.
-;; * Bidirectional communication
-;;   You can edit both on the browser and Emacs, they are synced to the same.
-;;
-;; Firefox is supported via the GhostText browser addon.
-
-;;; Code:
-
-(eval-when-compile (require 'cl-lib))
-(require 'json)
-(require 'let-alist)
-(eval-when-compile (require 'subr-x))
-(require 'websocket)
-
-(defgroup atomic-chrome nil
-  "Edit browser text area with Emacs using Atomic Chrome or GhostText."
-  :prefix "atomic-chrome-"
-  :group 'applications)
-
-(defcustom atomic-chrome-extension-type-list '(atomic-chrome ghost-text)
-  "List of browser extension type available."
-  :type '(repeat (choice (const :tag "Atomic Chrome" atomic-chrome)
-                         (const :tag "Ghost Text" ghost-text)))
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-buffer-open-style 'split
-  "Specify the style to open new buffer for editing."
-  :type '(choice (const :tag "Open buffer with full window" full)
-                 (const :tag "Open buffer with splitted window" split)
-                 (const :tag "Open buffer with new frame" frame))
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-buffer-frame-width 80
-  "Width of editing buffer frame."
-  :type 'integer
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-buffer-frame-height 25
-  "Height of editing buffer frame."
-  :type 'integer
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-server-ghost-text-port 4001
-  "HTTP server port for Ghost Text."
-  :type 'integer
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-enable-auto-update t
-  "If non-nil, edit on Emacs is reflected to the browser instantly.
-If nil, you need to type \"C-cC-s\" manually."
-  :type 'boolean
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-enable-bidirectional-edit t
-  "If non-nil, you can edit both on the browser text area and Emacs.
-If nil, edit on browser is ignored while editing on Emacs."
-  :type 'boolean
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-default-major-mode 'text-mode
-  "Default major mode for editing buffer."
-  :type 'function
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-url-major-mode-alist nil
-  "Association list to select a major mode for a website.
-Relates URL (or, for GhostText, hostname) regular expressions to
-corresponding major modes."
-  :type '(alist :key-type (regexp :tag "regexp")
-                :value-type (function :tag "major mode"))
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-edit-mode-hook nil
-  "Customizable hook which run when the editing buffer is created."
-  :type 'hook
-  :group 'atomic-chrome)
-
-(defcustom atomic-chrome-edit-done-hook nil
-  "Customizable hook which run when the editing buffer is closed."
-  :type 'hook
-  :group 'atomic-chrome)
-
-(defvar atomic-chrome-server-atomic-chrome nil
-  "Websocket server connection handle for Atomic Chrome.")
-
-(defvar atomic-chrome-server-ghost-text nil
-  "Websocket server connection handle for Ghost Text.")
-
-(defvar atomic-chrome-buffer-table (make-hash-table :test 'equal)
-  "Hash table of editing buffer and its assciated data.
-Each element has a list consisting of (websocket, frame).")
-
-(defun atomic-chrome-get-websocket (buffer)
-  "Look up websocket associated with buffer BUFFER.
-Looks in `atomic-chrome-buffer-table'."
-  (nth 0 (gethash buffer atomic-chrome-buffer-table)))
-
-(defun atomic-chrome-get-frame (buffer)
-  "Look up frame associated with buffer BUFFER.
-Looks in `atomic-chrome-buffer-table'."
-  (nth 1 (gethash buffer atomic-chrome-buffer-table)))
-
-(defun atomic-chrome-get-buffer-by-socket (socket)
-  "Look up buffer which is associated to the websocket SOCKET.
-Looks in `atomic-chrome-buffer-table'."
-  (let (buffer)
-    (cl-loop for key being the hash-keys of atomic-chrome-buffer-table
-             using (hash-values val)
-             do (when (equal (nth 0 val) socket) (setq buffer key)))
-    buffer))
-
-(defun atomic-chrome-close-connection ()
-  "Close client connection associated with current buffer."
-  (let ((socket (atomic-chrome-get-websocket (current-buffer))))
-    (when socket
-      (remhash (current-buffer) atomic-chrome-buffer-table)
-      (websocket-close socket))))
-
-(defun atomic-chrome-send-buffer-text ()
-  "Send request to update text with current buffer content."
-  (interactive)
-  (let ((socket (atomic-chrome-get-websocket (current-buffer)))
-        (text (buffer-substring-no-properties (point-min) (point-max))))
-    (when (and socket text)
-      (websocket-send-text
-       socket
-       (json-encode
-        (if (eq (websocket-server-conn socket) atomic-chrome-server-ghost-text)
-            (list (cons "text" text))
-          (list '("type" . "updateText")
-                (cons "payload" (list (cons "text" text))))))))
-    (set-buffer-modified-p nil)))
-
-(defun atomic-chrome-set-major-mode (url)
-  "Set major mode for editing buffer depending on URL.
-`atomic-chrome-url-major-mode-alist' can be used to select major mode.
-The specified major mode is used if URL matches to one of the alist,
-otherwise fallback to `atomic-chrome-default-major-mode'"
-  (funcall (or (and url (assoc-default url
-                                       atomic-chrome-url-major-mode-alist
-                                       'string-match))
-               atomic-chrome-default-major-mode)))
-
-(defun atomic-chrome-show-edit-buffer (buffer title)
-  "Show editing buffer BUFFER.
-Either creates a frame with title TITLE, or raises the selected
-frame, depending on `atomic-chrome-buffer-open-style'."
-  (let ((edit-frame nil)
-        (frame-params (list (cons 'name (format "Atomic Chrome: %s" title))
-                            (cons 'width atomic-chrome-buffer-frame-width)
-                            (cons 'height atomic-chrome-buffer-frame-height))))
-    (when (eq atomic-chrome-buffer-open-style 'frame)
-      (setq edit-frame
-            (cond
-             ((memq window-system '(pgtk x))
-              (if (string-match-p "wayland" x-display-name)
-                  (make-frame frame-params)
-                (make-frame-on-display (getenv "DISPLAY") frame-params)))
-             ;; Avoid using make-frame-on-display for Mac OS
-             ((memq window-system '(ns mac))
-              (make-frame frame-params))
-             ((memq window-system '(w32))
-              (make-frame-on-display "w32" frame-params))
-             (t
-              (make-frame frame-params))))
-      (select-frame edit-frame))
-    (if (eq atomic-chrome-buffer-open-style 'split)
-        (pop-to-buffer buffer)
-      (switch-to-buffer buffer))
-    (raise-frame edit-frame)
-    (select-frame-set-input-focus (window-frame (selected-window)))
-    edit-frame))
-
-(defun atomic-chrome-create-buffer (socket url title text)
-  "Create buffer associated with websocket specified by SOCKET.
-URL is used to determine the major mode of the buffer created,
-TITLE is used for the buffer name and TEXT is inserted to the buffer."
-  (let ((buffer (generate-new-buffer (if (string-empty-p title) "No title" title))))
-    (with-current-buffer buffer
-      (puthash buffer
-             (list socket (atomic-chrome-show-edit-buffer buffer title))
-             atomic-chrome-buffer-table)
-      (atomic-chrome-set-major-mode url)
-      (insert text))))
-
-(defun atomic-chrome-close-edit-buffer (buffer)
-  "Close buffer BUFFER if it's one of Atomic Chrome edit buffers."
-  (let ((frame (atomic-chrome-get-frame buffer))
-        (window (get-buffer-window buffer)))
-    (with-current-buffer buffer
-      (save-restriction
-        (run-hooks 'atomic-chrome-edit-done-hook)
-        (when frame (delete-frame frame))
-        (if (and (eq atomic-chrome-buffer-open-style 'split)
-                 window)
-            (quit-window t window)
-          (kill-buffer buffer))))))
-
-(defun atomic-chrome-close-current-buffer ()
-  "Close current buffer and connection from client."
-  (interactive)
-  (when (or (not (buffer-modified-p))
-           (yes-or-no-p "Buffer has not been saved, close anyway? "))
-    (atomic-chrome-close-edit-buffer (current-buffer))))
-
-(defun atomic-chrome-update-buffer (socket text)
-  "Update text on buffer associated with SOCKET to TEXT."
-  (let ((buffer (atomic-chrome-get-buffer-by-socket socket)))
-    (when buffer
-      (with-current-buffer buffer
-        (erase-buffer)
-        (insert text)))))
-
-(defun atomic-chrome-on-message (socket frame)
-  "Handle data received from the websocket client specified by SOCKET.
-FRAME holds the raw data received."
-  (let ((msg (json-read-from-string
-              (decode-coding-string
-               (encode-coding-string (websocket-frame-payload frame) 'utf-8)
-              'utf-8))))
-    (let-alist msg
-      (if (eq (websocket-server-conn socket) atomic-chrome-server-ghost-text)
-          (if (atomic-chrome-get-buffer-by-socket socket)
-              (atomic-chrome-update-buffer socket .text)
-            (atomic-chrome-create-buffer socket .url .title .text))
-        (cond ((string= .type "register")
-               (atomic-chrome-create-buffer socket .payload.url .payload.title .payload.text))
-              ((string= .type "updateText")
-               (when atomic-chrome-enable-bidirectional-edit
-                 (atomic-chrome-update-buffer socket .payload.text))))))))
-
-(defun atomic-chrome-on-close (socket)
-  "Function to handle request from client to close websocket SOCKET."
-  (let ((buffer (atomic-chrome-get-buffer-by-socket socket)))
-    (when buffer (atomic-chrome-close-edit-buffer buffer))))
-
-(defvar atomic-chrome-edit-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "C-c C-s") 'atomic-chrome-send-buffer-text)
-    (define-key map (kbd "C-c C-c") 'atomic-chrome-close-current-buffer)
-    map)
-  "Keymap for minor mode `atomic-chrome-edit-mode'.")
-
-(define-minor-mode atomic-chrome-edit-mode
-  "Minor mode enabled on buffers opened by Emacs Atomic Chrome server."
-  :group 'atomic-chrome
-  :lighter " AtomicChrome"
-  :init-value nil
-  :keymap atomic-chrome-edit-mode-map
-  (when atomic-chrome-edit-mode
-    (add-hook 'kill-buffer-hook 'atomic-chrome-close-connection nil t)
-    (when atomic-chrome-enable-auto-update
-      (add-hook 'post-command-hook 'atomic-chrome-send-buffer-text nil t))))
-
-(defun atomic-chrome-turn-on-edit-mode ()
-  "Turn on `atomic-chrome-edit-mode' if the buffer is an editing buffer."
-  (when (gethash (current-buffer) atomic-chrome-buffer-table)
-    (atomic-chrome-edit-mode t)))
-
-(define-global-minor-mode global-atomic-chrome-edit-mode
-  atomic-chrome-edit-mode atomic-chrome-turn-on-edit-mode)
-
-(defun atomic-chrome-start-websocket-server (port)
-  "Create websocket server on port PORT."
-  (websocket-server
-   port
-   :host 'local
-   :on-message #'atomic-chrome-on-message
-   :on-open nil
-   :on-close #'atomic-chrome-on-close))
-
-(defun atomic-chrome-start-httpd ()
-  "Start the HTTP server for Ghost Text query."
-  (interactive)
-  (make-network-process
-   :name "atomic-chrome-httpd"
-   :family 'ipv4
-   :host 'local
-   :service atomic-chrome-server-ghost-text-port
-   :filter 'atomic-chrome-httpd-process-filter
-   :filter-multibyte nil
-   :server t
-   :noquery t))
-
-(defun atomic-chrome-normalize-header (header)
-  "Destructively capitalize the components of HEADER."
-  (mapconcat #'capitalize (split-string header "-") "-"))
-
-(defun atomic-chrome-httpd-parse-string (string)
-  "Parse client http header STRING into alist."
-  (let* ((lines (split-string string "[\n\r]+"))
-         (req (list (split-string (car lines))))
-         (post (cadr (split-string string "\r\n\r\n"))))
-    (dolist (line (butlast (cdr lines)))
-      (push (list (atomic-chrome-normalize-header (car (split-string line ": ")))
-                  (mapconcat #'identity
-                             (cdr (split-string line ": ")) ": "))
-            req))
-    (push (list "Content" post) req)
-    (reverse req)))
-
-(defun atomic-chrome-httpd-process-filter (proc string)
-  "Process filter of PROC which run each time client make a request.
-STRING is the string process received."
-  (setf string (concat (process-get proc :previous-string) string))
-  (let* ((request (atomic-chrome-httpd-parse-string string))
-         (content-length (cadr (assoc "Content-Length" request)))
-         (uri (cl-cadar request))
-         (content (cadr (assoc "Content" request))))
-    (if (and content-length
-             (< (string-bytes content) (string-to-number content-length)))
-        (process-put proc :previous-string string)
-      (atomic-chrome-httpd-send-response proc))))
-
-(defun atomic-chrome-httpd-send-response (proc)
-  "Send an HTTP 200 OK response back to process PROC."
-  (when (processp proc)
-    (unless atomic-chrome-server-ghost-text
-      (setq atomic-chrome-server-ghost-text
-            (atomic-chrome-start-websocket-server 64293)))
-    (let ((header "HTTP/1.0 200 OK\nContent-Type: application/json\n")
-          (body (json-encode '(:ProtocolVersion 1 :WebSocketPort 64293))))
-      (process-send-string proc (concat header "\n" body))
-      (process-send-eof proc))))
-
-;;;###autoload
-(defun atomic-chrome-start-server ()
-  "Start websocket server for atomic-chrome.
-Fails silently if a server is already running."
-  (interactive)
-  (ignore-errors
-      (progn
-        (and (not atomic-chrome-server-atomic-chrome)
-             (memq 'atomic-chrome atomic-chrome-extension-type-list)
-             (setq atomic-chrome-server-atomic-chrome
-                   (atomic-chrome-start-websocket-server 64292)))
-        (and (not (process-status "atomic-chrome-httpd"))
-             (memq 'ghost-text atomic-chrome-extension-type-list)
-             (atomic-chrome-start-httpd))
-        (global-atomic-chrome-edit-mode 1))))
-
-;;;###autoload
-(defun atomic-chrome-stop-server nil
-  "Stop websocket server for atomic-chrome."
-  (interactive)
-  (when atomic-chrome-server-atomic-chrome
-    (websocket-server-close atomic-chrome-server-atomic-chrome)
-    (setq atomic-chrome-server-atomic-chrome nil))
-  (when atomic-chrome-server-ghost-text
-    (websocket-server-close atomic-chrome-server-ghost-text)
-    (setq atomic-chrome-server-ghost-text nil))
-  (when (process-status "atomic-chrome-httpd")
-    (delete-process "atomic-chrome-httpd"))
-  (global-atomic-chrome-edit-mode 0))
-
-(provide 'atomic-chrome)
-
-;;; atomic-chrome.el ends here
diff --git a/elpa/atomic-chrome-20230304.112/atomic-chrome-autoloads.el b/elpa/atomic-chrome-20230304.112/atomic-chrome-autoloads.el
new file mode 100644 (file)
index 0000000..c40bee7
--- /dev/null
@@ -0,0 +1,33 @@
+;;; atomic-chrome-autoloads.el --- automatically extracted autoloads  -*- lexical-binding: t -*-
+;;
+;;; Code:
+
+(add-to-list 'load-path (directory-file-name
+                         (or (file-name-directory #$) (car load-path))))
+
+\f
+;;;### (autoloads nil "atomic-chrome" "atomic-chrome.el" (0 0 0 0))
+;;; Generated autoloads from atomic-chrome.el
+
+(autoload 'atomic-chrome-start-server "atomic-chrome" "\
+Start websocket server for atomic-chrome.
+Fails silently if a server is already running." t nil)
+
+(autoload 'atomic-chrome-stop-server "atomic-chrome" "\
+Stop websocket server for atomic-chrome." t nil)
+
+(register-definition-prefixes "atomic-chrome" '("atomic-chrome-" "global-atomic-chrome-edit-mode"))
+
+;;;***
+\f
+;;;### (autoloads nil nil ("atomic-chrome-pkg.el") (0 0 0 0))
+
+;;;***
+\f
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; coding: utf-8
+;; End:
+;;; atomic-chrome-autoloads.el ends here
diff --git a/elpa/atomic-chrome-20230304.112/atomic-chrome-pkg.el b/elpa/atomic-chrome-20230304.112/atomic-chrome-pkg.el
new file mode 100644 (file)
index 0000000..3729c22
--- /dev/null
@@ -0,0 +1,16 @@
+(define-package "atomic-chrome" "20230304.112" "Edit Chrome text area with Emacs using Atomic Chrome"
+  '((emacs "24.4")
+    (let-alist "1.0.4")
+    (websocket "1.4"))
+  :commit "f1b077be7e414f457191d72dcf5eedb4371f9309" :authors
+  '(("alpha22jp" . "alpha22jpNOSPAMgmail.com"))
+  :maintainers
+  '(("alpha22jp" . "alpha22jpNOSPAMgmail.com"))
+  :maintainer
+  '("alpha22jp" . "alpha22jpNOSPAMgmail.com")
+  :keywords
+  '("chrome" "edit" "textarea")
+  :url "https://github.com/alpha22jp/atomic-chrome")
+;; Local Variables:
+;; no-byte-compile: t
+;; End:
diff --git a/elpa/atomic-chrome-20230304.112/atomic-chrome.el b/elpa/atomic-chrome-20230304.112/atomic-chrome.el
new file mode 100644 (file)
index 0000000..9affc7c
--- /dev/null
@@ -0,0 +1,391 @@
+;;; atomic-chrome.el --- Edit Chrome text area with Emacs using Atomic Chrome
+
+;; Copyright (C) 2016 alpha22jp <alpha22jpNOSPAMgmail.com>
+
+;; Author: alpha22jp <alpha22jpNOSPAMgmail.com>
+;; Package-Requires: ((emacs "24.4") (let-alist "1.0.4") (websocket "1.4"))
+;; Keywords: chrome edit textarea
+;; URL: https://github.com/alpha22jp/atomic-chrome
+;; Version: 2.0.0
+
+;; 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 2 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, write to the Free Software Foundation, Inc., 51
+;; Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This is the Emacs version of Atomic Chrome which is an extension for Google
+;; Chrome browser that allows you to edit text areas of the browser in Emacs.
+;;
+;; It's similar to Edit with Emacs, but has some advantages as below with the
+;; help of websocket.
+;;
+;; * Live update
+;;   The input on Emacs is reflected to the browser instantly and continuously.
+;; * Bidirectional communication
+;;   You can edit both on the browser and Emacs, they are synced to the same.
+;;
+;; Firefox is supported via the GhostText browser addon.
+
+;;; Code:
+
+(eval-when-compile (require 'cl-lib))
+(require 'json)
+(require 'let-alist)
+(eval-when-compile (require 'subr-x))
+(require 'websocket)
+
+(defgroup atomic-chrome nil
+  "Edit browser text area with Emacs using Atomic Chrome or GhostText."
+  :prefix "atomic-chrome-"
+  :group 'applications)
+
+(defcustom atomic-chrome-extension-type-list '(atomic-chrome ghost-text)
+  "List of browser extension type available."
+  :type '(repeat (choice (const :tag "Atomic Chrome" atomic-chrome)
+                         (const :tag "Ghost Text" ghost-text)))
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-buffer-open-style 'split
+  "Specify the style to open new buffer for editing."
+  :type '(choice (const :tag "Open buffer with full window" full)
+                 (const :tag "Open buffer with splitted window" split)
+                 (const :tag "Open buffer with new frame" frame))
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-buffer-frame-width 80
+  "Width of editing buffer frame."
+  :type 'integer
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-buffer-frame-height 25
+  "Height of editing buffer frame."
+  :type 'integer
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-server-ghost-text-port 4001
+  "HTTP server port for Ghost Text."
+  :type 'integer
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-enable-auto-update t
+  "If non-nil, edit on Emacs is reflected to the browser instantly.
+If nil, you need to type \"C-cC-s\" manually."
+  :type 'boolean
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-enable-bidirectional-edit t
+  "If non-nil, you can edit both on the browser text area and Emacs.
+If nil, edit on browser is ignored while editing on Emacs."
+  :type 'boolean
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-default-major-mode 'text-mode
+  "Default major mode for editing buffer."
+  :type 'function
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-url-major-mode-alist nil
+  "Association list to select a major mode for a website.
+Relates URL (or, for GhostText, hostname) regular expressions to
+corresponding major modes."
+  :type '(alist :key-type (regexp :tag "regexp")
+                :value-type (function :tag "major mode"))
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-edit-mode-hook nil
+  "Customizable hook which run when the editing buffer is created."
+  :type 'hook
+  :group 'atomic-chrome)
+
+(defcustom atomic-chrome-edit-done-hook nil
+  "Customizable hook which run when the editing buffer is closed."
+  :type 'hook
+  :group 'atomic-chrome)
+
+(defvar atomic-chrome-server-atomic-chrome nil
+  "Websocket server connection handle for Atomic Chrome.")
+
+(defvar atomic-chrome-server-ghost-text nil
+  "Websocket server connection handle for Ghost Text.")
+
+(defvar atomic-chrome-buffer-table (make-hash-table :test 'equal)
+  "Hash table of editing buffer and its assciated data.
+Each element has a list consisting of (websocket, frame).")
+
+(defun atomic-chrome-get-websocket (buffer)
+  "Look up websocket associated with buffer BUFFER.
+Looks in `atomic-chrome-buffer-table'."
+  (nth 0 (gethash buffer atomic-chrome-buffer-table)))
+
+(defun atomic-chrome-get-frame (buffer)
+  "Look up frame associated with buffer BUFFER.
+Looks in `atomic-chrome-buffer-table'."
+  (nth 1 (gethash buffer atomic-chrome-buffer-table)))
+
+(defun atomic-chrome-get-buffer-by-socket (socket)
+  "Look up buffer which is associated to the websocket SOCKET.
+Looks in `atomic-chrome-buffer-table'."
+  (let (buffer)
+    (cl-loop for key being the hash-keys of atomic-chrome-buffer-table
+             using (hash-values val)
+             do (when (equal (nth 0 val) socket) (setq buffer key)))
+    buffer))
+
+(defun atomic-chrome-close-connection ()
+  "Close client connection associated with current buffer."
+  (let ((socket (atomic-chrome-get-websocket (current-buffer))))
+    (when socket
+      (remhash (current-buffer) atomic-chrome-buffer-table)
+      (websocket-close socket))))
+
+(defun atomic-chrome-send-buffer-text ()
+  "Send request to update text with current buffer content."
+  (interactive)
+  (let ((socket (atomic-chrome-get-websocket (current-buffer)))
+        (text (buffer-substring-no-properties (point-min) (point-max))))
+    (when (and socket text)
+      (websocket-send-text
+       socket
+       (json-encode
+        (if (eq (websocket-server-conn socket) atomic-chrome-server-ghost-text)
+            (list (cons "text" text))
+          (list '("type" . "updateText")
+                (cons "payload" (list (cons "text" text))))))))
+    (set-buffer-modified-p nil)))
+
+(defun atomic-chrome-set-major-mode (url)
+  "Set major mode for editing buffer depending on URL.
+`atomic-chrome-url-major-mode-alist' can be used to select major mode.
+The specified major mode is used if URL matches to one of the alist,
+otherwise fallback to `atomic-chrome-default-major-mode'"
+  (funcall (or (and url (assoc-default url
+                                       atomic-chrome-url-major-mode-alist
+                                       'string-match))
+               atomic-chrome-default-major-mode)))
+
+(defun atomic-chrome-show-edit-buffer (buffer title)
+  "Show editing buffer BUFFER.
+Either creates a frame with title TITLE, or raises the selected
+frame, depending on `atomic-chrome-buffer-open-style'."
+  (let ((edit-frame nil)
+        (frame-params (list (cons 'name (format "Atomic Chrome: %s" title))
+                            (cons 'width atomic-chrome-buffer-frame-width)
+                            (cons 'height atomic-chrome-buffer-frame-height))))
+    (when (eq atomic-chrome-buffer-open-style 'frame)
+      (setq edit-frame
+            (cond
+             ((memq window-system '(pgtk x))
+              (if (or (not x-display-name) (string-match-p "wayland" x-display-name))
+                  (make-frame frame-params)
+                (make-frame-on-display (getenv "DISPLAY") frame-params)))
+             ;; Avoid using make-frame-on-display for Mac OS
+             ((memq window-system '(ns mac))
+              (make-frame frame-params))
+             ((memq window-system '(w32))
+              (make-frame-on-display "w32" frame-params))
+             (t
+              (make-frame frame-params))))
+      (select-frame edit-frame))
+    (if (eq atomic-chrome-buffer-open-style 'split)
+        (pop-to-buffer buffer)
+      (switch-to-buffer buffer))
+    (raise-frame edit-frame)
+    (select-frame-set-input-focus (window-frame (selected-window)))
+    edit-frame))
+
+(defun atomic-chrome-create-buffer (socket url title text)
+  "Create buffer associated with websocket specified by SOCKET.
+URL is used to determine the major mode of the buffer created,
+TITLE is used for the buffer name and TEXT is inserted to the buffer."
+  (let ((buffer (generate-new-buffer (if (string-empty-p title) "No title" title))))
+    (with-current-buffer buffer
+      (puthash buffer
+             (list socket (atomic-chrome-show-edit-buffer buffer title))
+             atomic-chrome-buffer-table)
+      (atomic-chrome-set-major-mode url)
+      (insert text))))
+
+(defun atomic-chrome-close-edit-buffer (buffer)
+  "Close buffer BUFFER if it's one of Atomic Chrome edit buffers."
+  (let ((frame (atomic-chrome-get-frame buffer))
+        (window (get-buffer-window buffer)))
+    (with-current-buffer buffer
+      (save-restriction
+        (run-hooks 'atomic-chrome-edit-done-hook)
+        (when frame (delete-frame frame))
+        (if (and (eq atomic-chrome-buffer-open-style 'split)
+                 window)
+            (quit-window t window)
+          (kill-buffer buffer))))))
+
+(defun atomic-chrome-close-current-buffer ()
+  "Close current buffer and connection from client."
+  (interactive)
+  (when (or (not (buffer-modified-p))
+           (yes-or-no-p "Buffer has not been saved, close anyway? "))
+    (atomic-chrome-close-edit-buffer (current-buffer))))
+
+(defun atomic-chrome-update-buffer (socket text)
+  "Update text on buffer associated with SOCKET to TEXT."
+  (let ((buffer (atomic-chrome-get-buffer-by-socket socket)))
+    (when buffer
+      (with-current-buffer buffer
+        (erase-buffer)
+        (insert text)))))
+
+(defun atomic-chrome-on-message (socket frame)
+  "Handle data received from the websocket client specified by SOCKET.
+FRAME holds the raw data received."
+  (let ((msg (json-read-from-string
+              (decode-coding-string
+               (encode-coding-string (websocket-frame-payload frame) 'utf-8)
+              'utf-8))))
+    (let-alist msg
+      (if (eq (websocket-server-conn socket) atomic-chrome-server-ghost-text)
+          (if (atomic-chrome-get-buffer-by-socket socket)
+              (atomic-chrome-update-buffer socket .text)
+            (atomic-chrome-create-buffer socket .url .title .text))
+        (cond ((string= .type "register")
+               (atomic-chrome-create-buffer socket .payload.url .payload.title .payload.text))
+              ((string= .type "updateText")
+               (when atomic-chrome-enable-bidirectional-edit
+                 (atomic-chrome-update-buffer socket .payload.text))))))))
+
+(defun atomic-chrome-on-close (socket)
+  "Function to handle request from client to close websocket SOCKET."
+  (let ((buffer (atomic-chrome-get-buffer-by-socket socket)))
+    (when buffer (atomic-chrome-close-edit-buffer buffer))))
+
+(defvar atomic-chrome-edit-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "C-c C-s") 'atomic-chrome-send-buffer-text)
+    (define-key map (kbd "C-c C-c") 'atomic-chrome-close-current-buffer)
+    map)
+  "Keymap for minor mode `atomic-chrome-edit-mode'.")
+
+(define-minor-mode atomic-chrome-edit-mode
+  "Minor mode enabled on buffers opened by Emacs Atomic Chrome server."
+  :group 'atomic-chrome
+  :lighter " AtomicChrome"
+  :init-value nil
+  :keymap atomic-chrome-edit-mode-map
+  (when atomic-chrome-edit-mode
+    (add-hook 'kill-buffer-hook 'atomic-chrome-close-connection nil t)
+    (when atomic-chrome-enable-auto-update
+      (add-hook 'post-command-hook 'atomic-chrome-send-buffer-text nil t))))
+
+(defun atomic-chrome-turn-on-edit-mode ()
+  "Turn on `atomic-chrome-edit-mode' if the buffer is an editing buffer."
+  (when (gethash (current-buffer) atomic-chrome-buffer-table)
+    (atomic-chrome-edit-mode t)))
+
+(define-global-minor-mode global-atomic-chrome-edit-mode
+  atomic-chrome-edit-mode atomic-chrome-turn-on-edit-mode)
+
+(defun atomic-chrome-start-websocket-server (port)
+  "Create websocket server on port PORT."
+  (websocket-server
+   port
+   :host 'local
+   :on-message #'atomic-chrome-on-message
+   :on-open nil
+   :on-close #'atomic-chrome-on-close))
+
+(defun atomic-chrome-start-httpd ()
+  "Start the HTTP server for Ghost Text query."
+  (interactive)
+  (make-network-process
+   :name "atomic-chrome-httpd"
+   :family 'ipv4
+   :host 'local
+   :service atomic-chrome-server-ghost-text-port
+   :filter 'atomic-chrome-httpd-process-filter
+   :filter-multibyte nil
+   :server t
+   :noquery t))
+
+(defun atomic-chrome-normalize-header (header)
+  "Destructively capitalize the components of HEADER."
+  (mapconcat #'capitalize (split-string header "-") "-"))
+
+(defun atomic-chrome-httpd-parse-string (string)
+  "Parse client http header STRING into alist."
+  (let* ((lines (split-string string "[\n\r]+"))
+         (req (list (split-string (car lines))))
+         (post (cadr (split-string string "\r\n\r\n"))))
+    (dolist (line (butlast (cdr lines)))
+      (push (list (atomic-chrome-normalize-header (car (split-string line ": ")))
+                  (mapconcat #'identity
+                             (cdr (split-string line ": ")) ": "))
+            req))
+    (push (list "Content" post) req)
+    (reverse req)))
+
+(defun atomic-chrome-httpd-process-filter (proc string)
+  "Process filter of PROC which run each time client make a request.
+STRING is the string process received."
+  (setf string (concat (process-get proc :previous-string) string))
+  (let* ((request (atomic-chrome-httpd-parse-string string))
+         (content-length (cadr (assoc "Content-Length" request)))
+         (uri (cl-cadar request))
+         (content (cadr (assoc "Content" request))))
+    (if (and content-length
+             (< (string-bytes content) (string-to-number content-length)))
+        (process-put proc :previous-string string)
+      (atomic-chrome-httpd-send-response proc))))
+
+(defun atomic-chrome-httpd-send-response (proc)
+  "Send an HTTP 200 OK response back to process PROC."
+  (when (processp proc)
+    (unless atomic-chrome-server-ghost-text
+      (setq atomic-chrome-server-ghost-text
+            (atomic-chrome-start-websocket-server 64293)))
+    (let ((header "HTTP/1.0 200 OK\nContent-Type: application/json\n")
+          (body (json-encode '(:ProtocolVersion 1 :WebSocketPort 64293))))
+      (process-send-string proc (concat header "\n" body))
+      (process-send-eof proc))))
+
+;;;###autoload
+(defun atomic-chrome-start-server ()
+  "Start websocket server for atomic-chrome.
+Fails silently if a server is already running."
+  (interactive)
+  (ignore-errors
+      (progn
+        (and (not atomic-chrome-server-atomic-chrome)
+             (memq 'atomic-chrome atomic-chrome-extension-type-list)
+             (setq atomic-chrome-server-atomic-chrome
+                   (atomic-chrome-start-websocket-server 64292)))
+        (and (not (process-status "atomic-chrome-httpd"))
+             (memq 'ghost-text atomic-chrome-extension-type-list)
+             (atomic-chrome-start-httpd))
+        (global-atomic-chrome-edit-mode 1))))
+
+;;;###autoload
+(defun atomic-chrome-stop-server nil
+  "Stop websocket server for atomic-chrome."
+  (interactive)
+  (when atomic-chrome-server-atomic-chrome
+    (websocket-server-close atomic-chrome-server-atomic-chrome)
+    (setq atomic-chrome-server-atomic-chrome nil))
+  (when atomic-chrome-server-ghost-text
+    (websocket-server-close atomic-chrome-server-ghost-text)
+    (setq atomic-chrome-server-ghost-text nil))
+  (when (process-status "atomic-chrome-httpd")
+    (delete-process "atomic-chrome-httpd"))
+  (global-atomic-chrome-edit-mode 0))
+
+(provide 'atomic-chrome)
+
+;;; atomic-chrome.el ends here