(function() { var version = "3.5.0"; console.log("Version: " + version); var global = {}; global.isDev = /.*jira.atlassian.com\/secure\/RapidBoard.jspa\?.*projectKey=ANERDS.*/g.test(document.URL) // Jira || /.*pivotaltracker.com\/n\/projects\/510733.*/g.test(document.URL) // PivotTracker || ( /.*trello.com\/.*/g.test(document.URL) && jQuery("span.js-member-name").text() =='Bengt Brodersen'); // Trello global.isProd = !global.isDev; window.addEventListener("error", function(event) { var error = event.error; console.log("ERROR: " + error.stack); if (global.isProd) { ga('send', 'exception', { 'exDescription': error.message, 'exFatal': true }); } }); // load jQuery if (window.jQuery === undefined) { appendScript('//ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js'); } // wait untill all scripts loaded appendScript('https://qoomon.github.io/void', function() { main(); }); function main() { init(); // determine application if (jQuery("meta[name='application-name'][ content='JIRA']").length > 0) { console.log("App: " + "Jira"); global.appFunctions = jiraFunctions; } else if (/.*pivotaltracker.com\/.*/g.test(document.URL)) { console.log("App: " + "PivotalTracker"); global.appFunctions = pivotalTrackerFunctions; } else if (/.*trello.com\/.*/g.test(document.URL)) { console.log("App: " + "Trello"); global.appFunctions = trelloFunctions; } else { alert("Unsupported app.Please create an issue at https://github.com/qoomon/Jira-Issue-Card-Printer"); return; } //preconditions if (jQuery("#card-print-overlay").length > 0) { alert("Print Card already opened!"); return; } // collect selcted issues var issueKeyList = global.appFunctions.getSelectedIssueKeyList(); if (issueKeyList.length <= 0) { alert("Please select at least one issue."); return; } // open print preview jQuery("body").append(printOverlayHTML()); jQuery("#card-print-overlay").prepend(printOverlayStyle()); var printFrame = jQuery("#card-print-dialog-content-iframe"); var printWindow = printFrame[0].contentWindow; printWindow.addEventListener("resize", function(){redrawCards;}); printWindow.matchMedia("print").addListener(function(){redrawCards;}); jQuery("#rowCount").val(readCookie("card_printer_row_count",2)); jQuery("#columnCount").val(readCookie("card_printer_column_count",1)); jQuery("#font-scale-range").val(readCookie("card_printer_font_scale",1)); jQuery("#single-card-page-checkbox").attr('checked',readCookie("card_printer_single_card_page",false) != 'false'); jQuery("#hide-description-checkbox").attr('checked',readCookie("card_printer_hide_description",false) != 'false'); jQuery("#card-print-dialog-title").text("Card Print - Loading " + issueKeyList.length + " issues..."); renderCards(issueKeyList, function() { jQuery("#card-print-dialog-title").text("Card Print"); //print(); }); if (global.isProd) { ga('send', 'pageview'); } } function init() { addStringFunctions(); addDateFunctions(); global.hostOrigin = "https://qoomon.github.io/Jira-Issue-Card-Printer/"; if (global.isDev) { console.log("DEVELOPMENT"); global.hostOrigin = "https://rawgit.com/qoomon/Jira-Issue-Card-Printer/develop/"; } global.resourceOrigin = global.hostOrigin + "resources/"; if (global.isProd){ initGoogleAnalytics(); } } function print() { if (global.isProd) { ga('send', 'event', 'button', 'click', 'print', jQuery(".card", printDocument).length); } var printFrame = jQuery("#card-print-dialog-content-iframe"); var printWindow = printFrame[0].contentWindow; printWindow.print(); } function renderCards(issueKeyList, callback) { var printFrame = jQuery("#card-print-dialog-content-iframe"); var printWindow = printFrame[0].contentWindow; var printDocument = printWindow.document; printDocument.open(); printDocument.write("
"); jQuery("head", printDocument).append(cardCss()); jQuery("body", printDocument).append(""); jQuery("#preload", printDocument).append(""); console.log("load " + issueKeyList.length + " issues..."); var deferredList = []; jQuery.each(issueKeyList, function(index, issueKey) { var page = cardHtml(issueKey); page.attr("index", index); page.hide(); page.find('.issue-id').text(issueKey); jQuery("body", printDocument).append(page); var deferred = addDeferred(deferredList); global.appFunctions.getCardData(issueKey, function(cardData) { //console.log("cardData: " + cardData); if (global.isProd) { ga('send', 'event', 'task', 'generate', 'card', cardData.type); } fillCard(page, cardData); page.show(); redrawCards(); deferred.resolve(); }); }); console.log("wait for issues loaded..."); applyDeferred(deferredList, function() { console.log("...all issues loaded."); jQuery(printWindow).load(function() { console.log("...all resources loaded."); callback(); }) printDocument.close(); console.log("wait for resources loaded..."); }); } function redrawCards() { var printFrame = jQuery("#card-print-dialog-content-iframe"); var printWindow = printFrame[0].contentWindow; var printDocument = printWindow.document; // hide/show description jQuery("#styleHideDescription", printDocument).remove(); if(jQuery("#hide-description-checkbox")[0].checked){ var style= document.createElement('style'); style.id = 'styleHideDescription'; style.type ='text/css'; style.innerHTML = ".issue-description { display: none; }" jQuery("head", printDocument).append(style); } // enable/disable single card page jQuery("#styleSingleCardPage", printDocument).remove(); if(jQuery("#single-card-page-checkbox")[0].checked){ var style= document.createElement('style'); style.id = 'styleSingleCardPage'; style.type ='text/css'; style.innerHTML = ".card { page-break-after: always; float: none; }" jQuery("head", printDocument).append(style); } scaleCards(); cropCards(); var printFrame = jQuery("#card-print-dialog-content-iframe"); resizeIframe(printFrame); } function fillCard(card, data) { //Key card.find('.issue-id').text(data.key); //Type card.find(".issue-icon").attr("type", data.type); //Summary card.find('.issue-summary').text(data.summary); //Description if (data.description) { card.find('.issue-description').html(data.description); } else { card.find(".issue-description").addClass("hidden"); } //Assignee if (data.assignee) { if (data.avatarUrl) { card.find(".issue-assignee").css("background-image", "url('" + data.avatarUrl + "')"); } else { card.find(".issue-assignee").text(data.assignee[0].toUpperCase()); } } else { card.find(".issue-assignee").addClass("hidden"); } //Due-Date if (data.dueDate) { card.find(".issue-due-date").text(data.dueDate); } else { card.find(".issue-due-box").addClass("hidden"); } //Attachment if (data.hasAttachment) {} else { card.find('.issue-attachment').addClass('hidden'); } //Story Points if (data.storyPoints) { card.find(".issue-estimate").text(data.storyPoints); } else { card.find(".issue-estimate").addClass("hidden"); } //Epic if (data.epicKey) { card.find(".issue-epic-id").text(data.epicKey); card.find(".issue-epic-name").text(data.epicName); } else { card.find(".issue-epic-box").addClass("hidden"); } //QR-Code var qrCodeUrl = 'https://chart.googleapis.com/chart?cht=qr&chs=256x256&chld=L|1&chl=' + encodeURIComponent(data.url); card.find(".issue-qr-code").css("background-image", "url('" + qrCodeUrl + "')"); } function scaleCards(){ var printFrame = jQuery("#card-print-dialog-content-iframe"); var printWindow = printFrame[0].contentWindow; var printDocument = printWindow.document; var columnCount = jQuery("#columnCount").val(); var rowCount = jQuery("#rowCount").val(); // size // size horizontal jQuery("#styleColumnCount", printDocument).remove(); var style= document.createElement('style'); style.id = 'styleColumnCount'; style.type ='text/css'; style.innerHTML = ".card { width: calc( 100% / " + columnCount + "); }" jQuery("head", printDocument).append(style); // size horizontal jQuery("#styleRowCount", printDocument).remove(); var style= document.createElement('style'); style.id = 'styleRowCount'; style.type ='text/css'; style.innerHTML = ".card { height: calc( 100% / " + rowCount + "); }" jQuery("head", printDocument).append(style); // scale jQuery("html", printDocument).css("font-size", "1cm"); // scale horizontal // substract one pixel due to rounding problems var cardMaxWidth = jQuery(".card", printDocument).outerWidth() / columnCount - 1; var cardMinWidth = jQuery(".card", printDocument).css("min-width").replace("px", ""); var scaleWidth = cardMaxWidth / cardMinWidth; // scale vertical // substract one pixel due to rounding problems var cardMaxHeight = jQuery(".card", printDocument).outerHeight() / rowCount - 1; var cardMinHeight = jQuery(".card", printDocument).css("min-height").replace("px", ""); var scaleHeight = cardMaxHeight / cardMinHeight; // scale min var scale = Math.min(scaleWidth, scaleHeight); if(scale < 1) { jQuery("html", printDocument).css("font-size",scale +"cm"); } } function cropCards() { var printFrame = jQuery("#card-print-dialog-content-iframe"); var printWindow = printFrame[0].contentWindow; var printDocument = printWindow.document; var cardElements = printDocument.querySelectorAll(".card"); forEach(cardElements, function (cardElement) { var cardContent = cardElement.querySelectorAll(".card-body")[0]; if (cardContent.scrollHeight > cardContent.offsetHeight) { cardContent.classList.add("zigzag"); } else { cardContent.classList.remove("zigzag"); } }); } function forEach(array, callback) { for (i = 0; i < array.length; i++) { callback(array[i]); } } function closePrintPreview() { jQuery("#card-print-overlay").remove(); jQuery("#card-print-overlay-style").remove(); } //############################################################################################################################ //############################################################################################################################ //############################################################################################################################ // http://www.cssdesk.com/T9hXg function printOverlayHTML() { var result = jQuery(document.createElement('div')) .attr("id", "card-print-overlay") .html(multilineString(function() { /*!/, ""); if (data.fields.assignee) { issueData.assignee = data.fields.assignee.displayName; var avatarUrl = data.fields.assignee.avatarUrls['48x48']; if (avatarUrl.indexOf("ownerId=") >= 0) { issueData.avatarUrl = avatarUrl; } } if (data.fields.duedate) { issueData.dueDate = new Date(data.fields.duedate).format('D d.m.'); } issueData.hasAttachment = data.fields.attachment.length > 0; issueData.storyPoints = data.fields.storyPoints; issueData.epicKey = data.fields.epicLink; if (issueData.epicKey) { jiraFunctions.getIssueData(issueData.epicKey, function(data) { issueData.epicName = data.fields.epicName; }, false); } issueData.url = window.location.origin + "/browse/" + issueData.key; //LRS Specific field mapping if (true) { //Desired-Date if (data.fields.desiredDate) { issueData.dueDate = new Date(data.fields.desiredDate).format('D d.m.'); } } callback(issueData); }); }; module.getIssueData = function(issueKey, callback, async) { async = typeof async !== 'undefined' ? async : true; //https://docs.atlassian.com/jira/REST/latest/ var url = '/rest/api/2/issue/' + issueKey + '?expand=renderedFields,names'; console.log("IssueUrl: " + url); //console.log("Issue: " + issueKey + " Loading..."); jQuery.ajax({ type: 'GET', url: url, data: {}, dataType: 'json', async: async, success: function(responseData) { //console.log("Issue: " + issueKey + " Loaded!"); // add custom fields with field names jQuery.each(responseData.names, function(key, value) { if (key.startsWith("customfield_")) { var newFieldId = value.toCamelCase(); //console.log("add new field: " + newFieldId + " with value from " + key); responseData.fields[value.toCamelCase()] = responseData.fields[key]; } }); callback(responseData); }, }); }; return module; }({})); var pivotalTrackerFunctions = (function (module) { module.getSelectedIssueKeyList = function() { //Single Story if (/.*\/stories\/.*/g.test(document.URL)) { return [document.URL.replace(/.*\/stories\/([^?]*).*/, '$1')]; // TODO } // Board if (/.*\/projects\/.*/g.test(document.URL)) { return jQuery('.story[data-id]:has(.selected)').map(function() { return jQuery(this).attr('data-id'); }); } return []; }; module.getCardData = function(issueKey, callback) { module.getIssueData(issueKey, function(data) { var issueData = {}; issueData.key = data.id; issueData.type = data.kind.toLowerCase(); issueData.summary = data.name; issueData.description = data.description; if (data.owned_by && data.owned_by.length > 0) { issueData.assignee = data.owner_ids[0].name; } if (data.deadline) { issueData.dueDate = new Date(data.deadline).format('D d.m.'); } // TODO issueData.hasAttachment = false; issueData.storyPoints = data.estimate; // TODO // issueData.epicKey = data.fields.epicLink; // if ( issueData.epicKey ) { // getIssueDataPivotalTracker(issueData.epicKey , function(data) { // issueData.epicName = data.fields.epicName; // }, false); // } issueData.url = data.url; callback(issueData); }); }; module.getIssueData = function(issueKey, callback, async) { async = typeof async !== 'undefined' ? async : true; //http://www.pivotaltracker.com/help/api var url = 'https://www.pivotaltracker.com/services/v5/stories/' + issueKey + "?fields=name,kind,description,story_type,owned_by(name),comments(file_attachments(kind)),estimate,deadline"; console.log("IssueUrl: " + url); //console.log("Issue: " + issueKey + " Loading..."); jQuery.ajax({ type: 'GET', url: url, data: {}, dataType: 'json', async: async, success: function(responseData) { //console.log("Issue: " + issueKey + " Loaded!"); callback(responseData); }, }); }; return module; }({})); var trelloFunctions = (function (module) { module.getSelectedIssueKeyList = function() { //Card View if (/.*\/c\/.*/g.test(document.URL)) { return [document.URL.replace(/.*\/c\/([^/]*).*/g, '$1')]; } return []; }; module.getCardData = function(issueKey, callback) { module.getIssueData(issueKey, function(data) { var issueData = {}; issueData.key = data.idShort; // TODO get kind from label name // issueData.type = data.kind.toLowerCase(); issueData.summary = data.name; issueData.description = data.desc; if (data.members && data.members.length > 0) { issueData.assignee = data.members[0].fullName; issueData.avatarUrl = "https://trello-avatars.s3.amazonaws.com/"+data.members[0].avatarHash+"/170.png"; } if (data.due) { issueData.dueDate = new Date(data.due).format('D d.m.'); } issueData.hasAttachment = data.attachments > 0; issueData.url = data.shortUrl; callback(issueData); }); }; module.getIssueData = function(issueKey, callback, async) { async = typeof async !== 'undefined' ? async : true; //http://www.pivotaltracker.com/help/api var url = "https://trello.com/1/cards/" + issueKey + "?members=true"; console.log("IssueUrl: " + url); //console.log("Issue: " + issueKey + " Loading..."); jQuery.ajax({ type: 'GET', url: url, data: {}, dataType: 'json', async: async, success: function(responseData) { //console.log("Issue: " + issueKey + " Loaded!"); callback(responseData); }, }); }; return module; }({})); })();