352 lines
12 KiB
JavaScript
352 lines
12 KiB
JavaScript
/**
|
|
* @license BSD
|
|
* @copyright 2014-2025 hizzgdev@163.com
|
|
*
|
|
* Project Home:
|
|
* https://github.com/hizzgdev/jsmind/
|
|
*/
|
|
|
|
(function ($w) {
|
|
'use strict';
|
|
console.warn("The version is outdated. see details: https://hizzgdev.github.io/jsmind/es6/")
|
|
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 = {};
|
|
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, url, 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 && callback();
|
|
}
|
|
img.src = url;
|
|
};
|
|
|
|
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._draw(function () {
|
|
!!callback && callback();
|
|
this.clean();
|
|
}.bind(this));
|
|
this._watermark();
|
|
},
|
|
|
|
shootDownload: function () {
|
|
this.shoot(function () {
|
|
this._download();
|
|
}.bind(this));
|
|
},
|
|
|
|
shootAsDataURL: function (callback) {
|
|
this.shoot(function () {
|
|
!!callback && callback(this.canvas_elem.toDataURL());
|
|
}.bind(this));
|
|
},
|
|
|
|
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(function () {
|
|
this._draw_nodes(callback);
|
|
}.bind(this));
|
|
},
|
|
|
|
_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('github.com/hizzgdev/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 (callback) {
|
|
this.jm.view.graph.copy_to(this.canvas_ctx, callback);
|
|
},
|
|
|
|
_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);
|