You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 11 години
преди 13 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 12 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 12 години
преди 12 години
преди 13 години
преди 13 години
преди 12 години
преди 13 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 13 години
преди 13 години
преди 12 години
преди 13 години
преди 13 години
преди 12 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. // MIT License. See license.txt
  3. // default print style
  4. _p.def_print_style_body = "html, body, div, span, td, p { \
  5. font-family: inherit; \
  6. font-size: inherit; \
  7. }\
  8. .page-settings {\
  9. font-family: Arial, Helvetica Neue, Sans;\
  10. font-size: 9pt;\
  11. }\
  12. pre { margin:0; padding:0;}";
  13. _p.def_print_style_other = "\n.simpletable, .noborder { \
  14. border-collapse: collapse;\
  15. margin-bottom: 10px;\
  16. }\
  17. .simpletable td {\
  18. border: 1pt solid #777;\
  19. vertical-align: top;\
  20. padding: 4px;\
  21. }\
  22. .noborder td {\
  23. vertical-align: top;\
  24. }";
  25. _p.go = function(html) {
  26. var w = _p.preview(html);
  27. w.print();
  28. w.close();
  29. }
  30. _p.preview = function(html) {
  31. var w = window.open();
  32. if(!w) {
  33. msgprint(_("Please enable pop-ups"));
  34. return;
  35. }
  36. w.document.write(html);
  37. return w
  38. }
  39. // _p can be referenced as this inside $.extend
  40. $.extend(_p, {
  41. show_dialog: function() {
  42. if(!_p.dialog) {
  43. _p.make_dialog();
  44. }
  45. _p.dialog.show();
  46. },
  47. make_dialog: function() {
  48. // Prepare Dialog Box Layout
  49. var dialog = new wn.ui.Dialog({
  50. title: "Print Formats",
  51. fields: [
  52. {fieldtype:"Select", label:"Print Format", fieldname:"print_format", reqd:1},
  53. {fieldtype:"Check", label:"No Letter Head", fieldname:"no_letterhead"},
  54. {fieldtype:"HTML", options: '<p style="text-align: right;">\
  55. <button class="btn btn-primary btn-print">Print</button>\
  56. <button class="btn btn-default btn-preview">Preview</button>\
  57. </p>'},
  58. ]
  59. });
  60. dialog.$wrapper.find(".btn-print").click(function() {
  61. var args = dialog.get_values();
  62. _p.build(
  63. args.print_format, // fmtname
  64. _p.go, // onload
  65. args.no_letterhead // no_letterhead
  66. );
  67. });
  68. dialog.$wrapper.find(".btn-preview").click(function() {
  69. var args = dialog.get_values();
  70. _p.build(
  71. args.print_format, // fmtname
  72. _p.preview, // onload
  73. args.no_letterhead // no_letterhead
  74. );
  75. });
  76. dialog.onshow = function() {
  77. var $print = dialog.fields_dict.print_format.$input;
  78. $print.empty().add_options(cur_frm.print_formats);
  79. if(cur_frm.$print_view_select && cur_frm.$print_view_select.val())
  80. $print.val(cur_frm.$print_view_select.val());
  81. }
  82. _p.dialog = dialog;
  83. },
  84. // Define formats dict
  85. formats: {},
  86. /* args dict can contain:
  87. + fmtname --> print format name
  88. + onload
  89. + no_letterhead
  90. + only_body
  91. */
  92. build: function(fmtname, onload, no_letterhead, only_body, no_heading) {
  93. if(!fmtname) {
  94. fmtname= "Standard";
  95. }
  96. args = {
  97. fmtname: fmtname,
  98. onload: onload,
  99. no_letterhead: no_letterhead,
  100. only_body: only_body
  101. };
  102. if(!cur_frm) {
  103. msgprint(wn._("No document selected."));
  104. return;
  105. }
  106. if(!wn.model.can_print(cur_frm.doctype, cur_frm)) {
  107. msgprint(wn._("You are not allowed to print this document."));
  108. return;
  109. }
  110. // Get current doc (record)
  111. var doc = locals[cur_frm.doctype][cur_frm.docname];
  112. if(args.fmtname == 'Standard') {
  113. args.onload(_p.render({
  114. body: _p.print_std(args.no_letterhead, no_heading),
  115. style: _p.print_style,
  116. doc: doc,
  117. title: doc.name,
  118. no_letterhead: args.no_letterhead,
  119. no_heading: no_heading,
  120. only_body: args.only_body
  121. }));
  122. } else {
  123. var print_format_doc = locals["Print Format"][args.fmtname];
  124. if(!print_format_doc) {
  125. msgprint("Unknown Print Format: " + args.fmtname);
  126. return;
  127. }
  128. args.onload(_p.render({
  129. body: print_format_doc.html,
  130. style: '',
  131. doc: doc,
  132. title: doc.name,
  133. no_letterhead: args.no_letterhead,
  134. no_heading: no_heading,
  135. only_body: args.only_body
  136. }));
  137. }
  138. },
  139. render: function(args) {
  140. var container = document.createElement('div');
  141. var stat = '';
  142. if(!args.no_heading) {
  143. // if draft/archived, show draft/archived banner
  144. stat += _p.show_draft(args);
  145. stat += _p.show_archived(args);
  146. stat += _p.show_cancelled(args);
  147. }
  148. // Append args.body's content as a child of container
  149. container.innerHTML = args.body;
  150. // Show letterhead?
  151. _p.show_letterhead(container, args);
  152. _p.run_embedded_js(container, args.doc);
  153. var style = _p.consolidate_css(container, args);
  154. _p.render_header_on_break(container, args);
  155. return _p.render_final(style, stat, container, args);
  156. },
  157. head_banner_format: function() {
  158. return "\
  159. <div style = '\
  160. text-align: center; \
  161. padding: 8px; \
  162. background-color: #CCC;'> \
  163. <div style = '\
  164. font-size: 20px; \
  165. font-weight: bold;'>\
  166. {{HEAD}}\
  167. </div>\
  168. {{DESCRIPTION}}\
  169. </div>"
  170. },
  171. /*
  172. Check if doc's status is not submitted (docstatus == 0)
  173. and submission is pending
  174. Display draft in header if true
  175. */
  176. show_draft: function(args) {
  177. var is_doctype_submittable = 0;
  178. var plist = locals['DocPerm'];
  179. for(var perm in plist) {
  180. var p = plist[perm];
  181. if((p.parent==args.doc.doctype) && (p.submit==1)){
  182. is_doctype_submittable = 1;
  183. break;
  184. }
  185. }
  186. if(args.doc && cint(args.doc.docstatus)==0 && is_doctype_submittable) {
  187. draft = _p.head_banner_format();
  188. draft = draft.replace("{{HEAD}}", "DRAFT");
  189. draft = draft.replace("{{DESCRIPTION}}", "This box will go away after the document is submitted.");
  190. return draft;
  191. } else {
  192. return "";
  193. }
  194. },
  195. /*
  196. Check if doc is archived
  197. Display archived in header if true
  198. */
  199. show_archived: function(args) {
  200. if(args.doc && args.doc.__archived) {
  201. archived = _p.head_banner_format();
  202. archived = archived.replace("{{HEAD}}", "ARCHIVED");
  203. archived = archived.replace("{{DESCRIPTION}}", "You must restore this document to make it editable.");
  204. return archived;
  205. } else {
  206. return "";
  207. }
  208. },
  209. /*
  210. Check if doc is cancelled
  211. Display cancelled in header if true
  212. */
  213. show_cancelled: function(args) {
  214. if(args.doc && args.doc.docstatus==2) {
  215. cancelled = _p.head_banner_format();
  216. cancelled = cancelled.replace("{{HEAD}}", "CANCELLED");
  217. cancelled = cancelled.replace("{{DESCRIPTION}}", "You must amend this document to make it editable.");
  218. return cancelled;
  219. } else {
  220. return "";
  221. }
  222. },
  223. consolidate_css: function(container, args) {
  224. // Extract <style> content from container
  225. var body_style = '';
  226. var style_list = container.getElementsByTagName('style');
  227. while(style_list && style_list.length>0) {
  228. for(i in style_list) {
  229. if(style_list[i] && style_list[i].innerHTML) {
  230. body_style += style_list[i].innerHTML;
  231. var parent = style_list[i].parentNode;
  232. if(parent) {
  233. parent.removeChild(style_list[i]);
  234. } else {
  235. container.removeChild(style_list[i]);
  236. }
  237. }
  238. }
  239. style_list = container.getElementsByTagName('style');
  240. }
  241. // Concatenate all styles
  242. style_concat = (args.only_body ? '' : _p.def_print_style_body)
  243. + _p.def_print_style_other + args.style + body_style;
  244. return style_concat;
  245. },
  246. // This is used to calculate and substitude values in the HTML
  247. run_embedded_js: function(container, doc) {
  248. script_list = $(container).find("script");
  249. for(var i=0; i<script_list.length; i++) {
  250. var element = script_list[i];
  251. var code = element.innerHTML;
  252. var new_html = code ? (eval(code) || "") : "";
  253. if(in_list(["string", "number"], typeof new_html)) {
  254. $(element).replaceWith(this.add_span(new_html + ""));
  255. }
  256. }
  257. },
  258. add_span: function(html) {
  259. var tags = ["<span[^>]>", "<p[^>]>", "<div[^>]>", "<br[^>]>", "<table[^>]>"];
  260. var match = false;
  261. for(var i=0; i<tags.length; i++) {
  262. if(html.match(tags[i])) {
  263. match = true;
  264. }
  265. }
  266. if(!match) {
  267. html = "<span>" + html + "</span>";
  268. }
  269. return html;
  270. },
  271. // Attach letterhead at top of container
  272. show_letterhead: function(container, args) {
  273. if(!args.no_letterhead) {
  274. container.innerHTML = '<div style="max-width: 100%">'
  275. + _p.get_letter_head() + '</div>'
  276. + container.innerHTML;
  277. }
  278. },
  279. render_header_on_break: function(container, args) {
  280. var page_set = container.getElementsByClassName('page-settings');
  281. if(page_set.length) {
  282. for(var i = 0; i < page_set.length; i++) {
  283. var tmp = '';
  284. // if draft/archived, show draft/archived banner
  285. tmp += _p.show_draft(args);
  286. tmp += _p.show_archived(args);
  287. _p.show_letterhead(page_set[i], args);
  288. page_set[i].innerHTML = tmp + page_set[i].innerHTML;
  289. }
  290. }
  291. },
  292. // called by _p.render for final render of print
  293. render_final: function(style, stat, container, args) {
  294. if(!args.only_body) {
  295. var header = '<!DOCTYPE html>\
  296. <html>\
  297. <head>\
  298. <meta charset="utf-8" />\
  299. <title>' + args.title + '</title>\
  300. <style>' + style + '</style>\
  301. </head>\
  302. <body>';
  303. var footer = '\
  304. </body>\
  305. </html>';
  306. } else {
  307. var header = '';
  308. var footer = '';
  309. }
  310. var finished = header
  311. + '<div class="page-settings">'
  312. + stat
  313. + container.innerHTML
  314. + '</div>'
  315. + footer;
  316. // replace relative links by absolute links
  317. var prefix = window.location.href.split("app.html")[0]
  318. // find unique matches
  319. var matches = $.unique(finished.match(/src=['"]([^'"]*)['"]/g) || []);
  320. $.each(matches, function(i, v) {
  321. if(v.substr(0,4)=="src=") {
  322. var v = v.substr(5, v.length-6);
  323. if(v.substr(0,4)!="http")
  324. finished = finished.split(v).join(prefix + v);
  325. }
  326. });
  327. return finished;
  328. },
  329. // fetches letter head from current doc or control panel
  330. get_letter_head: function() {
  331. var cp = wn.control_panel;
  332. var lh = '';
  333. if(cur_frm.doc.letter_head) {
  334. lh = cstr(wn.boot.letter_heads[cur_frm.doc.letter_head]);
  335. } else if (cp.letter_head) {
  336. lh = cp.letter_head;
  337. }
  338. return lh;
  339. },
  340. // common print style setting
  341. print_style: "\
  342. .datalabelcell { \
  343. padding: 2px 0px; \
  344. width: 38%; \
  345. vertical-align: top; \
  346. } \
  347. .datainputcell { \
  348. padding: 2px 0px; \
  349. width: 62%; \
  350. text-align: left; \
  351. }\
  352. .sectionHeading { \
  353. font-size: 16px; \
  354. font-weight: bold; \
  355. margin: 8px 0px; \
  356. } \
  357. .columnHeading { \
  358. font-size: 14px; \
  359. font-weight: bold; \
  360. margin: 8px 0px; \
  361. }",
  362. print_std: function(no_letterhead, no_heading) {
  363. // Get doctype, docname, layout for a doctype
  364. var docname = cur_frm.docname;
  365. var doctype = cur_frm.doctype;
  366. var data = getchildren('DocField', doctype, 'fields', 'DocType');
  367. var layout = _p.add_layout(doctype);
  368. this.pf_list = [layout];
  369. var me = this;
  370. me.layout = layout;
  371. $.extend(this, {
  372. build_head: function(data, doctype, docname) {
  373. // Heading
  374. var h1_style = {
  375. fontSize: '22px',
  376. marginBottom: '8px'
  377. }
  378. var h1 = $a(me.layout.cur_row.header, 'h1', '', h1_style);
  379. // Get print heading
  380. if (cur_frm.pformat[docname]) {
  381. // first check in cur_frm.pformat
  382. h1.innerHTML = cur_frm.pformat[docname];
  383. } else {
  384. // then check if select print heading exists and has a value
  385. var val = null;
  386. for (var i = 0; i < data.length; i++) {
  387. if (data[i].fieldname === 'select_print_heading') {
  388. val = _f.get_value(doctype, docname, data[i].fieldname);
  389. break;
  390. }
  391. }
  392. // if not, just have doctype has heading
  393. h1.innerHTML = val ? val : wn._(doctype);
  394. }
  395. var h2_style = {
  396. fontSize: '16px',
  397. color: '#888',
  398. marginBottom: '8px',
  399. paddingBottom: '8px',
  400. borderBottom: (me.layout.with_border ? '0px' :
  401. '1px solid #000')
  402. }
  403. var h2 = $a(me.layout.cur_row.header, 'div', '', h2_style);
  404. h2.innerHTML = docname;
  405. if(cur_frm.state_fieldname) {
  406. $a(h2, 'br');
  407. var span = $a(h2, 'span', '',
  408. {padding: "3px", color: "#fff", backgroundColor: "#777",
  409. display:"inline-block"});
  410. span.innerHTML = cur_frm.doc[cur_frm.state_fieldname];
  411. }
  412. },
  413. build_data: function(data, doctype, docname) {
  414. // Start with a row and a cell in that row
  415. if(data[0] && data[0].fieldtype != "Section Break") {
  416. me.layout.addrow();
  417. if(data[0].fieldtype != "Column Break") {
  418. me.layout.addcell();
  419. }
  420. }
  421. $.extend(this, {
  422. generate_custom_html: function(field, doctype, docname) {
  423. var container = $a(me.layout.cur_cell, 'div');
  424. container.innerHTML = cur_frm.pformat[field.fieldname](locals[doctype][docname]);
  425. },
  426. render_normal: function(field, data, i) {
  427. switch(field.fieldtype) {
  428. case 'Section Break':
  429. me.layout.addrow();
  430. // Add column if no column break after this field
  431. if(data[i+1] && data[i+1].fieldtype !=
  432. 'Column Break') {
  433. me.layout.addcell();
  434. }
  435. break;
  436. case 'Column Break':
  437. me.layout.addcell(field.width, field.label);
  438. break;
  439. case 'Table':
  440. var table = print_table(
  441. doctype, // dt
  442. docname, // dn
  443. field.fieldname,
  444. field.options, // tabletype
  445. null, // cols
  446. null, // head_labels
  447. null, // widths
  448. null); // condition
  449. me.layout = _p.print_std_add_table(table, me.layout, me.pf_list, doctype, no_letterhead);
  450. break;
  451. case 'HTML':
  452. var div = $a(me.layout.cur_cell, 'div');
  453. div.innerHTML = field.options;
  454. break;
  455. case 'Code':
  456. var div = $a(me.layout.cur_cell, 'div');
  457. var val = _f.get_value(doctype, docname,
  458. field.fieldname);
  459. div.innerHTML = '<div>' + wn._(field.label) +
  460. ': </div><pre style="font-family: Courier, Fixed;">' + (val ? val : '') +
  461. '</pre>';
  462. break;
  463. case 'Text Editor':
  464. var div = $a(me.layout.cur_cell, 'div');
  465. var val = _f.get_value(doctype, docname,
  466. field.fieldname);
  467. div.innerHTML = val ? val : '';
  468. break;
  469. default:
  470. // Add Cell Data
  471. _p.print_std_add_field(doctype, docname, field, me.layout);
  472. break;
  473. }
  474. }
  475. });
  476. // Then build each field
  477. for(var i = 0; i < data.length; i++) {
  478. var fieldname = data[i].fieldname ? data[i].fieldname :
  479. data[i].label;
  480. var field = fieldname ?
  481. wn.meta.get_docfield(doctype, fieldname, docname) : data[i];
  482. if(!field.print_hide) {
  483. if(cur_frm.pformat[field.fieldname]) {
  484. // If there is a custom method to generate the HTML, then use it
  485. this.generate_custom_html(field, doctype, docname);
  486. } else {
  487. // Do the normal rendering
  488. this.render_normal(field, data, i);
  489. }
  490. }
  491. }
  492. me.layout.close_borders();
  493. },
  494. build_html: function() {
  495. var html = '';
  496. for(var i = 0; i < me.pf_list.length; i++) {
  497. if(me.pf_list[i].wrapper) {
  498. html += me.pf_list[i].wrapper.innerHTML;
  499. } else if(me.pf_list[i].innerHTML) {
  500. html += me.pf_list[i].innerHTML;
  501. } else {
  502. html += me.pf_list[i];
  503. }
  504. }
  505. this.pf_list = [];
  506. return html;
  507. }
  508. });
  509. if(!no_heading) {
  510. this.build_head(data, doctype, docname);
  511. }
  512. this.build_data(data, doctype, docname);
  513. var html = this.build_html();
  514. return html;
  515. },
  516. add_layout: function(doctype) {
  517. var layout = new Layout();
  518. layout.addrow();
  519. if(locals['DocType'][doctype].print_outline == 'Yes') {
  520. layout.with_border = 1
  521. }
  522. return layout;
  523. },
  524. print_std_add_table: function(t, layout, pf_list, dt, no_letterhead) {
  525. if(t.appendChild) {
  526. // If only one table is passed
  527. layout.cur_cell.appendChild(t);
  528. } else {
  529. page_break = '\n\
  530. <div style = "page-break-after: always;" \
  531. class = "page_break"></div><div class="page-settings"></div>';
  532. // If a list of tables is passed
  533. for(var i = 0; i < t.length-1; i++) {
  534. // add to current page
  535. layout.cur_cell.appendChild(t[i]);
  536. layout.close_borders();
  537. pf_list.push(page_break);
  538. // Create new page
  539. layout = _p.add_layout(dt, no_letterhead);
  540. pf_list.push(layout);
  541. layout.addrow();
  542. layout.addcell();
  543. var div = $a(layout.cur_cell, 'div');
  544. div.innerHTML = 'Continued from previous page...';
  545. div.style.padding = '4px';
  546. }
  547. // Append last table
  548. layout.cur_cell.appendChild(t[t.length-1]);
  549. }
  550. return layout;
  551. },
  552. print_std_add_field: function(dt, dn, f, layout) {
  553. var val = _f.get_value(dt, dn, f.fieldname);
  554. if(f.fieldtype!='Button') {
  555. if(val || in_list(['Float', 'Int', 'Currency'], f.fieldtype)) {
  556. // If value or a numeric type then proceed
  557. // Add field table
  558. row = _p.field_tab(layout.cur_cell);
  559. // Add label
  560. row.cells[0].innerHTML = wn._(f.label ? f.label : f.fieldname);
  561. row.cells[1].innerHTML = wn.format(val, f, {for_print: true});
  562. // left align currency in normal display
  563. if(f.fieldtype == 'Currency') {
  564. $y(row.cells[1], { textAlign: 'left' });
  565. }
  566. }
  567. }
  568. },
  569. field_tab: function(layout_cell) {
  570. var tab = $a(layout_cell, 'table', '', {width:'100%'});
  571. var row = tab.insertRow(0);
  572. _p.row = row; // Don't know this line's purpose
  573. row.insertCell(0);
  574. row.insertCell(1);
  575. row.cells[0].className = 'datalabelcell';
  576. row.cells[0].style.width = "38%";
  577. row.cells[1].className = 'datainputcell';
  578. return row;
  579. }
  580. });