add lisp packages

This commit is contained in:
2020-12-05 21:29:49 +01:00
parent 85e20365ae
commit a6e2395755
7272 changed files with 1363243 additions and 0 deletions

View 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)

View 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

View 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>

View 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 {}

View 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);

File diff suppressed because it is too large Load Diff

View 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 __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);