add lisp packages
This commit is contained in:
204
lisp/emacs-application-framework/app/mindmap/buffer.py
Normal file
204
lisp/emacs-application-framework/app/mindmap/buffer.py
Normal file
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2018 Andy Stewart
|
||||
#
|
||||
# Author: Andy Stewart <lazycat.manatee@gmail.com>
|
||||
# Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
|
||||
#
|
||||
# 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
|
||||
# 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/>.
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import QUrl, QTimer
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtGui import QColor
|
||||
from core.browser import BrowserBuffer
|
||||
from core.utils import touch, string_to_base64, interactive
|
||||
import os
|
||||
import base64
|
||||
|
||||
class AppBuffer(BrowserBuffer):
|
||||
|
||||
export_org_json = QtCore.pyqtSignal(str, str)
|
||||
|
||||
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
|
||||
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, False)
|
||||
|
||||
self.url = url
|
||||
index_file = "file://" + (os.path.join(os.path.dirname(__file__), "index.html"))
|
||||
self.buffer_widget.setUrl(QUrl(index_file))
|
||||
|
||||
self.cut_node_id = None
|
||||
|
||||
for method_name in ["add_sub_node", "add_brother_node", "remove_node", "remove_middle_node", "add_middle_node", "update_node_topic_inline"]:
|
||||
self.build_js_method(method_name, True)
|
||||
|
||||
for method_name in ["zoom_in", "zoom_out", "zoom_reset",
|
||||
"select_up_node", "select_down_node", "select_left_node", "select_right_node",
|
||||
"toggle_node", "save_screenshot"]:
|
||||
self.build_js_method(method_name)
|
||||
|
||||
for method_name in ["zoom_in", "zoom_out", "zoom_reset", "remove_node",
|
||||
"remove_middle_node", "add_middle_node", "refresh_page",
|
||||
"select_up_node", "select_down_node", "select_left_node", "select_right_node",
|
||||
"toggle_node", "save_screenshot"]:
|
||||
self.build_insert_or_do(method_name)
|
||||
|
||||
self.build_all_methods(self)
|
||||
|
||||
QTimer.singleShot(500, self.init_file)
|
||||
|
||||
def resize_view(self):
|
||||
self.buffer_widget.eval_js("relayout();")
|
||||
|
||||
def init_file(self):
|
||||
self.url = os.path.expanduser(self.url)
|
||||
|
||||
if os.path.exists(self.url):
|
||||
with open(self.url, "r") as f:
|
||||
self.buffer_widget.execute_js("open_file('{}');".format(string_to_base64(f.read())))
|
||||
else:
|
||||
self.buffer_widget.eval_js("init_root_node();")
|
||||
|
||||
color = "#FFFFFF"
|
||||
if self.emacs_var_dict["eaf-mindmap-dark-mode"] == "true" or \
|
||||
(self.emacs_var_dict["eaf-mindmap-dark-mode"] == "" and self.emacs_var_dict["eaf-emacs-theme-mode"] == "dark"):
|
||||
color = "#242525"
|
||||
self.buffer_widget.eval_js("init_background('{}');".format(color))
|
||||
|
||||
self.change_title(self.get_root_node_topic())
|
||||
|
||||
def build_js_method(self, method_name, auto_save=False):
|
||||
def _do ():
|
||||
self.buffer_widget.eval_js("{}();".format(method_name))
|
||||
|
||||
if auto_save:
|
||||
self.save_file(False)
|
||||
setattr(self, method_name, _do)
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def change_background_color(self):
|
||||
self.send_input_message("Change node background color(Input color): ", "change_background_color")
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def change_text_color(self):
|
||||
self.send_input_message("Change node text color(Input color): ", "change_text_color")
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def copy_node_topic(self):
|
||||
node_topic = self.buffer_widget.execute_js("get_node_topic();")
|
||||
self.eval_in_emacs.emit('''(kill-new "{}")'''.format(node_topic))
|
||||
self.message_to_emacs.emit("Copy: {}".format(node_topic))
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def paste_node_topic(self):
|
||||
text = QApplication.clipboard().text()
|
||||
if text.strip() != "":
|
||||
self.buffer_widget.eval_js("update_node_topic('{}');".format(text))
|
||||
self.message_to_emacs.emit("Paste: {}".format(text))
|
||||
|
||||
self.save_file(False)
|
||||
else:
|
||||
self.message_to_emacs.emit("Nothing in clipboard, can't paste.")
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def cut_node_tree(self):
|
||||
self.cut_node_id = self.buffer_widget.execute_js("get_selected_nodeid();")
|
||||
if self.cut_node_id:
|
||||
if self.cut_node_id != "root":
|
||||
self.message_to_emacs.emit("Root node not allowed cut.")
|
||||
else:
|
||||
self.message_to_emacs.emit("Cut node tree: {}".format(self.cut_node_id))
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def paste_node_tree(self):
|
||||
if self.cut_node_id:
|
||||
self.buffer_widget.eval_js("paste_node_tree('{}');".format(self.cut_node_id))
|
||||
self.save_file(False)
|
||||
self.message_to_emacs.emit("Paste node tree: {}".format(self.cut_node_id))
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def change_node_background(self):
|
||||
self.send_input_message("Change node background: ", "change_node_background", "file")
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def update_node_topic(self):
|
||||
self.send_input_message(
|
||||
"Update topic: ",
|
||||
"update_node_topic",
|
||||
"string",
|
||||
self.buffer_widget.execute_js("get_node_topic();"))
|
||||
|
||||
def handle_update_node_topic(self, topic):
|
||||
self.buffer_widget.eval_js("update_node_topic('{}');".format(topic))
|
||||
|
||||
self.change_title(self.get_root_node_topic())
|
||||
|
||||
self.save_file(False)
|
||||
|
||||
def handle_input_message(self, result_type, result_content):
|
||||
if result_type == "update_node_topic":
|
||||
self.handle_update_node_topic(str(result_content))
|
||||
elif result_type == "change_node_background":
|
||||
print(str(result_content))
|
||||
self.buffer_widget.eval_js("change_node_background('{}');".format(str(result_content)))
|
||||
elif result_type == "change_background_color":
|
||||
self.buffer_widget.eval_js("change_background_color('{}');".format(str(result_content)))
|
||||
elif result_type == "change_text_color":
|
||||
self.buffer_widget.eval_js("change_text_color('{}');".format(str(result_content)))
|
||||
|
||||
def is_focus(self):
|
||||
return self.buffer_widget.execute_js("node_is_focus();")
|
||||
|
||||
def get_root_node_topic(self):
|
||||
return self.buffer_widget.execute_js("get_root_node_topic();")
|
||||
|
||||
def handle_download_request(self, download_item):
|
||||
download_data = download_item.url().toString()
|
||||
|
||||
# Note:
|
||||
# Set some delay to make get_root_node_topic works expect.
|
||||
# get_root_node_topic will return None if execute immediately.
|
||||
QTimer.singleShot(200, lambda : self.save_screenshot_data(download_data))
|
||||
|
||||
def get_save_path(self, extension_name):
|
||||
if self.url.strip() == "":
|
||||
return os.path.join(os.path.expanduser(self.emacs_var_dict["eaf-mindmap-save-path"]), self.get_root_node_topic() + "." + extension_name)
|
||||
else:
|
||||
return os.path.splitext(self.url)[0] + "." + extension_name
|
||||
|
||||
def save_screenshot_data(self, download_data):
|
||||
image_path = self.get_save_path("png")
|
||||
touch(image_path)
|
||||
with open(image_path, "wb") as f:
|
||||
f.write(base64.decodestring(download_data.split("data:image/png;base64,")[1].encode("utf-8")))
|
||||
|
||||
self.message_to_emacs.emit("Save image: " + image_path)
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def save_file(self, notify=True):
|
||||
file_path = self.get_save_path("emm")
|
||||
touch(file_path)
|
||||
with open(file_path, "w") as f:
|
||||
f.write(self.buffer_widget.execute_js("save_file();"))
|
||||
|
||||
if notify:
|
||||
self.message_to_emacs.emit("Save file: " + file_path)
|
||||
|
||||
@interactive(insert_or_do=True)
|
||||
def save_org_file(self):
|
||||
file_path = self.get_save_path("org")
|
||||
touch(file_path)
|
||||
self.export_org_json.emit(self.buffer_widget.execute_js("save_file();"), file_path)
|
||||
self.message_to_emacs.emit("Save org file: " + file_path)
|
||||
307
lisp/emacs-application-framework/app/mindmap/eaf-mindmap.el
Normal file
307
lisp/emacs-application-framework/app/mindmap/eaf-mindmap.el
Normal file
@@ -0,0 +1,307 @@
|
||||
;;; eaf-mindmap.el --- Simple description
|
||||
|
||||
;; Filename: eaf-mindmap.el
|
||||
;; Description: Simple description
|
||||
;; Author: Andy Stewart <lazycat.manatee@gmail.com>
|
||||
;; Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
|
||||
;; Copyright (C) 2020, Andy Stewart, all rights reserved.
|
||||
;; Created: 2020-02-28 16:09:02
|
||||
;; Version: 0.1
|
||||
;; Last-Updated: 2020-02-28 16:09:02
|
||||
;; By: Andy Stewart
|
||||
;; URL: http://www.emacswiki.org/emacs/download/eaf-mindmap.el
|
||||
;; Keywords:
|
||||
;; Compatibility: GNU Emacs 26.3
|
||||
;;
|
||||
;; Features that might be required by this library:
|
||||
;;
|
||||
;;
|
||||
;;
|
||||
|
||||
;;; This file is NOT part of GNU Emacs
|
||||
|
||||
;;; License
|
||||
;;
|
||||
;; 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, 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; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
||||
;; Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Simple description
|
||||
;;
|
||||
|
||||
;;; Installation:
|
||||
;;
|
||||
;; Put eaf-mindmap.el to your load-path.
|
||||
;; The load-path is usually ~/elisp/.
|
||||
;; It's set in your ~/.emacs like this:
|
||||
;; (add-to-list 'load-path (expand-file-name "~/elisp"))
|
||||
;;
|
||||
;; And the following to your ~/.emacs startup file.
|
||||
;;
|
||||
;; (require 'eaf-mindmap)
|
||||
;;
|
||||
;; No need more.
|
||||
|
||||
;;; Customize:
|
||||
;;
|
||||
;;
|
||||
;;
|
||||
;; All of the above can customize by:
|
||||
;; M-x customize-group RET eaf-mindmap RET
|
||||
;;
|
||||
|
||||
;;; Change log:
|
||||
;;
|
||||
;; 2020/02/28
|
||||
;; * First released.
|
||||
;;
|
||||
|
||||
;;; Acknowledgements:
|
||||
;;
|
||||
;;
|
||||
;;
|
||||
|
||||
;;; TODO
|
||||
;;
|
||||
;;
|
||||
;;
|
||||
|
||||
;;; Require
|
||||
(require 'json)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(dbus-register-signal
|
||||
:session "com.lazycat.eaf" "/com/lazycat/eaf"
|
||||
"com.lazycat.eaf" "export_org_json"
|
||||
#'eaf--export-org-json)
|
||||
|
||||
(defun eaf--export-org-json (org-json-content org-file-path)
|
||||
(let (org-parse-data)
|
||||
(with-temp-buffer
|
||||
(insert org-json-content)
|
||||
(beginning-of-buffer)
|
||||
(let* ((json (json-read))
|
||||
(root-node-name (cdr (assoc 'topic (assoc 'data json))))
|
||||
(children-node (cdr (assoc 'children (assoc 'data json)))))
|
||||
;; (message "%s" (org-json-decode (cdr (assoc 'data json))))
|
||||
(insert (org-json-decode (cdr (assoc 'data json))))
|
||||
(beginning-of-buffer)
|
||||
(while (search-forward-regexp "\\*\\s-topic\\s-\"" nil t)
|
||||
(let ((header-string (buffer-substring (save-excursion
|
||||
(beginning-of-line)
|
||||
(point))
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(search-forward-regexp "\\*\\s-" nil t)
|
||||
(point)
|
||||
)))
|
||||
(content-string (buffer-substring (point)
|
||||
(save-excursion
|
||||
(end-of-line)
|
||||
(backward-char 1)
|
||||
(point)))))
|
||||
(when (> (length header-string) 2)
|
||||
(setq header-string (string-remove-prefix "*" header-string)))
|
||||
(setq org-parse-data (concat org-parse-data (format "%s%s\n" header-string content-string)))))
|
||||
|
||||
(with-temp-file org-file-path
|
||||
(insert org-parse-data))
|
||||
))))
|
||||
|
||||
(defun json-read-r ()
|
||||
(let ((json-object-type 'alist))
|
||||
(let ((r (json-read)))
|
||||
(if (listp r) (reverse r) r))))
|
||||
|
||||
(defun org-json-raw ()
|
||||
(let (p1)
|
||||
(goto-char (point-min))
|
||||
(re-search-forward "[ \n\t]*")
|
||||
(beginning-of-line)
|
||||
(delete-region (point-min) (point))
|
||||
(if (re-search-forward "\\`#\\+begin_src" nil t)
|
||||
;; string
|
||||
(progn
|
||||
(if (re-search-forward "^#\\+end_src" nil t)
|
||||
(progn (beginning-of-line)
|
||||
(setq p1 (point)))
|
||||
(setq p1 (point-max)))
|
||||
(goto-char (point-min))
|
||||
(forward-line 1)
|
||||
(buffer-substring-no-properties (point) p1))
|
||||
(goto-char (point-min))
|
||||
(json-read-r))))
|
||||
|
||||
(defun org-json-entry (str)
|
||||
(let (lv header has-body p0 p1)
|
||||
(with-temp-buffer
|
||||
(insert str)
|
||||
(goto-char (point-min))
|
||||
(end-of-line)
|
||||
(setq header (buffer-substring (point-min) (point)))
|
||||
;; delete header
|
||||
(delete-region (point-min) (point))
|
||||
;; process header
|
||||
(string-match "^\\(\\*+\\) +\\(.+?\\) *$" header)
|
||||
(setq lv (length (match-string 1 header)))
|
||||
;; remove '*'
|
||||
(setq header (match-string 2 header))
|
||||
(goto-char (point-min))
|
||||
(if (re-search-forward "\\`[ \n\t]*\\'" nil t)
|
||||
(list lv header) ; body is empty
|
||||
(list lv header (org-json-raw))))))
|
||||
|
||||
(defun org-json-entries ()
|
||||
(let (p0 pl)
|
||||
;; get all entries
|
||||
(setq pl (let (ret tmp)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^\*+ " nil t)
|
||||
(setq tmp (point))
|
||||
(beginning-of-line)
|
||||
(setq ret (cons (point) ret))
|
||||
(goto-char tmp))
|
||||
(setq ret (cons (point-max) ret))
|
||||
(reverse ret)))
|
||||
(setq p0 (car pl))
|
||||
(mapcar (lambda (p1)
|
||||
(prog1 (org-json-entry
|
||||
(buffer-substring-no-properties p0 p1))
|
||||
(setq p0 p1)))
|
||||
(cdr pl))))
|
||||
|
||||
(defun org-json-split-header (lst &optional ret)
|
||||
"Split header for the return of `org-json-entries'."
|
||||
(if (null lst) (reverse ret)
|
||||
(let (e0 e1 lst1)
|
||||
(setq e0 (car lst))
|
||||
(setq lst1 (cdr lst))
|
||||
(setq e1 (if lst1 (car lst1)
|
||||
'(0 "" "")))
|
||||
(if (>= (car e0) (car e1))
|
||||
;; no child
|
||||
(when (= (length e0) 2)
|
||||
;; split header
|
||||
(if (string-match "^\\(.+?\\)[ \n\t]+\\(.+\\)$" (nth 1 e0))
|
||||
(setq e0 (list (nth 0 e0)
|
||||
(match-string 1 (nth 1 e0))
|
||||
(with-temp-buffer
|
||||
(insert (match-string 2 (nth 1 e0)))
|
||||
(goto-char (point-min))
|
||||
(json-read-r))))
|
||||
(setq e0 (list (nth 0 e0) (nth 1 e0) :json-null))))
|
||||
(when (> (length e0) 2)
|
||||
;; drop unnecessary elem
|
||||
(setq e0 (list (nth 0 e0)
|
||||
(nth 1 e0)))))
|
||||
(org-json-split-header lst1 (cons e0 ret)))))
|
||||
|
||||
(defun org-json-gen-alist (lst)
|
||||
"generate alist from return of `org-json-gen-alist',
|
||||
actural call `org-json-gen-alist1' to work."
|
||||
(cdr (org-json-gen-alist1 lst 1)))
|
||||
|
||||
(defun org-json-gen-alist1 (lst lv &optional ret)
|
||||
(if (or (null lst)
|
||||
(< (car (car lst)) lv))
|
||||
;; return list . ret
|
||||
(cons lst (reverse ret))
|
||||
;; not return
|
||||
(let (e r1 kv)
|
||||
;; r1 is return of children
|
||||
;; kv is key-value pair
|
||||
(setq e (car lst))
|
||||
(if (> (length e) 2)
|
||||
;; no child
|
||||
(progn (setq kv (cons (nth 1 e)
|
||||
(nth 2 e)))
|
||||
(org-json-gen-alist1 (cdr lst) lv (cons kv ret)))
|
||||
|
||||
(setq r1 (org-json-gen-alist1 (cdr lst) (1+ lv)))
|
||||
;; convert to array if necessray
|
||||
(let ((seq (mapcar 'car (cdr r1))))
|
||||
(when (equal (mapcar 'number-to-string
|
||||
(number-sequence 0 (1- (length seq))))
|
||||
seq)
|
||||
(setq r1 (cons (car r1)
|
||||
(vconcat (mapcar 'cdr (cdr r1)))))))
|
||||
(setq kv (cons (nth 1 e) (cdr r1)))
|
||||
(org-json-gen-alist1 (car r1) lv
|
||||
(cons kv ret))))))
|
||||
|
||||
|
||||
(defun org-json-encode ()
|
||||
(save-excursion
|
||||
(org-json-gen-alist
|
||||
(org-json-split-header
|
||||
(org-json-entries)))))
|
||||
|
||||
(defun org-json-decode (obj &optional lv)
|
||||
"Decode json object to org at level `lv'."
|
||||
(unless lv (setq lv 1))
|
||||
(cond ((stringp obj) (if (string-match "\n\\'" obj)
|
||||
;; end with '\n' using #+begin_src block
|
||||
(concat "#+begin_src\n"
|
||||
obj
|
||||
"#+end_src")
|
||||
(concat "\""
|
||||
(replace-regexp-in-string
|
||||
"\"" "\\\""
|
||||
(replace-regexp-in-string
|
||||
"\\\\" "\\\\" obj))
|
||||
"\"")))
|
||||
((numberp obj) (number-to-string obj))
|
||||
((vectorp obj) (if (= (length obj) 0)
|
||||
"[]"
|
||||
(let ((n 0) ns)
|
||||
(mapconcat
|
||||
(lambda (x)
|
||||
(prog1
|
||||
(org-json-kv-decode
|
||||
(cons (number-to-string n)
|
||||
x)
|
||||
lv)
|
||||
(setq n (1+ n))))
|
||||
obj "\n"))))
|
||||
((equal obj json-null) "{}")
|
||||
((listp obj) (mapconcat
|
||||
(lambda (x)
|
||||
(org-json-kv-decode x lv))
|
||||
obj "\n"))
|
||||
((equal obj t) "true")
|
||||
((equal obj json-false) "false")
|
||||
(t (error "org-json-decode type error: %S" obj))))
|
||||
|
||||
(defun org-json-kv-decode (kv lv)
|
||||
"Decode a key-value pair."
|
||||
(let ((k (car kv))
|
||||
(v (cdr kv))
|
||||
h ks vs)
|
||||
(setq ks (cond ((symbolp k) (symbol-name k))
|
||||
((stringp k) k)
|
||||
((numberp k) (number-to-string k))
|
||||
(t (error "org-json-kv-decode key type error: %S" k))))
|
||||
(setq h (concat (make-string lv ?*) " " ks))
|
||||
(setq vs (org-json-decode v (1+ lv)))
|
||||
(if (string-match "\n" vs)
|
||||
(concat h "\n" vs)
|
||||
(concat h " " vs))
|
||||
))
|
||||
|
||||
(provide 'eaf-mindmap)
|
||||
|
||||
;;; eaf-mindmap.el ends here
|
||||
289
lisp/emacs-application-framework/app/mindmap/index.html
Normal file
289
lisp/emacs-application-framework/app/mindmap/index.html
Normal file
@@ -0,0 +1,289 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link type="text/css" rel="stylesheet" href="jsmind.css" />
|
||||
|
||||
<script type="text/javascript" src="jsmind.js"></script>
|
||||
<script type="text/javascript" src="jsmind.draggable.js"></script>
|
||||
<script type="text/javascript" src="jsmind.screenshot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="jsmind_container"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var _jm = null;
|
||||
|
||||
var options = {
|
||||
container:'jsmind_container',
|
||||
editable:true
|
||||
}
|
||||
|
||||
function change_background_color(color) {
|
||||
var selected_id = get_selected_nodeid();
|
||||
if(selected_id) {
|
||||
_jm.set_node_color(selected_id, color, null);
|
||||
}
|
||||
}
|
||||
|
||||
function change_text_color(color) {
|
||||
var selected_id = get_selected_nodeid();
|
||||
if(selected_id) {
|
||||
_jm.set_node_color(selected_id, null, color);
|
||||
}
|
||||
}
|
||||
|
||||
function select_root_node() {
|
||||
_jm.select_node("root");
|
||||
}
|
||||
|
||||
function init_root_node() {
|
||||
_jm = jsMind.show(options);
|
||||
select_root_node();
|
||||
}
|
||||
|
||||
function init_background(color) {
|
||||
document.getElementById("jsmind_container").style.backgroundColor = color;
|
||||
}
|
||||
|
||||
function get_root_node_topic() {
|
||||
return _jm.get_node("root").topic;
|
||||
}
|
||||
|
||||
function get_node_topic() {
|
||||
var selected_node = _jm.get_selected_node(); // as parent of new node
|
||||
if (selected_node) {
|
||||
return selected_node.topic;
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
function add_sub_node(){
|
||||
var selected_node = _jm.get_selected_node(); // as parent of new node
|
||||
if(!selected_node) {
|
||||
_jm.move_node(selected_id,'_first_');
|
||||
}
|
||||
|
||||
var nodeid = jsMind.util.uuid.newid();
|
||||
var node = _jm.add_node(selected_node, nodeid, 'Topic');
|
||||
_jm.select_node(node);
|
||||
}
|
||||
|
||||
function add_brother_node(){
|
||||
var selected_node = _jm.get_selected_node(); // as parent of new node
|
||||
if(!selected_node) {
|
||||
_jm.move_node(selected_id,'_first_');
|
||||
}
|
||||
if(_jm.view.is_editing()) {
|
||||
_jm.end_edit();
|
||||
} else {
|
||||
var nodeid = jsMind.util.uuid.newid();
|
||||
var node = _jm.insert_node_after(selected_node, nodeid, 'Topic');
|
||||
_jm.select_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
function get_selected_nodeid(){
|
||||
var selected_node = _jm.get_selected_node();
|
||||
if(!!selected_node){
|
||||
return selected_node.id;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function remove_node(){
|
||||
var selected_id = get_selected_nodeid();
|
||||
if(selected_id) {
|
||||
_jm.select_node(_jm.get_selected_node().parent);
|
||||
_jm.remove_node(selected_id);
|
||||
}
|
||||
}
|
||||
|
||||
function update_node_topic(topic) {
|
||||
var selected_id = get_selected_nodeid();
|
||||
if (selected_id) {
|
||||
_jm.update_node(selected_id, topic);
|
||||
}
|
||||
}
|
||||
|
||||
function update_node_topic_inline() {
|
||||
var selected_id = get_selected_nodeid();
|
||||
if (selected_id) {
|
||||
_jm.begin_edit(selected_id);
|
||||
}
|
||||
}
|
||||
|
||||
function zoom_in() {
|
||||
_jm.view.zoom_in();
|
||||
};
|
||||
|
||||
function zoom_out() {
|
||||
_jm.view.zoom_out();
|
||||
};
|
||||
|
||||
function zoom_reset() {
|
||||
_jm.view.set_zoom(1);
|
||||
}
|
||||
|
||||
function select_up_node(){
|
||||
var selected_node = _jm.get_selected_node();
|
||||
if(!!selected_node){
|
||||
var up_node = _jm.find_node_before(selected_node);
|
||||
if(!up_node){
|
||||
var np = _jm.find_node_before(selected_node.parent);
|
||||
if(!!np && np.children.length > 0){
|
||||
up_node = np.children[np.children.length-1];
|
||||
}
|
||||
}
|
||||
if(!!up_node){
|
||||
_jm.select_node(up_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function select_down_node(){
|
||||
var selected_node = _jm.get_selected_node();
|
||||
if(!!selected_node){
|
||||
var down_node = _jm.find_node_after(selected_node);
|
||||
if(!down_node){
|
||||
var np = _jm.find_node_after(selected_node.parent);
|
||||
if(!!np && np.children.length > 0){
|
||||
down_node = np.children[0];
|
||||
}
|
||||
}
|
||||
if(!!down_node){
|
||||
_jm.select_node(down_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function select_left_node(){
|
||||
select_relative_node(-1);
|
||||
}
|
||||
|
||||
function select_right_node(){
|
||||
select_relative_node(1);
|
||||
}
|
||||
|
||||
function select_relative_node(d){
|
||||
var selected_node = _jm.get_selected_node();
|
||||
var node = null;
|
||||
if(!!selected_node){
|
||||
if(selected_node.isroot){
|
||||
var c = selected_node.children;
|
||||
var children = [];
|
||||
for(var i=0;i<c.length;i++){
|
||||
if(c[i].direction === d){
|
||||
children.push(i)
|
||||
}
|
||||
}
|
||||
node = c[children[Math.floor((children.length-1)/2)]];
|
||||
}
|
||||
else if(selected_node.direction === d){
|
||||
var children = selected_node.children;
|
||||
var childrencount = children.length;
|
||||
if(childrencount > 0){
|
||||
node = children[Math.floor((childrencount-1)/2)]
|
||||
}
|
||||
}else{
|
||||
node = selected_node.parent;
|
||||
}
|
||||
if(!!node){
|
||||
_jm.select_node(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function node_is_focus() {
|
||||
return _jm.view.is_editing();
|
||||
}
|
||||
|
||||
function toggle_node() {
|
||||
var selected_node = _jm.get_selected_node(); // as parent of new node
|
||||
if (selected_node) {
|
||||
_jm.toggle_node(selected_node);
|
||||
}
|
||||
}
|
||||
|
||||
function save_screenshot() {
|
||||
_jm.screenshot.shootDownload();
|
||||
}
|
||||
|
||||
function open_file(file_base64_content) {
|
||||
_jm = jsMind.show(options);
|
||||
|
||||
var file_data = decodeURIComponent(escape(window.atob(file_base64_content)));
|
||||
var mind = jsMind.util.json.string2json(file_data);
|
||||
|
||||
if(!!mind){
|
||||
_jm.show(mind);
|
||||
|
||||
select_root_node();
|
||||
}
|
||||
}
|
||||
|
||||
function save_file() {
|
||||
return jsMind.util.json.json2string(_jm.get_data());
|
||||
}
|
||||
|
||||
function change_node_background(image) {
|
||||
var selected_id = get_selected_nodeid();
|
||||
if(selected_id) {
|
||||
_jm.set_node_background_image(selected_id, "file://" + image);
|
||||
}
|
||||
}
|
||||
|
||||
function relayout() {
|
||||
_jm.view.relayout();
|
||||
}
|
||||
|
||||
function paste_node_tree(src_node_id) {
|
||||
/* var selected_node = _jm.get_selected_node();
|
||||
* if(!!selected_node){
|
||||
* var src_node = _jm.get_node(src_node_id);
|
||||
* _jm.move_node(src_node, selected_node, selected_node.direction);
|
||||
* } */
|
||||
|
||||
var selected_node = _jm.get_selected_node();
|
||||
if(!!selected_node){
|
||||
var src_node = _jm.get_node(src_node_id);
|
||||
_jm.move_node(src_node, src_node_id, selected_node.id, selected_node.direction);
|
||||
}
|
||||
}
|
||||
|
||||
function remove_middle_node() {
|
||||
var selected_node = _jm.get_selected_node();
|
||||
if(!!selected_node){
|
||||
var children = selected_node.children;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
_jm.move_node(child, child.id, selected_node.parent.id, selected_node.parent.direction);
|
||||
}
|
||||
|
||||
_jm.select_node(selected_node.parent);
|
||||
_jm.remove_node(selected_node.id);
|
||||
}
|
||||
}
|
||||
|
||||
function add_middle_node() {
|
||||
var selected_node = _jm.get_selected_node(); // as parent of new node
|
||||
if(!!selected_node) {
|
||||
var topic = selected_node.topic;
|
||||
|
||||
add_brother_node();
|
||||
|
||||
var brother_node = _jm.get_selected_node();
|
||||
_jm.update_node(brother_node.id, topic);
|
||||
_jm.update_node(selected_node.id, "Topic");
|
||||
|
||||
_jm.move_node(selected_node, selected_node.id, brother_node.id, brother_node.direction);
|
||||
|
||||
_jm.select_node(selected_node);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
131
lisp/emacs-application-framework/app/mindmap/jsmind.css
Normal file
131
lisp/emacs-application-framework/app/mindmap/jsmind.css
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Released under BSD License
|
||||
* Copyright (c) 2014-2015 hizzgdev@163.com
|
||||
*
|
||||
* Project Home:
|
||||
* https: //github.com/hizzgdev/jsmind/
|
||||
*/
|
||||
|
||||
/* important section */
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#jsmind_container{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.jsmind-inner {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.jsmind-inner {
|
||||
moz-user-select: -moz-none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* z-index: 1 */
|
||||
canvas {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
input {
|
||||
outline-style: none;
|
||||
border: blue;
|
||||
font-size: 24px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 5px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
/* z-index: 2 */
|
||||
jmnodes {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
jmnode {
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
jmexpander {
|
||||
position: absolute;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
line-height: 12px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media screen and (max-device-width: 1024px) {
|
||||
jmnode {padding: 5px;border-radius: 3px;font-size: 14px;}
|
||||
jmnode.root {font-size: 21px;}
|
||||
}
|
||||
|
||||
jmnode {
|
||||
padding: 10px;
|
||||
background-color: #30A3C0;
|
||||
color: #FFF;
|
||||
border-radius: 5px;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
jmnode:hover {
|
||||
background-color: #F3C250;
|
||||
color: #333;
|
||||
}
|
||||
jmnode.selected {
|
||||
background-color: #F56B85;
|
||||
color: #FFF;
|
||||
}
|
||||
jmnode.root {
|
||||
border-radius: 30px;
|
||||
background-color: #3598DB;
|
||||
color: #FFF;
|
||||
font-size: 24px;
|
||||
}
|
||||
jmnode.root.selected {
|
||||
background-color: #F56B85;
|
||||
}
|
||||
jmnode.root:hover {
|
||||
background-color: #F3C250;
|
||||
color: #333;
|
||||
}
|
||||
jmexpander {
|
||||
border-color: gray;
|
||||
}
|
||||
jmexpander:hover {
|
||||
border-color: #000;
|
||||
}
|
||||
|
||||
jmnodes.theme-greensea jmnode {background-color: #1abc9c;color: #fff;}
|
||||
jmnodes.theme-greensea jmnode:hover {background-color: #16a085;}
|
||||
jmnodes.theme-greensea jmnode.selected {background-color: #11f;color: #fff;}
|
||||
jmnodes.theme-greensea jmnode.root {}
|
||||
jmnodes.theme-greensea jmexpander {}
|
||||
jmnodes.theme-greensea jmexpander:hover {}
|
||||
349
lisp/emacs-application-framework/app/mindmap/jsmind.draggable.js
Normal file
349
lisp/emacs-application-framework/app/mindmap/jsmind.draggable.js
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Released under BSD License
|
||||
* Copyright (c) 2014-2015 hizzgdev@163.com
|
||||
*
|
||||
* Project Home:
|
||||
* https://github.com/hizzgdev/jsmind/
|
||||
*/
|
||||
|
||||
(function($w){
|
||||
'use strict';
|
||||
var $d = $w.document;
|
||||
var __name__ = 'jsMind';
|
||||
var jsMind = $w[__name__];
|
||||
if(!jsMind){return;}
|
||||
if(typeof jsMind.draggable != 'undefined'){return;}
|
||||
|
||||
var jdom = jsMind.util.dom;
|
||||
var jcanvas = jsMind.util.canvas;
|
||||
|
||||
var clear_selection = 'getSelection' in $w ? function(){
|
||||
$w.getSelection().removeAllRanges();
|
||||
} : function(){
|
||||
$d.selection.empty();
|
||||
};
|
||||
|
||||
var options = {
|
||||
line_width : 5,
|
||||
lookup_delay : 500,
|
||||
lookup_interval : 80
|
||||
};
|
||||
|
||||
jsMind.draggable = function(jm){
|
||||
this.jm = jm;
|
||||
this.e_canvas = null;
|
||||
this.canvas_ctx = null;
|
||||
this.shadow = null;
|
||||
this.shadow_w = 0;
|
||||
this.shadow_h = 0;
|
||||
this.active_node = null;
|
||||
this.target_node = null;
|
||||
this.target_direct = null;
|
||||
this.client_w = 0;
|
||||
this.client_h = 0;
|
||||
this.offset_x = 0;
|
||||
this.offset_y = 0;
|
||||
this.hlookup_delay = 0;
|
||||
this.hlookup_timer = 0;
|
||||
this.capture = false;
|
||||
this.moved = false;
|
||||
};
|
||||
|
||||
jsMind.draggable.prototype = {
|
||||
init:function(){
|
||||
this._create_canvas();
|
||||
this._create_shadow();
|
||||
this._event_bind();
|
||||
},
|
||||
|
||||
resize:function(){
|
||||
this.jm.view.e_nodes.appendChild(this.shadow);
|
||||
this.e_canvas.width=this.jm.view.size.w;
|
||||
this.e_canvas.height=this.jm.view.size.h;
|
||||
},
|
||||
|
||||
_create_canvas:function(){
|
||||
var c = $d.createElement('canvas');
|
||||
this.jm.view.e_panel.appendChild(c);
|
||||
var ctx = c.getContext('2d');
|
||||
this.e_canvas = c;
|
||||
this.canvas_ctx = ctx;
|
||||
},
|
||||
|
||||
_create_shadow:function(){
|
||||
var s = $d.createElement('jmnode');
|
||||
s.style.visibility = 'hidden';
|
||||
s.style.zIndex = '3';
|
||||
s.style.cursor = 'move';
|
||||
s.style.opacity= '0.7';
|
||||
this.shadow = s;
|
||||
},
|
||||
|
||||
reset_shadow:function(el){
|
||||
var s = this.shadow.style;
|
||||
this.shadow.innerHTML = el.innerHTML;
|
||||
s.left = el.style.left;
|
||||
s.top = el.style.top;
|
||||
s.width = el.style.width;
|
||||
s.height = el.style.height;
|
||||
s.backgroundImage = el.style.backgroundImage;
|
||||
s.backgroundSize = el.style.backgroundSize;
|
||||
s.transform = el.style.transform;
|
||||
this.shadow_w = this.shadow.clientWidth;
|
||||
this.shadow_h = this.shadow.clientHeight;
|
||||
|
||||
},
|
||||
|
||||
show_shadow:function(){
|
||||
if(!this.moved){
|
||||
this.shadow.style.visibility = 'visible';
|
||||
}
|
||||
},
|
||||
|
||||
hide_shadow:function(){
|
||||
this.shadow.style.visibility = 'hidden';
|
||||
},
|
||||
|
||||
clear_lines:function(){
|
||||
jcanvas.clear(this.canvas_ctx, 0, 0, this.jm.view.size.w, this.jm.view.size.h);
|
||||
},
|
||||
|
||||
_magnet_shadow:function(node){
|
||||
if(!!node){
|
||||
this.canvas_ctx.lineWidth = options.line_width;
|
||||
this.canvas_ctx.strokeStyle = 'rgba(0,0,0,0.3)';
|
||||
this.canvas_ctx.lineCap = 'round';
|
||||
this.clear_lines();
|
||||
jcanvas.lineto(this.canvas_ctx,
|
||||
node.sp.x,
|
||||
node.sp.y,
|
||||
node.np.x,
|
||||
node.np.y);
|
||||
}
|
||||
},
|
||||
|
||||
_lookup_close_node:function(){
|
||||
var root = this.jm.get_root();
|
||||
var root_location = root.get_location();
|
||||
var root_size = root.get_size();
|
||||
var root_x = root_location.x + root_size.w/2;
|
||||
|
||||
var sw = this.shadow_w;
|
||||
var sh = this.shadow_h;
|
||||
var sx = this.shadow.offsetLeft;
|
||||
var sy = this.shadow.offsetTop;
|
||||
|
||||
var ns,nl;
|
||||
|
||||
var direct = (sx + sw/2)>=root_x ?
|
||||
jsMind.direction.right : jsMind.direction.left;
|
||||
var nodes = this.jm.mind.nodes;
|
||||
var node = null;
|
||||
var min_distance = Number.MAX_VALUE;
|
||||
var distance = 0;
|
||||
var closest_node = null;
|
||||
var closest_p = null;
|
||||
var shadow_p = null;
|
||||
for(var nodeid in nodes){
|
||||
var np,sp;
|
||||
node = nodes[nodeid];
|
||||
if(node.isroot || node.direction == direct){
|
||||
if(node.id == this.active_node.id){
|
||||
continue;
|
||||
}
|
||||
ns = node.get_size();
|
||||
nl = node.get_location();
|
||||
if(direct == jsMind.direction.right){
|
||||
if(sx-nl.x-ns.w<=0){continue;}
|
||||
distance = Math.abs(sx-nl.x-ns.w) + Math.abs(sy+sh/2-nl.y-ns.h/2);
|
||||
np = {x:nl.x+ns.w-options.line_width,y:nl.y+ns.h/2};
|
||||
sp = {x:sx+options.line_width,y:sy+sh/2};
|
||||
}else{
|
||||
if(nl.x-sx-sw<=0){continue;}
|
||||
distance = Math.abs(sx+sw-nl.x) + Math.abs(sy+sh/2-nl.y-ns.h/2);
|
||||
np = {x:nl.x+options.line_width,y:nl.y+ns.h/2};
|
||||
sp = {x:sx+sw-options.line_width,y:sy+sh/2};
|
||||
}
|
||||
if(distance < min_distance){
|
||||
closest_node = node;
|
||||
closest_p = np;
|
||||
shadow_p = sp;
|
||||
min_distance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
var result_node = null;
|
||||
if(!!closest_node){
|
||||
result_node = {
|
||||
node:closest_node,
|
||||
direction:direct,
|
||||
sp:shadow_p,
|
||||
np:closest_p
|
||||
};
|
||||
}
|
||||
return result_node;
|
||||
},
|
||||
|
||||
lookup_close_node:function(){
|
||||
var node_data = this._lookup_close_node();
|
||||
if(!!node_data){
|
||||
this._magnet_shadow(node_data);
|
||||
this.target_node = node_data.node;
|
||||
this.target_direct = node_data.direction;
|
||||
}
|
||||
},
|
||||
|
||||
_event_bind:function(){
|
||||
var jd = this;
|
||||
var container = this.jm.view.container;
|
||||
jdom.add_event(container,'mousedown',function(e){
|
||||
var evt = e || event;
|
||||
jd.dragstart.call(jd,evt);
|
||||
});
|
||||
jdom.add_event(container,'mousemove',function(e){
|
||||
var evt = e || event;
|
||||
jd.drag.call(jd,evt);
|
||||
});
|
||||
jdom.add_event(container,'mouseup',function(e){
|
||||
var evt = e || event;
|
||||
jd.dragend.call(jd,evt);
|
||||
});
|
||||
jdom.add_event(container,'touchstart',function(e){
|
||||
var evt = e || event;
|
||||
jd.dragstart.call(jd,evt);
|
||||
});
|
||||
jdom.add_event(container,'touchmove',function(e){
|
||||
var evt = e || event;
|
||||
jd.drag.call(jd,evt);
|
||||
});
|
||||
jdom.add_event(container,'touchend',function(e){
|
||||
var evt = e || event;
|
||||
jd.dragend.call(jd,evt);
|
||||
});
|
||||
},
|
||||
|
||||
dragstart:function(e){
|
||||
if(!this.jm.get_editable()){return;}
|
||||
if(this.capture){return;}
|
||||
this.active_node = null;
|
||||
|
||||
var jview = this.jm.view;
|
||||
var el = e.target || event.srcElement;
|
||||
if(el.tagName.toLowerCase() != 'jmnode'){return;}
|
||||
var nodeid = jview.get_binded_nodeid(el);
|
||||
if(!!nodeid){
|
||||
var node = this.jm.get_node(nodeid);
|
||||
if(!node.isroot){
|
||||
this.reset_shadow(el);
|
||||
this.active_node = node;
|
||||
this.offset_x = (e.clientX || e.touches[0].clientX) - el.offsetLeft;
|
||||
this.offset_y = (e.clientY || e.touches[0].clientY) - el.offsetTop;
|
||||
this.client_hw = Math.floor(el.clientWidth/2);
|
||||
this.client_hh = Math.floor(el.clientHeight/2);
|
||||
if(this.hlookup_delay != 0){
|
||||
$w.clearTimeout(this.hlookup_delay);
|
||||
}
|
||||
if(this.hlookup_timer != 0){
|
||||
$w.clearInterval(this.hlookup_timer);
|
||||
}
|
||||
var jd = this;
|
||||
this.hlookup_delay = $w.setTimeout(function(){
|
||||
jd.hlookup_delay = 0;
|
||||
jd.hlookup_timer = $w.setInterval(function(){
|
||||
jd.lookup_close_node.call(jd);
|
||||
},options.lookup_interval);
|
||||
},options.lookup_delay);
|
||||
this.capture = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
drag:function(e){
|
||||
if(!this.jm.get_editable()){return;}
|
||||
if(this.capture){
|
||||
e.preventDefault();
|
||||
this.show_shadow();
|
||||
this.moved = true;
|
||||
clear_selection();
|
||||
var px = (e.clientX || e.touches[0].clientX) - this.offset_x;
|
||||
var py = (e.clientY || e.touches[0].clientY) - this.offset_y;
|
||||
var cx = px + this.client_hw;
|
||||
var cy = py + this.client_hh;
|
||||
this.shadow.style.left = px + 'px';
|
||||
this.shadow.style.top = py + 'px';
|
||||
clear_selection();
|
||||
}
|
||||
},
|
||||
|
||||
dragend:function(e){
|
||||
if(!this.jm.get_editable()){return;}
|
||||
if(this.capture){
|
||||
if(this.hlookup_delay != 0){
|
||||
$w.clearTimeout(this.hlookup_delay);
|
||||
this.hlookup_delay = 0;
|
||||
this.clear_lines();
|
||||
}
|
||||
if(this.hlookup_timer != 0){
|
||||
$w.clearInterval(this.hlookup_timer);
|
||||
this.hlookup_timer = 0;
|
||||
this.clear_lines();
|
||||
}
|
||||
if(this.moved){
|
||||
var src_node = this.active_node;
|
||||
var target_node = this.target_node;
|
||||
var target_direct = this.target_direct;
|
||||
this.move_node(src_node,target_node,target_direct);
|
||||
}
|
||||
this.hide_shadow();
|
||||
}
|
||||
this.moved = false;
|
||||
this.capture = false;
|
||||
},
|
||||
|
||||
move_node:function(src_node,target_node,target_direct){
|
||||
var shadow_h = this.shadow.offsetTop;
|
||||
if(!!target_node && !!src_node && !jsMind.node.inherited(src_node, target_node)){
|
||||
// lookup before_node
|
||||
var sibling_nodes = target_node.children;
|
||||
var sc = sibling_nodes.length;
|
||||
var node = null;
|
||||
var delta_y = Number.MAX_VALUE;
|
||||
var node_before = null;
|
||||
var beforeid = '_last_';
|
||||
while(sc--){
|
||||
node = sibling_nodes[sc];
|
||||
if(node.direction == target_direct && node.id != src_node.id){
|
||||
var dy = node.get_location().y - shadow_h;
|
||||
if(dy > 0 && dy < delta_y){
|
||||
delta_y = dy;
|
||||
node_before = node;
|
||||
beforeid = '_first_';
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!!node_before){beforeid = node_before.id;}
|
||||
this.jm.move_node(src_node.id, beforeid, target_node.id, target_direct);
|
||||
}
|
||||
this.active_node = null;
|
||||
this.target_node = null;
|
||||
this.target_direct = null;
|
||||
},
|
||||
|
||||
jm_event_handle:function(type,data){
|
||||
if(type === jsMind.event_type.resize){
|
||||
this.resize();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var draggable_plugin = new jsMind.plugin('draggable',function(jm){
|
||||
var jd = new jsMind.draggable(jm);
|
||||
jd.init();
|
||||
jm.add_event_listener(function(type,data){
|
||||
jd.jm_event_handle.call(jd,type,data);
|
||||
});
|
||||
});
|
||||
|
||||
jsMind.register_plugin(draggable_plugin);
|
||||
|
||||
})(window);
|
||||
2930
lisp/emacs-application-framework/app/mindmap/jsmind.js
Normal file
2930
lisp/emacs-application-framework/app/mindmap/jsmind.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Released under BSD License
|
||||
* Copyright (c) 2014-2015 hizzgdev@163.com
|
||||
*
|
||||
* Project Home:
|
||||
* https://github.com/hizzgdev/jsmind/
|
||||
*/
|
||||
|
||||
(function($w){
|
||||
'use strict';
|
||||
|
||||
var __name__ = 'jsMind';
|
||||
var jsMind = $w[__name__];
|
||||
if(!jsMind){return;}
|
||||
if(typeof jsMind.screenshot != 'undefined'){return;}
|
||||
|
||||
var $d = $w.document;
|
||||
var $c = function(tag){return $d.createElement(tag);};
|
||||
|
||||
var css = function(cstyle,property_name){
|
||||
return cstyle.getPropertyValue(property_name);
|
||||
};
|
||||
var is_visible = function(cstyle){
|
||||
var visibility = css(cstyle,'visibility');
|
||||
var display = css(cstyle,'display');
|
||||
return (visibility !== 'hidden' && display !== 'none');
|
||||
};
|
||||
var jcanvas = jsMind.util.canvas;
|
||||
jcanvas.rect = function (ctx,x,y,w,h,r) {
|
||||
if (w < 2 * r) r = w / 2;
|
||||
if (h < 2 * r) r = h / 2;
|
||||
ctx.moveTo(x+r, y);
|
||||
ctx.arcTo(x+w, y, x+w, y+h, r);
|
||||
ctx.arcTo(x+w, y+h, x, y+h, r);
|
||||
ctx.arcTo(x, y+h, x, y, r);
|
||||
ctx.arcTo(x, y, x+w, y, r);
|
||||
};
|
||||
|
||||
jcanvas.text_multiline = function(ctx,text,x,y,w,h,lineheight){
|
||||
var line = '';
|
||||
var text_len = text.length;
|
||||
var chars = text.split('');
|
||||
var test_line = null;
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'top';
|
||||
for(var i=0;i<text_len;i++){
|
||||
test_line = line + chars[i];
|
||||
if(ctx.measureText(test_line).width > w && i>0){
|
||||
ctx.fillText(line,x,y);
|
||||
line = chars[i];
|
||||
y += lineheight;
|
||||
}else{
|
||||
line = test_line;
|
||||
}
|
||||
}
|
||||
ctx.fillText(line,x,y);
|
||||
};
|
||||
|
||||
jcanvas.text_ellipsis = function(ctx,text,x,y,w,h){
|
||||
var center_y = y+h/2;
|
||||
var text = jcanvas.fittingString(ctx,text,w);
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(text,x,center_y,w);
|
||||
};
|
||||
|
||||
jcanvas.fittingString = function(ctx,text,max_width) {
|
||||
var width = ctx.measureText(text).width;
|
||||
var ellipsis = '…'
|
||||
var ellipsis_width = ctx.measureText(ellipsis).width;
|
||||
if (width<=max_width || width<=ellipsis_width) {
|
||||
return text;
|
||||
} else {
|
||||
var len = text.length;
|
||||
while (width>=max_width-ellipsis_width && len-->0) {
|
||||
text = text.substring(0, len);
|
||||
width = ctx.measureText(text).width;
|
||||
}
|
||||
return text+ellipsis;
|
||||
}
|
||||
};
|
||||
|
||||
jcanvas.image = function(ctx,backgroundUrl,x,y,w,h,r,rotation,callback){
|
||||
var img = new Image();
|
||||
img.onload = function () {
|
||||
ctx.save();
|
||||
ctx.translate(x,y);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
jcanvas.rect(ctx,0,0,w,h,r);
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
ctx.translate(w/2,h/2);
|
||||
ctx.rotate(rotation*Math.PI/180);
|
||||
ctx.drawImage(img,-w/2,-h/2);
|
||||
ctx.restore();
|
||||
ctx.restore();
|
||||
callback();
|
||||
}
|
||||
img.src = backgroundUrl;
|
||||
};
|
||||
|
||||
jsMind.screenshot = function(jm){
|
||||
this.jm = jm;
|
||||
this.canvas_elem = null;
|
||||
this.canvas_ctx = null;
|
||||
this._inited = false;
|
||||
};
|
||||
|
||||
jsMind.screenshot.prototype = {
|
||||
init:function(){
|
||||
if(this._inited){return;}
|
||||
console.log('init');
|
||||
var c = $c('canvas');
|
||||
var ctx = c.getContext('2d');
|
||||
|
||||
this.canvas_elem = c;
|
||||
this.canvas_ctx = ctx;
|
||||
this.jm.view.e_panel.appendChild(c);
|
||||
this._inited = true;
|
||||
this.resize();
|
||||
},
|
||||
|
||||
shoot:function(callback){
|
||||
this.init();
|
||||
this._watermark();
|
||||
var jms = this;
|
||||
this._draw(function(){
|
||||
if(!!callback){
|
||||
callback(jms);
|
||||
}
|
||||
jms.clean();
|
||||
});
|
||||
},
|
||||
|
||||
shootDownload: function(){
|
||||
this.shoot(function(jms){
|
||||
jms._download();
|
||||
});
|
||||
},
|
||||
|
||||
shootAsDataURL: function(callback){
|
||||
this.shoot(function(jms){
|
||||
callback(jms.canvas_elem.toDataURL());
|
||||
});
|
||||
},
|
||||
|
||||
resize:function(){
|
||||
if(this._inited){
|
||||
this.canvas_elem.width=this.jm.view.size.w;
|
||||
this.canvas_elem.height=this.jm.view.size.h;
|
||||
}
|
||||
},
|
||||
|
||||
clean:function(){
|
||||
var c = this.canvas_elem;
|
||||
this.canvas_ctx.clearRect(0,0,c.width,c.height);
|
||||
},
|
||||
|
||||
_draw:function(callback){
|
||||
var ctx = this.canvas_ctx;
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'top';
|
||||
this._draw_lines();
|
||||
this._draw_nodes(callback);
|
||||
},
|
||||
|
||||
_watermark:function(){
|
||||
var c = this.canvas_elem;
|
||||
var ctx = this.canvas_ctx;
|
||||
ctx.textAlign='right';
|
||||
ctx.textBaseline='bottom';
|
||||
ctx.fillStyle='#000';
|
||||
ctx.font='11px Verdana,Arial,Helvetica,sans-serif';
|
||||
ctx.fillText('hizzgdev.github.io/jsmind',c.width-5.5,c.height-2.5);
|
||||
ctx.textAlign='left';
|
||||
ctx.fillText($w.location,5.5,c.height-2.5);
|
||||
},
|
||||
|
||||
_draw_lines:function(){
|
||||
this.jm.view.show_lines(this.canvas_ctx, true);
|
||||
},
|
||||
|
||||
_draw_nodes:function(callback){
|
||||
var nodes = this.jm.mind.nodes;
|
||||
var node;
|
||||
for(var nodeid in nodes){
|
||||
node = nodes[nodeid];
|
||||
this._draw_node(node);
|
||||
}
|
||||
|
||||
function check_nodes_ready() {
|
||||
console.log('check_node_ready'+new Date());
|
||||
var allOk = true;
|
||||
for(var nodeid in nodes){
|
||||
node = nodes[nodeid];
|
||||
allOk = allOk & node.ready;
|
||||
}
|
||||
|
||||
if(!allOk) {
|
||||
$w.setTimeout(check_nodes_ready, 200);
|
||||
} else {
|
||||
$w.setTimeout(callback, 200);
|
||||
}
|
||||
}
|
||||
check_nodes_ready();
|
||||
},
|
||||
|
||||
_draw_node:function(node){
|
||||
var ctx = this.canvas_ctx;
|
||||
var view_data = node._data.view;
|
||||
var node_element = view_data.element;
|
||||
var ncs = getComputedStyle(node_element);
|
||||
if(!is_visible(ncs)){
|
||||
node.ready = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var bgcolor = css(ncs,'background-color');
|
||||
var round_radius = parseInt(css(ncs,'border-top-left-radius'));
|
||||
var color = css(ncs,'color');
|
||||
var padding_left = parseInt(css(ncs,'padding-left'));
|
||||
var padding_right = parseInt(css(ncs,'padding-right'));
|
||||
var padding_top = parseInt(css(ncs,'padding-top'));
|
||||
var padding_bottom = parseInt(css(ncs,'padding-bottom'));
|
||||
var text_overflow = css(ncs,'text-overflow');
|
||||
var font = css(ncs,'font-style')+' '+
|
||||
css(ncs,'font-variant')+' '+
|
||||
css(ncs,'font-weight')+' '+
|
||||
css(ncs,'font-size')+'/'+css(ncs,'line-height')+' '+
|
||||
css(ncs,'font-family');
|
||||
|
||||
var rb = {x:view_data.abs_x,
|
||||
y:view_data.abs_y,
|
||||
w:view_data.width+1,
|
||||
h:view_data.height+1};
|
||||
var tb = {x:rb.x+padding_left,
|
||||
y:rb.y+padding_top,
|
||||
w:rb.w-padding_left-padding_right,
|
||||
h:rb.h-padding_top-padding_bottom};
|
||||
|
||||
ctx.font=font;
|
||||
ctx.fillStyle = bgcolor;
|
||||
ctx.beginPath();
|
||||
jcanvas.rect(ctx, rb.x, rb.y, rb.w, rb.h, round_radius);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = color;
|
||||
if ('background-image' in node.data) {
|
||||
var backgroundUrl = css(ncs,'background-image').slice(5, -2);
|
||||
node.ready = false;
|
||||
var rotation = 0;
|
||||
if ('background-rotation' in node.data) {
|
||||
rotation = node.data['background-rotation'];
|
||||
}
|
||||
jcanvas.image(ctx, backgroundUrl, rb.x, rb.y, rb.w, rb.h, round_radius, rotation,
|
||||
function() {
|
||||
node.ready = true;
|
||||
});
|
||||
}
|
||||
if (!!node.topic) {
|
||||
if(text_overflow === 'ellipsis'){
|
||||
jcanvas.text_ellipsis(ctx, node.topic, tb.x, tb.y, tb.w, tb.h);
|
||||
}else{
|
||||
var line_height = parseInt(css(ncs,'line-height'));
|
||||
jcanvas.text_multiline(ctx, node.topic, tb.x, tb.y, tb.w, tb.h,line_height);
|
||||
}
|
||||
}
|
||||
if(!!view_data.expander){
|
||||
this._draw_expander(view_data.expander);
|
||||
}
|
||||
if (!('background-image' in node.data)) {
|
||||
node.ready = true;
|
||||
}
|
||||
},
|
||||
|
||||
_draw_expander:function(expander){
|
||||
var ctx = this.canvas_ctx;
|
||||
var ncs = getComputedStyle(expander);
|
||||
if(!is_visible(ncs)){ return; }
|
||||
|
||||
var style_left = css(ncs,'left');
|
||||
var style_top = css(ncs,'top');
|
||||
var font = css(ncs,'font');
|
||||
var left = parseInt(style_left);
|
||||
var top = parseInt(style_top);
|
||||
var is_plus = expander.innerHTML === '+';
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(left+7,top+7,5,0,Math.PI*2,true);
|
||||
ctx.moveTo(left+10,top+7);
|
||||
ctx.lineTo(left+4,top+7);
|
||||
if(is_plus){
|
||||
ctx.moveTo(left+7,top+4);
|
||||
ctx.lineTo(left+7,top+10);
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
},
|
||||
|
||||
_download:function(){
|
||||
var c = this.canvas_elem;
|
||||
var name = this.jm.mind.name+'.png';
|
||||
|
||||
if (navigator.msSaveBlob && (!!c.msToBlob)) {
|
||||
var blob = c.msToBlob();
|
||||
navigator.msSaveBlob(blob,name);
|
||||
} else {
|
||||
var bloburl = this.canvas_elem.toDataURL();
|
||||
var anchor = $c('a');
|
||||
if ('download' in anchor) {
|
||||
anchor.style.visibility = 'hidden';
|
||||
anchor.href = bloburl;
|
||||
anchor.download = name;
|
||||
$d.body.appendChild(anchor);
|
||||
var evt = $d.createEvent('MouseEvents');
|
||||
evt.initEvent('click', true, true);
|
||||
anchor.dispatchEvent(evt);
|
||||
$d.body.removeChild(anchor);
|
||||
} else {
|
||||
location.href = bloburl;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
jm_event_handle:function(type,data){
|
||||
if(type === jsMind.event_type.resize){
|
||||
this.resize();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var screenshot_plugin = new jsMind.plugin('screenshot',function(jm){
|
||||
var jss = new jsMind.screenshot(jm);
|
||||
jm.screenshot = jss;
|
||||
jm.shoot = function(){
|
||||
jss.shoot();
|
||||
};
|
||||
jm.add_event_listener(function(type,data){
|
||||
jss.jm_event_handle.call(jss,type,data);
|
||||
});
|
||||
});
|
||||
|
||||
jsMind.register_plugin(screenshot_plugin);
|
||||
|
||||
})(window);
|
||||
Reference in New Issue
Block a user