seitime-frappe/frappe/public/js/legacy/print_format.js

687 lines
17 KiB
JavaScript

// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
// default print style
_p.def_print_style_body = "html, body, div, span, td, p { \
font-family: inherit; \
font-size: inherit; \
}\
.page-settings {\
font-family: Helvetica, 'Open Sans', sans-serif;\
font-size: 9pt;\
}\
pre { margin:0; padding:0;}";
_p.def_print_style_other = "\n.simpletable, .noborder { \
border-collapse: collapse;\
margin-bottom: 10px;\
}\
.simpletable td {\
border: 1pt solid #777;\
vertical-align: top;\
padding: 4px;\
}\
.noborder td {\
vertical-align: top;\
}";
_p.go = function(html) {
var w = _p.preview(html);
w.print();
w.close();
}
_p.preview = function(html) {
var w = window.open();
if(!w) {
msgprint(__("Please enable pop-ups"));
return;
}
w.document.write(html);
w.document.close();
return w
}
// _p can be referenced as this inside $.extend
$.extend(_p, {
show_dialog: function() {
if(!_p.dialog) {
_p.make_dialog();
}
_p.dialog.show();
},
make_dialog: function() {
// Prepare Dialog Box Layout
var dialog = new frappe.ui.Dialog({
title: "Print Formats",
fields: [
{fieldtype:"Select", label:"Print Format", fieldname:"print_format", reqd:1},
{fieldtype:"Check", label:"No Letter Head", fieldname:"no_letterhead"},
{fieldtype:"HTML", options: '<p style="text-align: right;">\
<button class="btn btn-primary btn-print">Print</button>\
<button class="btn btn-default btn-preview">Preview</button>\
</p>'},
]
});
dialog.$wrapper.find(".btn-print").click(function() {
var args = dialog.get_values();
_p.build(
args.print_format, // fmtname
_p.go, // onload
args.no_letterhead // no_letterhead
);
});
dialog.$wrapper.find(".btn-preview").click(function() {
var args = dialog.get_values();
_p.build(
args.print_format, // fmtname
_p.preview, // onload
args.no_letterhead // no_letterhead
);
});
dialog.on_page_show = function() {
var $print = dialog.fields_dict.print_format.$input;
$print.empty().add_options(cur_frm.print_preview.print_formats);
if(cur_frm.$print_view_select && cur_frm.$print_view_select.val())
$print.val(cur_frm.$print_view_select.val());
}
_p.dialog = dialog;
},
// Define formats dict
formats: {},
/* args dict can contain:
+ fmtname --> print format name
+ onload
+ no_letterhead
+ only_body
*/
build: function(fmtname, onload, no_letterhead, only_body, no_heading) {
if(!fmtname) {
fmtname= "Standard";
}
args = {
fmtname: fmtname,
onload: onload,
no_letterhead: no_letterhead,
only_body: only_body
};
if(!cur_frm) {
msgprint(__("No document selected"));
return;
}
if(!frappe.model.can_print(cur_frm.doctype, cur_frm)) {
msgprint(__("You are not allowed to print this document"));
return;
}
// Get current doc (record)
var doc = locals[cur_frm.doctype][cur_frm.docname];
if(args.fmtname == 'Standard') {
args.onload(_p.render({
body: _p.print_std(args.no_letterhead, no_heading),
style: _p.print_style,
doc: doc,
title: doc.name,
no_letterhead: args.no_letterhead,
no_heading: no_heading,
only_body: args.only_body
}));
} else {
var print_format_doc = locals["Print Format"][args.fmtname];
if(!print_format_doc) {
msgprint(__("Unknown Print Format: {0}", [args.fmtname]));
return;
}
args.onload(_p.render({
body: print_format_doc.html,
style: '',
doc: doc,
title: doc.name,
no_letterhead: args.no_letterhead,
no_heading: no_heading,
only_body: args.only_body
}));
}
},
render: function(args) {
var container = document.createElement('div');
var stat = '';
if(!args.no_heading) {
// if draft/archived, show draft/archived banner
stat += _p.show_draft(args);
stat += _p.show_archived(args);
stat += _p.show_cancelled(args);
}
// Append args.body's content as a child of container
container.innerHTML = args.body;
// Show letterhead?
_p.show_letterhead(container, args);
_p.run_embedded_js(container, args.doc);
var style = _p.consolidate_css(container, args);
_p.render_header_on_break(container, args);
return _p.render_final(style, stat, container, args);
},
head_banner_format: function() {
return "\
<div style = '\
text-align: center; \
padding: 8px; \
background-color: #CCC;'> \
<div style = '\
font-size: 20px; \
font-weight: bold;'>\
{{HEAD}}\
</div>\
{{DESCRIPTION}}\
</div>"
},
/*
Check if doc's status is not submitted (docstatus == 0)
and submission is pending
Display draft in header if true
*/
show_draft: function(args) {
var is_doctype_submittable = 0;
var plist = locals['DocPerm'];
for(var perm in plist) {
var p = plist[perm];
if((p.parent==args.doc.doctype) && (p.submit==1)){
is_doctype_submittable = 1;
break;
}
}
if(args.doc && cint(args.doc.docstatus)==0 && is_doctype_submittable) {
draft = _p.head_banner_format();
draft = draft.replace("{{HEAD}}", "DRAFT");
draft = draft.replace("{{DESCRIPTION}}", "This box will go away after the document is submitted.");
return draft;
} else {
return "";
}
},
/*
Check if doc is archived
Display archived in header if true
*/
show_archived: function(args) {
if(args.doc && args.doc.__archived) {
archived = _p.head_banner_format();
archived = archived.replace("{{HEAD}}", "ARCHIVED");
archived = archived.replace("{{DESCRIPTION}}", "You must restore this document to make it editable.");
return archived;
} else {
return "";
}
},
/*
Check if doc is cancelled
Display cancelled in header if true
*/
show_cancelled: function(args) {
if(args.doc && args.doc.docstatus==2) {
cancelled = _p.head_banner_format();
cancelled = cancelled.replace("{{HEAD}}", "CANCELLED");
cancelled = cancelled.replace("{{DESCRIPTION}}", "You must amend this document to make it editable.");
return cancelled;
} else {
return "";
}
},
consolidate_css: function(container, args) {
// Extract <style> content from container
var body_style = '';
var style_list = container.getElementsByTagName('style');
while(style_list && style_list.length>0) {
for(i in style_list) {
if(style_list[i] && style_list[i].innerHTML) {
body_style += style_list[i].innerHTML;
var parent = style_list[i].parentNode;
if(parent) {
parent.removeChild(style_list[i]);
} else {
container.removeChild(style_list[i]);
}
}
}
style_list = container.getElementsByTagName('style');
}
// Concatenate all styles
style_concat = (args.only_body ? '' : _p.def_print_style_body)
+ _p.def_print_style_other + args.style + body_style;
return style_concat;
},
// This is used to calculate and substitude values in the HTML
run_embedded_js: function(container, doc) {
var script_list = $(container).find("script");
for(var i=0, j=script_list.length; i<j; i++) {
var element = script_list[i];
var code = element.innerHTML;
try {
var new_html = code ? (eval(code) || "") : "";
} catch(e) {
console.log("Error in Custom Script:" + e + "\n" + code);
console.trace(e);
throw e;
}
if(in_list(["string", "number"], typeof new_html)) {
$(element).replaceWith(this.add_span(new_html + ""));
}
}
// remove scripts once executed
$(container).find("script").remove();
},
add_span: function(html) {
var tags = ["<span", "<p", "<div", "<br", "<table"];
var match = false;
for(var i=0; i<tags.length; i++) {
if(html.match(tags[i])) {
match = true;
break;
}
}
if(!match) {
html = "<span>" + html + "</span>";
}
return html;
},
// Attach letterhead at top of container
show_letterhead: function(container, args) {
if(!args.no_letterhead) {
container.innerHTML = '<div style="max-width: 100%">'
+ _p.get_letter_head() + '</div>'
+ container.innerHTML;
}
},
render_header_on_break: function(container, args) {
var page_set = container.getElementsByClassName('page-settings');
if(page_set.length) {
for(var i = 0; i < page_set.length; i++) {
var tmp = '';
// if draft/archived, show draft/archived banner
tmp += _p.show_draft(args);
tmp += _p.show_archived(args);
_p.show_letterhead(page_set[i], args);
page_set[i].innerHTML = tmp + page_set[i].innerHTML;
}
}
},
// called by _p.render for final render of print
render_final: function(style, stat, container, args) {
if(!args.only_body) {
var header = '<!DOCTYPE html>\
<html>\
<head>\
<meta charset="utf-8" />\
<title>' + args.title + '</title>\
<style>' + style + '</style>\
</head>\
<body>';
var footer = '\
</body>\
</html>';
} else {
var header = '';
var footer = '';
}
var finished = header
+ '<div class="page-settings">'
+ stat
+ container.innerHTML
+ '</div>'
+ footer;
// replace relative links by absolute links
var prefix = window.location.href.split("desk")[0]
// find unique matches
var matches = $.unique(finished.match(/src=['"]([^'"]*)['"]/g) || []);
$.each(matches, function(i, v) {
if(v.substr(0,4)=="src=") {
var v = v.substr(5, v.length-6);
if(v.substr(0,4)!="http") {
finished = finished.split(v).join(prefix + lstrip(v, "/"));
}
}
});
return finished;
},
// fetches letter head from current doc or control panel
get_letter_head: function() {
var lh = '';
if(cur_frm.doc.letter_head) {
lh = cstr(frappe.boot.letter_heads[cur_frm.doc.letter_head]);
} else if (frappe.boot.sysdefaults.default_letter_head_content) {
lh = frappe.boot.sysdefaults.default_letter_head_content;
}
return lh;
},
// common print style setting
print_style: "\
.datalabelcell { \
padding: 2px 0px; \
width: 38%; \
vertical-align: top; \
} \
.datainputcell { \
padding: 2px 0px; \
width: 62%; \
text-align: left; \
}\
.sectionHeading { \
font-size: 16px; \
font-weight: bold; \
margin: 8px 0px; \
} \
.columnHeading { \
font-size: 14px; \
font-weight: bold; \
margin: 8px 0px; \
}",
print_std: function(no_letterhead, no_heading) {
// Get doctype, docname, layout for a doctype
var docname = cur_frm.docname;
var doctype = cur_frm.doctype;
var data = frappe.get_children("DocType", doctype, "fields");
var layout = _p.add_layout(doctype);
this.pf_list = [layout];
var me = this;
me.layout = layout;
$.extend(this, {
build_head: function(data, doctype, docname) {
// Heading
var h1_style = {
fontSize: '22px',
marginBottom: '8px'
}
var h1 = $a(me.layout.cur_row.header, 'h1', '', h1_style);
// Get print heading
if (cur_frm.pformat[docname]) {
// first check in cur_frm.pformat
h1.innerHTML = cur_frm.pformat[docname];
} else {
// then check if select print heading exists and has a value
var val = null;
for (var i = 0; i < data.length; i++) {
if (data[i].fieldname === 'select_print_heading') {
val = _f.get_value(doctype, docname, data[i].fieldname);
break;
}
}
// if not, just have doctype has heading
h1.innerHTML = val ? val : __(doctype);
}
var h2_style = {
fontSize: '16px',
color: '#888',
marginBottom: '8px',
paddingBottom: '8px',
borderBottom: (me.layout.with_border ? '0px' :
'1px solid #000')
}
var h2 = $a(me.layout.cur_row.header, 'div', '', h2_style);
h2.innerHTML = docname;
if(cur_frm.state_fieldname && !cur_frm.fields_dict[cur_frm.state_fieldname].df.print_hide) {
$a(h2, 'br');
var span = $a(h2, 'span', '',
{padding: "3px", color: "#fff", backgroundColor: "#777",
display:"inline-block"});
span.innerHTML = cur_frm.doc[cur_frm.state_fieldname];
}
},
build_data: function(data, doctype, docname) {
// Start with a row and a cell in that row
if(data[0] && data[0].fieldtype != "Section Break") {
me.layout.addrow();
if(data[0].fieldtype != "Column Break") {
me.layout.addcell();
}
}
$.extend(this, {
generate_custom_html: function(field, doctype, docname) {
var container = $a(me.layout.cur_cell, 'div');
container.innerHTML = cur_frm.pformat[field.fieldname](locals[doctype][docname]);
},
render_normal: function(field, data, i) {
switch(field.fieldtype) {
case 'Fold': break;
case 'Section Break':
me.layout.addrow();
// Add column if no column break after this field
if(data[i+1] && data[i+1].fieldtype !=
'Column Break') {
me.layout.addcell();
}
break;
case 'Column Break':
me.layout.addcell(field.width, field.label);
break;
case 'Table':
var table = print_table(
doctype, // dt
docname, // dn
field.fieldname,
field.options, // tabletype
null, // cols
null, // head_labels
null, // widths
null); // condition
me.layout = _p.print_std_add_table(table, me.layout, me.pf_list, doctype, no_letterhead);
break;
case 'HTML':
var div = $a(me.layout.cur_cell, 'div');
div.innerHTML = cstr(field.options);
break;
case 'Code':
var div = $a(me.layout.cur_cell, 'div');
var val = _f.get_value(doctype, docname,
field.fieldname);
div.innerHTML = '<div>' + __(field.label) +
': </div><pre style="font-family: Courier, Fixed;">' + (val ? val : '') +
'</pre>';
break;
case 'Text Editor':
var div = $a(me.layout.cur_cell, 'div');
var val = _f.get_value(doctype, docname,
field.fieldname);
div.innerHTML = val ? val : '';
break;
default:
// Add Cell Data
_p.print_std_add_field(doctype, docname, field, me.layout);
break;
}
}
});
// Then build each field
for(var i = 0; i < data.length; i++) {
var fieldname = data[i].fieldname ? data[i].fieldname :
data[i].label;
var field = fieldname ?
frappe.meta.get_docfield(doctype, fieldname, docname) : data[i];
if(!field.print_hide) {
if(cur_frm.pformat[field.fieldname]) {
// If there is a custom method to generate the HTML, then use it
this.generate_custom_html(field, doctype, docname);
} else {
// Do the normal rendering
this.render_normal(field, data, i);
}
}
}
me.layout.close_borders();
},
build_html: function() {
var html = '';
for(var i = 0; i < me.pf_list.length; i++) {
if(me.pf_list[i].wrapper) {
html += me.pf_list[i].wrapper.innerHTML;
} else if(me.pf_list[i].innerHTML) {
html += me.pf_list[i].innerHTML;
} else {
html += me.pf_list[i];
}
}
this.pf_list = [];
return html;
}
});
if(!no_heading) {
this.build_head(data, doctype, docname);
}
this.build_data(data, doctype, docname);
var html = this.build_html();
return html;
},
add_layout: function(doctype) {
var layout = new Layout();
layout.addrow();
if(locals['DocType'][doctype].print_outline == 'Yes') {
layout.with_border = 1
}
return layout;
},
print_std_add_table: function(t, layout, pf_list, dt, no_letterhead) {
if(t.appendChild) {
// If only one table is passed
layout.cur_cell.appendChild(t);
} else {
page_break = '\n\
<div style = "page-break-after: always;" \
class = "page_break"></div><div class="page-settings"></div>';
// If a list of tables is passed
for(var i = 0; i < t.length-1; i++) {
// add to current page
layout.cur_cell.appendChild(t[i]);
layout.close_borders();
pf_list.push(page_break);
// Create new page
layout = _p.add_layout(dt, no_letterhead);
pf_list.push(layout);
layout.addrow();
layout.addcell();
var div = $a(layout.cur_cell, 'div');
div.innerHTML = 'Continued from previous page...';
div.style.padding = '4px';
}
// Append last table
layout.cur_cell.appendChild(t[t.length-1]);
}
return layout;
},
print_std_add_field: function(dt, dn, f, layout) {
var val = _f.get_value(dt, dn, f.fieldname);
if(f.fieldtype!='Button') {
if(val || in_list(['Float', 'Int', 'Currency'], f.fieldtype)) {
// If value or a numeric type then proceed
// Add field table
row = _p.field_tab(layout.cur_cell);
// Add label
row.cells[0].innerHTML = __(f.label ? f.label : f.fieldname);
row.cells[1].innerHTML = frappe.format(val, f, {for_print: true});
// left align currency in normal display
if(f.fieldtype == 'Currency') {
$y(row.cells[1], { textAlign: 'left' });
}
}
}
},
field_tab: function(layout_cell) {
var tab = $a(layout_cell, 'table', '', {width:'100%'});
var row = tab.insertRow(0);
_p.row = row; // Don't know this line's purpose
row.insertCell(0);
row.insertCell(1);
row.cells[0].className = 'datalabelcell';
row.cells[0].style.width = "38%";
row.cells[1].className = 'datainputcell';
return row;
}
});