909 lines
37 KiB
JavaScript
Executable File
909 lines
37 KiB
JavaScript
Executable File
JSReports = function(input){
|
|
// Require html2canvas.min.js, purify.min.js & jspdf.umd.min.js for PDF files.
|
|
|
|
const self = this,
|
|
default_settings = {
|
|
nulls : false,
|
|
default_value : null,
|
|
timeout : 2000,
|
|
cache_box : "body",
|
|
preload_show_exception : true,
|
|
preload_timeout : 2000,
|
|
frames_per_second : 24,
|
|
default_cache_box : "body",
|
|
width : 17,
|
|
margin_top : 20,
|
|
margin_left : 20,
|
|
margin_right : 20,
|
|
margin_bottom : 20,
|
|
// margin : [50, 50, 50, 40], // [top, right, bottom, left]
|
|
proxy : null,
|
|
autostart : true,
|
|
hash_alphabet : "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
|
hash_length : 13,
|
|
default_list_mode : "ul",
|
|
dpi : 96,
|
|
page_format : "a4",
|
|
margin_footer : 5,
|
|
margin_header : 5,
|
|
header_height : 30,
|
|
footer_height : 10,
|
|
date_format : "{dd}/{mm}/{yyyy} {hh}:{ii}:{ss}",
|
|
months : [
|
|
"January", "Febrary", "March", "April", "May", "June", "July", "August",
|
|
"September", "October", "November", "December"
|
|
],
|
|
week_days : [
|
|
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
|
|
],
|
|
// pdf.addFont("/data/OpenSans-Regular.ttf", "Open Sans", "normal");
|
|
default_fonts : [
|
|
["FA5FB", "https://cdn.k3y.pw/fonts/FontAwesome/5.15/fa-brands-400.ttf", "normal"],
|
|
["FA5FR", "https://cdn.k3y.pw/fonts/FontAwesome/5.15/fa-regular-400.ttf", "normal"],
|
|
["FA5FS", "https://cdn.k3y.pw/fonts/FontAwesome/5.15/fa-solid-900.ttf", "normal"],
|
|
// ["Open Sans Condensed", "https://cdn.k3y.pw/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Bold.ttf", "bold"],
|
|
// ["Open Sans Condensed", "https://cdn.k3y.pw/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Medium.ttf", "normal"],
|
|
// ["Open Sans Condensed", "https://cdn.k3y.pw/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Italic.ttf", "italic"],
|
|
// ["Open Sans", "https://cdn.k3y.pw/fonts/Open_Sans/static/OpenSans/OpenSans-Bold.ttf", "bold"],
|
|
// ["Open Sans", "https://cdn.k3y.pw/fonts/Open_Sans/static/OpenSans/OpenSans-Medium.ttf", "normal"],
|
|
// ["Open Sans", "https://cdn.k3y.pw/fonts/Open_Sans/static/OpenSans/OpenSans-Italic.ttf", "italic"]
|
|
["Open Sans", "https://cdn.k3y.pw/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf", "normal"],
|
|
["Oxygen", "https://cdn.k3y.pw/fonts/Oxygen/Oxygen-Regular.ttf", "normal"],
|
|
["Roboto", "https://cdn.k3y.pw/fonts/Roboto/Roboto-Medium.ttf", "normal"],
|
|
["Roboto Mono", "https://cdn.k3y.pw/fonts/Roboto_Mono/RobotoMono-VariableFont_wght.ttf", "normal"],
|
|
["Source Code Pro", "https://cdn.k3y.pw/fonts/Source_Code_Pro/SourceCodePro-VariableFont_wght.ttf", "normal"],
|
|
["Ubuntu", "https://cdn.k3y.pw/fonts/Ubuntu/Ubuntu-Medium.ttf", "normal"],
|
|
["Ubuntu Mono", "https://cdn.k3y.pw/fonts/Ubuntu_Mono/UbuntuMono-Regular.ttf", "normal"]
|
|
]
|
|
},
|
|
events = {},
|
|
hashes = [],
|
|
fonts = {};
|
|
let started = false,
|
|
cache_box = document,
|
|
proxy = null,
|
|
thread = null,
|
|
is_ready = false,
|
|
months = [],
|
|
week_days = [];
|
|
|
|
const event_execute = this.event_execute = key => events[key] && events[key].forEach(event => event && event());
|
|
|
|
const event_add = this.event_add = (key, method) => {
|
|
if(!key || typeof method != "function")
|
|
return null;
|
|
|
|
let i = 0;
|
|
const l = (events[key] || (events[key] = [])).length;
|
|
|
|
for(; i < l; i ++)
|
|
if(!events[key][i])
|
|
break;
|
|
|
|
events[key][i] = method;
|
|
|
|
return i;
|
|
};
|
|
|
|
const event_remove = this.event_remove = (key, i) => key && !isNaN(i) && events[key] && events[key][i] && (events[key][i] = null);
|
|
|
|
const is_html_item = this.is_html_item = item => item && (item.tagName || item.nodeName);
|
|
|
|
const default_value = this.default_value = (_default, nulls) => _default !== undefined && (_default !== null || (typeof nulls == "boolean" ? nulls : settings("nulls", null, false, false))) ? _default : settings(["default_value", "default"], null, null, true);
|
|
|
|
const settings = this.settings = (names, inputs, _default, nulls) => {
|
|
if(!names)
|
|
return default_value(_default, nulls);
|
|
|
|
const l = (names instanceof Array ? names : names = [names]).length,
|
|
m = (inputs = (typeof inputs == "object" ? inputs instanceof Array ? inputs : [inputs] : []).concat([input, default_settings])).length;
|
|
|
|
typeof nulls != "boolean" && (nulls = settings("nulls", null, false, false));
|
|
|
|
for(let j = 0; j < m; j ++)
|
|
if(inputs[j] && typeof inputs[j] == "object")
|
|
for(let i = 0; i < l; i ++)
|
|
if(names[i] && inputs[j][names[i]] !== undefined && (nulls || inputs[j][names[i]] !== null))
|
|
return inputs[j][names[i]];
|
|
return default_value(_default, nulls);
|
|
};
|
|
|
|
const threads_method = () => event_execute("threads");
|
|
|
|
const threads_start = this.threads_start = frames_per_second => thread === null && (thread = setInterval(threads_method, 1000 / (isNaN(frames_per_second) ? settings(["frames_per_second", "fps"]) : frames_per_second)));
|
|
|
|
this.threads_stop = () => {
|
|
if(thread === null)
|
|
return;
|
|
|
|
clearInterval(thread);
|
|
thread = null;
|
|
|
|
};
|
|
|
|
const threads_add = this.threads_add = method => event_add("threads", method);
|
|
|
|
const threads_remove = this.threads_remove = i => event_remove("threads", i);
|
|
|
|
const preload = this.preload = (selector, callback) => {
|
|
if(typeof callback != "function")
|
|
return;
|
|
|
|
if(!selector){
|
|
callback(null, false, "NO_CALLBACK");
|
|
return;
|
|
};
|
|
if(is_html_item(selector)){
|
|
callback(selector, false, "OK");
|
|
return;
|
|
};
|
|
if(typeof selector != "string"){
|
|
callback(null, false, "NOT_SELECTOR");
|
|
return;
|
|
};
|
|
|
|
let item;
|
|
|
|
try{
|
|
if(item = document.querySelector(selector)){
|
|
callback(item, false, "OK");
|
|
return;
|
|
};
|
|
}catch(exception){
|
|
settings(["preload_show_exception", "show_exception"]) && console.error(excetpion);
|
|
callback(null, false, "BAD_SELECTOR");
|
|
return;
|
|
};
|
|
|
|
const date = Date.now(),
|
|
timeout = settings(["preload_timeout", "timeout"]),
|
|
thread = threads_add(() => {
|
|
if(item = document.querySelector(selector)){
|
|
callback(item, true, "OK");
|
|
threads_remove(thread);
|
|
}else if(Date.now() - date > timeout){
|
|
callback(null, true, "TIMEOUT");
|
|
threads_remove(thread);
|
|
};
|
|
});
|
|
|
|
};
|
|
|
|
const ready = position => {
|
|
|
|
is_ready = true;
|
|
cache_box = position;
|
|
|
|
event_execute("on_ready");
|
|
|
|
};
|
|
|
|
this.start = () => {
|
|
|
|
if(started)
|
|
return;
|
|
started = true;
|
|
|
|
threads_start();
|
|
|
|
proxy = settings("proxy");
|
|
|
|
preload(settings("cache_box"), position => position ? ready(position) : preload(settings("default_cache_box"), ready));
|
|
|
|
};
|
|
|
|
this.on_ready = method => is_ready ? method() : event_add("on_ready", method);
|
|
|
|
const string_variables = this.string_variables = (string, variables, _default) => {
|
|
|
|
const l = (variables = variables ? variables instanceof Array ? variables : typeof variables == "object" ? [variables] : [] : []).length;
|
|
|
|
return string.replace(/\{([^\{\}]+)\}/g, (...arguments) => {
|
|
for(let i = 0; i < l; i ++)
|
|
if(variables[i][arguments[1]] !== undefined)
|
|
return variables[i][arguments[1]];
|
|
return _default !== undefined ? _default : arguments[0];
|
|
});
|
|
};
|
|
|
|
const base64_to_blob = this.base64_to_blob = (uri, type) => {
|
|
|
|
const array = atob(uri.split(",")[1]),
|
|
l = array.length,
|
|
buffer = new ArrayBuffer(l),
|
|
integers = new Uint8Array(buffer);
|
|
|
|
for(let i = 0; i < l; i ++)
|
|
buffer[i] = array.charCodeAt(i);
|
|
|
|
return new Blob([buffer], {type : type});
|
|
};
|
|
|
|
const hash = this.hash = () => {
|
|
|
|
const alphabet = settings(["hash_alphabet", "alphabet"]),
|
|
l = alphabet.length,
|
|
length = settings(["hash_length", "length"]);
|
|
let hash;
|
|
|
|
do{
|
|
hash = "";
|
|
while((hash += alphabet[Math.random() * l >> 0]).length < length);
|
|
}while(
|
|
hashes.includes(hash) ||
|
|
/^[0-9]/.test(hash) ||
|
|
document.querySelector("." + hash + ",#" + hash + ",[name=" + hash + "]")
|
|
);
|
|
hashes.push(hash);
|
|
|
|
return hash;
|
|
};
|
|
|
|
const grid_to_html = this.grid_to_html = (grid, name) => {
|
|
|
|
let html = (`
|
|
<table class="grid` + (name ? " " + name : "") + `">
|
|
<thead>
|
|
<tr>
|
|
`);
|
|
|
|
grid.header.forEach((header, j) => html += `<th data-j="` + j + `">` + header + `</th>`);
|
|
html += (`
|
|
</th>
|
|
</thead>
|
|
<tbody>
|
|
`);
|
|
grid.body.forEach((row, i) => {
|
|
html += `<tr data-i="` + i + `">`;
|
|
row.forEach((value, j) => html += `<td data-j="` + j + `">` + value + `</td>`);
|
|
html += `</tr>`;
|
|
});
|
|
html += (`
|
|
</tbody>
|
|
</table>
|
|
`);
|
|
|
|
return html;
|
|
};
|
|
|
|
const list_to_html = this.list_to_html = (list, name) => {
|
|
|
|
const mode = {
|
|
ordered : "ol",
|
|
unordered : "ul"
|
|
}[list.mode] || list.mode || settings(["default_list_mode", "list_mode"]);
|
|
let html = `<` + mode + ` class="list` + (name ? " " + name : "") + `"` + (isNaN(list.start) ? `` : ` start="` + list.start + `"`) + `>`;
|
|
|
|
list.items.forEach((item, i) => html += `<li data-i="` + i + `">` + (typeof item == "object" ? item[0] + list_to_html(item[1]) : item) + `</li>`);
|
|
html += `</` + mode + `>`;
|
|
|
|
return html;
|
|
};
|
|
|
|
const css_to_html = this.css_to_html = (html, css, type, body_size, dpi, callback) => {
|
|
|
|
const html_item = cache_box.appendChild(document.createElement("div")),
|
|
cache = cache_box.appendChild(document.createElement("div")),
|
|
images_items = {},
|
|
images_data = {},
|
|
preload_hash = hash(),
|
|
is_doc = type == "doc";
|
|
|
|
preload("[data-preload=" + preload_hash + "]", preloader => {
|
|
preloader.remove();
|
|
|
|
let loaded = 0;
|
|
const images = html_item.querySelectorAll("img"),
|
|
l = images.length,
|
|
end = (url, image) => {
|
|
|
|
if(image){
|
|
|
|
const canvas = cache.appendChild(document.createElement("canvas")),
|
|
context = canvas.getContext("2d");
|
|
let unit;
|
|
|
|
canvas.setAttribute("width", image.width);
|
|
canvas.setAttribute("height", image.height);
|
|
|
|
context.drawImage(image, 0, 0, image.width, image.height);
|
|
images_data[url] = canvas.toDataURL("image/png", 0.9);
|
|
images_items[url].forEach(item => {
|
|
item.setAttribute("src", images_data[url]);
|
|
if(!is_doc)
|
|
return;
|
|
if(item.style.width == "auto" && item.style.height && item.style.height != "auto"){
|
|
unit = item.style.height.match(/[^0-9\.]+$/)[0];
|
|
item.style.width = unit == "%" ? (body_size.height * parseInt(item.style.height) / 1000) + "cm" : (parseInt(item.style.height) * image.width / image.height) + unit;
|
|
}else if(item.style.height == "auto" && item.style.width && item.style.width != "auto"){
|
|
unit = item.style.width.match(/[^0-9\.]+$/)[0];
|
|
item.style.height = unit == "%" ? (body_size.width * parseInt(item.style.width) / 1000) + "cm" : (parseInt(item.style.width) * image.height / image.width) + unit;
|
|
};
|
|
});
|
|
|
|
canvas.remove();
|
|
|
|
};
|
|
|
|
if(++ loaded < l)
|
|
return;
|
|
|
|
cache.remove();
|
|
html = html_item.innerHTML;
|
|
html_item.remove();
|
|
callback(html);
|
|
|
|
};
|
|
|
|
(css ? css instanceof Array ? css : [css] : []).forEach(sheet => sheet && sheet.replace(/\/\*(([^\*]+|[\r\n]+|\*[^\/])*)(\*\/)?|(([^\{]+|[\r\n]+)*)\{(([^\}]+|[\r\n]+)*)\}/g, (...arguments) => {
|
|
if(arguments[1])
|
|
return;
|
|
html_item.querySelectorAll(arguments[5].trim()).forEach(item => {
|
|
arguments[6].replace(/([^\s\:]+)\s*\:([^;\}]+)/g, (...subarguments) => item.style[subarguments[1].trim().replace(/\-(.)/g, (all, capital) => capital.toUpperCase())] = subarguments[2]);
|
|
item.tagName && item.tagName.toLowerCase() == "table" && item.style.width && !item.hasAttribute("width") && (item.setAttribute("width", item.style.width));
|
|
});
|
|
}));
|
|
|
|
is_doc && html_item.querySelectorAll("table").forEach(table => {
|
|
if(table.querySelector("colgroup"))
|
|
return;
|
|
|
|
const group = table.insertBefore(document.createElement("colgroup"), table.querySelector("tbody,thead,tfoot")),
|
|
sizes = [];
|
|
let total = 0,
|
|
unsized = 0,
|
|
medium = 0;
|
|
|
|
table.querySelector("tr").childNodes.forEach(column => {
|
|
if(!column || column.substr || !column.tagName || !["th", "td"].includes(column.tagName.toLowerCase()))
|
|
return;
|
|
|
|
const matches = column.style.width ? column.style.width.match(/^([0-9\.]+)([^0-9\.]+)$/) : null,
|
|
l = column.hasAttribute("colspan") ? Number(column.getAttribute("colspan")) || 1 : 1;
|
|
|
|
if(!matches){
|
|
unsized += l;
|
|
sizes.push(null);
|
|
return;
|
|
};
|
|
|
|
let size = parseInt(matches[1]);
|
|
switch(matches[2].toLowerCase()){
|
|
case "px":
|
|
size *= dpi / 25.4;
|
|
break;
|
|
case "mm":
|
|
size *= 100 / body_size.width;
|
|
break;
|
|
case "cm":
|
|
size *= 10 / body_size.width;
|
|
break;
|
|
case "em":
|
|
case "ex":
|
|
case "in":
|
|
size *= 2540 / body_size.width;
|
|
break;
|
|
case "%":
|
|
break;
|
|
};
|
|
total += size;
|
|
size /= l;
|
|
for(let i = 0; i < l; i ++)
|
|
sizes[sizes.length] = size;
|
|
|
|
});
|
|
|
|
medium = total < 100 ? (100 - total) / unsized : 0;
|
|
total < 100 && (total = 100);
|
|
sizes.forEach(size => group.appendChild(document.createElement("col")).setAttribute("width", 500 * (size === null ? medium : size) / total));
|
|
|
|
});
|
|
|
|
if(!images.length){
|
|
end();
|
|
return;
|
|
};
|
|
|
|
images.forEach(item => {
|
|
|
|
const url = string_variables(proxy || "{url}", {url : item.getAttribute("src")});
|
|
|
|
if(images_data[url]){
|
|
++ loaded;
|
|
item.setAttribute("src", images_data[url]);
|
|
return;
|
|
};
|
|
|
|
if(images_items[url]){
|
|
++ loaded;
|
|
images_items[url].push(item);
|
|
return;
|
|
};
|
|
|
|
let image = new Image();
|
|
|
|
images_items[url] = [item];
|
|
|
|
image.src = url;
|
|
image.crossOrigin = "anonymous";
|
|
image.onload = () => end(url, image);
|
|
image.onerror = () => end(url, null);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
cache.style.position = html_item.style.position = "absolute";
|
|
cache.style.left = html_item.style.left = "100%";
|
|
html_item.innerHTML = html + `<div data-preload="` + preload_hash + `"></div>`;
|
|
|
|
};
|
|
|
|
const get_page_size = this.get_page_size = format => {
|
|
|
|
switch(format = format.toLowerCase()){
|
|
case "dl":
|
|
return [99, 210];
|
|
case "letter":
|
|
return [216, 279];
|
|
case "legal":
|
|
return [216, 356];
|
|
};
|
|
|
|
const matches = format.match(/^([abc])(10|[0-9])$/);
|
|
|
|
if(!matches)
|
|
return null;
|
|
|
|
const [_, type, size] = matches,
|
|
results = {
|
|
a : [26.3, 37.16],
|
|
b : [31.25, 44.2],
|
|
c : [28.66, 40.535]
|
|
}[type];
|
|
|
|
for(let i = 9, temporary; i >= size; i --){
|
|
temporary = results[0];
|
|
results[0] = results[1];
|
|
results[1] = 2 * temporary;
|
|
};
|
|
|
|
return results.map(x => x >> 0);
|
|
};
|
|
|
|
this.to_pixels = (size, dpi) => size * (dpi || settings("dpi")) / 25.1;
|
|
|
|
const create_canvas = this.create_canas = (html, width, height, callback) => {
|
|
|
|
const iframe = cache_box.appendChild(document.createElement("iframe")),
|
|
on_load_callback = event => {
|
|
|
|
const body = (iframe.contentDocument || iframe.contentWindow.document).body;
|
|
|
|
body.innerHTML = (`
|
|
<!DOCTYPE html>
|
|
<html lang="en" dir="ltr">
|
|
<head>
|
|
<title></title>
|
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
|
<meta charset="utf-8" />
|
|
<style data-type="text/css" data-language="CSS3" data-rel="stylesheet" charset="utf-8">
|
|
html,body{margin : 0px;}
|
|
.iframe-box{
|
|
position : absolute;
|
|
width : ` + width + `px;
|
|
height : ` + height + `px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="iframe-box">` + html + `</div>
|
|
</body>
|
|
</html>
|
|
`);
|
|
new html2canvas(body, {
|
|
scale : 2,
|
|
backgroundColor : null,
|
|
width : width,
|
|
height : height
|
|
}).then(canvas => {
|
|
iframe.remove();
|
|
|
|
const uri = canvas.toDataURL("image/png", .9),
|
|
context = canvas.getContext("2d");
|
|
|
|
callback(canvas.toDataURL("image/png", .9));
|
|
});
|
|
|
|
};
|
|
|
|
iframe.setAttribute("style", "position:absolute;left:100%;top:100%;opacity:0;");
|
|
// iframe.addEventListener("load", on_load_callback);
|
|
setTimeout(on_load_callback, 50);
|
|
|
|
};
|
|
|
|
const date_format = this.date_format = this.datetime_format = this.time_format = (date, format) => {
|
|
|
|
const year = (date || (date = new Date())).getFullYear(),
|
|
month = date.getMonth() + 1,
|
|
day = date.getDate(),
|
|
hour = date.getHours(),
|
|
minute = date.getMinutes(),
|
|
second = date.getSeconds(),
|
|
timestamp = date.getTime(),
|
|
milliseconds = timestamp % 1000,
|
|
week_day = date.getDay();
|
|
|
|
return string_variables(format || (format = settings("date_format")), {
|
|
yyyy : ("0000" + year).slice(-4),
|
|
yy : ("00" + (year % 100)).slice(-2),
|
|
y : year,
|
|
year : year,
|
|
mmmm : months[month],
|
|
mmm : months[month].substr(0, 3),
|
|
mm : ("00" + month).slice(-2),
|
|
m : month,
|
|
month : month,
|
|
dd : ("00" + day).slice(-2),
|
|
d : day,
|
|
day : day,
|
|
hh : ("00" + hour).slice(-2),
|
|
h : hour,
|
|
hour : hour,
|
|
ii : ("00" + minute).slice(-2),
|
|
i : minute,
|
|
minute : minute,
|
|
ss : ("00" + second).slice(-2),
|
|
s : second,
|
|
second : second,
|
|
nnn : ("000" + milliseconds).slice(-3),
|
|
n : milliseconds,
|
|
milliseconds : milliseconds,
|
|
www : week_days[week_day],
|
|
ww : week_days[week_day].substr(0, 3),
|
|
w : week_day,
|
|
week_day : week_day,
|
|
weekday : week_day
|
|
});
|
|
};
|
|
|
|
const page_variables = (html, variables) => html.replace(/\{([^\{\}]+)\}/g, (...arguments) => {
|
|
if(variables[arguments[1]])
|
|
return variables[arguments[1]];
|
|
|
|
const matches = arguments[1].match(/^([^\:]+)(\:(.+)?)?$/);
|
|
|
|
if(matches)
|
|
switch(matches[1]){
|
|
case "date":
|
|
case "datetime":
|
|
case "time":
|
|
return date_format(new Date(), matches[3] ? matches[3].replace(/[\[\]]/g, (...arguments) => arguments[0] == "]" ? "]" : "[") : settings([matches[1] + "_format", "date_format"]))
|
|
};
|
|
return arguments[0];
|
|
});
|
|
|
|
const pdf_header_footer_create = (pdf, header, footer, margin, pages, callback, i) => {
|
|
|
|
const has_header = header,
|
|
has_footer = footer,
|
|
m = (has_header ? 1 : 0) + (has_footer ? 1 : 0);
|
|
|
|
if(!m || i > pages){
|
|
typeof callback == "function" && callback(pdf);
|
|
return;
|
|
};
|
|
|
|
let j = 0;
|
|
const end = () => {
|
|
if(++ j < m)
|
|
return;
|
|
pdf_header_footer_create(pdf, header, footer, margin, pages, callback, i + 1);
|
|
},
|
|
width = margin.page_width - margin.left - margin.right,
|
|
variables = {
|
|
page : i,
|
|
pages : pages
|
|
};
|
|
|
|
pdf.setPage(i);
|
|
has_header && create_canvas(page_variables(header[i % header.length], variables), width, margin.header_height, uri => {
|
|
pdf.addImage(uri, "PNG", margin.left, margin.header_top, width, margin.header_height);
|
|
end();
|
|
});
|
|
has_footer && create_canvas(page_variables(footer[i % footer.length], variables), width, margin.footer_height, uri => {
|
|
pdf.addImage(uri, "PNG", margin.left, margin.footer_top, width, margin.footer_height);
|
|
end();
|
|
});
|
|
|
|
};
|
|
|
|
this.create = (input, callback) => {
|
|
|
|
if(!input)
|
|
input = {};
|
|
else if(typeof input == "string")
|
|
input = {body : input};
|
|
else if(typeof input != "object")
|
|
input = {};
|
|
|
|
let body = `<div class="body">` + settings(["body", "html"], input, ``) + `</div>`,
|
|
header = settings(["header", "head"], input),
|
|
footer = settings(["footer", "foot"], input),
|
|
ended = false,
|
|
i = 0,
|
|
css = settings(["css", "styles", "style"], input),
|
|
margin_processed;
|
|
const variables = [{}, input.variables || {}, input],
|
|
type = settings("type", input),
|
|
margin = {
|
|
top : settings(["margin_top", "margin"], input),
|
|
left : settings(["margin_left", "margin"], input),
|
|
right : settings(["margin_right", "margin"], input),
|
|
bottom : settings(["margin_bottom", "margin"], input)
|
|
},
|
|
page_format = settings("page_format", input),
|
|
page_size = get_page_size(page_format),
|
|
body_size = {
|
|
width : page_size[0] - margin.left - margin.right,
|
|
height : page_size[1] - margin.top - margin.bottom
|
|
},
|
|
dpi = settings(["dpi", "ppp", "dpp"], input),
|
|
l = (
|
|
(header ? (header.push ? header : header = [header]).length : 0) +
|
|
(footer ? (footer.push ? footer : footer = [footer]).length : 0) +
|
|
1
|
|
),
|
|
end = () => {
|
|
|
|
if(ended || ++ i < l)
|
|
return;
|
|
ended = true;
|
|
|
|
body = string_variables(body, variables);
|
|
header && header.map(html => string_variables(html, variables));
|
|
footer && footer.map(html => string_variables(html, variables));
|
|
|
|
switch(type){
|
|
case "pdf":
|
|
|
|
const pdf = new window.jspdf.jsPDF({
|
|
unit : "px",
|
|
format : page_format,
|
|
hotfixes : ["px_scaling"]
|
|
}),
|
|
width = settings(["width", "width_" + page_format], input),
|
|
fonts_added = [];
|
|
let key;
|
|
|
|
css && css.forEach(string => string.replace(/font-family\s*\:\s*([^;]+)/g, (...arguments) => {
|
|
arguments[1].replace(/"([^"]+)"|'([^']+)'|([^\s]+)/g, (...subarguments) => {
|
|
fonts[key = subarguments[1] || subarguments[2] || subarguments[3]] && fonts[key].forEach(font => pdf.addFont(font[0], key, font[1]));
|
|
});
|
|
}));
|
|
|
|
pdf.html(body, {
|
|
callback : pdf => pdf_header_footer_create(pdf, header, footer, margin, pdf.internal.getNumberOfPages(), callback, 1),
|
|
// width : width,
|
|
margin : margin_processed,
|
|
autoPaging : "text",
|
|
html2canvas : {
|
|
letterRendering : 1,
|
|
allowTaint : true,
|
|
useCORS : true,
|
|
logging : true
|
|
}
|
|
});
|
|
|
|
break;
|
|
case "doc":
|
|
/*
|
|
xmlns:v="urn:schemas-microsoft-com:vml"
|
|
xmlns:o='urn:schemas-microsoft-com:office:office'
|
|
xmlns:w='urn:schemas-microsoft-com:office:word'
|
|
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"
|
|
xmlns='http://www.w3.org/TR/REC-html40'
|
|
|
|
xmlns:o='urn:schemas-microsoft-com:office:office'
|
|
xmlns:w='urn:schemas-microsoft-com:office:word'
|
|
xmlns='http://www.w3.org/TR/REC-html40'
|
|
*/
|
|
callback(page_variables(string_variables((`
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<html
|
|
xmlns:v="urn:schemas-microsoft-com:vml"
|
|
xmlns:o='urn:schemas-microsoft-com:office:office'
|
|
xmlns:w='urn:schemas-microsoft-com:office:word'
|
|
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"
|
|
xmlns='http://www.w3.org/TR/REC-html40'
|
|
>
|
|
<head>
|
|
<title>` + (settings("title", input) || "") + `</title>
|
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
|
<meta charset="utf-8" />
|
|
<style>
|
|
@page{
|
|
size : ` + page_format + `;
|
|
margin : ` + (
|
|
(margin.top / 10) + "cm " +
|
|
(margin.right / 10) + "cm " +
|
|
(margin.bottom / 10) + "cm " +
|
|
(margin.left / 10) + "cm"
|
|
) + `;
|
|
}
|
|
@page Section1{
|
|
/* Margin ---- */
|
|
/* Size ----- */
|
|
mso-page-orientation : landscape;
|
|
mso-header-margin : ` + (10 * settings("margin_header", input) / 10) + `cm; /* Why mm? */
|
|
mso-header : h1;
|
|
mso-footer-margin : ` + (10 * settings("margin_footer", input) / 10) + `cm; /* Why mm? */
|
|
mso-footer : f1;
|
|
}
|
|
div.Section1{page : Section1;}
|
|
</style>
|
|
<!--[if gte mso 9]><xml>
|
|
<w:WordDocument>
|
|
<w:View>Print</w:View>
|
|
<w:Zoom>100</w:Zoom>
|
|
<w:DoNotOptimizeForBrowser />
|
|
</w:WordDocument>
|
|
</xml><![endif]-->
|
|
</head>
|
|
<body>
|
|
<div class="Section1">
|
|
{header}
|
|
{footer}
|
|
{body}
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`), [{
|
|
header : header && header.length ? `<div title="header" style="mso-element : header;" id="h1">` + header[0] + `</div>` : ``,
|
|
footer : footer && footer.length ? `<div title="footer" style="mso-element : footer;" id="f1">` + footer[0] + `</div>` : ``,
|
|
body : body
|
|
}].concat(variables)), {
|
|
page : `<sdfield type="PAGE" subtype="RANDOM" format="PAGE" style="mso-field-code : PAGE;">1</sdfield>`,
|
|
pages : `<sdfield type="DOCSTAT" subtype="PAGE" format="PAGE" style="mso-field-code : NUMPAGES;">1</sdfield>`
|
|
}));
|
|
break;
|
|
};
|
|
|
|
};
|
|
|
|
header && header.map(html => `<div class="header">` + html + `</div>`);
|
|
footer && footer.map(html => `<div class="header">` + html + `</div>`);
|
|
|
|
for(const key in variables[1])
|
|
if(variables[1][key] && typeof variables[1][key] == "object" && variables[1][key].type)
|
|
switch(variables[1][key].type){
|
|
case "grid":
|
|
case "table":
|
|
variables[0][key] = grid_to_html(variables[1][key], key);
|
|
break;
|
|
case "list":
|
|
variables[0][key] = list_to_html(variables[1][key], key);
|
|
break;
|
|
};
|
|
|
|
if(css)
|
|
!(css instanceof Array) && (css = [css]);
|
|
else
|
|
css = [];
|
|
|
|
switch(type){
|
|
case "pdf":
|
|
|
|
const header_height = header ? settings("header_height", input) : 0,
|
|
footer_height = footer ? settings("footer_height", input) : 0;
|
|
|
|
margin.header_top = margin.top + 0;
|
|
margin.header_height = header_height;
|
|
margin.header_bottom = page_size[1] - margin.top - header_height;
|
|
margin.footer_top = page_size[1] - margin.bottom - footer_height;
|
|
margin.footer_height = footer_height;
|
|
margin.footer_bottom = margin.bottom + 0;
|
|
margin.page_height = page_size[1];
|
|
margin.page_width = page_size[0];
|
|
|
|
header && (margin.top += header_height + settings("margin_header", input));
|
|
footer && (margin.bottom += footer_height + settings("margin_footer", input));
|
|
|
|
for(const key in margin)
|
|
margin[key] = dpi * margin[key] / 25.4;
|
|
margin_processed = [
|
|
margin.top,
|
|
margin.right,
|
|
margin.bottom,
|
|
margin.left
|
|
];
|
|
css.push(`
|
|
.header,.body,.footer{width : ` + ((dpi * page_size[0] / 25.4) - margin.left - margin.right) + `px;}
|
|
.header{
|
|
position : absolute;
|
|
top : 0px;
|
|
left : 0px;
|
|
}
|
|
`);
|
|
|
|
break;
|
|
case "doc":
|
|
break;
|
|
};
|
|
|
|
css_to_html(string_variables(body, variables), css, type, body_size, dpi, html => {body = html;end();});
|
|
header && header.forEach((html, i) => css_to_html(string_variables(html, variables), css, type, body_size, dpi, html => {header[i] = html;end();}));
|
|
footer && footer.forEach((html, i) => css_to_html(string_variables(html, variables), css, type, body_size, dpi, html => {footer[i] = html;end();}));
|
|
|
|
};
|
|
|
|
const get_uri_data = this.get_uri_data = (data, type) => {
|
|
if(typeof data == "string" && data.match(/^data\:[^,]+,[^,]+$/))
|
|
return data;
|
|
if(data instanceof Blob)
|
|
return URL.createObjectURL(data);
|
|
switch(type){
|
|
case "pdf":
|
|
return data.output("datauristring");
|
|
case "doc":
|
|
return "data:application/vnd.ms-word;charset=utf-8," + encodeURIComponent(data);
|
|
default:
|
|
return "data:application/octetstream;base64," + encodeURIComponent(btoa(typeof data == "object" ? JSON.stringify(data) : "" + data))
|
|
};
|
|
return null;
|
|
};
|
|
|
|
this.download = (data, file_name) => {
|
|
|
|
const anchor = cache_box.appendChild(document.createElement("a"));
|
|
|
|
anchor.setAttribute("download", file_name);
|
|
anchor.setAttribute("target", "_blank");
|
|
anchor.setAttribute("href", get_uri_data(data, file_name.match(/\.([^\.]+)$/)[1].toLowerCase()));
|
|
|
|
anchor.click();
|
|
anchor.remove();
|
|
|
|
// window.open(get_uri_data(data, file_name.match(/\.([^\.]+)$/)[1].toLowerCase()), "_blank");
|
|
|
|
};
|
|
|
|
const add_fonts = this.add_fonts = array => array && array.push && array.forEach(font => {
|
|
|
|
if(!fonts[font[0]])
|
|
fonts[font[0]] = [[font[1], font[2]]];
|
|
else{
|
|
|
|
let i;
|
|
const exists = fonts[font[0]].some((source, j) => {
|
|
if(source[1] == font[2]){
|
|
i = j;
|
|
return true;
|
|
};
|
|
});
|
|
|
|
if(exists)
|
|
fonts[font[0]][i][0] = font[1];
|
|
else
|
|
fonts[font[0]].push([font[1], font[2]]);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
const construct = () => {
|
|
|
|
week_days = settings("week_days");
|
|
months = settings("months");
|
|
|
|
["default_fonts", "fonts"].forEach(key => add_fonts(settings(key)));
|
|
|
|
settings("autostart") && self.start();
|
|
|
|
};
|
|
|
|
construct();
|
|
|
|
};
|