UNPKG

11.8 kBJavaScriptView Raw
1/**
2 * @license BSD
3 * @copyright 2014-2023 hizzgdev@163.com
4 *
5 * Project Home:
6 * https://github.com/hizzgdev/jsmind/
7 */
8
9(function ($w) {
10 'use strict';
11 console.warn("The version is outdated. see details: https://hizzgdev.github.io/jsmind/es6/")
12 var __name__ = 'jsMind';
13 var jsMind = $w[__name__];
14 if (!jsMind) { return; }
15 if (typeof jsMind.screenshot != 'undefined') { return; }
16
17 var $d = $w.document;
18 var $c = function (tag) { return $d.createElement(tag); };
19
20 var css = function (cstyle, property_name) {
21 return cstyle.getPropertyValue(property_name);
22 };
23 var is_visible = function (cstyle) {
24 var visibility = css(cstyle, 'visibility');
25 var display = css(cstyle, 'display');
26 return (visibility !== 'hidden' && display !== 'none');
27 };
28 var jcanvas = {};
29 jcanvas.rect = function (ctx, x, y, w, h, r) {
30 if (w < 2 * r) r = w / 2;
31 if (h < 2 * r) r = h / 2;
32 ctx.moveTo(x + r, y);
33 ctx.arcTo(x + w, y, x + w, y + h, r);
34 ctx.arcTo(x + w, y + h, x, y + h, r);
35 ctx.arcTo(x, y + h, x, y, r);
36 ctx.arcTo(x, y, x + w, y, r);
37 };
38
39 jcanvas.text_multiline = function (ctx, text, x, y, w, h, lineheight) {
40 var line = '';
41 var text_len = text.length;
42 var chars = text.split('');
43 var test_line = null;
44 ctx.textAlign = 'left';
45 ctx.textBaseline = 'top';
46 for (var i = 0; i < text_len; i++) {
47 test_line = line + chars[i];
48 if (ctx.measureText(test_line).width > w && i > 0) {
49 ctx.fillText(line, x, y);
50 line = chars[i];
51 y += lineheight;
52 } else {
53 line = test_line;
54 }
55 }
56 ctx.fillText(line, x, y);
57 };
58
59 jcanvas.text_ellipsis = function (ctx, text, x, y, w, h) {
60 var center_y = y + h / 2;
61 var text = jcanvas.fittingString(ctx, text, w);
62 ctx.textAlign = 'left';
63 ctx.textBaseline = 'middle';
64 ctx.fillText(text, x, center_y, w);
65 };
66
67 jcanvas.fittingString = function (ctx, text, max_width) {
68 var width = ctx.measureText(text).width;
69 var ellipsis = '…';
70 var ellipsis_width = ctx.measureText(ellipsis).width;
71 if (width <= max_width || width <= ellipsis_width) {
72 return text;
73 } else {
74 var len = text.length;
75 while (width >= max_width - ellipsis_width && len-- > 0) {
76 text = text.substring(0, len);
77 width = ctx.measureText(text).width;
78 }
79 return text + ellipsis;
80 }
81 };
82
83 jcanvas.image = function (ctx, url, x, y, w, h, r, rotation, callback) {
84 var img = new Image();
85 img.onload = function () {
86 ctx.save();
87 ctx.translate(x, y);
88 ctx.save();
89 ctx.beginPath();
90 jcanvas.rect(ctx, 0, 0, w, h, r);
91 ctx.closePath();
92 ctx.clip();
93 ctx.translate(w / 2, h / 2);
94 ctx.rotate(rotation * Math.PI / 180);
95 ctx.drawImage(img, -w / 2, -h / 2);
96 ctx.restore();
97 ctx.restore();
98 !!callback && callback();
99 }
100 img.src = url;
101 };
102
103 jsMind.screenshot = function (jm) {
104 this.jm = jm;
105 this.canvas_elem = null;
106 this.canvas_ctx = null;
107 this._inited = false;
108 };
109
110 jsMind.screenshot.prototype = {
111 init: function () {
112 if (this._inited) { return; }
113 console.log('init');
114 var c = $c('canvas');
115 var ctx = c.getContext('2d');
116
117 this.canvas_elem = c;
118 this.canvas_ctx = ctx;
119 this.jm.view.e_panel.appendChild(c);
120 this._inited = true;
121 this.resize();
122 },
123
124 shoot: function (callback) {
125 this.init();
126 this._draw(function () {
127 !!callback && callback();
128 this.clean();
129 }.bind(this));
130 this._watermark();
131 },
132
133 shootDownload: function () {
134 this.shoot(function () {
135 this._download();
136 }.bind(this));
137 },
138
139 shootAsDataURL: function (callback) {
140 this.shoot(function () {
141 !!callback && callback(this.canvas_elem.toDataURL());
142 }.bind(this));
143 },
144
145 resize: function () {
146 if (this._inited) {
147 this.canvas_elem.width = this.jm.view.size.w;
148 this.canvas_elem.height = this.jm.view.size.h;
149 }
150 },
151
152 clean: function () {
153 var c = this.canvas_elem;
154 this.canvas_ctx.clearRect(0, 0, c.width, c.height);
155 },
156
157 _draw: function (callback) {
158 var ctx = this.canvas_ctx;
159 ctx.textAlign = 'left';
160 ctx.textBaseline = 'top';
161 this._draw_lines(function () {
162 this._draw_nodes(callback);
163 }.bind(this));
164 },
165
166 _watermark: function () {
167 var c = this.canvas_elem;
168 var ctx = this.canvas_ctx;
169 ctx.textAlign = 'right';
170 ctx.textBaseline = 'bottom';
171 ctx.fillStyle = '#000';
172 ctx.font = '11px Verdana,Arial,Helvetica,sans-serif';
173 ctx.fillText('github.com/hizzgdev/jsmind', c.width - 5.5, c.height - 2.5);
174 ctx.textAlign = 'left';
175 ctx.fillText($w.location, 5.5, c.height - 2.5);
176 },
177
178 _draw_lines: function (callback) {
179 this.jm.view.graph.copy_to(this.canvas_ctx, callback);
180 },
181
182 _draw_nodes: function (callback) {
183 var nodes = this.jm.mind.nodes;
184 var node;
185 for (var nodeid in nodes) {
186 node = nodes[nodeid];
187 this._draw_node(node);
188 }
189
190 function check_nodes_ready() {
191 console.log('check_node_ready' + new Date());
192 var allOk = true;
193 for (var nodeid in nodes) {
194 node = nodes[nodeid];
195 allOk = allOk & node.ready;
196 }
197
198 if (!allOk) {
199 $w.setTimeout(check_nodes_ready, 200);
200 } else {
201 $w.setTimeout(callback, 200);
202 }
203 }
204 check_nodes_ready();
205 },
206
207 _draw_node: function (node) {
208 var ctx = this.canvas_ctx;
209 var view_data = node._data.view;
210 var node_element = view_data.element;
211 var ncs = getComputedStyle(node_element);
212 if (!is_visible(ncs)) {
213 node.ready = true;
214 return;
215 }
216
217 var bgcolor = css(ncs, 'background-color');
218 var round_radius = parseInt(css(ncs, 'border-top-left-radius'));
219 var color = css(ncs, 'color');
220 var padding_left = parseInt(css(ncs, 'padding-left'));
221 var padding_right = parseInt(css(ncs, 'padding-right'));
222 var padding_top = parseInt(css(ncs, 'padding-top'));
223 var padding_bottom = parseInt(css(ncs, 'padding-bottom'));
224 var text_overflow = css(ncs, 'text-overflow');
225 var font = css(ncs, 'font-style') + ' ' +
226 css(ncs, 'font-variant') + ' ' +
227 css(ncs, 'font-weight') + ' ' +
228 css(ncs, 'font-size') + '/' + css(ncs, 'line-height') + ' ' +
229 css(ncs, 'font-family');
230
231 var rb = {
232 x: view_data.abs_x,
233 y: view_data.abs_y,
234 w: view_data.width + 1,
235 h: view_data.height + 1
236 };
237 var tb = {
238 x: rb.x + padding_left,
239 y: rb.y + padding_top,
240 w: rb.w - padding_left - padding_right,
241 h: rb.h - padding_top - padding_bottom
242 };
243
244 ctx.font = font;
245 ctx.fillStyle = bgcolor;
246 ctx.beginPath();
247 jcanvas.rect(ctx, rb.x, rb.y, rb.w, rb.h, round_radius);
248 ctx.closePath();
249 ctx.fill();
250
251 ctx.fillStyle = color;
252 if ('background-image' in node.data) {
253 var backgroundUrl = css(ncs, 'background-image').slice(5, -2);
254 node.ready = false;
255 var rotation = 0;
256 if ('background-rotation' in node.data) {
257 rotation = node.data['background-rotation'];
258 }
259 jcanvas.image(ctx, backgroundUrl, rb.x, rb.y, rb.w, rb.h, round_radius, rotation,
260 function () {
261 node.ready = true;
262 });
263 }
264 if (!!node.topic) {
265 if (text_overflow === 'ellipsis') {
266 jcanvas.text_ellipsis(ctx, node.topic, tb.x, tb.y, tb.w, tb.h);
267 } else {
268 var line_height = parseInt(css(ncs, 'line-height'));
269 jcanvas.text_multiline(ctx, node.topic, tb.x, tb.y, tb.w, tb.h, line_height);
270 }
271 }
272 if (!!view_data.expander) {
273 this._draw_expander(view_data.expander);
274 }
275 if (!('background-image' in node.data)) {
276 node.ready = true;
277 }
278 },
279
280 _draw_expander: function (expander) {
281 var ctx = this.canvas_ctx;
282 var ncs = getComputedStyle(expander);
283 if (!is_visible(ncs)) { return; }
284
285 var style_left = css(ncs, 'left');
286 var style_top = css(ncs, 'top');
287 var font = css(ncs, 'font');
288 var left = parseInt(style_left);
289 var top = parseInt(style_top);
290 var is_plus = expander.innerHTML === '+';
291
292 ctx.lineWidth = 1;
293
294 ctx.beginPath();
295 ctx.arc(left + 7, top + 7, 5, 0, Math.PI * 2, true);
296 ctx.moveTo(left + 10, top + 7);
297 ctx.lineTo(left + 4, top + 7);
298 if (is_plus) {
299 ctx.moveTo(left + 7, top + 4);
300 ctx.lineTo(left + 7, top + 10);
301 }
302 ctx.closePath();
303 ctx.stroke();
304 },
305
306 _download: function () {
307 var c = this.canvas_elem;
308 var name = this.jm.mind.name + '.png';
309
310 if (navigator.msSaveBlob && (!!c.msToBlob)) {
311 var blob = c.msToBlob();
312 navigator.msSaveBlob(blob, name);
313 } else {
314 var bloburl = this.canvas_elem.toDataURL();
315 var anchor = $c('a');
316 if ('download' in anchor) {
317 anchor.style.visibility = 'hidden';
318 anchor.href = bloburl;
319 anchor.download = name;
320 $d.body.appendChild(anchor);
321 var evt = $d.createEvent('MouseEvents');
322 evt.initEvent('click', true, true);
323 anchor.dispatchEvent(evt);
324 $d.body.removeChild(anchor);
325 } else {
326 location.href = bloburl;
327 }
328 }
329 },
330
331 jm_event_handle: function (type, data) {
332 if (type === jsMind.event_type.resize) {
333 this.resize();
334 }
335 }
336 };
337
338 var screenshot_plugin = new jsMind.plugin('screenshot', function (jm) {
339 var jss = new jsMind.screenshot(jm);
340 jm.screenshot = jss;
341 jm.shoot = function () {
342 jss.shoot();
343 };
344 jm.add_event_listener(function (type, data) {
345 jss.jm_event_handle.call(jss, type, data);
346 });
347 });
348
349 jsMind.register_plugin(screenshot_plugin);
350
351})(window);