Merge pull request #2488 from tsiegleauq/evacuatePDF
moves pdfmake related code in own files (fixes #2307)
This commit is contained in:
commit
74b80bdb50
527
openslides/core/static/js/core/pdf.js
Normal file
527
openslides/core/static/js/core/pdf.js
Normal file
@ -0,0 +1,527 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.core.pdf', [])
|
||||||
|
|
||||||
|
.factory('HTMLValidizer', function() {
|
||||||
|
var HTMLValidizer = {};
|
||||||
|
|
||||||
|
//checks if str is valid HTML. Returns valid HTML if not,
|
||||||
|
//return emptystring if empty
|
||||||
|
HTMLValidizer.validize = function(str) {
|
||||||
|
if (str) {
|
||||||
|
var a = document.createElement('div');
|
||||||
|
a.innerHTML = str;
|
||||||
|
angular.forEach(a.childNodes, function (child) {
|
||||||
|
if (child.nodeType == 1) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return "<p>" + str + "</p>";
|
||||||
|
} else {
|
||||||
|
return ""; //needed for blank "reaons" field
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return HTMLValidizer;
|
||||||
|
})
|
||||||
|
|
||||||
|
.factory('PdfMakeDocumentProvider', [
|
||||||
|
'gettextCatalog',
|
||||||
|
'Config',
|
||||||
|
function(gettextCatalog, Config) {
|
||||||
|
/**
|
||||||
|
* Provides the global Document
|
||||||
|
* @constructor
|
||||||
|
* @param {object} contentProvider - Object with on method `getContent`, which returns an array for content
|
||||||
|
* @param {string} defaultFont - Default font for the document
|
||||||
|
*/
|
||||||
|
var createInstance = function(contentProvider, defaultFont) {
|
||||||
|
/**
|
||||||
|
* Generates header for PDF
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
var header = function() {
|
||||||
|
var date = new Date();
|
||||||
|
return {
|
||||||
|
// alignment: 'center',
|
||||||
|
color: '#555',
|
||||||
|
fontSize: 10,
|
||||||
|
margin: [80, 50, 80, 0], //margin: [left, top, right, bottom]
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
text: Config.get('general_event_name').value + ' · ' + Config.get('general_event_description').value ,
|
||||||
|
fontSize:10,
|
||||||
|
width: '70%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fontSize: 6,
|
||||||
|
width: '30%',
|
||||||
|
text: gettextCatalog.getString('As of') + " " + date.toLocaleDateString() + " " + date.toLocaleTimeString(),
|
||||||
|
alignment: 'right'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generates footer line
|
||||||
|
* @function
|
||||||
|
* @param {object} currentPage - An object representing the current page
|
||||||
|
* @param {number} pageCount - number for pages
|
||||||
|
*/
|
||||||
|
footer = function(currentPage, pageCount) {
|
||||||
|
return {
|
||||||
|
alignment: 'center',
|
||||||
|
fontSize: 8,
|
||||||
|
color: '#555',
|
||||||
|
text: gettextCatalog.getString('Page') + ' ' + currentPage.toString() + ' / ' + pageCount.toString()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generates the document(definition) for pdfMake
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
getDocument = function() {
|
||||||
|
var content = contentProvider.getContent();
|
||||||
|
return {
|
||||||
|
pageSize: 'A4',
|
||||||
|
pageMargins: [80, 90, 80, 60],
|
||||||
|
defaultStyle: {
|
||||||
|
font: defaultFont,
|
||||||
|
fontSize: 10
|
||||||
|
},
|
||||||
|
header: header,
|
||||||
|
footer: footer,
|
||||||
|
content: content,
|
||||||
|
styles: {
|
||||||
|
title: {
|
||||||
|
fontSize: 30,
|
||||||
|
margin: [0,0,0,20],
|
||||||
|
bold: true
|
||||||
|
},
|
||||||
|
heading: {
|
||||||
|
fontSize: 16,
|
||||||
|
margin: [0,0,0,10],
|
||||||
|
bold: true
|
||||||
|
},
|
||||||
|
tableofcontent: {
|
||||||
|
fontSize: 12,
|
||||||
|
margin: [0,3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
getDocument: getDocument
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
createInstance: createInstance
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.factory('PdfMakeConverter', [
|
||||||
|
'HTMLValidizer',
|
||||||
|
function(HTMLValidizer) {
|
||||||
|
/**
|
||||||
|
* Converter component for HTML->JSON for pdfMake
|
||||||
|
* @constructor
|
||||||
|
* @param {object} images - Key-Value structure representing image.src/BASE64 of images
|
||||||
|
* @param {object} fonts - Key-Value structure representing fonts (detailed description below)
|
||||||
|
* @param {object} pdfMake - the converter component enhances pdfMake
|
||||||
|
*/
|
||||||
|
var createInstance = function(images, fonts, pdfMake) {
|
||||||
|
var slice = Function.prototype.call.bind([].slice),
|
||||||
|
map = Function.prototype.call.bind([].map),
|
||||||
|
/**
|
||||||
|
* Adds a custom font to pdfMake.vfs
|
||||||
|
* @function
|
||||||
|
* @param {object} fontFiles - object with Files to add to pdfMake.vfs
|
||||||
|
* {
|
||||||
|
* normal: $Filename
|
||||||
|
* bold: $Filename
|
||||||
|
* italics: $Filename
|
||||||
|
* bolditalics: $Filename
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
addFontToVfs = function(fontFiles) {
|
||||||
|
Object.keys(fontFiles).forEach(function(name) {
|
||||||
|
var file = fontFiles[name];
|
||||||
|
pdfMake.vfs[file.name] = file.content;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Adds custom fonts to pdfMake
|
||||||
|
* @function
|
||||||
|
* @param {object} fontInfo - Font configuration from Backend
|
||||||
|
* {
|
||||||
|
* $FontName : {
|
||||||
|
* normal: $Filename
|
||||||
|
* bold: $Filename
|
||||||
|
* italics: $Filename
|
||||||
|
* bolditalics: $Filename
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
registerFont = function(fontInfo) {
|
||||||
|
Object.keys(fontInfo).forEach(function(name) {
|
||||||
|
var font = fontInfo[name];
|
||||||
|
addFontToVfs(font);
|
||||||
|
pdfMake.fonts = pdfMake.fonts || {};
|
||||||
|
pdfMake.fonts[name] = Object.keys(font).reduce(function(fontDefinition, style) {
|
||||||
|
fontDefinition[style] = font[style].name;
|
||||||
|
return fontDefinition;
|
||||||
|
}, {});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Convertes HTML for use with pdfMake
|
||||||
|
* @function
|
||||||
|
* @param {object} html - html
|
||||||
|
*/
|
||||||
|
convertHTML = function(html, scope) {
|
||||||
|
var elementStyles = {
|
||||||
|
"b": ["font-weight:bold"],
|
||||||
|
"strong": ["font-weight:bold"],
|
||||||
|
"u": ["text-decoration:underline"],
|
||||||
|
"em": ["font-style:italic"],
|
||||||
|
"i": ["font-style:italic"],
|
||||||
|
"h1": ["font-size:30"],
|
||||||
|
"h2": ["font-size:28"],
|
||||||
|
"h3": ["font-size:26"],
|
||||||
|
"h4": ["font-size:24"],
|
||||||
|
"h5": ["font-size:22"],
|
||||||
|
"h6": ["font-size:20"],
|
||||||
|
"a": ["color:blue", "text-decoration:underline"]
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Parses Children of the current paragraph
|
||||||
|
* @function
|
||||||
|
* @param {object} converted -
|
||||||
|
* @param {object} element -
|
||||||
|
* @param {object} currentParagraph -
|
||||||
|
* @param {object} styles -
|
||||||
|
*/
|
||||||
|
parseChildren = function(converted, element, currentParagraph, styles) {
|
||||||
|
var elements = [];
|
||||||
|
var children = element.childNodes;
|
||||||
|
if (children.length !== 0) {
|
||||||
|
_.forEach(children, function(child) {
|
||||||
|
currentParagraph = ParseElement(elements, child, currentParagraph, styles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (elements.length !== 0) {
|
||||||
|
_.forEach(elements, function(el) {
|
||||||
|
converted.push(el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return currentParagraph;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Extracts the style from an object
|
||||||
|
* @function
|
||||||
|
* @param {object} o - the current object
|
||||||
|
* @param {object} styles - an array with styles
|
||||||
|
*/
|
||||||
|
ComputeStyle = function(o, styles) {
|
||||||
|
styles.forEach(function(singleStyle) {
|
||||||
|
var styleDefinition = singleStyle.trim().toLowerCase().split(":");
|
||||||
|
var style = styleDefinition[0];
|
||||||
|
var value = styleDefinition[1];
|
||||||
|
if (styleDefinition.length == 2) {
|
||||||
|
switch (style) {
|
||||||
|
case "padding-left":
|
||||||
|
o.margin = [parseInt(value), 0, 0, 0];
|
||||||
|
break;
|
||||||
|
case "font-size":
|
||||||
|
o.fontSize = parseInt(value);
|
||||||
|
break;
|
||||||
|
case "text-align":
|
||||||
|
switch (value) {
|
||||||
|
case "right":
|
||||||
|
case "center":
|
||||||
|
case "justify":
|
||||||
|
o.alignment = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "font-weight":
|
||||||
|
switch (value) {
|
||||||
|
case "bold":
|
||||||
|
o.bold = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "text-decoration":
|
||||||
|
switch (value) {
|
||||||
|
case "underline":
|
||||||
|
o.decoration = "underline";
|
||||||
|
break;
|
||||||
|
case "line-through":
|
||||||
|
o.decoration = "lineThrough";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "font-style":
|
||||||
|
switch (value) {
|
||||||
|
case "italic":
|
||||||
|
o.italics = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "color":
|
||||||
|
o.color = value;
|
||||||
|
break;
|
||||||
|
case "background-color":
|
||||||
|
o.background = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Parses a single HTML element
|
||||||
|
* @function
|
||||||
|
* @param {object} alreadyConverted -
|
||||||
|
* @param {object} element -
|
||||||
|
* @param {object} currentParagraph -
|
||||||
|
* @param {object} styles -
|
||||||
|
*/
|
||||||
|
ParseElement = function(alreadyConverted, element, currentParagraph, styles) {
|
||||||
|
styles = styles || [];
|
||||||
|
if (element.getAttribute) {
|
||||||
|
var nodeStyle = element.getAttribute("style");
|
||||||
|
if (nodeStyle) {
|
||||||
|
nodeStyle.split(";").forEach(function(nodeStyle) {
|
||||||
|
var tmp = nodeStyle.replace(/\s/g, '');
|
||||||
|
styles.push(tmp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var nodeName = element.nodeName.toLowerCase();
|
||||||
|
switch (nodeName) {
|
||||||
|
case "h1":
|
||||||
|
case "h2":
|
||||||
|
case "h3":
|
||||||
|
case "h4":
|
||||||
|
case "h5":
|
||||||
|
case "h6":
|
||||||
|
currentParagraph = create("text");
|
||||||
|
/* falls through */
|
||||||
|
case "a":
|
||||||
|
parseChildren(alreadyConverted, element, currentParagraph, styles.concat(elementStyles[nodeName]));
|
||||||
|
alreadyConverted.push(currentParagraph);
|
||||||
|
break;
|
||||||
|
case "b":
|
||||||
|
case "strong":
|
||||||
|
case "u":
|
||||||
|
case "em":
|
||||||
|
case "i":
|
||||||
|
parseChildren(alreadyConverted, element, currentParagraph, styles.concat(elementStyles[nodeName]));
|
||||||
|
break;
|
||||||
|
case "table":
|
||||||
|
var t = create("table", {
|
||||||
|
widths: [],
|
||||||
|
body: []
|
||||||
|
});
|
||||||
|
var border = element.getAttribute("border");
|
||||||
|
var isBorder = false;
|
||||||
|
if (border)
|
||||||
|
if (parseInt(border) == 1) isBorder = true;
|
||||||
|
if (!isBorder) t.layout = 'noBorders';
|
||||||
|
parseChildren(t.table.body, element, currentParagraph, styles);
|
||||||
|
var widths = element.getAttribute("widths");
|
||||||
|
if (!widths) {
|
||||||
|
if (t.table.body.length !== 0) {
|
||||||
|
if (t.table.body[0].length !== 0)
|
||||||
|
for (var k = 0; k < t.table.body[0].length; k++)
|
||||||
|
t.table.widths.push("*");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var w = widths.split(",");
|
||||||
|
for (var ko = 0; ko < w.length; ko++) t.table.widths.push(w[ko]);
|
||||||
|
}
|
||||||
|
alreadyConverted.push(t);
|
||||||
|
break;
|
||||||
|
case "tbody":
|
||||||
|
parseChildren(alreadyConverted, element, currentParagraph, styles);
|
||||||
|
break;
|
||||||
|
case "tr":
|
||||||
|
var row = [];
|
||||||
|
parseChildren(row, element, currentParagraph, styles);
|
||||||
|
alreadyConverted.push(row);
|
||||||
|
break;
|
||||||
|
case "td":
|
||||||
|
currentParagraph = create("text");
|
||||||
|
var st = create("stack");
|
||||||
|
st.stack.push(currentParagraph);
|
||||||
|
var rspan = element.getAttribute("rowspan");
|
||||||
|
if (rspan)
|
||||||
|
st.rowSpan = parseInt(rspan);
|
||||||
|
var cspan = element.getAttribute("colspan");
|
||||||
|
if (cspan)
|
||||||
|
st.colSpan = parseInt(cspan);
|
||||||
|
parseChildren(st.stack, element, currentParagraph, styles);
|
||||||
|
alreadyConverted.push(st);
|
||||||
|
break;
|
||||||
|
case "span":
|
||||||
|
if (scope.lineNumberMode == "inline") {
|
||||||
|
var lineNumberInline = element.getAttribute("data-line-number"),
|
||||||
|
lineNumberObjInline = {
|
||||||
|
text: lineNumberInline,
|
||||||
|
color: "gray",
|
||||||
|
fontSize: 5
|
||||||
|
};
|
||||||
|
currentParagraph.text.push(lineNumberObjInline);
|
||||||
|
parseChildren(alreadyConverted, element, currentParagraph, styles);
|
||||||
|
} else if (scope.lineNumberMode == "outside") {
|
||||||
|
var lineNumberOutline = element.getAttribute("data-line-number"),
|
||||||
|
lineNumberObject = {
|
||||||
|
width: 20,
|
||||||
|
text: lineNumberOutline,
|
||||||
|
color: "gray",
|
||||||
|
fontSize: 8,
|
||||||
|
margin: [0, 2, 0, 0]
|
||||||
|
},
|
||||||
|
col = {
|
||||||
|
columns: [
|
||||||
|
lineNumberObject,
|
||||||
|
]
|
||||||
|
};
|
||||||
|
currentParagraph = create("text");
|
||||||
|
col.columns.push(currentParagraph);
|
||||||
|
parseChildren(col.columns[0], element, currentParagraph, styles);
|
||||||
|
alreadyConverted.push(col);
|
||||||
|
} else {
|
||||||
|
parseChildren(alreadyConverted, element, currentParagraph, styles);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "br":
|
||||||
|
//in case of inline-line-numbers and the os-line-break class ignore the break
|
||||||
|
if (!(scope.lineNumberMode == "inline" && element.getAttribute("class") == "os-line-break")) {
|
||||||
|
currentParagraph = create("text");
|
||||||
|
alreadyConverted.push(currentParagraph);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "li":
|
||||||
|
case "div":
|
||||||
|
currentParagraph = create("text");
|
||||||
|
var stackDiv = create("stack");
|
||||||
|
stackDiv.stack.push(currentParagraph);
|
||||||
|
ComputeStyle(stackDiv, styles);
|
||||||
|
parseChildren(stackDiv.stack, element, currentParagraph);
|
||||||
|
alreadyConverted.push(stackDiv);
|
||||||
|
break;
|
||||||
|
case "p":
|
||||||
|
currentParagraph = create("text");
|
||||||
|
currentParagraph.margin = [0,5];
|
||||||
|
var stackP = create("stack");
|
||||||
|
stackP.stack.push(currentParagraph);
|
||||||
|
ComputeStyle(stackP, styles);
|
||||||
|
parseChildren(stackP.stack, element, currentParagraph);
|
||||||
|
alreadyConverted.push(stackP);
|
||||||
|
break;
|
||||||
|
case "img":
|
||||||
|
// TODO: need a proper way to calculate the space
|
||||||
|
// left on the page.
|
||||||
|
// This requires further information
|
||||||
|
// A4 in 72dpi: 595px x 842px
|
||||||
|
var maxResolution = {
|
||||||
|
width: 435,
|
||||||
|
height: 830
|
||||||
|
},
|
||||||
|
width = parseInt(element.getAttribute("width")),
|
||||||
|
height = parseInt(element.getAttribute("height"));
|
||||||
|
|
||||||
|
if (width > maxResolution.width) {
|
||||||
|
var scaleByWidth = maxResolution.width/width;
|
||||||
|
width *= scaleByWidth;
|
||||||
|
height *= scaleByWidth;
|
||||||
|
}
|
||||||
|
if (height > maxResolution.height) {
|
||||||
|
var scaleByHeight = maxResolution.height/height;
|
||||||
|
width *= scaleByHeight;
|
||||||
|
height *= scaleByHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyConverted.push({
|
||||||
|
image: BaseMap[element.getAttribute("src")],
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "ul":
|
||||||
|
var u = create("ul");
|
||||||
|
parseChildren(u.ul, element, currentParagraph, styles);
|
||||||
|
alreadyConverted.push(u);
|
||||||
|
break;
|
||||||
|
case "ol":
|
||||||
|
var o = create("ol");
|
||||||
|
parseChildren(o.ol, element, currentParagraph, styles);
|
||||||
|
alreadyConverted.push(o);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var temporary = create("text", element.textContent.replace(/\n/g, ""));
|
||||||
|
if (styles) {
|
||||||
|
ComputeStyle(temporary, styles);
|
||||||
|
}
|
||||||
|
// TODO: This if-clause is a hotfix for issue #2442.
|
||||||
|
// Do this right! Why is currentParagraph undefined?
|
||||||
|
if (!currentParagraph) {
|
||||||
|
currentParagraph = {};
|
||||||
|
currentParagraph.text = [];
|
||||||
|
}
|
||||||
|
currentParagraph.text.push(temporary);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return currentParagraph;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Parses HTML
|
||||||
|
* @function
|
||||||
|
* @param {string} converted -
|
||||||
|
* @param {object} htmlText -
|
||||||
|
*/
|
||||||
|
ParseHtml = function(converted, htmlText) {
|
||||||
|
var html = HTMLValidizer.validize(htmlText);
|
||||||
|
html = $(html.replace(/\t/g, "").replace(/\n/g, ""));
|
||||||
|
var emptyParagraph = create("text");
|
||||||
|
slice(html).forEach(function(element) {
|
||||||
|
ParseElement(converted, element);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
content = [];
|
||||||
|
ParseHtml(content, html);
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
BaseMap = images,
|
||||||
|
/**
|
||||||
|
* Creates containerelements for pdfMake
|
||||||
|
* e.g create("text":"MyText") result in { text: "MyText" }
|
||||||
|
* or complex objects create("stack", [{text:"MyText"}, {text:"MyText2"}])
|
||||||
|
*for units / paragraphs of text
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @param {string} name - name of the attribute holding content
|
||||||
|
* @param {object} content - the actual content (maybe empty)
|
||||||
|
*/
|
||||||
|
create = function(name, content) {
|
||||||
|
var o = {};
|
||||||
|
content = content || [];
|
||||||
|
o[name] = content;
|
||||||
|
return o;
|
||||||
|
};
|
||||||
|
fonts.forEach(function(fontInfo) {
|
||||||
|
registerFont(fontInfo);
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
convertHTML: convertHTML,
|
||||||
|
createElement: create
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
createInstance: createInstance
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
|
||||||
|
}());
|
@ -23,526 +23,6 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
// Can be used to find out if the projector or the side is used
|
// Can be used to find out if the projector or the side is used
|
||||||
.constant('REALM', 'site')
|
.constant('REALM', 'site')
|
||||||
|
|
||||||
//for global usage
|
|
||||||
.factory('HTMLValidizer', function() {
|
|
||||||
var HTMLValidizer = {};
|
|
||||||
|
|
||||||
//checks if str is valid HTML. Returns valid HTML if not,
|
|
||||||
//return emptystring if empty
|
|
||||||
HTMLValidizer.validize = function(str) {
|
|
||||||
if (str) {
|
|
||||||
var a = document.createElement('div');
|
|
||||||
a.innerHTML = str;
|
|
||||||
angular.forEach(a.childNodes, function (child) {
|
|
||||||
if (child.nodeType == 1) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return "<p>" + str + "</p>";
|
|
||||||
} else {
|
|
||||||
return ""; //needed for blank "reaons" field
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return HTMLValidizer;
|
|
||||||
})
|
|
||||||
|
|
||||||
.factory('PdfMakeDocumentProvider', [
|
|
||||||
'gettextCatalog',
|
|
||||||
'Config',
|
|
||||||
function(gettextCatalog, Config) {
|
|
||||||
/**
|
|
||||||
* Provides the global Document
|
|
||||||
* @constructor
|
|
||||||
* @param {object} contentProvider - Object with on method `getContent`, which returns an array for content
|
|
||||||
* @param {string} defaultFont - Default font for the document
|
|
||||||
*/
|
|
||||||
var createInstance = function(contentProvider, defaultFont) {
|
|
||||||
/**
|
|
||||||
* Generates header for PDF
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
var header = function() {
|
|
||||||
var date = new Date();
|
|
||||||
return {
|
|
||||||
// alignment: 'center',
|
|
||||||
color: '#555',
|
|
||||||
fontSize: 10,
|
|
||||||
margin: [80, 50, 80, 0], //margin: [left, top, right, bottom]
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
text: Config.get('general_event_name').value + ' · ' + Config.get('general_event_description').value ,
|
|
||||||
fontSize:10,
|
|
||||||
width: '70%'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fontSize: 6,
|
|
||||||
width: '30%',
|
|
||||||
text: gettextCatalog.getString('As of') + " " + date.toLocaleDateString() + " " + date.toLocaleTimeString(),
|
|
||||||
alignment: 'right'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Generates footer line
|
|
||||||
* @function
|
|
||||||
* @param {object} currentPage - An object representing the current page
|
|
||||||
* @param {number} pageCount - number for pages
|
|
||||||
*/
|
|
||||||
footer = function(currentPage, pageCount) {
|
|
||||||
return {
|
|
||||||
alignment: 'center',
|
|
||||||
fontSize: 8,
|
|
||||||
color: '#555',
|
|
||||||
text: gettextCatalog.getString('Page') + ' ' + currentPage.toString() + ' / ' + pageCount.toString()
|
|
||||||
};
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Generates the document(definition) for pdfMake
|
|
||||||
* @function
|
|
||||||
*/
|
|
||||||
getDocument = function() {
|
|
||||||
var content = contentProvider.getContent();
|
|
||||||
return {
|
|
||||||
pageSize: 'A4',
|
|
||||||
pageMargins: [80, 90, 80, 60],
|
|
||||||
defaultStyle: {
|
|
||||||
font: defaultFont,
|
|
||||||
fontSize: 10
|
|
||||||
},
|
|
||||||
header: header,
|
|
||||||
footer: footer,
|
|
||||||
content: content,
|
|
||||||
styles: {
|
|
||||||
title: {
|
|
||||||
fontSize: 30,
|
|
||||||
margin: [0,0,0,20],
|
|
||||||
bold: true
|
|
||||||
},
|
|
||||||
heading: {
|
|
||||||
fontSize: 16,
|
|
||||||
margin: [0,0,0,10],
|
|
||||||
bold: true
|
|
||||||
},
|
|
||||||
tableofcontent: {
|
|
||||||
fontSize: 12,
|
|
||||||
margin: [0,3]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
getDocument: getDocument
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
createInstance: createInstance
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.factory('PdfMakeConverter', [
|
|
||||||
'HTMLValidizer',
|
|
||||||
function(HTMLValidizer) {
|
|
||||||
/**
|
|
||||||
* Converter component for HTML->JSON for pdfMake
|
|
||||||
* @constructor
|
|
||||||
* @param {object} images - Key-Value structure representing image.src/BASE64 of images
|
|
||||||
* @param {object} fonts - Key-Value structure representing fonts (detailed description below)
|
|
||||||
* @param {object} pdfMake - the converter component enhances pdfMake
|
|
||||||
*/
|
|
||||||
var createInstance = function(images, fonts, pdfMake) {
|
|
||||||
var slice = Function.prototype.call.bind([].slice),
|
|
||||||
map = Function.prototype.call.bind([].map),
|
|
||||||
/**
|
|
||||||
* Adds a custom font to pdfMake.vfs
|
|
||||||
* @function
|
|
||||||
* @param {object} fontFiles - object with Files to add to pdfMake.vfs
|
|
||||||
* {
|
|
||||||
* normal: $Filename
|
|
||||||
* bold: $Filename
|
|
||||||
* italics: $Filename
|
|
||||||
* bolditalics: $Filename
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
addFontToVfs = function(fontFiles) {
|
|
||||||
Object.keys(fontFiles).forEach(function(name) {
|
|
||||||
var file = fontFiles[name];
|
|
||||||
pdfMake.vfs[file.name] = file.content;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Adds custom fonts to pdfMake
|
|
||||||
* @function
|
|
||||||
* @param {object} fontInfo - Font configuration from Backend
|
|
||||||
* {
|
|
||||||
* $FontName : {
|
|
||||||
* normal: $Filename
|
|
||||||
* bold: $Filename
|
|
||||||
* italics: $Filename
|
|
||||||
* bolditalics: $Filename
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
registerFont = function(fontInfo) {
|
|
||||||
Object.keys(fontInfo).forEach(function(name) {
|
|
||||||
var font = fontInfo[name];
|
|
||||||
addFontToVfs(font);
|
|
||||||
pdfMake.fonts = pdfMake.fonts || {};
|
|
||||||
pdfMake.fonts[name] = Object.keys(font).reduce(function(fontDefinition, style) {
|
|
||||||
fontDefinition[style] = font[style].name;
|
|
||||||
return fontDefinition;
|
|
||||||
}, {});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Convertes HTML for use with pdfMake
|
|
||||||
* @function
|
|
||||||
* @param {object} html - html
|
|
||||||
*/
|
|
||||||
convertHTML = function(html, scope) {
|
|
||||||
var elementStyles = {
|
|
||||||
"b": ["font-weight:bold"],
|
|
||||||
"strong": ["font-weight:bold"],
|
|
||||||
"u": ["text-decoration:underline"],
|
|
||||||
"em": ["font-style:italic"],
|
|
||||||
"i": ["font-style:italic"],
|
|
||||||
"h1": ["font-size:30"],
|
|
||||||
"h2": ["font-size:28"],
|
|
||||||
"h3": ["font-size:26"],
|
|
||||||
"h4": ["font-size:24"],
|
|
||||||
"h5": ["font-size:22"],
|
|
||||||
"h6": ["font-size:20"],
|
|
||||||
"a": ["color:blue", "text-decoration:underline"]
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Parses Children of the current paragraph
|
|
||||||
* @function
|
|
||||||
* @param {object} converted -
|
|
||||||
* @param {object} element -
|
|
||||||
* @param {object} currentParagraph -
|
|
||||||
* @param {object} styles -
|
|
||||||
*/
|
|
||||||
parseChildren = function(converted, element, currentParagraph, styles) {
|
|
||||||
var elements = [];
|
|
||||||
var children = element.childNodes;
|
|
||||||
if (children.length !== 0) {
|
|
||||||
_.forEach(children, function(child) {
|
|
||||||
currentParagraph = ParseElement(elements, child, currentParagraph, styles);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (elements.length !== 0) {
|
|
||||||
_.forEach(elements, function(el) {
|
|
||||||
converted.push(el);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return currentParagraph;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Extracts the style from an object
|
|
||||||
* @function
|
|
||||||
* @param {object} o - the current object
|
|
||||||
* @param {object} styles - an array with styles
|
|
||||||
*/
|
|
||||||
ComputeStyle = function(o, styles) {
|
|
||||||
styles.forEach(function(singleStyle) {
|
|
||||||
var styleDefinition = singleStyle.trim().toLowerCase().split(":");
|
|
||||||
var style = styleDefinition[0];
|
|
||||||
var value = styleDefinition[1];
|
|
||||||
if (styleDefinition.length == 2) {
|
|
||||||
switch (style) {
|
|
||||||
case "padding-left":
|
|
||||||
o.margin = [parseInt(value), 0, 0, 0];
|
|
||||||
break;
|
|
||||||
case "font-size":
|
|
||||||
o.fontSize = parseInt(value);
|
|
||||||
break;
|
|
||||||
case "text-align":
|
|
||||||
switch (value) {
|
|
||||||
case "right":
|
|
||||||
case "center":
|
|
||||||
case "justify":
|
|
||||||
o.alignment = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "font-weight":
|
|
||||||
switch (value) {
|
|
||||||
case "bold":
|
|
||||||
o.bold = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "text-decoration":
|
|
||||||
switch (value) {
|
|
||||||
case "underline":
|
|
||||||
o.decoration = "underline";
|
|
||||||
break;
|
|
||||||
case "line-through":
|
|
||||||
o.decoration = "lineThrough";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "font-style":
|
|
||||||
switch (value) {
|
|
||||||
case "italic":
|
|
||||||
o.italics = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "color":
|
|
||||||
o.color = value;
|
|
||||||
break;
|
|
||||||
case "background-color":
|
|
||||||
o.background = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Parses a single HTML element
|
|
||||||
* @function
|
|
||||||
* @param {object} alreadyConverted -
|
|
||||||
* @param {object} element -
|
|
||||||
* @param {object} currentParagraph -
|
|
||||||
* @param {object} styles -
|
|
||||||
*/
|
|
||||||
ParseElement = function(alreadyConverted, element, currentParagraph, styles) {
|
|
||||||
styles = styles || [];
|
|
||||||
if (element.getAttribute) {
|
|
||||||
var nodeStyle = element.getAttribute("style");
|
|
||||||
if (nodeStyle) {
|
|
||||||
nodeStyle.split(";").forEach(function(nodeStyle) {
|
|
||||||
var tmp = nodeStyle.replace(/\s/g, '');
|
|
||||||
styles.push(tmp);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var nodeName = element.nodeName.toLowerCase();
|
|
||||||
switch (nodeName) {
|
|
||||||
case "h1":
|
|
||||||
case "h2":
|
|
||||||
case "h3":
|
|
||||||
case "h4":
|
|
||||||
case "h5":
|
|
||||||
case "h6":
|
|
||||||
currentParagraph = create("text");
|
|
||||||
/* falls through */
|
|
||||||
case "a":
|
|
||||||
parseChildren(alreadyConverted, element, currentParagraph, styles.concat(elementStyles[nodeName]));
|
|
||||||
alreadyConverted.push(currentParagraph);
|
|
||||||
break;
|
|
||||||
case "b":
|
|
||||||
case "strong":
|
|
||||||
case "u":
|
|
||||||
case "em":
|
|
||||||
case "i":
|
|
||||||
parseChildren(alreadyConverted, element, currentParagraph, styles.concat(elementStyles[nodeName]));
|
|
||||||
break;
|
|
||||||
case "table":
|
|
||||||
var t = create("table", {
|
|
||||||
widths: [],
|
|
||||||
body: []
|
|
||||||
});
|
|
||||||
var border = element.getAttribute("border");
|
|
||||||
var isBorder = false;
|
|
||||||
if (border)
|
|
||||||
if (parseInt(border) == 1) isBorder = true;
|
|
||||||
if (!isBorder) t.layout = 'noBorders';
|
|
||||||
parseChildren(t.table.body, element, currentParagraph, styles);
|
|
||||||
var widths = element.getAttribute("widths");
|
|
||||||
if (!widths) {
|
|
||||||
if (t.table.body.length !== 0) {
|
|
||||||
if (t.table.body[0].length !== 0)
|
|
||||||
for (var k = 0; k < t.table.body[0].length; k++)
|
|
||||||
t.table.widths.push("*");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var w = widths.split(",");
|
|
||||||
for (var ko = 0; ko < w.length; ko++) t.table.widths.push(w[ko]);
|
|
||||||
}
|
|
||||||
alreadyConverted.push(t);
|
|
||||||
break;
|
|
||||||
case "tbody":
|
|
||||||
parseChildren(alreadyConverted, element, currentParagraph, styles);
|
|
||||||
break;
|
|
||||||
case "tr":
|
|
||||||
var row = [];
|
|
||||||
parseChildren(row, element, currentParagraph, styles);
|
|
||||||
alreadyConverted.push(row);
|
|
||||||
break;
|
|
||||||
case "td":
|
|
||||||
currentParagraph = create("text");
|
|
||||||
var st = create("stack");
|
|
||||||
st.stack.push(currentParagraph);
|
|
||||||
var rspan = element.getAttribute("rowspan");
|
|
||||||
if (rspan)
|
|
||||||
st.rowSpan = parseInt(rspan);
|
|
||||||
var cspan = element.getAttribute("colspan");
|
|
||||||
if (cspan)
|
|
||||||
st.colSpan = parseInt(cspan);
|
|
||||||
parseChildren(st.stack, element, currentParagraph, styles);
|
|
||||||
alreadyConverted.push(st);
|
|
||||||
break;
|
|
||||||
case "span":
|
|
||||||
if (scope.lineNumberMode == "inline") {
|
|
||||||
var lineNumberInline = element.getAttribute("data-line-number"),
|
|
||||||
lineNumberObjInline = {
|
|
||||||
text: lineNumberInline,
|
|
||||||
color: "gray",
|
|
||||||
fontSize: 5
|
|
||||||
};
|
|
||||||
currentParagraph.text.push(lineNumberObjInline);
|
|
||||||
parseChildren(alreadyConverted, element, currentParagraph, styles);
|
|
||||||
} else if (scope.lineNumberMode == "outside") {
|
|
||||||
var lineNumberOutline = element.getAttribute("data-line-number"),
|
|
||||||
lineNumberObject = {
|
|
||||||
width: 20,
|
|
||||||
text: lineNumberOutline,
|
|
||||||
color: "gray",
|
|
||||||
fontSize: 8,
|
|
||||||
margin: [0, 2, 0, 0]
|
|
||||||
},
|
|
||||||
col = {
|
|
||||||
columns: [
|
|
||||||
lineNumberObject,
|
|
||||||
]
|
|
||||||
};
|
|
||||||
currentParagraph = create("text");
|
|
||||||
col.columns.push(currentParagraph);
|
|
||||||
parseChildren(col.columns[0], element, currentParagraph, styles);
|
|
||||||
alreadyConverted.push(col);
|
|
||||||
} else {
|
|
||||||
parseChildren(alreadyConverted, element, currentParagraph, styles);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "br":
|
|
||||||
//in case of inline-line-numbers and the os-line-break class ignore the break
|
|
||||||
if (!(scope.lineNumberMode == "inline" && element.getAttribute("class") == "os-line-break")) {
|
|
||||||
currentParagraph = create("text");
|
|
||||||
alreadyConverted.push(currentParagraph);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "li":
|
|
||||||
case "div":
|
|
||||||
currentParagraph = create("text");
|
|
||||||
var stackDiv = create("stack");
|
|
||||||
stackDiv.stack.push(currentParagraph);
|
|
||||||
ComputeStyle(stackDiv, styles);
|
|
||||||
parseChildren(stackDiv.stack, element, currentParagraph);
|
|
||||||
alreadyConverted.push(stackDiv);
|
|
||||||
break;
|
|
||||||
case "p":
|
|
||||||
currentParagraph = create("text");
|
|
||||||
currentParagraph.margin = [0,5];
|
|
||||||
var stackP = create("stack");
|
|
||||||
stackP.stack.push(currentParagraph);
|
|
||||||
ComputeStyle(stackP, styles);
|
|
||||||
parseChildren(stackP.stack, element, currentParagraph);
|
|
||||||
alreadyConverted.push(stackP);
|
|
||||||
break;
|
|
||||||
case "img":
|
|
||||||
// TODO: need a proper way to calculate the space
|
|
||||||
// left on the page.
|
|
||||||
// This requires further information
|
|
||||||
// A4 in 72dpi: 595px x 842px
|
|
||||||
var maxResolution = {
|
|
||||||
width: 435,
|
|
||||||
height: 830
|
|
||||||
},
|
|
||||||
width = parseInt(element.getAttribute("width")),
|
|
||||||
height = parseInt(element.getAttribute("height"));
|
|
||||||
|
|
||||||
if (width > maxResolution.width) {
|
|
||||||
var scaleByWidth = maxResolution.width/width;
|
|
||||||
width *= scaleByWidth;
|
|
||||||
height *= scaleByWidth;
|
|
||||||
}
|
|
||||||
if (height > maxResolution.height) {
|
|
||||||
var scaleByHeight = maxResolution.height/height;
|
|
||||||
width *= scaleByHeight;
|
|
||||||
height *= scaleByHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
alreadyConverted.push({
|
|
||||||
image: BaseMap[element.getAttribute("src")],
|
|
||||||
width: width,
|
|
||||||
height: height
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "ul":
|
|
||||||
var u = create("ul");
|
|
||||||
parseChildren(u.ul, element, currentParagraph, styles);
|
|
||||||
alreadyConverted.push(u);
|
|
||||||
break;
|
|
||||||
case "ol":
|
|
||||||
var o = create("ol");
|
|
||||||
parseChildren(o.ol, element, currentParagraph, styles);
|
|
||||||
alreadyConverted.push(o);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
var temporary = create("text", element.textContent.replace(/\n/g, ""));
|
|
||||||
if (styles) {
|
|
||||||
ComputeStyle(temporary, styles);
|
|
||||||
}
|
|
||||||
// TODO: This if-clause is a hotfix for issue #2442.
|
|
||||||
// Do this right! Why is currentParagraph undefined?
|
|
||||||
if (!currentParagraph) {
|
|
||||||
currentParagraph = {};
|
|
||||||
currentParagraph.text = [];
|
|
||||||
}
|
|
||||||
currentParagraph.text.push(temporary);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return currentParagraph;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Parses HTML
|
|
||||||
* @function
|
|
||||||
* @param {string} converted -
|
|
||||||
* @param {object} htmlText -
|
|
||||||
*/
|
|
||||||
ParseHtml = function(converted, htmlText) {
|
|
||||||
var html = HTMLValidizer.validize(htmlText);
|
|
||||||
html = $(html.replace(/\t/g, "").replace(/\n/g, ""));
|
|
||||||
var emptyParagraph = create("text");
|
|
||||||
slice(html).forEach(function(element) {
|
|
||||||
ParseElement(converted, element);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
content = [];
|
|
||||||
ParseHtml(content, html);
|
|
||||||
return content;
|
|
||||||
},
|
|
||||||
BaseMap = images,
|
|
||||||
/**
|
|
||||||
* Creates containerelements for pdfMake
|
|
||||||
* e.g create("text":"MyText") result in { text: "MyText" }
|
|
||||||
* or complex objects create("stack", [{text:"MyText"}, {text:"MyText2"}])
|
|
||||||
*for units / paragraphs of text
|
|
||||||
*
|
|
||||||
* @function
|
|
||||||
* @param {string} name - name of the attribute holding content
|
|
||||||
* @param {object} content - the actual content (maybe empty)
|
|
||||||
*/
|
|
||||||
create = function(name, content) {
|
|
||||||
var o = {};
|
|
||||||
content = content || [];
|
|
||||||
o[name] = content;
|
|
||||||
return o;
|
|
||||||
};
|
|
||||||
fonts.forEach(function(fontInfo) {
|
|
||||||
registerFont(fontInfo);
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
convertHTML: convertHTML,
|
|
||||||
createElement: create
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
createInstance: createInstance
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
// Provider to register entries for the main menu.
|
// Provider to register entries for the main menu.
|
||||||
.provider('mainMenu', [
|
.provider('mainMenu', [
|
||||||
function() {
|
function() {
|
||||||
|
439
openslides/motions/static/js/motions/pdf.js
Normal file
439
openslides/motions/static/js/motions/pdf.js
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.motions.pdf', [])
|
||||||
|
|
||||||
|
.factory('MotionContentProvider', ['gettextCatalog', function(gettextCatalog) {
|
||||||
|
/**
|
||||||
|
* Provides the content as JS objects for Motions in pdfMake context
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
var createInstance = function(converter, motion, $scope, User) {
|
||||||
|
|
||||||
|
// generates the text of the motion. Also septerates between line-numbers
|
||||||
|
var textContent = function() {
|
||||||
|
if ($scope.lineNumberMode == "inline" || $scope.lineNumberMode == "outside") {
|
||||||
|
/* in order to distinguish between the line-number-types we need to pass the scope
|
||||||
|
* to the convertHTML function.
|
||||||
|
* We should avoid this, since this completly breaks compatibilty for every
|
||||||
|
* other project that might want to use this HTML to PDF parser.
|
||||||
|
* https://github.com/OpenSlides/OpenSlides/issues/2361
|
||||||
|
*/
|
||||||
|
return converter.convertHTML(motion.getTextWithLineBreaks($scope.version), $scope);
|
||||||
|
} else {
|
||||||
|
return converter.convertHTML(motion.getText($scope.version), $scope);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate text of reason
|
||||||
|
var reasonContent = function() {
|
||||||
|
return converter.convertHTML(motion.getReason($scope.version), $scope);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate header text of motion
|
||||||
|
var motionHeader = function() {
|
||||||
|
var header = converter.createElement("text", gettextCatalog.getString("Motion") + " " + motion.identifier + ": " + motion.getTitle($scope.version));
|
||||||
|
header.bold = true;
|
||||||
|
header.fontSize = 26;
|
||||||
|
return header;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate text of signment
|
||||||
|
var signment = function() {
|
||||||
|
var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:');
|
||||||
|
var state = converter.createElement("text", User.get(motion.submitters_id[0]).full_name + '\n'+gettextCatalog.getString(motion.state.name));
|
||||||
|
state.width = "70%";
|
||||||
|
label.width = "30%";
|
||||||
|
label.bold = true;
|
||||||
|
var signment = converter.createElement("columns", [label, state]);
|
||||||
|
signment.margin = [10, 20, 0, 10];
|
||||||
|
signment.lineHeight = 2.5;
|
||||||
|
return signment;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates polls
|
||||||
|
var polls = function() {
|
||||||
|
if (!motion.polls.length) return {};
|
||||||
|
var pollLabel = converter.createElement("text", gettextCatalog.getString('Voting result') + ":"),
|
||||||
|
results = function() {
|
||||||
|
return motion.polls.map(function(poll, index) {
|
||||||
|
var id = index + 1,
|
||||||
|
yes = poll.yes ? poll.yes : '-', // if no poll.yes is given set it to '-'
|
||||||
|
yesRelative = poll.getVote(poll.yes, 'yes').percentStr,
|
||||||
|
no = poll.no ? poll.no : '-',
|
||||||
|
noRelative = poll.getVote(poll.no, 'no').percentStr,
|
||||||
|
abstain = poll.abstain ? poll.abstain : '-',
|
||||||
|
abstainrelativeGet = poll.getVote(poll.abstain, 'abstain').percentStr,
|
||||||
|
abstainRelative = abstainrelativeGet ? abstainrelativeGet : '',
|
||||||
|
valid = poll.votesvalid ? poll.votesvalid : '-',
|
||||||
|
validRelative = poll.getVote(poll.votesvalid, 'votesvalid').percentStr,
|
||||||
|
number = {
|
||||||
|
text: id + ".",
|
||||||
|
width: "5%"
|
||||||
|
},
|
||||||
|
headerText = {
|
||||||
|
text: gettextCatalog.getString('Vote'),
|
||||||
|
width: "15%"
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generates a part (consisting of different columns) of the polls
|
||||||
|
*
|
||||||
|
* Example Ja 100 ( 90% )
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @param {string} name - E.g. "Ja"
|
||||||
|
* @param {number} value - E.g.100
|
||||||
|
* @param {number} relValue - E.g. 90
|
||||||
|
*/
|
||||||
|
createPart = function(name, value, relValue) {
|
||||||
|
var indexColumn = converter.createElement("text");
|
||||||
|
var nameColumn = converter.createElement("text", "" + name);
|
||||||
|
var valueColumn = converter.createElement("text", "" + value);
|
||||||
|
var relColumn = converter.createElement("text", relValue);
|
||||||
|
valueColumn.width = "40%";
|
||||||
|
indexColumn.width = "5%";
|
||||||
|
valueColumn.width = "5%";
|
||||||
|
valueColumn.alignment = "right";
|
||||||
|
relColumn.margin = [5, 0, 0, 0];
|
||||||
|
return [indexColumn, nameColumn, valueColumn, relColumn];
|
||||||
|
},
|
||||||
|
yesPart = converter.createElement("columns", createPart(gettextCatalog.getString("Yes"), yes, yesRelative)),
|
||||||
|
noPart = converter.createElement("columns", createPart(gettextCatalog.getString("No"), no, noRelative)),
|
||||||
|
abstainPart = converter.createElement("columns", createPart(gettextCatalog.getString("Abstain"), abstain, abstainRelative)),
|
||||||
|
totalPart = converter.createElement("columns", createPart(gettextCatalog.getString("Valid votes"), valid, validRelative)),
|
||||||
|
heading = converter.createElement("columns", [number, headerText]),
|
||||||
|
pollResult = converter.createElement("stack", [
|
||||||
|
heading, yesPart, noPart, abstainPart, totalPart
|
||||||
|
]);
|
||||||
|
|
||||||
|
return pollResult;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
pollLabel.width = '35%';
|
||||||
|
pollLabel.bold = true;
|
||||||
|
var result = converter.createElement("columns", [pollLabel, results()]);
|
||||||
|
result.margin = [10, 0, 0, 10];
|
||||||
|
result.lineHeight = 1;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates title section for motion
|
||||||
|
var titleSection = function() {
|
||||||
|
var title = converter.createElement("text", motion.getTitle($scope.version));
|
||||||
|
title.bold = true;
|
||||||
|
title.fontSize = 14;
|
||||||
|
title.margin = [0, 0, 0, 10];
|
||||||
|
return title;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates reason section for polls
|
||||||
|
var reason = function() {
|
||||||
|
var r = converter.createElement("text", gettextCatalog.getString("Reason") + ":");
|
||||||
|
r.bold = true;
|
||||||
|
r.fontSize = 14;
|
||||||
|
r.margin = [0, 30, 0, 10];
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
//getters
|
||||||
|
var getTitle = function() {
|
||||||
|
return motion.getTitle($scope.verion);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getIdentifier = function() {
|
||||||
|
return motion.identifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getCategory = function() {
|
||||||
|
return motion.category;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates content as a pdfmake consumable
|
||||||
|
var getContent = function() {
|
||||||
|
if (reasonContent().length === 0 ) {
|
||||||
|
return [
|
||||||
|
motionHeader(),
|
||||||
|
signment(),
|
||||||
|
polls(),
|
||||||
|
titleSection(),
|
||||||
|
textContent(),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
motionHeader(),
|
||||||
|
signment(),
|
||||||
|
polls(),
|
||||||
|
titleSection(),
|
||||||
|
textContent(),
|
||||||
|
reason(),
|
||||||
|
reasonContent()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
getContent: getContent,
|
||||||
|
getTitle: getTitle,
|
||||||
|
getIdentifier: getIdentifier,
|
||||||
|
getCategory: getCategory
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
createInstance: createInstance
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
|
||||||
|
.factory('PollContentProvider', function() {
|
||||||
|
/**
|
||||||
|
* Generates a content provider for polls
|
||||||
|
* @constructor
|
||||||
|
* @param {string} title - title of poll
|
||||||
|
* @param {string} id - if of poll
|
||||||
|
* @param {object} gettextCatalog - for translation
|
||||||
|
*/
|
||||||
|
var createInstance = function(title, id, gettextCatalog){
|
||||||
|
|
||||||
|
//left and top margin for a single sheet
|
||||||
|
var space = {
|
||||||
|
left: 30,
|
||||||
|
top: 30,
|
||||||
|
bottom: 10
|
||||||
|
},
|
||||||
|
//size and position of the signing circle
|
||||||
|
circle = {
|
||||||
|
yDistance: 6,
|
||||||
|
size: 8
|
||||||
|
},
|
||||||
|
//margin for the decision
|
||||||
|
singleItemMargin = 10,
|
||||||
|
//space between circle and dicision
|
||||||
|
columnwidth = 20,
|
||||||
|
//defines the space under a single sheet
|
||||||
|
sheetend = 65,
|
||||||
|
//defines the used fontsize
|
||||||
|
fontSize = 14;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draws a single circle
|
||||||
|
* @function
|
||||||
|
* @param {int} y - the relative y coordinate
|
||||||
|
* @param {int} size - size of the circle in px
|
||||||
|
*/
|
||||||
|
var drawCircle = function(y, size) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: 'ellipse',
|
||||||
|
x: 0,
|
||||||
|
y: y,
|
||||||
|
lineColor: 'black',
|
||||||
|
r1: size,
|
||||||
|
r2: size
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an entry in the ballot with a circle to draw into
|
||||||
|
* @function
|
||||||
|
* @param {string} decision - the name of an entry to decide between, e.g. 'yes' or 'no'
|
||||||
|
*/
|
||||||
|
var createBallotEntry = function(decision) {
|
||||||
|
return {
|
||||||
|
margin: [space.left+circle.size, singleItemMargin, 0, 0],
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
width: columnwidth,
|
||||||
|
canvas: drawCircle(circle.yDistance, circle.size)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: decision
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a single section on the ballot paper
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
var createSection = function() {
|
||||||
|
return {
|
||||||
|
stack: [{
|
||||||
|
text: gettextCatalog.getString("Motion") + " " + id,
|
||||||
|
style: 'header',
|
||||||
|
margin: [space.left, space.top, 0, 0]
|
||||||
|
}, {
|
||||||
|
text: title,
|
||||||
|
margin: [space.left, 0, 0, space.bottom]
|
||||||
|
},
|
||||||
|
createBallotEntry(gettextCatalog.getString("Yes")),
|
||||||
|
createBallotEntry(gettextCatalog.getString("No")),
|
||||||
|
createBallotEntry(gettextCatalog.getString("Abstain")),
|
||||||
|
],
|
||||||
|
margin: [0, 0, 0, sheetend]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Content for single motion
|
||||||
|
* @function
|
||||||
|
* @param {string} id - if of poll
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
content: [{
|
||||||
|
table: {
|
||||||
|
headerRows: 1,
|
||||||
|
widths: ['*', '*'],
|
||||||
|
body: [
|
||||||
|
[createSection(), createSection()],
|
||||||
|
[createSection(), createSection()],
|
||||||
|
[createSection(), createSection()],
|
||||||
|
[createSection(), createSection()]
|
||||||
|
],
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
hLineWidth: function() {return 0.5;},
|
||||||
|
vLineWidth: function() {return 0.5;},
|
||||||
|
hLineColor: function() {return 'gray';},
|
||||||
|
vLineColor: function() {return 'gray';},
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
pageSize: 'A4',
|
||||||
|
pageMargins: [0, 0, 0, 0],
|
||||||
|
styles: {
|
||||||
|
header: {
|
||||||
|
fontSize: fontSize,
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
createInstance: createInstance
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
.factory('MotionCatalogContentProvider', ['gettextCatalog', function(gettextCatalog) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @function
|
||||||
|
* @param {object} allMotions - A sorted array of all motions to parse
|
||||||
|
* @param {object} $scope - Current $scope
|
||||||
|
* @param {object} User - Current user
|
||||||
|
*/
|
||||||
|
var createInstance = function(allMotions, $scope, User, Category) {
|
||||||
|
|
||||||
|
//function to create the Table of contents
|
||||||
|
var createTitle = function() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: gettextCatalog.getString("Motions"),
|
||||||
|
style: "title"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var createTOContent = function(motionTitles) {
|
||||||
|
|
||||||
|
var heading = {
|
||||||
|
text: gettextCatalog.getString("Table of contents"),
|
||||||
|
style: "heading",
|
||||||
|
};
|
||||||
|
|
||||||
|
var toc = [];
|
||||||
|
angular.forEach(motionTitles, function(title) {
|
||||||
|
toc.push({
|
||||||
|
text: gettextCatalog.getString("Motion") + " " + title,
|
||||||
|
style: "tableofcontent",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
heading,
|
||||||
|
toc,
|
||||||
|
addPageBreak()
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
// function to create the table of catergories (if any)
|
||||||
|
var createTOCatergories = function() {
|
||||||
|
if (Category.getAll().length > 0) {
|
||||||
|
var heading = {
|
||||||
|
text: gettextCatalog.getString("Categories"),
|
||||||
|
style: "heading"
|
||||||
|
};
|
||||||
|
|
||||||
|
var toc = [];
|
||||||
|
angular.forEach(Category.getAll(), function(cat) {
|
||||||
|
toc.push(
|
||||||
|
{
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
text: cat.prefix,
|
||||||
|
style: 'tableofcontent',
|
||||||
|
width: 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: cat.name,
|
||||||
|
style: 'tableofcontent'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
heading,
|
||||||
|
toc,
|
||||||
|
addPageBreak()
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// if there are no categories, return "empty string"
|
||||||
|
// pdfmake takes "null" literally and throws an error
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// function to apply a pagebreak-keyword
|
||||||
|
var addPageBreak = function() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: '',
|
||||||
|
pageBreak: 'after'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
// returns the pure content of the motion, parseable by makepdf
|
||||||
|
var getContent = function() {
|
||||||
|
var motionContent = [];
|
||||||
|
var motionTitles = [];
|
||||||
|
var motionCategories = [];
|
||||||
|
angular.forEach(allMotions, function(motion, key) {
|
||||||
|
motionTitles.push(motion.getIdentifier() + ": " + motion.getTitle());
|
||||||
|
motionContent.push(motion.getContent());
|
||||||
|
if (key < allMotions.length - 1) {
|
||||||
|
motionContent.push(addPageBreak());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
createTitle(),
|
||||||
|
createTOCatergories(),
|
||||||
|
createTOContent(motionTitles),
|
||||||
|
motionContent
|
||||||
|
];
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
getContent: getContent
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
createInstance: createInstance
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
|
||||||
|
}());
|
@ -2,439 +2,13 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlidesApp.motions.diff', 'OpenSlidesApp.motions.motionservices'])
|
angular.module('OpenSlidesApp.motions.site', [
|
||||||
|
'OpenSlidesApp.motions',
|
||||||
.factory('MotionContentProvider', ['gettextCatalog', function(gettextCatalog) {
|
'OpenSlidesApp.motions.diff',
|
||||||
/**
|
'OpenSlidesApp.motions.motionservices',
|
||||||
* Provides the content as JS objects for Motions in pdfMake context
|
'OpenSlidesApp.core.pdf',
|
||||||
* @constructor
|
'OpenSlidesApp.motions.pdf'
|
||||||
*/
|
])
|
||||||
|
|
||||||
var createInstance = function(converter, motion, $scope, User) {
|
|
||||||
|
|
||||||
// generates the text of the motion. Also septerates between line-numbers
|
|
||||||
var textContent = function() {
|
|
||||||
if ($scope.lineNumberMode == "inline" || $scope.lineNumberMode == "outside") {
|
|
||||||
/* in order to distinguish between the line-number-types we need to pass the scope
|
|
||||||
* to the convertHTML function.
|
|
||||||
* We should avoid this, since this completly breaks compatibilty for every
|
|
||||||
* other project that might want to use this HTML to PDF parser.
|
|
||||||
* https://github.com/OpenSlides/OpenSlides/issues/2361
|
|
||||||
*/
|
|
||||||
return converter.convertHTML(motion.getTextWithLineBreaks($scope.version), $scope);
|
|
||||||
} else {
|
|
||||||
return converter.convertHTML(motion.getText($scope.version), $scope);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate text of reason
|
|
||||||
var reasonContent = function() {
|
|
||||||
return converter.convertHTML(motion.getReason($scope.version), $scope);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate header text of motion
|
|
||||||
var motionHeader = function() {
|
|
||||||
var header = converter.createElement("text", gettextCatalog.getString("Motion") + " " + motion.identifier + ": " + motion.getTitle($scope.version));
|
|
||||||
header.bold = true;
|
|
||||||
header.fontSize = 26;
|
|
||||||
return header;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate text of signment
|
|
||||||
var signment = function() {
|
|
||||||
var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:');
|
|
||||||
var state = converter.createElement("text", User.get(motion.submitters_id[0]).full_name + '\n'+gettextCatalog.getString(motion.state.name));
|
|
||||||
state.width = "70%";
|
|
||||||
label.width = "30%";
|
|
||||||
label.bold = true;
|
|
||||||
var signment = converter.createElement("columns", [label, state]);
|
|
||||||
signment.margin = [10, 20, 0, 10];
|
|
||||||
signment.lineHeight = 2.5;
|
|
||||||
return signment;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generates polls
|
|
||||||
var polls = function() {
|
|
||||||
if (!motion.polls.length) return {};
|
|
||||||
var pollLabel = converter.createElement("text", gettextCatalog.getString('Voting result') + ":"),
|
|
||||||
results = function() {
|
|
||||||
return motion.polls.map(function(poll, index) {
|
|
||||||
var id = index + 1,
|
|
||||||
yes = poll.yes ? poll.yes : '-', // if no poll.yes is given set it to '-'
|
|
||||||
yesRelative = poll.getVote(poll.yes, 'yes').percentStr,
|
|
||||||
no = poll.no ? poll.no : '-',
|
|
||||||
noRelative = poll.getVote(poll.no, 'no').percentStr,
|
|
||||||
abstain = poll.abstain ? poll.abstain : '-',
|
|
||||||
abstainrelativeGet = poll.getVote(poll.abstain, 'abstain').percentStr,
|
|
||||||
abstainRelative = abstainrelativeGet ? abstainrelativeGet : '',
|
|
||||||
valid = poll.votesvalid ? poll.votesvalid : '-',
|
|
||||||
validRelative = poll.getVote(poll.votesvalid, 'votesvalid').percentStr,
|
|
||||||
number = {
|
|
||||||
text: id + ".",
|
|
||||||
width: "5%"
|
|
||||||
},
|
|
||||||
headerText = {
|
|
||||||
text: gettextCatalog.getString('Vote'),
|
|
||||||
width: "15%"
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Generates a part (consisting of different columns) of the polls
|
|
||||||
*
|
|
||||||
* Example Ja 100 ( 90% )
|
|
||||||
*
|
|
||||||
* @function
|
|
||||||
* @param {string} name - E.g. "Ja"
|
|
||||||
* @param {number} value - E.g.100
|
|
||||||
* @param {number} relValue - E.g. 90
|
|
||||||
*/
|
|
||||||
createPart = function(name, value, relValue) {
|
|
||||||
var indexColumn = converter.createElement("text");
|
|
||||||
var nameColumn = converter.createElement("text", "" + name);
|
|
||||||
var valueColumn = converter.createElement("text", "" + value);
|
|
||||||
var relColumn = converter.createElement("text", relValue);
|
|
||||||
valueColumn.width = "40%";
|
|
||||||
indexColumn.width = "5%";
|
|
||||||
valueColumn.width = "5%";
|
|
||||||
valueColumn.alignment = "right";
|
|
||||||
relColumn.margin = [5, 0, 0, 0];
|
|
||||||
return [indexColumn, nameColumn, valueColumn, relColumn];
|
|
||||||
},
|
|
||||||
yesPart = converter.createElement("columns", createPart(gettextCatalog.getString("Yes"), yes, yesRelative)),
|
|
||||||
noPart = converter.createElement("columns", createPart(gettextCatalog.getString("No"), no, noRelative)),
|
|
||||||
abstainPart = converter.createElement("columns", createPart(gettextCatalog.getString("Abstain"), abstain, abstainRelative)),
|
|
||||||
totalPart = converter.createElement("columns", createPart(gettextCatalog.getString("Valid votes"), valid, validRelative)),
|
|
||||||
heading = converter.createElement("columns", [number, headerText]),
|
|
||||||
pollResult = converter.createElement("stack", [
|
|
||||||
heading, yesPart, noPart, abstainPart, totalPart
|
|
||||||
]);
|
|
||||||
|
|
||||||
return pollResult;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
pollLabel.width = '35%';
|
|
||||||
pollLabel.bold = true;
|
|
||||||
var result = converter.createElement("columns", [pollLabel, results()]);
|
|
||||||
result.margin = [10, 0, 0, 10];
|
|
||||||
result.lineHeight = 1;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generates title section for motion
|
|
||||||
var titleSection = function() {
|
|
||||||
var title = converter.createElement("text", motion.getTitle($scope.version));
|
|
||||||
title.bold = true;
|
|
||||||
title.fontSize = 14;
|
|
||||||
title.margin = [0, 0, 0, 10];
|
|
||||||
return title;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generates reason section for polls
|
|
||||||
var reason = function() {
|
|
||||||
var r = converter.createElement("text", gettextCatalog.getString("Reason") + ":");
|
|
||||||
r.bold = true;
|
|
||||||
r.fontSize = 14;
|
|
||||||
r.margin = [0, 30, 0, 10];
|
|
||||||
return r;
|
|
||||||
};
|
|
||||||
|
|
||||||
//getters
|
|
||||||
var getTitle = function() {
|
|
||||||
return motion.getTitle($scope.verion);
|
|
||||||
};
|
|
||||||
|
|
||||||
var getIdentifier = function() {
|
|
||||||
return motion.identifier;
|
|
||||||
};
|
|
||||||
|
|
||||||
var getCategory = function() {
|
|
||||||
return motion.category;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generates content as a pdfmake consumable
|
|
||||||
var getContent = function() {
|
|
||||||
if (reasonContent().length === 0 ) {
|
|
||||||
return [
|
|
||||||
motionHeader(),
|
|
||||||
signment(),
|
|
||||||
polls(),
|
|
||||||
titleSection(),
|
|
||||||
textContent(),
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
return [
|
|
||||||
motionHeader(),
|
|
||||||
signment(),
|
|
||||||
polls(),
|
|
||||||
titleSection(),
|
|
||||||
textContent(),
|
|
||||||
reason(),
|
|
||||||
reasonContent()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
getContent: getContent,
|
|
||||||
getTitle: getTitle,
|
|
||||||
getIdentifier: getIdentifier,
|
|
||||||
getCategory: getCategory
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
createInstance: createInstance
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('PollContentProvider', function() {
|
|
||||||
/**
|
|
||||||
* Generates a content provider for polls
|
|
||||||
* @constructor
|
|
||||||
* @param {string} title - title of poll
|
|
||||||
* @param {string} id - if of poll
|
|
||||||
* @param {object} gettextCatalog - for translation
|
|
||||||
*/
|
|
||||||
var createInstance = function(title, id, gettextCatalog){
|
|
||||||
|
|
||||||
//left and top margin for a single sheet
|
|
||||||
var space = {
|
|
||||||
left: 30,
|
|
||||||
top: 30,
|
|
||||||
bottom: 10
|
|
||||||
},
|
|
||||||
//size and position of the signing circle
|
|
||||||
circle = {
|
|
||||||
yDistance: 6,
|
|
||||||
size: 8
|
|
||||||
},
|
|
||||||
//margin for the decision
|
|
||||||
singleItemMargin = 10,
|
|
||||||
//space between circle and dicision
|
|
||||||
columnwidth = 20,
|
|
||||||
//defines the space under a single sheet
|
|
||||||
sheetend = 65,
|
|
||||||
//defines the used fontsize
|
|
||||||
fontSize = 14;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draws a single circle
|
|
||||||
* @function
|
|
||||||
* @param {int} y - the relative y coordinate
|
|
||||||
* @param {int} size - size of the circle in px
|
|
||||||
*/
|
|
||||||
var drawCircle = function(y, size) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
type: 'ellipse',
|
|
||||||
x: 0,
|
|
||||||
y: y,
|
|
||||||
lineColor: 'black',
|
|
||||||
r1: size,
|
|
||||||
r2: size
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an entry in the ballot with a circle to draw into
|
|
||||||
* @function
|
|
||||||
* @param {string} decision - the name of an entry to decide between, e.g. 'yes' or 'no'
|
|
||||||
*/
|
|
||||||
var createBallotEntry = function(decision) {
|
|
||||||
return {
|
|
||||||
margin: [space.left+circle.size, singleItemMargin, 0, 0],
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
width: columnwidth,
|
|
||||||
canvas: drawCircle(circle.yDistance, circle.size)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: decision
|
|
||||||
}
|
|
||||||
],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a single section on the ballot paper
|
|
||||||
* @function
|
|
||||||
*/
|
|
||||||
var createSection = function() {
|
|
||||||
return {
|
|
||||||
stack: [{
|
|
||||||
text: gettextCatalog.getString("Motion") + " " + id,
|
|
||||||
style: 'header',
|
|
||||||
margin: [space.left, space.top, 0, 0]
|
|
||||||
}, {
|
|
||||||
text: title,
|
|
||||||
margin: [space.left, 0, 0, space.bottom]
|
|
||||||
},
|
|
||||||
createBallotEntry(gettextCatalog.getString("Yes")),
|
|
||||||
createBallotEntry(gettextCatalog.getString("No")),
|
|
||||||
createBallotEntry(gettextCatalog.getString("Abstain")),
|
|
||||||
],
|
|
||||||
margin: [0, 0, 0, sheetend]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns Content for single motion
|
|
||||||
* @function
|
|
||||||
* @param {string} id - if of poll
|
|
||||||
*/
|
|
||||||
return {
|
|
||||||
content: [{
|
|
||||||
table: {
|
|
||||||
headerRows: 1,
|
|
||||||
widths: ['*', '*'],
|
|
||||||
body: [
|
|
||||||
[createSection(), createSection()],
|
|
||||||
[createSection(), createSection()],
|
|
||||||
[createSection(), createSection()],
|
|
||||||
[createSection(), createSection()]
|
|
||||||
],
|
|
||||||
},
|
|
||||||
layout: {
|
|
||||||
hLineWidth: function() {return 0.5;},
|
|
||||||
vLineWidth: function() {return 0.5;},
|
|
||||||
hLineColor: function() {return 'gray';},
|
|
||||||
vLineColor: function() {return 'gray';},
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
pageSize: 'A4',
|
|
||||||
pageMargins: [0, 0, 0, 0],
|
|
||||||
styles: {
|
|
||||||
header: {
|
|
||||||
fontSize: fontSize,
|
|
||||||
bold: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
createInstance: createInstance
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
.factory('MotionCatalogContentProvider', ['gettextCatalog', function(gettextCatalog) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @function
|
|
||||||
* @param {object} allMotions - A sorted array of all motions to parse
|
|
||||||
* @param {object} $scope - Current $scope
|
|
||||||
* @param {object} User - Current user
|
|
||||||
*/
|
|
||||||
var createInstance = function(allMotions, $scope, User, Category) {
|
|
||||||
|
|
||||||
//function to create the Table of contents
|
|
||||||
var createTitle = function() {
|
|
||||||
|
|
||||||
return {
|
|
||||||
text: gettextCatalog.getString("Motions"),
|
|
||||||
style: "title"
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var createTOContent = function(motionTitles) {
|
|
||||||
|
|
||||||
var heading = {
|
|
||||||
text: gettextCatalog.getString("Table of contents"),
|
|
||||||
style: "heading",
|
|
||||||
};
|
|
||||||
|
|
||||||
var toc = [];
|
|
||||||
angular.forEach(motionTitles, function(title) {
|
|
||||||
toc.push({
|
|
||||||
text: gettextCatalog.getString("Motion") + " " + title,
|
|
||||||
style: "tableofcontent",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
|
||||||
heading,
|
|
||||||
toc,
|
|
||||||
addPageBreak()
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
// function to create the table of catergories (if any)
|
|
||||||
var createTOCatergories = function() {
|
|
||||||
if (Category.getAll().length > 0) {
|
|
||||||
var heading = {
|
|
||||||
text: gettextCatalog.getString("Categories"),
|
|
||||||
style: "heading"
|
|
||||||
};
|
|
||||||
|
|
||||||
var toc = [];
|
|
||||||
angular.forEach(Category.getAll(), function(cat) {
|
|
||||||
toc.push(
|
|
||||||
{
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
text: cat.prefix,
|
|
||||||
style: 'tableofcontent',
|
|
||||||
width: 30
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: cat.name,
|
|
||||||
style: 'tableofcontent'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
|
||||||
heading,
|
|
||||||
toc,
|
|
||||||
addPageBreak()
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
// if there are no categories, return "empty string"
|
|
||||||
// pdfmake takes "null" literally and throws an error
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// function to apply a pagebreak-keyword
|
|
||||||
var addPageBreak = function() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
text: '',
|
|
||||||
pageBreak: 'after'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns the pure content of the motion, parseable by makepdf
|
|
||||||
var getContent = function() {
|
|
||||||
var motionContent = [];
|
|
||||||
var motionTitles = [];
|
|
||||||
var motionCategories = [];
|
|
||||||
angular.forEach(allMotions, function(motion, key) {
|
|
||||||
motionTitles.push(motion.getIdentifier() + ": " + motion.getTitle());
|
|
||||||
motionContent.push(motion.getContent());
|
|
||||||
if (key < allMotions.length - 1) {
|
|
||||||
motionContent.push(addPageBreak());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
|
||||||
createTitle(),
|
|
||||||
createTOCatergories(),
|
|
||||||
createTOContent(motionTitles),
|
|
||||||
motionContent
|
|
||||||
];
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
getContent: getContent
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
createInstance: createInstance
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.config([
|
.config([
|
||||||
'mainMenuProvider',
|
'mainMenuProvider',
|
||||||
|
Loading…
Reference in New Issue
Block a user