diff --git a/.gitignore b/.gitignore index 32b9cc9..37a4804 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /example/node_modules /node_modules +*xlsx* diff --git a/Readme.md b/Readme.md index 089a6d8..1a9e9e3 100644 --- a/Readme.md +++ b/Readme.md @@ -1,19 +1,22 @@ # excel-export # -A simple node.js module for exporting data set to Excel xlsx file. +A simple js module for exporting data set to Excel xlsx file, it works in node and in the browser. ## Using excel-export ## -Setup configs object before passing it into the execute method. If generating multiple sheets, configs object can be an array of worksheet configuration. Or passing in a worksheet configuration to generate single worksheet xlsx file. Within a worksheet configuration uses **name** attribute to specify worksheet name. **cols** is an array for column definition. Column definition should have caption and type properties while width property is not required. The unit for width property is character. **beforeCellWrite** callback is optional. beforeCellWrite is invoked with row, cell data and option object (eOpt detail later) parameters. The return value from beforeCellWrite is what get written into the cell. Supported valid types are string, date, bool and number. **rows** is the data to be exported. It is an Array of Array (row). Each row should be the same length as cols. Styling is optional. However, if you want to style your spreadsheet, a valid excel styles xml file is needed. An easy way to get a styles xml file is to unzip an existing xlsx file which has the desired styles and copy out the styles.xml file. Use **stylesXmlFile** property of configuartion object to specify the relative path and file name of the xml file. Google for "spreadsheetml style" to learn more detail on styling spreadsheet. eOpt in beforeCellWrite callback contains rowNum for current row number. eOpt.styleIndex should be a valid zero based index from cellXfs tag of the selected styles xml file. eOpt.cellType is default to the type value specified in column definition. However, in some scenario you might want to change it for different format. +Setup configs object before passing it into the execute method. If generating multiple sheets, configs object can be an array of worksheet configuration. Or passing in a worksheet configuration to generate single worksheet xlsx file. Within a worksheet configuration uses **name** attribute to specify worksheet name. **cols** is an array for column definition. Column definition should have caption and type properties while width property is not required. The unit for width property is character. **beforeCellWrite** callback is optional. beforeCellWrite is invoked with row, cell data and option object (eOpt detail later) parameters. The return value from beforeCellWrite is what get written into the cell. Supported valid types are string, date, bool and number. **rows** is the data to be exported. It is an Array of Array (row). Each row should be the same length as cols. Styling is optional. However, if you want to style your spreadsheet, a valid excel styles xml file is needed. An easy way to get a styles xml file is to unzip an existing xlsx file which has the desired styles and copy out the styles.xml file. Use **stylesXml** +property of configuration object to specify the content of the xml file. Google for "spreadsheetml style" to learn more detail on styling spreadsheet. eOpt in beforeCellWrite callback contains rowNum for current row number. eOpt.styleIndex should be a valid zero based index from cellXfs tag of the selected styles xml file. eOpt.cellType is default to the type value specified in column definition. However, in some scenario you might want to change it for different format. var express = require('express'); var nodeExcel = require('excel-export'); var app = express(); + var fs = require('fs') app.get('/Excel', function(req, res){ var conf ={}; - conf.stylesXmlFile = "styles.xml"; + var xmlFile = fs.readFileSync("styles.xml") + conf.stylesXml = xmlFile conf.name = "mysheet"; conf.cols = [{ caption:'string', @@ -62,3 +65,14 @@ Setup configs object before passing it into the execute method. If generating mu app.listen(3000); console.log('Listening on port 3000'); + +Easiest way to use it in the browser is using buffer. Do npm install buffer + + var Buffer = require('buffer/').Buffer + // ... same as previous example + var result = nodeExcel.execute(conf) + var blob = new Blob([new Buffer(result, 'binary')], {type: 'application/vnd.openxmlformats'}) + saveAs(blob, 'excel.xlsx') + +### Disclaimer ### +This package is based in the [original](https://github.com/functionscope/Node-Excel-Export) but ported to support browser and avoid global side effects. \ No newline at end of file diff --git a/example/app.js b/example/app.js index bd5aa5d..74455c1 100644 --- a/example/app.js +++ b/example/app.js @@ -1,86 +1,86 @@ var express = require('express'), - nodeExcel = require('excel-export'), - uuid = require('node-uuid'), - app = express(); + nodeExcel = require('excel-export'), + uuid = require('node-uuid'), + app = express() -app.get('/Large', function(req, res){ - var conf ={}; - conf.cols = []; - for (i = 0; i < 100; i++){ +app.get('/Large', function (req, res) { + var conf = {} + conf.cols = [] + for (i = 0; i < 100; i++) { conf.cols.push({ - caption:'string ' + i, - captionStyleIndex: 1, - type:'string' - }); + caption: 'string ' + i, + captionStyleIndex: 1, + type: 'string' + }) } - conf.rows = []; - for (j = 0; j < 1000; j++){ - var row = []; - for (k = 0; k < 100; k++){ - row.push(uuid.v4()); - } - conf.rows.push(row); - conf.rows.push(row); + conf.rows = [] + for (j = 0; j < 1000; j++) { + var row = [] + for (k = 0; k < 100; k++) { + row.push(uuid.v4()) + } + conf.rows.push(row) + conf.rows.push(row) } - var result = nodeExcel.execute(conf); - res.setHeader('Content-Type', 'application/vnd.openxmlformats'); - res.setHeader("Content-Disposition", "attachment; filename=" + "Large.xlsx"); - res.end(result, 'binary'); + var result = nodeExcel.execute(conf) + res.setHeader('Content-Type', 'application/vnd.openxmlformats') + res.setHeader('Content-Disposition', 'attachment; filename=' + 'Large.xlsx') + res.end(result, 'binary') +}) -}); - -app.get('/Excel', function(req, res){ - var conf ={}; - // uncomment it for style example +app.get('/Excel', function (req, res) { + var conf = {} + // uncomment it for style example // conf.stylesXmlFile = "styles.xml"; - conf.cols = [{ - caption:'string', - captionStyleIndex: 1, - type:'string', - beforeCellWrite:function(row, cellData){ - return cellData.toUpperCase(); - } - , width:15 - },{ - caption:'date', - type:'date', - beforeCellWrite:function(){ - var originDate = new Date(Date.UTC(1899,11,30)); - return function(row, cellData, eOpt){ - // uncomment it for style example + conf.cols = [{ + caption: 'string', + captionStyleIndex: 1, + type: 'string', + beforeCellWrite: function (row, cellData) { + return cellData.toUpperCase() + }, + width: 15 + }, { + caption: 'date', + type: 'date', + beforeCellWrite: (function () { + var originDate = new Date(Date.UTC(1899, 11, 30)) + return function (row, cellData, eOpt) { + // uncomment it for style example // if (eOpt.rowNum%2){ // eOpt.styleIndex = 1; - // } + // } // else{ // eOpt.styleIndex = 2; // } - if (cellData === null){ - eOpt.cellType = 'string'; - return 'N/A'; - } else - return (cellData - originDate) / (24 * 60 * 60 * 1000); - } - }() - , width:20.85 - },{ - caption:'bool', - type:'bool' - },{ - caption:'number', - type:'number', - width:30 - }]; - conf.rows = [ + if (cellData === null) { + eOpt.cellType = 'string' + return 'N/A' + } else { + return (cellData - originDate) / (24 * 60 * 60 * 1000) + } + } + }()), + width: 20.85 + }, { + caption: 'bool', + type: 'bool' + }, { + caption: 'number', + type: 'number', + width: 30 + }] + conf.rows = [ ['pi', new Date(Date.UTC(2013, 4, 1)), true, 3.14159], - ["e", new Date(2012, 4, 1), false, 2.7182], + ['e', new Date(2012, 4, 1), false, 2.7182], ["M&M<>'", new Date(Date.UTC(2013, 6, 9)), false, 1.61803], - ["null date", null, true, 1.414] - ]; - var result = nodeExcel.execute(conf); - res.setHeader('Content-Type', 'application/vnd.openxmlformats'); - res.setHeader("Content-Disposition", "attachment; filename=" + "Report.xlsx"); - res.end(result, 'binary'); -}); + ['null date', null, true, 1.414] + ] + var result = nodeExcel.execute(conf) + res.setHeader('Content-Type', 'application/vnd.openxmlformats') + res.setHeader('Content-Disposition', 'attachment; filename=' + 'Report.xlsx') + res.end(result, 'binary') +}) -app.listen(3000); -console.log('Listening on port 3000'); \ No newline at end of file +app.listen(3000) +console.log('Listening on port 3000') diff --git a/index.js b/index.js index ea2dbc4..1032f80 100644 --- a/index.js +++ b/index.js @@ -1,123 +1,104 @@ -require('node-zip'); -var fs = require('fs'), -Sheet = require('./sheet'), -SortedMap = require('collections/sorted-map'); +// TODO update jszip to 3.x +var JSZip = require('jszip') +var Sheet = require('./sheet') -Date.prototype.getJulian = function() { - return Math.floor((this / 86400000) - (this.getTimezoneOffset() / 1440) + 2440587.5); -}; - -Date.prototype.oaDate = function() { - return (this - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); -}; - -var templateXLSX = "UEsDBAoAAAAAABN7eUK9Z10uNQQAADUEAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbO+7vzw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+PFR5cGVzIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvcGFja2FnZS8yMDA2L2NvbnRlbnQtdHlwZXMiPjxEZWZhdWx0IEV4dGVuc2lvbj0ieG1sIiBDb250ZW50VHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQubWFpbit4bWwiIC8+PERlZmF1bHQgRXh0ZW5zaW9uPSJyZWxzIiBDb250ZW50VHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLXBhY2thZ2UucmVsYXRpb25zaGlwcyt4bWwiIC8+PERlZmF1bHQgRXh0ZW5zaW9uPSJwc21kY3AiIENvbnRlbnRUeXBlPSJhcHBsaWNhdGlvbi92bmQub3BlbnhtbGZvcm1hdHMtcGFja2FnZS5jb3JlLXByb3BlcnRpZXMreG1sIiAvPjxPdmVycmlkZSBQYXJ0TmFtZT0iL2RvY1Byb3BzL2FwcC54bWwiIENvbnRlbnRUeXBlPSJhcHBsaWNhdGlvbi92bmQub3BlbnhtbGZvcm1hdHMtb2ZmaWNlZG9jdW1lbnQuZXh0ZW5kZWQtcHJvcGVydGllcyt4bWwiIC8+PE92ZXJyaWRlIFBhcnROYW1lPSIveGwvc2hhcmVkU3RyaW5ncy54bWwiIENvbnRlbnRUeXBlPSJhcHBsaWNhdGlvbi92bmQub3BlbnhtbGZvcm1hdHMtb2ZmaWNlZG9jdW1lbnQuc3ByZWFkc2hlZXRtbC5zaGFyZWRTdHJpbmdzK3htbCIgLz48T3ZlcnJpZGUgUGFydE5hbWU9Ii94bC9zdHlsZXMueG1sIiBDb250ZW50VHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc3R5bGVzK3htbCIgLz48T3ZlcnJpZGUgUGFydE5hbWU9Ii94bC93b3Jrc2hlZXRzL3NoZWV0LnhtbCIgQ29udGVudFR5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLndvcmtzaGVldCt4bWwiIC8+PE92ZXJyaWRlIFBhcnROYW1lPSIveGwvdGhlbWUvdGhlbWUueG1sIiBDb250ZW50VHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnRoZW1lK3htbCIgLz48L1R5cGVzPlBLAwQKAAAAAACLdTlIAAAAAAAAAAAAAAAABgAAAF9yZWxzL1BLAwQKAAAAAAATe3lCdJmAA5wCAACcAgAACwAAAF9yZWxzLy5yZWxz77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48UmVsYXRpb25zaGlwcyB4bWxucz0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL3BhY2thZ2UvMjAwNi9yZWxhdGlvbnNoaXBzIj48UmVsYXRpb25zaGlwIFR5cGU9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9vZmZpY2VEb2N1bWVudC8yMDA2L3JlbGF0aW9uc2hpcHMvb2ZmaWNlRG9jdW1lbnQiIFRhcmdldD0iL3hsL3dvcmtib29rLnhtbCIgSWQ9IlI0YWEyMmIzMWExYTc0MjkxIiAvPjxSZWxhdGlvbnNoaXAgVHlwZT0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL29mZmljZURvY3VtZW50LzIwMDYvcmVsYXRpb25zaGlwcy9leHRlbmRlZC1wcm9wZXJ0aWVzIiBUYXJnZXQ9Ii9kb2NQcm9wcy9hcHAueG1sIiBJZD0icklkMSIgLz48UmVsYXRpb25zaGlwIFR5cGU9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9wYWNrYWdlLzIwMDYvcmVsYXRpb25zaGlwcy9tZXRhZGF0YS9jb3JlLXByb3BlcnRpZXMiIFRhcmdldD0iL3BhY2thZ2Uvc2VydmljZXMvbWV0YWRhdGEvY29yZS1wcm9wZXJ0aWVzL2VjZmRkMzE0M2YyMTQ4OTA5NWE0NGM3MTExNWI3MjNiLnBzbWRjcCIgSWQ9IlJlZjQ4N2MzZTBjNzQ0YTg3IiAvPjwvUmVsYXRpb25zaGlwcz5QSwMECgAAAAAAi3U5SAAAAAAAAAAAAAAAAAkAAABkb2NQcm9wcy9QSwMECgAAAAAAE3t5Qu9e3149AwAAPQMAABAAAABkb2NQcm9wcy9hcHAueG1s77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48YXA6UHJvcGVydGllcyB4bWxuczp2dD0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL29mZmljZURvY3VtZW50LzIwMDYvZG9jUHJvcHNWVHlwZXMiIHhtbG5zOmFwPSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvb2ZmaWNlRG9jdW1lbnQvMjAwNi9leHRlbmRlZC1wcm9wZXJ0aWVzIj48YXA6QXBwbGljYXRpb24+TWljcm9zb2Z0IEV4Y2VsPC9hcDpBcHBsaWNhdGlvbj48YXA6RG9jU2VjdXJpdHk+MDwvYXA6RG9jU2VjdXJpdHk+PGFwOlNjYWxlQ3JvcD5mYWxzZTwvYXA6U2NhbGVDcm9wPjxhcDpIZWFkaW5nUGFpcnM+PHZ0OnZlY3RvciBiYXNlVHlwZT0idmFyaWFudCIgc2l6ZT0iNCI+PHZ0OnZhcmlhbnQ+PHZ0Omxwc3RyPldvcmtzaGVldHM8L3Z0Omxwc3RyPjwvdnQ6dmFyaWFudD48dnQ6dmFyaWFudD48dnQ6aTQ+MTwvdnQ6aTQ+PC92dDp2YXJpYW50Pjx2dDp2YXJpYW50Pjx2dDpscHN0cj5OYW1lZCBSYW5nZXM8L3Z0Omxwc3RyPjwvdnQ6dmFyaWFudD48dnQ6dmFyaWFudD48dnQ6aTQ+MjwvdnQ6aTQ+PC92dDp2YXJpYW50PjwvdnQ6dmVjdG9yPjwvYXA6SGVhZGluZ1BhaXJzPjxhcDpUaXRsZXNPZlBhcnRzPjx2dDp2ZWN0b3IgYmFzZVR5cGU9Imxwc3RyIiBzaXplPSIzIj48dnQ6bHBzdHI+U2hlZXQgMTwvdnQ6bHBzdHI+PHZ0Omxwc3RyPlNoZWV0IDEhUHJpbnRfQXJlYTwvdnQ6bHBzdHI+PHZ0Omxwc3RyPlNoZWV0IDEhUHJpbnRfVGl0bGVzPC92dDpscHN0cj48L3Z0OnZlY3Rvcj48L2FwOlRpdGxlc09mUGFydHM+PC9hcDpQcm9wZXJ0aWVzPlBLAwQKAAAAAACLdTlIAAAAAAAAAAAAAAAACAAAAHBhY2thZ2UvUEsDBAoAAAAAAMWFeUIAAAAAAAAAAAAAAAARAAAAcGFja2FnZS9zZXJ2aWNlcy9QSwMECgAAAAAAxYV5QgAAAAAAAAAAAAAAABoAAABwYWNrYWdlL3NlcnZpY2VzL21ldGFkYXRhL1BLAwQKAAAAAADFhXlCAAAAAAAAAAAAAAAAKgAAAHBhY2thZ2Uvc2VydmljZXMvbWV0YWRhdGEvY29yZS1wcm9wZXJ0aWVzL1BLAwQKAAAAAAATe3lCc4c2yNoBAADaAQAAUQAAAHBhY2thZ2Uvc2VydmljZXMvbWV0YWRhdGEvY29yZS1wcm9wZXJ0aWVzL2VjZmRkMzE0M2YyMTQ4OTA5NWE0NGM3MTExNWI3MjNiLnBzbWRjcO+7vzw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+PGNvcmVQcm9wZXJ0aWVzIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6ZGN0ZXJtcz0iaHR0cDovL3B1cmwub3JnL2RjL3Rlcm1zLyIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9wYWNrYWdlLzIwMDYvbWV0YWRhdGEvY29yZS1wcm9wZXJ0aWVzIj48ZGN0ZXJtczpjcmVhdGVkIHhzaTp0eXBlPSJkY3Rlcm1zOlczQ0RURiI+MjAxMy0wMy0yNVQxOToyNDozOS44NjYxOTY5WjwvZGN0ZXJtczpjcmVhdGVkPjxkY3Rlcm1zOm1vZGlmaWVkIHhzaTp0eXBlPSJkY3Rlcm1zOlczQ0RURiI+MjAxMy0wMy0yNVQxOToyNDozOS44NjYxOTY5WjwvZGN0ZXJtczptb2RpZmllZD48L2NvcmVQcm9wZXJ0aWVzPlBLAwQKAAAAAACLdTlIAAAAAAAAAAAAAAAAAwAAAHhsL1BLAwQKAAAAAADFhXlCAAAAAAAAAAAAAAAACQAAAHhsL19yZWxzL1BLAwQKAAAAAAATe3lCJ0p8MrwCAAC8AgAAGgAAAHhsL19yZWxzL3dvcmtib29rLnhtbC5yZWxz77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48UmVsYXRpb25zaGlwcyB4bWxucz0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL3BhY2thZ2UvMjAwNi9yZWxhdGlvbnNoaXBzIj48UmVsYXRpb25zaGlwIFR5cGU9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9vZmZpY2VEb2N1bWVudC8yMDA2L3JlbGF0aW9uc2hpcHMvc2hhcmVkU3RyaW5ncyIgVGFyZ2V0PSIveGwvc2hhcmVkU3RyaW5ncy54bWwiIElkPSJySWQzIiAvPjxSZWxhdGlvbnNoaXAgVHlwZT0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL29mZmljZURvY3VtZW50LzIwMDYvcmVsYXRpb25zaGlwcy9zdHlsZXMiIFRhcmdldD0iL3hsL3N0eWxlcy54bWwiIElkPSJySWQ0IiAvPjxSZWxhdGlvbnNoaXAgVHlwZT0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL29mZmljZURvY3VtZW50LzIwMDYvcmVsYXRpb25zaGlwcy93b3Jrc2hlZXQiIFRhcmdldD0iL3hsL3dvcmtzaGVldHMvc2hlZXQueG1sIiBJZD0icklkMiIgLz48UmVsYXRpb25zaGlwIFR5cGU9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9vZmZpY2VEb2N1bWVudC8yMDA2L3JlbGF0aW9uc2hpcHMvdGhlbWUiIFRhcmdldD0iL3hsL3RoZW1lL3RoZW1lLnhtbCIgSWQ9InJJZDYiIC8+PC9SZWxhdGlvbnNoaXBzPlBLAwQKAAAAAAATe3lCflKRBZAAAACQAAAAFAAAAHhsL3NoYXJlZFN0cmluZ3MueG1s77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48eDpzc3QgY291bnQ9IjAiIHVuaXF1ZUNvdW50PSIwIiB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvc3ByZWFkc2hlZXRtbC8yMDA2L21haW4iIC8+UEsDBAoAAAAAABN7eUIi2lsreggAAHoIAAANAAAAeGwvc3R5bGVzLnhtbO+7vzw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+PHg6c3R5bGVTaGVldCB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvc3ByZWFkc2hlZXRtbC8yMDA2L21haW4iPjx4Om51bUZtdHMgY291bnQ9IjEiPjx4Om51bUZtdCBudW1GbXRJZD0iMCIgZm9ybWF0Q29kZT0iIiAvPjwveDpudW1GbXRzPjx4OmZvbnRzIGNvdW50PSIxIj48eDpmb250Pjx4OnZlcnRBbGlnbiB2YWw9ImJhc2VsaW5lIiAvPjx4OnN6IHZhbD0iMTEiIC8+PHg6Y29sb3IgcmdiPSJGRjAwMDAwMCIgLz48eDpuYW1lIHZhbD0iQ2FsaWJyaSIgLz48eDpmYW1pbHkgdmFsPSIyIiAvPjwveDpmb250PjwveDpmb250cz48eDpmaWxscyBjb3VudD0iMiI+PHg6ZmlsbD48eDpwYXR0ZXJuRmlsbCBwYXR0ZXJuVHlwZT0ibm9uZSIgLz48L3g6ZmlsbD48eDpmaWxsPjx4OnBhdHRlcm5GaWxsIHBhdHRlcm5UeXBlPSJncmF5MTI1IiAvPjwveDpmaWxsPjwveDpmaWxscz48eDpib3JkZXJzIGNvdW50PSIxIj48eDpib3JkZXIgZGlhZ29uYWxVcD0iMCIgZGlhZ29uYWxEb3duPSIwIj48eDpsZWZ0IHN0eWxlPSJub25lIj48eDpjb2xvciByZ2I9IkZGMDAwMDAwIiAvPjwveDpsZWZ0Pjx4OnJpZ2h0IHN0eWxlPSJub25lIj48eDpjb2xvciByZ2I9IkZGMDAwMDAwIiAvPjwveDpyaWdodD48eDp0b3Agc3R5bGU9Im5vbmUiPjx4OmNvbG9yIHJnYj0iRkYwMDAwMDAiIC8+PC94OnRvcD48eDpib3R0b20gc3R5bGU9Im5vbmUiPjx4OmNvbG9yIHJnYj0iRkYwMDAwMDAiIC8+PC94OmJvdHRvbT48eDpkaWFnb25hbCBzdHlsZT0ibm9uZSI+PHg6Y29sb3IgcmdiPSJGRjAwMDAwMCIgLz48L3g6ZGlhZ29uYWw+PC94OmJvcmRlcj48L3g6Ym9yZGVycz48eDpjZWxsU3R5bGVYZnMgY291bnQ9IjIiPjx4OnhmIG51bUZtdElkPSIwIiBmb250SWQ9IjAiIGZpbGxJZD0iMCIgYm9yZGVySWQ9IjAiIGFwcGx5TnVtYmVyRm9ybWF0PSIwIiBhcHBseUZpbGw9IjEiIGFwcGx5Qm9yZGVyPSIwIiBhcHBseUFsaWdubWVudD0iMCIgYXBwbHlQcm90ZWN0aW9uPSIxIj48eDpwcm90ZWN0aW9uIGxvY2tlZD0iMSIgaGlkZGVuPSIwIiAvPjwveDp4Zj48eDp4ZiBudW1GbXRJZD0iMTQiIGZvbnRJZD0iMCIgZmlsbElkPSIwIiBib3JkZXJJZD0iMCIgYXBwbHlOdW1iZXJGb3JtYXQ9IjAiIGFwcGx5RmlsbD0iMSIgYXBwbHlCb3JkZXI9IjAiIGFwcGx5QWxpZ25tZW50PSIwIiBhcHBseVByb3RlY3Rpb249IjEiPjx4OnByb3RlY3Rpb24gbG9ja2VkPSIxIiBoaWRkZW49IjAiIC8+PC94OnhmPjwveDpjZWxsU3R5bGVYZnM+PHg6Y2VsbFhmcyBjb3VudD0iMiI+PHg6eGYgbnVtRm10SWQ9IjAiIGZvbnRJZD0iMCIgZmlsbElkPSIwIiBib3JkZXJJZD0iMCIgeGZJZD0iMCIgYXBwbHlOdW1iZXJGb3JtYXQ9IjAiIGFwcGx5RmlsbD0iMSIgYXBwbHlCb3JkZXI9IjAiIGFwcGx5QWxpZ25tZW50PSIwIiBhcHBseVByb3RlY3Rpb249IjEiPjx4OmFsaWdubWVudCBob3Jpem9udGFsPSJnZW5lcmFsIiB2ZXJ0aWNhbD0iYm90dG9tIiB0ZXh0Um90YXRpb249IjAiIHdyYXBUZXh0PSIwIiBpbmRlbnQ9IjAiIHJlbGF0aXZlSW5kZW50PSIwIiBqdXN0aWZ5TGFzdExpbmU9IjAiIHNocmlua1RvRml0PSIwIiByZWFkaW5nT3JkZXI9IjAiIC8+PHg6cHJvdGVjdGlvbiBsb2NrZWQ9IjEiIGhpZGRlbj0iMCIgLz48L3g6eGY+PHg6eGYgbnVtRm10SWQ9IjE0IiBmb250SWQ9IjAiIGZpbGxJZD0iMCIgYm9yZGVySWQ9IjAiIHhmSWQ9IjAiIGFwcGx5TnVtYmVyRm9ybWF0PSIwIiBhcHBseUZpbGw9IjEiIGFwcGx5Qm9yZGVyPSIwIiBhcHBseUFsaWdubWVudD0iMCIgYXBwbHlQcm90ZWN0aW9uPSIxIj48eDphbGlnbm1lbnQgaG9yaXpvbnRhbD0iZ2VuZXJhbCIgdmVydGljYWw9ImJvdHRvbSIgdGV4dFJvdGF0aW9uPSIwIiB3cmFwVGV4dD0iMCIgaW5kZW50PSIwIiByZWxhdGl2ZUluZGVudD0iMCIganVzdGlmeUxhc3RMaW5lPSIwIiBzaHJpbmtUb0ZpdD0iMCIgcmVhZGluZ09yZGVyPSIwIiAvPjx4OnByb3RlY3Rpb24gbG9ja2VkPSIxIiBoaWRkZW49IjAiIC8+PC94OnhmPjwveDpjZWxsWGZzPjx4OmNlbGxTdHlsZXMgY291bnQ9IjEiPjx4OmNlbGxTdHlsZSBuYW1lPSJOb3JtYWwiIHhmSWQ9IjAiIGJ1aWx0aW5JZD0iMCIgLz48L3g6Y2VsbFN0eWxlcz48L3g6c3R5bGVTaGVldD5QSwMECgAAAAAAxYV5QgAAAAAAAAAAAAAAAAkAAAB4bC90aGVtZS9QSwMECgAAAAAAE3t5QnWxkV67GwAAuxsAABIAAAB4bC90aGVtZS90aGVtZS54bWzvu788P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJ1dGYtOCI/PjxhOnRoZW1lIHhtbG5zOmE9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9kcmF3aW5nbWwvMjAwNi9tYWluIiBuYW1lPSJPZmZpY2UgVGhlbWUiPjxhOnRoZW1lRWxlbWVudHM+PGE6Y2xyU2NoZW1lIG5hbWU9Ik9mZmljZSI+PGE6ZGsxPjxhOnN5c0NsciB2YWw9IndpbmRvd1RleHQiIGxhc3RDbHI9IjAwMDAwMCIgLz48L2E6ZGsxPjxhOmx0MT48YTpzeXNDbHIgdmFsPSJ3aW5kb3ciIGxhc3RDbHI9IkZGRkZGRiIgLz48L2E6bHQxPjxhOmRrMj48YTpzcmdiQ2xyIHZhbD0iMUY0OTdEIiAvPjwvYTpkazI+PGE6bHQyPjxhOnNyZ2JDbHIgdmFsPSJFRUVDRTEiIC8+PC9hOmx0Mj48YTphY2NlbnQxPjxhOnNyZ2JDbHIgdmFsPSI0RjgxQkQiIC8+PC9hOmFjY2VudDE+PGE6YWNjZW50Mj48YTpzcmdiQ2xyIHZhbD0iQzA1MDREIiAvPjwvYTphY2NlbnQyPjxhOmFjY2VudDM+PGE6c3JnYkNsciB2YWw9IjlCQkI1OSIgLz48L2E6YWNjZW50Mz48YTphY2NlbnQ0PjxhOnNyZ2JDbHIgdmFsPSI4MDY0QTIiIC8+PC9hOmFjY2VudDQ+PGE6YWNjZW50NT48YTpzcmdiQ2xyIHZhbD0iNEJBQ0M2IiAvPjwvYTphY2NlbnQ1PjxhOmFjY2VudDY+PGE6c3JnYkNsciB2YWw9IkY3OTY0NiIgLz48L2E6YWNjZW50Nj48YTpobGluaz48YTpzcmdiQ2xyIHZhbD0iMDAwMEZGIiAvPjwvYTpobGluaz48YTpmb2xIbGluaz48YTpzcmdiQ2xyIHZhbD0iODAwMDgwIiAvPjwvYTpmb2xIbGluaz48L2E6Y2xyU2NoZW1lPjxhOmZvbnRTY2hlbWUgbmFtZT0iT2ZmaWNlIj48YTptYWpvckZvbnQ+PGE6bGF0aW4gdHlwZWZhY2U9IkNhbWJyaWEiIC8+PGE6ZWEgdHlwZWZhY2U9IiIgLz48YTpjcyB0eXBlZmFjZT0iIiAvPjxhOmZvbnQgc2NyaXB0PSJKcGFuIiB0eXBlZmFjZT0i77yt77yzIO+8sOOCtOOCt+ODg+OCryIgLz48YTpmb250IHNjcmlwdD0iSGFuZyIgdHlwZWZhY2U9IuunkeydgCDqs6DrlJUiIC8+PGE6Zm9udCBzY3JpcHQ9IkhhbnMiIHR5cGVmYWNlPSLlrovkvZMiIC8+PGE6Zm9udCBzY3JpcHQ9IkhhbnQiIHR5cGVmYWNlPSLmlrDntLDmmI7pq5QiIC8+PGE6Zm9udCBzY3JpcHQ9IkFyYWIiIHR5cGVmYWNlPSJUaW1lcyBOZXcgUm9tYW4iIC8+PGE6Zm9udCBzY3JpcHQ9IkhlYnIiIHR5cGVmYWNlPSJUaW1lcyBOZXcgUm9tYW4iIC8+PGE6Zm9udCBzY3JpcHQ9IlRoYWkiIHR5cGVmYWNlPSJUYWhvbWEiIC8+PGE6Zm9udCBzY3JpcHQ9IkV0aGkiIHR5cGVmYWNlPSJOeWFsYSIgLz48YTpmb250IHNjcmlwdD0iQmVuZyIgdHlwZWZhY2U9IlZyaW5kYSIgLz48YTpmb250IHNjcmlwdD0iR3VqciIgdHlwZWZhY2U9IlNocnV0aSIgLz48YTpmb250IHNjcmlwdD0iS2htciIgdHlwZWZhY2U9Ik1vb2xCb3JhbiIgLz48YTpmb250IHNjcmlwdD0iS25kYSIgdHlwZWZhY2U9IlR1bmdhIiAvPjxhOmZvbnQgc2NyaXB0PSJHdXJ1IiB0eXBlZmFjZT0iUmFhdmkiIC8+PGE6Zm9udCBzY3JpcHQ9IkNhbnMiIHR5cGVmYWNlPSJFdXBoZW1pYSIgLz48YTpmb250IHNjcmlwdD0iQ2hlciIgdHlwZWZhY2U9IlBsYW50YWdlbmV0IENoZXJva2VlIiAvPjxhOmZvbnQgc2NyaXB0PSJZaWlpIiB0eXBlZmFjZT0iTWljcm9zb2Z0IFlpIEJhaXRpIiAvPjxhOmZvbnQgc2NyaXB0PSJUaWJ0IiB0eXBlZmFjZT0iTWljcm9zb2Z0IEhpbWFsYXlhIiAvPjxhOmZvbnQgc2NyaXB0PSJUaGFhIiB0eXBlZmFjZT0iTVYgQm9saSIgLz48YTpmb250IHNjcmlwdD0iRGV2YSIgdHlwZWZhY2U9Ik1hbmdhbCIgLz48YTpmb250IHNjcmlwdD0iVGVsdSIgdHlwZWZhY2U9IkdhdXRhbWkiIC8+PGE6Zm9udCBzY3JpcHQ9IlRhbWwiIHR5cGVmYWNlPSJMYXRoYSIgLz48YTpmb250IHNjcmlwdD0iU3lyYyIgdHlwZWZhY2U9IkVzdHJhbmdlbG8gRWRlc3NhIiAvPjxhOmZvbnQgc2NyaXB0PSJPcnlhIiB0eXBlZmFjZT0iS2FsaW5nYSIgLz48YTpmb250IHNjcmlwdD0iTWx5bSIgdHlwZWZhY2U9IkthcnRpa2EiIC8+PGE6Zm9udCBzY3JpcHQ9Ikxhb28iIHR5cGVmYWNlPSJEb2tDaGFtcGEiIC8+PGE6Zm9udCBzY3JpcHQ9IlNpbmgiIHR5cGVmYWNlPSJJc2tvb2xhIFBvdGEiIC8+PGE6Zm9udCBzY3JpcHQ9Ik1vbmciIHR5cGVmYWNlPSJNb25nb2xpYW4gQmFpdGkiIC8+PGE6Zm9udCBzY3JpcHQ9IlZpZXQiIHR5cGVmYWNlPSJUaW1lcyBOZXcgUm9tYW4iIC8+PGE6Zm9udCBzY3JpcHQ9IlVpZ2giIHR5cGVmYWNlPSJNaWNyb3NvZnQgVWlnaHVyIiAvPjwvYTptYWpvckZvbnQ+PGE6bWlub3JGb250PjxhOmxhdGluIHR5cGVmYWNlPSJDYWxpYnJpIiAvPjxhOmVhIHR5cGVmYWNlPSIiIC8+PGE6Y3MgdHlwZWZhY2U9IiIgLz48YTpmb250IHNjcmlwdD0iSnBhbiIgdHlwZWZhY2U9Iu+8re+8syDvvLDjgrTjgrfjg4Pjgq8iIC8+PGE6Zm9udCBzY3JpcHQ9IkhhbmciIHR5cGVmYWNlPSLrp5HsnYAg6rOg65SVIiAvPjxhOmZvbnQgc2NyaXB0PSJIYW5zIiB0eXBlZmFjZT0i5a6L5L2TIiAvPjxhOmZvbnQgc2NyaXB0PSJIYW50IiB0eXBlZmFjZT0i5paw57Sw5piO6auUIiAvPjxhOmZvbnQgc2NyaXB0PSJBcmFiIiB0eXBlZmFjZT0iQXJpYWwiIC8+PGE6Zm9udCBzY3JpcHQ9IkhlYnIiIHR5cGVmYWNlPSJBcmlhbCIgLz48YTpmb250IHNjcmlwdD0iVGhhaSIgdHlwZWZhY2U9IlRhaG9tYSIgLz48YTpmb250IHNjcmlwdD0iRXRoaSIgdHlwZWZhY2U9Ik55YWxhIiAvPjxhOmZvbnQgc2NyaXB0PSJCZW5nIiB0eXBlZmFjZT0iVnJpbmRhIiAvPjxhOmZvbnQgc2NyaXB0PSJHdWpyIiB0eXBlZmFjZT0iU2hydXRpIiAvPjxhOmZvbnQgc2NyaXB0PSJLaG1yIiB0eXBlZmFjZT0iRGF1blBlbmgiIC8+PGE6Zm9udCBzY3JpcHQ9IktuZGEiIHR5cGVmYWNlPSJUdW5nYSIgLz48YTpmb250IHNjcmlwdD0iR3VydSIgdHlwZWZhY2U9IlJhYXZpIiAvPjxhOmZvbnQgc2NyaXB0PSJDYW5zIiB0eXBlZmFjZT0iRXVwaGVtaWEiIC8+PGE6Zm9udCBzY3JpcHQ9IkNoZXIiIHR5cGVmYWNlPSJQbGFudGFnZW5ldCBDaGVyb2tlZSIgLz48YTpmb250IHNjcmlwdD0iWWlpaSIgdHlwZWZhY2U9Ik1pY3Jvc29mdCBZaSBCYWl0aSIgLz48YTpmb250IHNjcmlwdD0iVGlidCIgdHlwZWZhY2U9Ik1pY3Jvc29mdCBIaW1hbGF5YSIgLz48YTpmb250IHNjcmlwdD0iVGhhYSIgdHlwZWZhY2U9Ik1WIEJvbGkiIC8+PGE6Zm9udCBzY3JpcHQ9IkRldmEiIHR5cGVmYWNlPSJNYW5nYWwiIC8+PGE6Zm9udCBzY3JpcHQ9IlRlbHUiIHR5cGVmYWNlPSJHYXV0YW1pIiAvPjxhOmZvbnQgc2NyaXB0PSJUYW1sIiB0eXBlZmFjZT0iTGF0aGEiIC8+PGE6Zm9udCBzY3JpcHQ9IlN5cmMiIHR5cGVmYWNlPSJFc3RyYW5nZWxvIEVkZXNzYSIgLz48YTpmb250IHNjcmlwdD0iT3J5YSIgdHlwZWZhY2U9IkthbGluZ2EiIC8+PGE6Zm9udCBzY3JpcHQ9Ik1seW0iIHR5cGVmYWNlPSJLYXJ0aWthIiAvPjxhOmZvbnQgc2NyaXB0PSJMYW9vIiB0eXBlZmFjZT0iRG9rQ2hhbXBhIiAvPjxhOmZvbnQgc2NyaXB0PSJTaW5oIiB0eXBlZmFjZT0iSXNrb29sYSBQb3RhIiAvPjxhOmZvbnQgc2NyaXB0PSJNb25nIiB0eXBlZmFjZT0iTW9uZ29saWFuIEJhaXRpIiAvPjxhOmZvbnQgc2NyaXB0PSJWaWV0IiB0eXBlZmFjZT0iQXJpYWwiIC8+PGE6Zm9udCBzY3JpcHQ9IlVpZ2giIHR5cGVmYWNlPSJNaWNyb3NvZnQgVWlnaHVyIiAvPjwvYTptaW5vckZvbnQ+PC9hOmZvbnRTY2hlbWU+PGE6Zm10U2NoZW1lIG5hbWU9Ik9mZmljZSI+PGE6ZmlsbFN0eWxlTHN0PjxhOnNvbGlkRmlsbD48YTpzY2hlbWVDbHIgdmFsPSJwaENsciIgLz48L2E6c29saWRGaWxsPjxhOmdyYWRGaWxsIHJvdFdpdGhTaGFwZT0iMSI+PGE6Z3NMc3Q+PGE6Z3MgcG9zPSIwIj48YTpzY2hlbWVDbHIgdmFsPSJwaENsciI+PGE6dGludCB2YWw9IjUwMDAwIiAvPjxhOnNhdE1vZCB2YWw9IjMwMDAwMCIgLz48L2E6c2NoZW1lQ2xyPjwvYTpncz48YTpncyBwb3M9IjM1MDAwIj48YTpzY2hlbWVDbHIgdmFsPSJwaENsciI+PGE6dGludCB2YWw9IjM3MDAwIiAvPjxhOnNhdE1vZCB2YWw9IjMwMDAwMCIgLz48L2E6c2NoZW1lQ2xyPjwvYTpncz48YTpncyBwb3M9IjEwMDAwMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnRpbnQgdmFsPSIxNTAwMCIgLz48YTpzYXRNb2QgdmFsPSIzNTAwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PC9hOmdzTHN0PjxhOmxpbiBhbmc9IjE2MjAwMDAwIiBzY2FsZWQ9IjEiIC8+PC9hOmdyYWRGaWxsPjxhOmdyYWRGaWxsIHJvdFdpdGhTaGFwZT0iMSI+PGE6Z3NMc3Q+PGE6Z3MgcG9zPSIwIj48YTpzY2hlbWVDbHIgdmFsPSJwaENsciI+PGE6c2hhZGUgdmFsPSI1MTAwMCIgLz48YTpzYXRNb2QgdmFsPSIxMzAwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PGE6Z3MgcG9zPSI4MDAwMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnNoYWRlIHZhbD0iOTMwMDAiIC8+PGE6c2F0TW9kIHZhbD0iMTMwMDAwIiAvPjwvYTpzY2hlbWVDbHI+PC9hOmdzPjxhOmdzIHBvcz0iMTAwMDAwIj48YTpzY2hlbWVDbHIgdmFsPSJwaENsciI+PGE6c2hhZGUgdmFsPSI5NDAwMCIgLz48YTpzYXRNb2QgdmFsPSIxMzUwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PC9hOmdzTHN0PjxhOmxpbiBhbmc9IjE2MjAwMDAwIiBzY2FsZWQ9IjAiIC8+PC9hOmdyYWRGaWxsPjwvYTpmaWxsU3R5bGVMc3Q+PGE6bG5TdHlsZUxzdD48YTpsbiB3PSI5NTI1IiBjYXA9ImZsYXQiIGNtcGQ9InNuZyIgYWxnbj0iY3RyIj48YTpzb2xpZEZpbGw+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnNoYWRlIHZhbD0iOTUwMDAiIC8+PGE6c2F0TW9kIHZhbD0iMTA1MDAwIiAvPjwvYTpzY2hlbWVDbHI+PC9hOnNvbGlkRmlsbD48YTpwcnN0RGFzaCB2YWw9InNvbGlkIiAvPjwvYTpsbj48YTpsbiB3PSIyNTQwMCIgY2FwPSJmbGF0IiBjbXBkPSJzbmciIGFsZ249ImN0ciI+PGE6c29saWRGaWxsPjxhOnNjaGVtZUNsciB2YWw9InBoQ2xyIiAvPjwvYTpzb2xpZEZpbGw+PGE6cHJzdERhc2ggdmFsPSJzb2xpZCIgLz48L2E6bG4+PGE6bG4gdz0iMzgxMDAiIGNhcD0iZmxhdCIgY21wZD0ic25nIiBhbGduPSJjdHIiPjxhOnNvbGlkRmlsbD48YTpzY2hlbWVDbHIgdmFsPSJwaENsciIgLz48L2E6c29saWRGaWxsPjxhOnByc3REYXNoIHZhbD0ic29saWQiIC8+PC9hOmxuPjwvYTpsblN0eWxlTHN0PjxhOmVmZmVjdFN0eWxlTHN0PjxhOmVmZmVjdFN0eWxlPjxhOmVmZmVjdExzdD48YTpvdXRlclNoZHcgYmx1clJhZD0iNDAwMDAiIGRpc3Q9IjIwMDAwIiBkaXI9IjU0MDAwMDAiIHJvdFdpdGhTaGFwZT0iMCI+PGE6c3JnYkNsciB2YWw9IjAwMDAwMCI+PGE6YWxwaGEgdmFsPSIzODAwMCIgLz48L2E6c3JnYkNscj48L2E6b3V0ZXJTaGR3PjwvYTplZmZlY3RMc3Q+PC9hOmVmZmVjdFN0eWxlPjxhOmVmZmVjdFN0eWxlPjxhOmVmZmVjdExzdD48YTpvdXRlclNoZHcgYmx1clJhZD0iNDAwMDAiIGRpc3Q9IjIzMDAwIiBkaXI9IjU0MDAwMDAiIHJvdFdpdGhTaGFwZT0iMCI+PGE6c3JnYkNsciB2YWw9IjAwMDAwMCI+PGE6YWxwaGEgdmFsPSIzNTAwMCIgLz48L2E6c3JnYkNscj48L2E6b3V0ZXJTaGR3PjwvYTplZmZlY3RMc3Q+PC9hOmVmZmVjdFN0eWxlPjxhOmVmZmVjdFN0eWxlPjxhOmVmZmVjdExzdD48YTpvdXRlclNoZHcgYmx1clJhZD0iNDAwMDAiIGRpc3Q9IjIzMDAwIiBkaXI9IjU0MDAwMDAiIHJvdFdpdGhTaGFwZT0iMCI+PGE6c3JnYkNsciB2YWw9IjAwMDAwMCI+PGE6YWxwaGEgdmFsPSIzNTAwMCIgLz48L2E6c3JnYkNscj48L2E6b3V0ZXJTaGR3PjwvYTplZmZlY3RMc3Q+PGE6c2NlbmUzZD48YTpjYW1lcmEgcHJzdD0ib3J0aG9ncmFwaGljRnJvbnQiPjxhOnJvdCBsYXQ9IjAiIGxvbj0iMCIgcmV2PSIwIiAvPjwvYTpjYW1lcmE+PGE6bGlnaHRSaWcgcmlnPSJ0aHJlZVB0IiBkaXI9InQiPjxhOnJvdCBsYXQ9IjAiIGxvbj0iMCIgcmV2PSIxMjAwMDAwIiAvPjwvYTpsaWdodFJpZz48L2E6c2NlbmUzZD48YTpzcDNkPjxhOmJldmVsVCB3PSI2MzUwMCIgaD0iMjU0MDAiIC8+PC9hOnNwM2Q+PC9hOmVmZmVjdFN0eWxlPjwvYTplZmZlY3RTdHlsZUxzdD48YTpiZ0ZpbGxTdHlsZUxzdD48YTpzb2xpZEZpbGw+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiIC8+PC9hOnNvbGlkRmlsbD48YTpncmFkRmlsbCByb3RXaXRoU2hhcGU9IjEiPjxhOmdzTHN0PjxhOmdzIHBvcz0iMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnRpbnQgdmFsPSI0MDAwMCIgLz48YTpzYXRNb2QgdmFsPSIzNTAwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PGE6Z3MgcG9zPSI0MDAwMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnRpbnQgdmFsPSI0NTAwMCIgLz48YTpzaGFkZSB2YWw9Ijk5MDAwIiAvPjxhOnNhdE1vZCB2YWw9IjM1MDAwMCIgLz48L2E6c2NoZW1lQ2xyPjwvYTpncz48YTpncyBwb3M9IjEwMDAwMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnNoYWRlIHZhbD0iMjAwMDAiIC8+PGE6c2F0TW9kIHZhbD0iMjU1MDAwIiAvPjwvYTpzY2hlbWVDbHI+PC9hOmdzPjwvYTpnc0xzdD48YTpwYXRoIHBhdGg9ImNpcmNsZSI+PGE6ZmlsbFRvUmVjdCBsPSI1MDAwMCIgdD0iLTgwMDAwIiByPSI1MDAwMCIgYj0iMTgwMDAwIiAvPjwvYTpwYXRoPjwvYTpncmFkRmlsbD48YTpncmFkRmlsbCByb3RXaXRoU2hhcGU9IjEiPjxhOmdzTHN0PjxhOmdzIHBvcz0iMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnRpbnQgdmFsPSI4MDAwMCIgLz48YTpzYXRNb2QgdmFsPSIzMDAwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PGE6Z3MgcG9zPSIxMDAwMDAiPjxhOnNjaGVtZUNsciB2YWw9InBoQ2xyIj48YTpzaGFkZSB2YWw9IjMwMDAwIiAvPjxhOnNhdE1vZCB2YWw9IjIwMDAwMCIgLz48L2E6c2NoZW1lQ2xyPjwvYTpncz48L2E6Z3NMc3Q+PGE6cGF0aCBwYXRoPSJjaXJjbGUiPjxhOmZpbGxUb1JlY3QgbD0iNTAwMDAiIHQ9IjUwMDAwIiByPSI1MDAwMCIgYj0iNTAwMDAiIC8+PC9hOnBhdGg+PC9hOmdyYWRGaWxsPjwvYTpiZ0ZpbGxTdHlsZUxzdD48L2E6Zm10U2NoZW1lPjwvYTp0aGVtZUVsZW1lbnRzPjxhOm9iamVjdERlZmF1bHRzIC8+PGE6ZXh0cmFDbHJTY2hlbWVMc3QgLz48L2E6dGhlbWU+UEsDBAoAAAAAABN7eUKJ3nBGuwEAALsBAAAPAAAAeGwvd29ya2Jvb2sueG1s77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48eDp3b3JrYm9vayB4bWxuczpyPSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvb2ZmaWNlRG9jdW1lbnQvMjAwNi9yZWxhdGlvbnNoaXBzIiB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvc3ByZWFkc2hlZXRtbC8yMDA2L21haW4iPjx4Ondvcmtib29rUHIgY29kZU5hbWU9IlRoaXNXb3JrYm9vayIgLz48eDpib29rVmlld3M+PHg6d29ya2Jvb2tWaWV3IGZpcnN0U2hlZXQ9IjAiIGFjdGl2ZVRhYj0iMCIgLz48L3g6Ym9va1ZpZXdzPjx4OnNoZWV0cz48eDpzaGVldCBuYW1lPSJTaGVldCAxIiBzaGVldElkPSIyIiByOmlkPSJySWQyIiAvPjwveDpzaGVldHM+PHg6ZGVmaW5lZE5hbWVzIC8+PHg6Y2FsY1ByIGNhbGNJZD0iMTI1NzI1IiAvPjwveDp3b3JrYm9vaz5QSwMECgAAAAAAxYV5QgAAAAAAAAAAAAAAAA4AAAB4bC93b3Jrc2hlZXRzL1BLAQIUAAoAAAAAABN7eUK9Z10uNQQAADUEAAATAAAAAAAAAAAAAAAAAAAAAABbQ29udGVudF9UeXBlc10ueG1sUEsBAhQACgAAAAAAi3U5SAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAQAAAAZgQAAF9yZWxzL1BLAQIUAAoAAAAAABN7eUJ0mYADnAIAAJwCAAALAAAAAAAAAAAAAAAAAIoEAABfcmVscy8ucmVsc1BLAQIUAAoAAAAAAIt1OUgAAAAAAAAAAAAAAAAJAAAAAAAAAAAAEAAAAE8HAABkb2NQcm9wcy9QSwECFAAKAAAAAAATe3lC717fXj0DAAA9AwAAEAAAAAAAAAAAAAAAAAB2BwAAZG9jUHJvcHMvYXBwLnhtbFBLAQIUAAoAAAAAAIt1OUgAAAAAAAAAAAAAAAAIAAAAAAAAAAAAEAAAAOEKAABwYWNrYWdlL1BLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAARAAAAAAAAAAAAEAAAAAcLAABwYWNrYWdlL3NlcnZpY2VzL1BLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAaAAAAAAAAAAAAEAAAADYLAABwYWNrYWdlL3NlcnZpY2VzL21ldGFkYXRhL1BLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAqAAAAAAAAAAAAEAAAAG4LAABwYWNrYWdlL3NlcnZpY2VzL21ldGFkYXRhL2NvcmUtcHJvcGVydGllcy9QSwECFAAKAAAAAAATe3lCc4c2yNoBAADaAQAAUQAAAAAAAAAAAAAAAAC2CwAAcGFja2FnZS9zZXJ2aWNlcy9tZXRhZGF0YS9jb3JlLXByb3BlcnRpZXMvZWNmZGQzMTQzZjIxNDg5MDk1YTQ0YzcxMTE1YjcyM2IucHNtZGNwUEsBAhQACgAAAAAAi3U5SAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAQAAAA/w0AAHhsL1BLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAJAAAAAAAAAAAAEAAAACAOAAB4bC9fcmVscy9QSwECFAAKAAAAAAATe3lCJ0p8MrwCAAC8AgAAGgAAAAAAAAAAAAAAAABHDgAAeGwvX3JlbHMvd29ya2Jvb2sueG1sLnJlbHNQSwECFAAKAAAAAAATe3lCflKRBZAAAACQAAAAFAAAAAAAAAAAAAAAAAA7EQAAeGwvc2hhcmVkU3RyaW5ncy54bWxQSwECFAAKAAAAAAATe3lCItpbK3oIAAB6CAAADQAAAAAAAAAAAAAAAAD9EQAAeGwvc3R5bGVzLnhtbFBLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAJAAAAAAAAAAAAEAAAAKIaAAB4bC90aGVtZS9QSwECFAAKAAAAAAATe3lCdbGRXrsbAAC7GwAAEgAAAAAAAAAAAAAAAADJGgAAeGwvdGhlbWUvdGhlbWUueG1sUEsBAhQACgAAAAAAE3t5QonecEa7AQAAuwEAAA8AAAAAAAAAAAAAAAAAtDYAAHhsL3dvcmtib29rLnhtbFBLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAOAAAAAAAAAAAAEAAAAJw4AAB4bC93b3Jrc2hlZXRzL1BLBQYAAAAAEwATANQEAADIOAAAAAA="; +var templateXLSX = 'UEsDBAoAAAAAABN7eUK9Z10uNQQAADUEAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbO+7vzw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+PFR5cGVzIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvcGFja2FnZS8yMDA2L2NvbnRlbnQtdHlwZXMiPjxEZWZhdWx0IEV4dGVuc2lvbj0ieG1sIiBDb250ZW50VHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQubWFpbit4bWwiIC8+PERlZmF1bHQgRXh0ZW5zaW9uPSJyZWxzIiBDb250ZW50VHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLXBhY2thZ2UucmVsYXRpb25zaGlwcyt4bWwiIC8+PERlZmF1bHQgRXh0ZW5zaW9uPSJwc21kY3AiIENvbnRlbnRUeXBlPSJhcHBsaWNhdGlvbi92bmQub3BlbnhtbGZvcm1hdHMtcGFja2FnZS5jb3JlLXByb3BlcnRpZXMreG1sIiAvPjxPdmVycmlkZSBQYXJ0TmFtZT0iL2RvY1Byb3BzL2FwcC54bWwiIENvbnRlbnRUeXBlPSJhcHBsaWNhdGlvbi92bmQub3BlbnhtbGZvcm1hdHMtb2ZmaWNlZG9jdW1lbnQuZXh0ZW5kZWQtcHJvcGVydGllcyt4bWwiIC8+PE92ZXJyaWRlIFBhcnROYW1lPSIveGwvc2hhcmVkU3RyaW5ncy54bWwiIENvbnRlbnRUeXBlPSJhcHBsaWNhdGlvbi92bmQub3BlbnhtbGZvcm1hdHMtb2ZmaWNlZG9jdW1lbnQuc3ByZWFkc2hlZXRtbC5zaGFyZWRTdHJpbmdzK3htbCIgLz48T3ZlcnJpZGUgUGFydE5hbWU9Ii94bC9zdHlsZXMueG1sIiBDb250ZW50VHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc3R5bGVzK3htbCIgLz48T3ZlcnJpZGUgUGFydE5hbWU9Ii94bC93b3Jrc2hlZXRzL3NoZWV0LnhtbCIgQ29udGVudFR5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLndvcmtzaGVldCt4bWwiIC8+PE92ZXJyaWRlIFBhcnROYW1lPSIveGwvdGhlbWUvdGhlbWUueG1sIiBDb250ZW50VHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnRoZW1lK3htbCIgLz48L1R5cGVzPlBLAwQKAAAAAACLdTlIAAAAAAAAAAAAAAAABgAAAF9yZWxzL1BLAwQKAAAAAAATe3lCdJmAA5wCAACcAgAACwAAAF9yZWxzLy5yZWxz77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48UmVsYXRpb25zaGlwcyB4bWxucz0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL3BhY2thZ2UvMjAwNi9yZWxhdGlvbnNoaXBzIj48UmVsYXRpb25zaGlwIFR5cGU9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9vZmZpY2VEb2N1bWVudC8yMDA2L3JlbGF0aW9uc2hpcHMvb2ZmaWNlRG9jdW1lbnQiIFRhcmdldD0iL3hsL3dvcmtib29rLnhtbCIgSWQ9IlI0YWEyMmIzMWExYTc0MjkxIiAvPjxSZWxhdGlvbnNoaXAgVHlwZT0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL29mZmljZURvY3VtZW50LzIwMDYvcmVsYXRpb25zaGlwcy9leHRlbmRlZC1wcm9wZXJ0aWVzIiBUYXJnZXQ9Ii9kb2NQcm9wcy9hcHAueG1sIiBJZD0icklkMSIgLz48UmVsYXRpb25zaGlwIFR5cGU9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9wYWNrYWdlLzIwMDYvcmVsYXRpb25zaGlwcy9tZXRhZGF0YS9jb3JlLXByb3BlcnRpZXMiIFRhcmdldD0iL3BhY2thZ2Uvc2VydmljZXMvbWV0YWRhdGEvY29yZS1wcm9wZXJ0aWVzL2VjZmRkMzE0M2YyMTQ4OTA5NWE0NGM3MTExNWI3MjNiLnBzbWRjcCIgSWQ9IlJlZjQ4N2MzZTBjNzQ0YTg3IiAvPjwvUmVsYXRpb25zaGlwcz5QSwMECgAAAAAAi3U5SAAAAAAAAAAAAAAAAAkAAABkb2NQcm9wcy9QSwMECgAAAAAAE3t5Qu9e3149AwAAPQMAABAAAABkb2NQcm9wcy9hcHAueG1s77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48YXA6UHJvcGVydGllcyB4bWxuczp2dD0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL29mZmljZURvY3VtZW50LzIwMDYvZG9jUHJvcHNWVHlwZXMiIHhtbG5zOmFwPSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvb2ZmaWNlRG9jdW1lbnQvMjAwNi9leHRlbmRlZC1wcm9wZXJ0aWVzIj48YXA6QXBwbGljYXRpb24+TWljcm9zb2Z0IEV4Y2VsPC9hcDpBcHBsaWNhdGlvbj48YXA6RG9jU2VjdXJpdHk+MDwvYXA6RG9jU2VjdXJpdHk+PGFwOlNjYWxlQ3JvcD5mYWxzZTwvYXA6U2NhbGVDcm9wPjxhcDpIZWFkaW5nUGFpcnM+PHZ0OnZlY3RvciBiYXNlVHlwZT0idmFyaWFudCIgc2l6ZT0iNCI+PHZ0OnZhcmlhbnQ+PHZ0Omxwc3RyPldvcmtzaGVldHM8L3Z0Omxwc3RyPjwvdnQ6dmFyaWFudD48dnQ6dmFyaWFudD48dnQ6aTQ+MTwvdnQ6aTQ+PC92dDp2YXJpYW50Pjx2dDp2YXJpYW50Pjx2dDpscHN0cj5OYW1lZCBSYW5nZXM8L3Z0Omxwc3RyPjwvdnQ6dmFyaWFudD48dnQ6dmFyaWFudD48dnQ6aTQ+MjwvdnQ6aTQ+PC92dDp2YXJpYW50PjwvdnQ6dmVjdG9yPjwvYXA6SGVhZGluZ1BhaXJzPjxhcDpUaXRsZXNPZlBhcnRzPjx2dDp2ZWN0b3IgYmFzZVR5cGU9Imxwc3RyIiBzaXplPSIzIj48dnQ6bHBzdHI+U2hlZXQgMTwvdnQ6bHBzdHI+PHZ0Omxwc3RyPlNoZWV0IDEhUHJpbnRfQXJlYTwvdnQ6bHBzdHI+PHZ0Omxwc3RyPlNoZWV0IDEhUHJpbnRfVGl0bGVzPC92dDpscHN0cj48L3Z0OnZlY3Rvcj48L2FwOlRpdGxlc09mUGFydHM+PC9hcDpQcm9wZXJ0aWVzPlBLAwQKAAAAAACLdTlIAAAAAAAAAAAAAAAACAAAAHBhY2thZ2UvUEsDBAoAAAAAAMWFeUIAAAAAAAAAAAAAAAARAAAAcGFja2FnZS9zZXJ2aWNlcy9QSwMECgAAAAAAxYV5QgAAAAAAAAAAAAAAABoAAABwYWNrYWdlL3NlcnZpY2VzL21ldGFkYXRhL1BLAwQKAAAAAADFhXlCAAAAAAAAAAAAAAAAKgAAAHBhY2thZ2Uvc2VydmljZXMvbWV0YWRhdGEvY29yZS1wcm9wZXJ0aWVzL1BLAwQKAAAAAAATe3lCc4c2yNoBAADaAQAAUQAAAHBhY2thZ2Uvc2VydmljZXMvbWV0YWRhdGEvY29yZS1wcm9wZXJ0aWVzL2VjZmRkMzE0M2YyMTQ4OTA5NWE0NGM3MTExNWI3MjNiLnBzbWRjcO+7vzw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+PGNvcmVQcm9wZXJ0aWVzIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6ZGN0ZXJtcz0iaHR0cDovL3B1cmwub3JnL2RjL3Rlcm1zLyIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9wYWNrYWdlLzIwMDYvbWV0YWRhdGEvY29yZS1wcm9wZXJ0aWVzIj48ZGN0ZXJtczpjcmVhdGVkIHhzaTp0eXBlPSJkY3Rlcm1zOlczQ0RURiI+MjAxMy0wMy0yNVQxOToyNDozOS44NjYxOTY5WjwvZGN0ZXJtczpjcmVhdGVkPjxkY3Rlcm1zOm1vZGlmaWVkIHhzaTp0eXBlPSJkY3Rlcm1zOlczQ0RURiI+MjAxMy0wMy0yNVQxOToyNDozOS44NjYxOTY5WjwvZGN0ZXJtczptb2RpZmllZD48L2NvcmVQcm9wZXJ0aWVzPlBLAwQKAAAAAACLdTlIAAAAAAAAAAAAAAAAAwAAAHhsL1BLAwQKAAAAAADFhXlCAAAAAAAAAAAAAAAACQAAAHhsL19yZWxzL1BLAwQKAAAAAAATe3lCJ0p8MrwCAAC8AgAAGgAAAHhsL19yZWxzL3dvcmtib29rLnhtbC5yZWxz77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48UmVsYXRpb25zaGlwcyB4bWxucz0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL3BhY2thZ2UvMjAwNi9yZWxhdGlvbnNoaXBzIj48UmVsYXRpb25zaGlwIFR5cGU9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9vZmZpY2VEb2N1bWVudC8yMDA2L3JlbGF0aW9uc2hpcHMvc2hhcmVkU3RyaW5ncyIgVGFyZ2V0PSIveGwvc2hhcmVkU3RyaW5ncy54bWwiIElkPSJySWQzIiAvPjxSZWxhdGlvbnNoaXAgVHlwZT0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL29mZmljZURvY3VtZW50LzIwMDYvcmVsYXRpb25zaGlwcy9zdHlsZXMiIFRhcmdldD0iL3hsL3N0eWxlcy54bWwiIElkPSJySWQ0IiAvPjxSZWxhdGlvbnNoaXAgVHlwZT0iaHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL29mZmljZURvY3VtZW50LzIwMDYvcmVsYXRpb25zaGlwcy93b3Jrc2hlZXQiIFRhcmdldD0iL3hsL3dvcmtzaGVldHMvc2hlZXQueG1sIiBJZD0icklkMiIgLz48UmVsYXRpb25zaGlwIFR5cGU9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9vZmZpY2VEb2N1bWVudC8yMDA2L3JlbGF0aW9uc2hpcHMvdGhlbWUiIFRhcmdldD0iL3hsL3RoZW1lL3RoZW1lLnhtbCIgSWQ9InJJZDYiIC8+PC9SZWxhdGlvbnNoaXBzPlBLAwQKAAAAAAATe3lCflKRBZAAAACQAAAAFAAAAHhsL3NoYXJlZFN0cmluZ3MueG1s77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48eDpzc3QgY291bnQ9IjAiIHVuaXF1ZUNvdW50PSIwIiB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvc3ByZWFkc2hlZXRtbC8yMDA2L21haW4iIC8+UEsDBAoAAAAAABN7eUIi2lsreggAAHoIAAANAAAAeGwvc3R5bGVzLnhtbO+7vzw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+PHg6c3R5bGVTaGVldCB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvc3ByZWFkc2hlZXRtbC8yMDA2L21haW4iPjx4Om51bUZtdHMgY291bnQ9IjEiPjx4Om51bUZtdCBudW1GbXRJZD0iMCIgZm9ybWF0Q29kZT0iIiAvPjwveDpudW1GbXRzPjx4OmZvbnRzIGNvdW50PSIxIj48eDpmb250Pjx4OnZlcnRBbGlnbiB2YWw9ImJhc2VsaW5lIiAvPjx4OnN6IHZhbD0iMTEiIC8+PHg6Y29sb3IgcmdiPSJGRjAwMDAwMCIgLz48eDpuYW1lIHZhbD0iQ2FsaWJyaSIgLz48eDpmYW1pbHkgdmFsPSIyIiAvPjwveDpmb250PjwveDpmb250cz48eDpmaWxscyBjb3VudD0iMiI+PHg6ZmlsbD48eDpwYXR0ZXJuRmlsbCBwYXR0ZXJuVHlwZT0ibm9uZSIgLz48L3g6ZmlsbD48eDpmaWxsPjx4OnBhdHRlcm5GaWxsIHBhdHRlcm5UeXBlPSJncmF5MTI1IiAvPjwveDpmaWxsPjwveDpmaWxscz48eDpib3JkZXJzIGNvdW50PSIxIj48eDpib3JkZXIgZGlhZ29uYWxVcD0iMCIgZGlhZ29uYWxEb3duPSIwIj48eDpsZWZ0IHN0eWxlPSJub25lIj48eDpjb2xvciByZ2I9IkZGMDAwMDAwIiAvPjwveDpsZWZ0Pjx4OnJpZ2h0IHN0eWxlPSJub25lIj48eDpjb2xvciByZ2I9IkZGMDAwMDAwIiAvPjwveDpyaWdodD48eDp0b3Agc3R5bGU9Im5vbmUiPjx4OmNvbG9yIHJnYj0iRkYwMDAwMDAiIC8+PC94OnRvcD48eDpib3R0b20gc3R5bGU9Im5vbmUiPjx4OmNvbG9yIHJnYj0iRkYwMDAwMDAiIC8+PC94OmJvdHRvbT48eDpkaWFnb25hbCBzdHlsZT0ibm9uZSI+PHg6Y29sb3IgcmdiPSJGRjAwMDAwMCIgLz48L3g6ZGlhZ29uYWw+PC94OmJvcmRlcj48L3g6Ym9yZGVycz48eDpjZWxsU3R5bGVYZnMgY291bnQ9IjIiPjx4OnhmIG51bUZtdElkPSIwIiBmb250SWQ9IjAiIGZpbGxJZD0iMCIgYm9yZGVySWQ9IjAiIGFwcGx5TnVtYmVyRm9ybWF0PSIwIiBhcHBseUZpbGw9IjEiIGFwcGx5Qm9yZGVyPSIwIiBhcHBseUFsaWdubWVudD0iMCIgYXBwbHlQcm90ZWN0aW9uPSIxIj48eDpwcm90ZWN0aW9uIGxvY2tlZD0iMSIgaGlkZGVuPSIwIiAvPjwveDp4Zj48eDp4ZiBudW1GbXRJZD0iMTQiIGZvbnRJZD0iMCIgZmlsbElkPSIwIiBib3JkZXJJZD0iMCIgYXBwbHlOdW1iZXJGb3JtYXQ9IjAiIGFwcGx5RmlsbD0iMSIgYXBwbHlCb3JkZXI9IjAiIGFwcGx5QWxpZ25tZW50PSIwIiBhcHBseVByb3RlY3Rpb249IjEiPjx4OnByb3RlY3Rpb24gbG9ja2VkPSIxIiBoaWRkZW49IjAiIC8+PC94OnhmPjwveDpjZWxsU3R5bGVYZnM+PHg6Y2VsbFhmcyBjb3VudD0iMiI+PHg6eGYgbnVtRm10SWQ9IjAiIGZvbnRJZD0iMCIgZmlsbElkPSIwIiBib3JkZXJJZD0iMCIgeGZJZD0iMCIgYXBwbHlOdW1iZXJGb3JtYXQ9IjAiIGFwcGx5RmlsbD0iMSIgYXBwbHlCb3JkZXI9IjAiIGFwcGx5QWxpZ25tZW50PSIwIiBhcHBseVByb3RlY3Rpb249IjEiPjx4OmFsaWdubWVudCBob3Jpem9udGFsPSJnZW5lcmFsIiB2ZXJ0aWNhbD0iYm90dG9tIiB0ZXh0Um90YXRpb249IjAiIHdyYXBUZXh0PSIwIiBpbmRlbnQ9IjAiIHJlbGF0aXZlSW5kZW50PSIwIiBqdXN0aWZ5TGFzdExpbmU9IjAiIHNocmlua1RvRml0PSIwIiByZWFkaW5nT3JkZXI9IjAiIC8+PHg6cHJvdGVjdGlvbiBsb2NrZWQ9IjEiIGhpZGRlbj0iMCIgLz48L3g6eGY+PHg6eGYgbnVtRm10SWQ9IjE0IiBmb250SWQ9IjAiIGZpbGxJZD0iMCIgYm9yZGVySWQ9IjAiIHhmSWQ9IjAiIGFwcGx5TnVtYmVyRm9ybWF0PSIwIiBhcHBseUZpbGw9IjEiIGFwcGx5Qm9yZGVyPSIwIiBhcHBseUFsaWdubWVudD0iMCIgYXBwbHlQcm90ZWN0aW9uPSIxIj48eDphbGlnbm1lbnQgaG9yaXpvbnRhbD0iZ2VuZXJhbCIgdmVydGljYWw9ImJvdHRvbSIgdGV4dFJvdGF0aW9uPSIwIiB3cmFwVGV4dD0iMCIgaW5kZW50PSIwIiByZWxhdGl2ZUluZGVudD0iMCIganVzdGlmeUxhc3RMaW5lPSIwIiBzaHJpbmtUb0ZpdD0iMCIgcmVhZGluZ09yZGVyPSIwIiAvPjx4OnByb3RlY3Rpb24gbG9ja2VkPSIxIiBoaWRkZW49IjAiIC8+PC94OnhmPjwveDpjZWxsWGZzPjx4OmNlbGxTdHlsZXMgY291bnQ9IjEiPjx4OmNlbGxTdHlsZSBuYW1lPSJOb3JtYWwiIHhmSWQ9IjAiIGJ1aWx0aW5JZD0iMCIgLz48L3g6Y2VsbFN0eWxlcz48L3g6c3R5bGVTaGVldD5QSwMECgAAAAAAxYV5QgAAAAAAAAAAAAAAAAkAAAB4bC90aGVtZS9QSwMECgAAAAAAE3t5QnWxkV67GwAAuxsAABIAAAB4bC90aGVtZS90aGVtZS54bWzvu788P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJ1dGYtOCI/PjxhOnRoZW1lIHhtbG5zOmE9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9kcmF3aW5nbWwvMjAwNi9tYWluIiBuYW1lPSJPZmZpY2UgVGhlbWUiPjxhOnRoZW1lRWxlbWVudHM+PGE6Y2xyU2NoZW1lIG5hbWU9Ik9mZmljZSI+PGE6ZGsxPjxhOnN5c0NsciB2YWw9IndpbmRvd1RleHQiIGxhc3RDbHI9IjAwMDAwMCIgLz48L2E6ZGsxPjxhOmx0MT48YTpzeXNDbHIgdmFsPSJ3aW5kb3ciIGxhc3RDbHI9IkZGRkZGRiIgLz48L2E6bHQxPjxhOmRrMj48YTpzcmdiQ2xyIHZhbD0iMUY0OTdEIiAvPjwvYTpkazI+PGE6bHQyPjxhOnNyZ2JDbHIgdmFsPSJFRUVDRTEiIC8+PC9hOmx0Mj48YTphY2NlbnQxPjxhOnNyZ2JDbHIgdmFsPSI0RjgxQkQiIC8+PC9hOmFjY2VudDE+PGE6YWNjZW50Mj48YTpzcmdiQ2xyIHZhbD0iQzA1MDREIiAvPjwvYTphY2NlbnQyPjxhOmFjY2VudDM+PGE6c3JnYkNsciB2YWw9IjlCQkI1OSIgLz48L2E6YWNjZW50Mz48YTphY2NlbnQ0PjxhOnNyZ2JDbHIgdmFsPSI4MDY0QTIiIC8+PC9hOmFjY2VudDQ+PGE6YWNjZW50NT48YTpzcmdiQ2xyIHZhbD0iNEJBQ0M2IiAvPjwvYTphY2NlbnQ1PjxhOmFjY2VudDY+PGE6c3JnYkNsciB2YWw9IkY3OTY0NiIgLz48L2E6YWNjZW50Nj48YTpobGluaz48YTpzcmdiQ2xyIHZhbD0iMDAwMEZGIiAvPjwvYTpobGluaz48YTpmb2xIbGluaz48YTpzcmdiQ2xyIHZhbD0iODAwMDgwIiAvPjwvYTpmb2xIbGluaz48L2E6Y2xyU2NoZW1lPjxhOmZvbnRTY2hlbWUgbmFtZT0iT2ZmaWNlIj48YTptYWpvckZvbnQ+PGE6bGF0aW4gdHlwZWZhY2U9IkNhbWJyaWEiIC8+PGE6ZWEgdHlwZWZhY2U9IiIgLz48YTpjcyB0eXBlZmFjZT0iIiAvPjxhOmZvbnQgc2NyaXB0PSJKcGFuIiB0eXBlZmFjZT0i77yt77yzIO+8sOOCtOOCt+ODg+OCryIgLz48YTpmb250IHNjcmlwdD0iSGFuZyIgdHlwZWZhY2U9IuunkeydgCDqs6DrlJUiIC8+PGE6Zm9udCBzY3JpcHQ9IkhhbnMiIHR5cGVmYWNlPSLlrovkvZMiIC8+PGE6Zm9udCBzY3JpcHQ9IkhhbnQiIHR5cGVmYWNlPSLmlrDntLDmmI7pq5QiIC8+PGE6Zm9udCBzY3JpcHQ9IkFyYWIiIHR5cGVmYWNlPSJUaW1lcyBOZXcgUm9tYW4iIC8+PGE6Zm9udCBzY3JpcHQ9IkhlYnIiIHR5cGVmYWNlPSJUaW1lcyBOZXcgUm9tYW4iIC8+PGE6Zm9udCBzY3JpcHQ9IlRoYWkiIHR5cGVmYWNlPSJUYWhvbWEiIC8+PGE6Zm9udCBzY3JpcHQ9IkV0aGkiIHR5cGVmYWNlPSJOeWFsYSIgLz48YTpmb250IHNjcmlwdD0iQmVuZyIgdHlwZWZhY2U9IlZyaW5kYSIgLz48YTpmb250IHNjcmlwdD0iR3VqciIgdHlwZWZhY2U9IlNocnV0aSIgLz48YTpmb250IHNjcmlwdD0iS2htciIgdHlwZWZhY2U9Ik1vb2xCb3JhbiIgLz48YTpmb250IHNjcmlwdD0iS25kYSIgdHlwZWZhY2U9IlR1bmdhIiAvPjxhOmZvbnQgc2NyaXB0PSJHdXJ1IiB0eXBlZmFjZT0iUmFhdmkiIC8+PGE6Zm9udCBzY3JpcHQ9IkNhbnMiIHR5cGVmYWNlPSJFdXBoZW1pYSIgLz48YTpmb250IHNjcmlwdD0iQ2hlciIgdHlwZWZhY2U9IlBsYW50YWdlbmV0IENoZXJva2VlIiAvPjxhOmZvbnQgc2NyaXB0PSJZaWlpIiB0eXBlZmFjZT0iTWljcm9zb2Z0IFlpIEJhaXRpIiAvPjxhOmZvbnQgc2NyaXB0PSJUaWJ0IiB0eXBlZmFjZT0iTWljcm9zb2Z0IEhpbWFsYXlhIiAvPjxhOmZvbnQgc2NyaXB0PSJUaGFhIiB0eXBlZmFjZT0iTVYgQm9saSIgLz48YTpmb250IHNjcmlwdD0iRGV2YSIgdHlwZWZhY2U9Ik1hbmdhbCIgLz48YTpmb250IHNjcmlwdD0iVGVsdSIgdHlwZWZhY2U9IkdhdXRhbWkiIC8+PGE6Zm9udCBzY3JpcHQ9IlRhbWwiIHR5cGVmYWNlPSJMYXRoYSIgLz48YTpmb250IHNjcmlwdD0iU3lyYyIgdHlwZWZhY2U9IkVzdHJhbmdlbG8gRWRlc3NhIiAvPjxhOmZvbnQgc2NyaXB0PSJPcnlhIiB0eXBlZmFjZT0iS2FsaW5nYSIgLz48YTpmb250IHNjcmlwdD0iTWx5bSIgdHlwZWZhY2U9IkthcnRpa2EiIC8+PGE6Zm9udCBzY3JpcHQ9Ikxhb28iIHR5cGVmYWNlPSJEb2tDaGFtcGEiIC8+PGE6Zm9udCBzY3JpcHQ9IlNpbmgiIHR5cGVmYWNlPSJJc2tvb2xhIFBvdGEiIC8+PGE6Zm9udCBzY3JpcHQ9Ik1vbmciIHR5cGVmYWNlPSJNb25nb2xpYW4gQmFpdGkiIC8+PGE6Zm9udCBzY3JpcHQ9IlZpZXQiIHR5cGVmYWNlPSJUaW1lcyBOZXcgUm9tYW4iIC8+PGE6Zm9udCBzY3JpcHQ9IlVpZ2giIHR5cGVmYWNlPSJNaWNyb3NvZnQgVWlnaHVyIiAvPjwvYTptYWpvckZvbnQ+PGE6bWlub3JGb250PjxhOmxhdGluIHR5cGVmYWNlPSJDYWxpYnJpIiAvPjxhOmVhIHR5cGVmYWNlPSIiIC8+PGE6Y3MgdHlwZWZhY2U9IiIgLz48YTpmb250IHNjcmlwdD0iSnBhbiIgdHlwZWZhY2U9Iu+8re+8syDvvLDjgrTjgrfjg4Pjgq8iIC8+PGE6Zm9udCBzY3JpcHQ9IkhhbmciIHR5cGVmYWNlPSLrp5HsnYAg6rOg65SVIiAvPjxhOmZvbnQgc2NyaXB0PSJIYW5zIiB0eXBlZmFjZT0i5a6L5L2TIiAvPjxhOmZvbnQgc2NyaXB0PSJIYW50IiB0eXBlZmFjZT0i5paw57Sw5piO6auUIiAvPjxhOmZvbnQgc2NyaXB0PSJBcmFiIiB0eXBlZmFjZT0iQXJpYWwiIC8+PGE6Zm9udCBzY3JpcHQ9IkhlYnIiIHR5cGVmYWNlPSJBcmlhbCIgLz48YTpmb250IHNjcmlwdD0iVGhhaSIgdHlwZWZhY2U9IlRhaG9tYSIgLz48YTpmb250IHNjcmlwdD0iRXRoaSIgdHlwZWZhY2U9Ik55YWxhIiAvPjxhOmZvbnQgc2NyaXB0PSJCZW5nIiB0eXBlZmFjZT0iVnJpbmRhIiAvPjxhOmZvbnQgc2NyaXB0PSJHdWpyIiB0eXBlZmFjZT0iU2hydXRpIiAvPjxhOmZvbnQgc2NyaXB0PSJLaG1yIiB0eXBlZmFjZT0iRGF1blBlbmgiIC8+PGE6Zm9udCBzY3JpcHQ9IktuZGEiIHR5cGVmYWNlPSJUdW5nYSIgLz48YTpmb250IHNjcmlwdD0iR3VydSIgdHlwZWZhY2U9IlJhYXZpIiAvPjxhOmZvbnQgc2NyaXB0PSJDYW5zIiB0eXBlZmFjZT0iRXVwaGVtaWEiIC8+PGE6Zm9udCBzY3JpcHQ9IkNoZXIiIHR5cGVmYWNlPSJQbGFudGFnZW5ldCBDaGVyb2tlZSIgLz48YTpmb250IHNjcmlwdD0iWWlpaSIgdHlwZWZhY2U9Ik1pY3Jvc29mdCBZaSBCYWl0aSIgLz48YTpmb250IHNjcmlwdD0iVGlidCIgdHlwZWZhY2U9Ik1pY3Jvc29mdCBIaW1hbGF5YSIgLz48YTpmb250IHNjcmlwdD0iVGhhYSIgdHlwZWZhY2U9Ik1WIEJvbGkiIC8+PGE6Zm9udCBzY3JpcHQ9IkRldmEiIHR5cGVmYWNlPSJNYW5nYWwiIC8+PGE6Zm9udCBzY3JpcHQ9IlRlbHUiIHR5cGVmYWNlPSJHYXV0YW1pIiAvPjxhOmZvbnQgc2NyaXB0PSJUYW1sIiB0eXBlZmFjZT0iTGF0aGEiIC8+PGE6Zm9udCBzY3JpcHQ9IlN5cmMiIHR5cGVmYWNlPSJFc3RyYW5nZWxvIEVkZXNzYSIgLz48YTpmb250IHNjcmlwdD0iT3J5YSIgdHlwZWZhY2U9IkthbGluZ2EiIC8+PGE6Zm9udCBzY3JpcHQ9Ik1seW0iIHR5cGVmYWNlPSJLYXJ0aWthIiAvPjxhOmZvbnQgc2NyaXB0PSJMYW9vIiB0eXBlZmFjZT0iRG9rQ2hhbXBhIiAvPjxhOmZvbnQgc2NyaXB0PSJTaW5oIiB0eXBlZmFjZT0iSXNrb29sYSBQb3RhIiAvPjxhOmZvbnQgc2NyaXB0PSJNb25nIiB0eXBlZmFjZT0iTW9uZ29saWFuIEJhaXRpIiAvPjxhOmZvbnQgc2NyaXB0PSJWaWV0IiB0eXBlZmFjZT0iQXJpYWwiIC8+PGE6Zm9udCBzY3JpcHQ9IlVpZ2giIHR5cGVmYWNlPSJNaWNyb3NvZnQgVWlnaHVyIiAvPjwvYTptaW5vckZvbnQ+PC9hOmZvbnRTY2hlbWU+PGE6Zm10U2NoZW1lIG5hbWU9Ik9mZmljZSI+PGE6ZmlsbFN0eWxlTHN0PjxhOnNvbGlkRmlsbD48YTpzY2hlbWVDbHIgdmFsPSJwaENsciIgLz48L2E6c29saWRGaWxsPjxhOmdyYWRGaWxsIHJvdFdpdGhTaGFwZT0iMSI+PGE6Z3NMc3Q+PGE6Z3MgcG9zPSIwIj48YTpzY2hlbWVDbHIgdmFsPSJwaENsciI+PGE6dGludCB2YWw9IjUwMDAwIiAvPjxhOnNhdE1vZCB2YWw9IjMwMDAwMCIgLz48L2E6c2NoZW1lQ2xyPjwvYTpncz48YTpncyBwb3M9IjM1MDAwIj48YTpzY2hlbWVDbHIgdmFsPSJwaENsciI+PGE6dGludCB2YWw9IjM3MDAwIiAvPjxhOnNhdE1vZCB2YWw9IjMwMDAwMCIgLz48L2E6c2NoZW1lQ2xyPjwvYTpncz48YTpncyBwb3M9IjEwMDAwMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnRpbnQgdmFsPSIxNTAwMCIgLz48YTpzYXRNb2QgdmFsPSIzNTAwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PC9hOmdzTHN0PjxhOmxpbiBhbmc9IjE2MjAwMDAwIiBzY2FsZWQ9IjEiIC8+PC9hOmdyYWRGaWxsPjxhOmdyYWRGaWxsIHJvdFdpdGhTaGFwZT0iMSI+PGE6Z3NMc3Q+PGE6Z3MgcG9zPSIwIj48YTpzY2hlbWVDbHIgdmFsPSJwaENsciI+PGE6c2hhZGUgdmFsPSI1MTAwMCIgLz48YTpzYXRNb2QgdmFsPSIxMzAwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PGE6Z3MgcG9zPSI4MDAwMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnNoYWRlIHZhbD0iOTMwMDAiIC8+PGE6c2F0TW9kIHZhbD0iMTMwMDAwIiAvPjwvYTpzY2hlbWVDbHI+PC9hOmdzPjxhOmdzIHBvcz0iMTAwMDAwIj48YTpzY2hlbWVDbHIgdmFsPSJwaENsciI+PGE6c2hhZGUgdmFsPSI5NDAwMCIgLz48YTpzYXRNb2QgdmFsPSIxMzUwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PC9hOmdzTHN0PjxhOmxpbiBhbmc9IjE2MjAwMDAwIiBzY2FsZWQ9IjAiIC8+PC9hOmdyYWRGaWxsPjwvYTpmaWxsU3R5bGVMc3Q+PGE6bG5TdHlsZUxzdD48YTpsbiB3PSI5NTI1IiBjYXA9ImZsYXQiIGNtcGQ9InNuZyIgYWxnbj0iY3RyIj48YTpzb2xpZEZpbGw+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnNoYWRlIHZhbD0iOTUwMDAiIC8+PGE6c2F0TW9kIHZhbD0iMTA1MDAwIiAvPjwvYTpzY2hlbWVDbHI+PC9hOnNvbGlkRmlsbD48YTpwcnN0RGFzaCB2YWw9InNvbGlkIiAvPjwvYTpsbj48YTpsbiB3PSIyNTQwMCIgY2FwPSJmbGF0IiBjbXBkPSJzbmciIGFsZ249ImN0ciI+PGE6c29saWRGaWxsPjxhOnNjaGVtZUNsciB2YWw9InBoQ2xyIiAvPjwvYTpzb2xpZEZpbGw+PGE6cHJzdERhc2ggdmFsPSJzb2xpZCIgLz48L2E6bG4+PGE6bG4gdz0iMzgxMDAiIGNhcD0iZmxhdCIgY21wZD0ic25nIiBhbGduPSJjdHIiPjxhOnNvbGlkRmlsbD48YTpzY2hlbWVDbHIgdmFsPSJwaENsciIgLz48L2E6c29saWRGaWxsPjxhOnByc3REYXNoIHZhbD0ic29saWQiIC8+PC9hOmxuPjwvYTpsblN0eWxlTHN0PjxhOmVmZmVjdFN0eWxlTHN0PjxhOmVmZmVjdFN0eWxlPjxhOmVmZmVjdExzdD48YTpvdXRlclNoZHcgYmx1clJhZD0iNDAwMDAiIGRpc3Q9IjIwMDAwIiBkaXI9IjU0MDAwMDAiIHJvdFdpdGhTaGFwZT0iMCI+PGE6c3JnYkNsciB2YWw9IjAwMDAwMCI+PGE6YWxwaGEgdmFsPSIzODAwMCIgLz48L2E6c3JnYkNscj48L2E6b3V0ZXJTaGR3PjwvYTplZmZlY3RMc3Q+PC9hOmVmZmVjdFN0eWxlPjxhOmVmZmVjdFN0eWxlPjxhOmVmZmVjdExzdD48YTpvdXRlclNoZHcgYmx1clJhZD0iNDAwMDAiIGRpc3Q9IjIzMDAwIiBkaXI9IjU0MDAwMDAiIHJvdFdpdGhTaGFwZT0iMCI+PGE6c3JnYkNsciB2YWw9IjAwMDAwMCI+PGE6YWxwaGEgdmFsPSIzNTAwMCIgLz48L2E6c3JnYkNscj48L2E6b3V0ZXJTaGR3PjwvYTplZmZlY3RMc3Q+PC9hOmVmZmVjdFN0eWxlPjxhOmVmZmVjdFN0eWxlPjxhOmVmZmVjdExzdD48YTpvdXRlclNoZHcgYmx1clJhZD0iNDAwMDAiIGRpc3Q9IjIzMDAwIiBkaXI9IjU0MDAwMDAiIHJvdFdpdGhTaGFwZT0iMCI+PGE6c3JnYkNsciB2YWw9IjAwMDAwMCI+PGE6YWxwaGEgdmFsPSIzNTAwMCIgLz48L2E6c3JnYkNscj48L2E6b3V0ZXJTaGR3PjwvYTplZmZlY3RMc3Q+PGE6c2NlbmUzZD48YTpjYW1lcmEgcHJzdD0ib3J0aG9ncmFwaGljRnJvbnQiPjxhOnJvdCBsYXQ9IjAiIGxvbj0iMCIgcmV2PSIwIiAvPjwvYTpjYW1lcmE+PGE6bGlnaHRSaWcgcmlnPSJ0aHJlZVB0IiBkaXI9InQiPjxhOnJvdCBsYXQ9IjAiIGxvbj0iMCIgcmV2PSIxMjAwMDAwIiAvPjwvYTpsaWdodFJpZz48L2E6c2NlbmUzZD48YTpzcDNkPjxhOmJldmVsVCB3PSI2MzUwMCIgaD0iMjU0MDAiIC8+PC9hOnNwM2Q+PC9hOmVmZmVjdFN0eWxlPjwvYTplZmZlY3RTdHlsZUxzdD48YTpiZ0ZpbGxTdHlsZUxzdD48YTpzb2xpZEZpbGw+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiIC8+PC9hOnNvbGlkRmlsbD48YTpncmFkRmlsbCByb3RXaXRoU2hhcGU9IjEiPjxhOmdzTHN0PjxhOmdzIHBvcz0iMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnRpbnQgdmFsPSI0MDAwMCIgLz48YTpzYXRNb2QgdmFsPSIzNTAwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PGE6Z3MgcG9zPSI0MDAwMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnRpbnQgdmFsPSI0NTAwMCIgLz48YTpzaGFkZSB2YWw9Ijk5MDAwIiAvPjxhOnNhdE1vZCB2YWw9IjM1MDAwMCIgLz48L2E6c2NoZW1lQ2xyPjwvYTpncz48YTpncyBwb3M9IjEwMDAwMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnNoYWRlIHZhbD0iMjAwMDAiIC8+PGE6c2F0TW9kIHZhbD0iMjU1MDAwIiAvPjwvYTpzY2hlbWVDbHI+PC9hOmdzPjwvYTpnc0xzdD48YTpwYXRoIHBhdGg9ImNpcmNsZSI+PGE6ZmlsbFRvUmVjdCBsPSI1MDAwMCIgdD0iLTgwMDAwIiByPSI1MDAwMCIgYj0iMTgwMDAwIiAvPjwvYTpwYXRoPjwvYTpncmFkRmlsbD48YTpncmFkRmlsbCByb3RXaXRoU2hhcGU9IjEiPjxhOmdzTHN0PjxhOmdzIHBvcz0iMCI+PGE6c2NoZW1lQ2xyIHZhbD0icGhDbHIiPjxhOnRpbnQgdmFsPSI4MDAwMCIgLz48YTpzYXRNb2QgdmFsPSIzMDAwMDAiIC8+PC9hOnNjaGVtZUNscj48L2E6Z3M+PGE6Z3MgcG9zPSIxMDAwMDAiPjxhOnNjaGVtZUNsciB2YWw9InBoQ2xyIj48YTpzaGFkZSB2YWw9IjMwMDAwIiAvPjxhOnNhdE1vZCB2YWw9IjIwMDAwMCIgLz48L2E6c2NoZW1lQ2xyPjwvYTpncz48L2E6Z3NMc3Q+PGE6cGF0aCBwYXRoPSJjaXJjbGUiPjxhOmZpbGxUb1JlY3QgbD0iNTAwMDAiIHQ9IjUwMDAwIiByPSI1MDAwMCIgYj0iNTAwMDAiIC8+PC9hOnBhdGg+PC9hOmdyYWRGaWxsPjwvYTpiZ0ZpbGxTdHlsZUxzdD48L2E6Zm10U2NoZW1lPjwvYTp0aGVtZUVsZW1lbnRzPjxhOm9iamVjdERlZmF1bHRzIC8+PGE6ZXh0cmFDbHJTY2hlbWVMc3QgLz48L2E6dGhlbWU+UEsDBAoAAAAAABN7eUKJ3nBGuwEAALsBAAAPAAAAeGwvd29ya2Jvb2sueG1s77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48eDp3b3JrYm9vayB4bWxuczpyPSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvb2ZmaWNlRG9jdW1lbnQvMjAwNi9yZWxhdGlvbnNoaXBzIiB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5vcGVueG1sZm9ybWF0cy5vcmcvc3ByZWFkc2hlZXRtbC8yMDA2L21haW4iPjx4Ondvcmtib29rUHIgY29kZU5hbWU9IlRoaXNXb3JrYm9vayIgLz48eDpib29rVmlld3M+PHg6d29ya2Jvb2tWaWV3IGZpcnN0U2hlZXQ9IjAiIGFjdGl2ZVRhYj0iMCIgLz48L3g6Ym9va1ZpZXdzPjx4OnNoZWV0cz48eDpzaGVldCBuYW1lPSJTaGVldCAxIiBzaGVldElkPSIyIiByOmlkPSJySWQyIiAvPjwveDpzaGVldHM+PHg6ZGVmaW5lZE5hbWVzIC8+PHg6Y2FsY1ByIGNhbGNJZD0iMTI1NzI1IiAvPjwveDp3b3JrYm9vaz5QSwMECgAAAAAAxYV5QgAAAAAAAAAAAAAAAA4AAAB4bC93b3Jrc2hlZXRzL1BLAQIUAAoAAAAAABN7eUK9Z10uNQQAADUEAAATAAAAAAAAAAAAAAAAAAAAAABbQ29udGVudF9UeXBlc10ueG1sUEsBAhQACgAAAAAAi3U5SAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAQAAAAZgQAAF9yZWxzL1BLAQIUAAoAAAAAABN7eUJ0mYADnAIAAJwCAAALAAAAAAAAAAAAAAAAAIoEAABfcmVscy8ucmVsc1BLAQIUAAoAAAAAAIt1OUgAAAAAAAAAAAAAAAAJAAAAAAAAAAAAEAAAAE8HAABkb2NQcm9wcy9QSwECFAAKAAAAAAATe3lC717fXj0DAAA9AwAAEAAAAAAAAAAAAAAAAAB2BwAAZG9jUHJvcHMvYXBwLnhtbFBLAQIUAAoAAAAAAIt1OUgAAAAAAAAAAAAAAAAIAAAAAAAAAAAAEAAAAOEKAABwYWNrYWdlL1BLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAARAAAAAAAAAAAAEAAAAAcLAABwYWNrYWdlL3NlcnZpY2VzL1BLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAaAAAAAAAAAAAAEAAAADYLAABwYWNrYWdlL3NlcnZpY2VzL21ldGFkYXRhL1BLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAqAAAAAAAAAAAAEAAAAG4LAABwYWNrYWdlL3NlcnZpY2VzL21ldGFkYXRhL2NvcmUtcHJvcGVydGllcy9QSwECFAAKAAAAAAATe3lCc4c2yNoBAADaAQAAUQAAAAAAAAAAAAAAAAC2CwAAcGFja2FnZS9zZXJ2aWNlcy9tZXRhZGF0YS9jb3JlLXByb3BlcnRpZXMvZWNmZGQzMTQzZjIxNDg5MDk1YTQ0YzcxMTE1YjcyM2IucHNtZGNwUEsBAhQACgAAAAAAi3U5SAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAQAAAA/w0AAHhsL1BLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAJAAAAAAAAAAAAEAAAACAOAAB4bC9fcmVscy9QSwECFAAKAAAAAAATe3lCJ0p8MrwCAAC8AgAAGgAAAAAAAAAAAAAAAABHDgAAeGwvX3JlbHMvd29ya2Jvb2sueG1sLnJlbHNQSwECFAAKAAAAAAATe3lCflKRBZAAAACQAAAAFAAAAAAAAAAAAAAAAAA7EQAAeGwvc2hhcmVkU3RyaW5ncy54bWxQSwECFAAKAAAAAAATe3lCItpbK3oIAAB6CAAADQAAAAAAAAAAAAAAAAD9EQAAeGwvc3R5bGVzLnhtbFBLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAJAAAAAAAAAAAAEAAAAKIaAAB4bC90aGVtZS9QSwECFAAKAAAAAAATe3lCdbGRXrsbAAC7GwAAEgAAAAAAAAAAAAAAAADJGgAAeGwvdGhlbWUvdGhlbWUueG1sUEsBAhQACgAAAAAAE3t5QonecEa7AQAAuwEAAA8AAAAAAAAAAAAAAAAAtDYAAHhsL3dvcmtib29rLnhtbFBLAQIUAAoAAAAAAMWFeUIAAAAAAAAAAAAAAAAOAAAAAAAAAAAAEAAAAJw4AAB4bC93b3Jrc2hlZXRzL1BLBQYAAAAAEwATANQEAADIOAAAAAA=' var sheetsFront = '' + '' - + ''; -var sheetsBack = ''; + + '' +var sheetsBack = '' -var relFront = ''; +var relFront = '' -var relBack = ''; +var relBack = '' var contentTypeFront = '' + '' + '' + '' + '' + '' - + ''; -var contentTypeBack = ''; -var sharedStringsFront = ''; -var sharedStringsBack = ''; -var shareStrings, convertedShareStrings; + + '' +var contentTypeBack = '' +var sharedStringsFront = '' +var sharedStringsBack = '' +var convertedShareStrings -function generateMultiSheets(configs, xlsx) { - var i = 1; - configs.forEach(function(config) { - config.name = config.name ? config.name : ('sheet'+i); - i++; - var sheet = new Sheet(config, xlsx, shareStrings, convertedShareStrings); - sheet.generate(); +function generateMultiSheets (configs, xlsx, shareStrings) { + var i = 1 + configs.forEach(function (config) { + config.name = config.name ? config.name : ('sheet' + i) + i++ + var sheet = new Sheet(config, xlsx, shareStrings, convertedShareStrings) + sheet.generate() convertedShareStrings = sheet.convertedShareStrings - }); + }) } -function generateContentType(configs, xlsx) { - var workbook = contentTypeFront; - configs.forEach( function(config) { - workbook += ''; - }); - workbook += contentTypeBack; - xlsx.file('[Content_Types].xml', workbook); +function generateContentType (configs, xlsx) { + var workbook = contentTypeFront + configs.forEach(function (config) { + workbook += '' + }) + workbook += contentTypeBack + xlsx.file('[Content_Types].xml', workbook) } -function generateRel(configs,xlsx) { - var workbook = relFront; - var i = 1; - configs.forEach( function(config) { - workbook += ''; - i++; - }); - workbook += relBack; - xlsx.file('xl/_rels/workbook.xml.rels', workbook); - xlsx.file('_rels/.rels', '' - + '' - + '' - + ''); +function generateRel (configs, xlsx) { + var workbook = relFront + var i = 1 + configs.forEach(function (config) { + workbook += '' + i++ + }) + workbook += relBack + xlsx.file('xl/_rels/workbook.xml.rels', workbook) + xlsx.file('_rels/.rels', '' + + '' + + '' + + '') } -function generateWorkbook(configs,xlsx) { - var workbook = sheetsFront; - var i = 1; - configs.forEach( function(config) { - workbook += ''; - i++; - }); - workbook += sheetsBack; - xlsx.file('xl/workbook.xml', workbook); +function generateWorkbook (configs, xlsx) { + var workbook = sheetsFront + var i = 1 + configs.forEach(function (config) { + workbook += '' + i++ + }) + workbook += sheetsBack + xlsx.file('xl/workbook.xml', workbook) } -function generateSharedStringsFile(xlsx){ - if (shareStrings.length > 0) { - var sharedStringsFrontTmp = sharedStringsFront.replace(/\$count/g, shareStrings.length); - xlsx.file("xl/sharedStrings.xml", (sharedStringsFrontTmp + convertedShareStrings + sharedStringsBack)); - } - convertedShareStrings = ""; +function generateSharedStringsFile (xlsx, shareStrings) { + if (shareStrings.size > 0) { + var sharedStringsFrontTmp = sharedStringsFront.replace(/\$count/g, shareStrings.size) + xlsx.file('xl/sharedStrings.xml', (sharedStringsFrontTmp + convertedShareStrings + sharedStringsBack)) + } + convertedShareStrings = '' } -exports.executeAsync = function(config, callBack) { - return process.nextTick(function() { - var r = exports.execute(config); - callBack(r); - }); -}; +exports.execute = function (config) { + var xlsx = new JSZip(templateXLSX, { + base64: true, + checkCRC32: false + }) + var shareStrings = new Map() + convertedShareStrings = '' -exports.execute = function(config) { - var xlsx = new JSZip(templateXLSX, { - base64: true, - checkCRC32: false - }); - shareStrings = new SortedMap(); - convertedShareStrings = ""; - - var configs = []; - if (config instanceof Array) { - configs = config; - }else{ - configs.push(config); - } - generateMultiSheets(configs, xlsx); - generateWorkbook(configs, xlsx); - generateRel(configs,xlsx) ; - generateContentType(configs, xlsx); - generateSharedStringsFile(xlsx); - var results = xlsx.generate({ - base64: false, - compression: "DEFLATE" - }); - delete shareStrings; - delete xlsx; - return results; + var configs = [] + if (config instanceof Array) { + configs = config + } else { + configs.push(config) + } + generateMultiSheets(configs, xlsx, shareStrings) + generateWorkbook(configs, xlsx) + generateRel(configs, xlsx) + generateContentType(configs, xlsx) + generateSharedStringsFile(xlsx, shareStrings) + return xlsx.generate({ + base64: false, + compression: 'DEFLATE' + }) } diff --git a/package.json b/package.json index 6180b13..886e543 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { - "name": "excel-export", - "version": "0.5.1", - "description": "Simple data set export to Excel xlsx file", + "name": "js-excel", + "version": "1.0.0", + "description": "Data to excel in the browser and the server", "main": "index.js", "scripts": { "test": "mocha test/main" }, - "repository": "https://github.com/functionscope/Node-Excel-Export", + "repository": "https://github.com/furstenheim/js-excel", "keywords": [ "Excel", "xlsx" @@ -14,11 +14,11 @@ "author": "Ber-Lin Lai ", "license": "BSD", "dependencies": { - "collections": "^3.0.0", - "node-zip": "1.x" + "jszip": "2.5.0" }, "devDependencies": { "mocha": "", - "should": "" + "should": "", + "standard": "^8.6.0" } } diff --git a/sheet.js b/sheet.js index e63ffe7..fc84667 100644 --- a/sheet.js +++ b/sheet.js @@ -1,175 +1,165 @@ -var sheetFront = '' - + ' ' - + ' '; -var sheetBack =' ' - + ' '; - -var fs = require('fs'); +module.exports = Sheet -function Sheet(config, xlsx, shareStrings, convertedShareStrings){ - this.config = config; - this.xlsx = xlsx; - this.shareStrings = shareStrings; - this.convertedShareStrings = convertedShareStrings; -} - -Sheet.prototype.generate = function(){ - var config = this.config, xlsx = this.xlsx; - var cols = config.cols, - data = config.rows, - colsLength = cols.length, - rows = "", - row = "", - colsWidth = "", - styleIndex, - self = this, - k; - config.fileName = 'xl/worksheets/' + (config.name || "sheet").replace(/[*?\]\[\/\/]/g, '') + '.xml'; - if (config.stylesXmlFile) { - var path = config.stylesXmlFile; - var styles = null; - styles = fs.readFileSync(path, 'utf8'); - if (styles) { - xlsx.file("xl/styles.xml", styles); - } - } - - //first row for column caption - row = ''; - var colStyleIndex; - for (k = 0; k < colsLength; k++) { - colStyleIndex = cols[k].captionStyleIndex || 0; - row += addStringCell(self, getColumnLetter(k + 1) + 1, cols[k].caption, colStyleIndex); - if (cols[k].width) { - colsWidth += ''; - } - } - row += ''; - rows += row; +var sheetFront = '' + + ' ' + + ' ' +var sheetBack = ' ' + + ' ' - //fill in data - var i, j, r, cellData, currRow, cellType, dataLength = data.length; - - for (i = 0; i < dataLength; i++) { - r = data[i], - currRow = i + 2; - row = ''; - for (j = 0; j < colsLength; j++) { - styleIndex = null; - cellData = r[j]; - cellType = cols[j].type; - if (typeof cols[j].beforeCellWrite === 'function') { - var e = { - rowNum: currRow, - styleIndex: null, - cellType: cellType - }; - cellData = cols[j].beforeCellWrite(r, cellData, e); - styleIndex = e.styleIndex || styleIndex; - cellType = e.cellType; - delete e; - } - switch (cellType) { - case 'number': - row += addNumberCell(getColumnLetter(j + 1) + currRow, cellData, styleIndex); - break; - case 'date': - row += addDateCell(getColumnLetter(j + 1) + currRow, cellData, styleIndex); - break; - case 'bool': - row += addBoolCell(getColumnLetter(j + 1) + currRow, cellData, styleIndex); - break; - default: - row += addStringCell(self, getColumnLetter(j + 1) + currRow, cellData, styleIndex); - } - } - row += ''; - rows += row; - } - if (colsWidth !== "") { - sheetFront += '' + colsWidth + ''; - } - xlsx.file(config.fileName, sheetFront + '' + rows + '' + sheetBack); +function Sheet (config, xlsx, shareStrings, convertedShareStrings) { + this.config = config + this.xlsx = xlsx + this.shareStrings = shareStrings + this.convertedShareStrings = convertedShareStrings } -module.exports = Sheet; +Sheet.prototype.generate = function () { + var config = this.config, xlsx = this.xlsx + var cols = config.cols, + data = config.rows, + colsLength = cols.length, + rows = '', + row = '', + colsWidth = '', + styleIndex, + self = this, + k + config.fileName = 'xl/worksheets/' + (config.name || 'sheet').replace(/[*?\]\[\/\/]/g, '') + '.xml' + if (config.stylesXml) { + var styles = config.stylesXml + xlsx.file('xl/styles.xml', styles) + } -var startTag = function (obj, tagName, closed){ - var result = "<" + tagName, p; - for (p in obj){ - result += " " + p + "=" + obj[p]; + // first row for column caption + row = '' + var colStyleIndex + for (k = 0; k < colsLength; k++) { + colStyleIndex = cols[k].captionStyleIndex || 0 + row += addStringCell(self, getColumnLetter(k + 1) + 1, cols[k].caption, colStyleIndex) + if (cols[k].width) { + colsWidth += '' + } } - if (!closed) - result += ">"; - else - result += "/>"; - return result; -}; + row += '' + rows += row -var endTag = function(tagName){ - return ""; -}; + // fill in data + var i, j, r, cellData, currRow, cellType, el, dataLength = data.length -var addNumberCell = function(cellRef, value, styleIndex){ - styleIndex = styleIndex || 0; - if (value===null) - return ""; - else - return ''+value+''; -}; + for (i = 0; i < dataLength; i++) { + r = data[i], + currRow = i + 2 + row = '' + for (j = 0; j < colsLength; j++) { + styleIndex = null + cellData = r[j] + cellType = cols[j].type + if (typeof cols[j].beforeCellWrite === 'function') { + el = { + rowNum: currRow, + styleIndex: null, + cellType: cellType + } + cellData = cols[j].beforeCellWrite(r, cellData, el) + styleIndex = el.styleIndex || styleIndex + cellType = el.cellType + } + switch (cellType) { + case 'number': + row += addNumberCell(getColumnLetter(j + 1) + currRow, cellData, styleIndex) + break + case 'date': + row += addDateCell(getColumnLetter(j + 1) + currRow, cellData, styleIndex) + break + case 'bool': + row += addBoolCell(getColumnLetter(j + 1) + currRow, cellData, styleIndex) + break + default: + row += addStringCell(self, getColumnLetter(j + 1) + currRow, cellData, styleIndex) + } + } + row += '' + rows += row + } + if (colsWidth !== '') { + sheetFront += '' + colsWidth + '' + } + xlsx.file(config.fileName, sheetFront + '' + rows + '' + sheetBack) +} +var startTag = function (obj, tagName, closed) { + var result = '<' + tagName, p + for (p in obj) { + result += ' ' + p + '=' + obj[p] + } + if (!closed) { result += '>' } else { result += '/>' } + return result +} -var addDateCell = function(cellRef, value, styleIndex){ - styleIndex = styleIndex || 1; - if (value===null) - return ""; - else - return ''+value+''; -}; +var endTag = function (tagName) { + return '' +} -var addBoolCell = function(cellRef, value, styleIndex){ - styleIndex = styleIndex || 0; - if (value===null) - return ""; - if (value){ - value = 1; - } else - value = 0; - return ''+value+''; -}; +var addNumberCell = function (cellRef, value, styleIndex) { + styleIndex = styleIndex || 0 + if (value === null) { return '' } else { + return '' + value + '' + } +} +var addDateCell = function (cellRef, value, styleIndex) { + styleIndex = styleIndex || 1 + if (value === null) { + return '' + } else { + return '' + value + '' + } +} -var addStringCell = function(sheet, cellRef, value, styleIndex){ - styleIndex = styleIndex || 0; - if (value===null) - return ""; - if (typeof value ==='string'){ - value = value.replace(/&/g, "&").replace(/'/g, "'").replace(/>/g, ">").replace(/"; - } - return ''+i+''; -}; + return '' + value + '' +} +var addStringCell = function (sheet, cellRef, value, styleIndex) { + styleIndex = styleIndex || 0 + if (value === null) { return '' } + if (typeof value === 'string') { + value = value.replace(/&/g, '&').replace(/'/g, ''').replace(/>/g, '>').replace(/' + } + return '' + i + '' +} -var getColumnLetter = function(col){ - if (col <= 0) - throw "col must be more than 0"; - var array = new Array(); - while (col > 0) - { - var remainder = col % 26; - col /= 26; - col = Math.floor(col); - if(remainder ===0) - { - remainder = 26; - col--; - } - array.push(64 + remainder); +var getColumnLetter = function (col) { + if (col <= 0) { throw 'col must be more than 0' } + var array = [] + while (col > 0) { + var remainder = col % 26 + col /= 26 + col = Math.floor(col) + if (remainder === 0) { + remainder = 26 + col-- + } + array.push(64 + remainder) } - return String.fromCharCode.apply(null, array.reverse()); -}; + return String.fromCharCode.apply(null, array.reverse()) +} diff --git a/test/date-utils.js b/test/date-utils.js new file mode 100644 index 0000000..6bb3f17 --- /dev/null +++ b/test/date-utils.js @@ -0,0 +1,9 @@ +module.exports = {getJulian, oaDate} + +function getJulian (date) { + return Math.floor((date / 86400000) - (date.getTimezoneOffset() / 1440) + 2440587.5) +} + +function oaDate (date) { + return (date - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000) +} \ No newline at end of file diff --git a/test/main.js b/test/main.js index 1d0d528..2c186e0 100644 --- a/test/main.js +++ b/test/main.js @@ -1,65 +1,66 @@ // test/main.js -var should = require('should'); -var nodeExcel = require('../index'); +var should = require('should') +var nodeExcel = require('../index') +var dateUtils = require('./date-utils') -describe('Simple Excel xlsx Export', function() { - describe('Export', function() { - it('returns xlsx', function() { - var conf = {}; - conf.name = 'xxxxxx'; - conf.cols = [{ - caption: 'string', - type: 'string' - }, - { - caption: 'date', - type: 'date' - }, - { - caption: 'bool', - type: 'bool' - }, - { - caption: 'number 2', - type: 'number' - }]; - conf.rows = [['pi', (new Date(Date.UTC(2013, 4, 1))).oaDate(), true, 3.14], ["e", (new Date(2012, 4, 1)).oaDate(), false, 2.7182], ["M&M<>'", (new Date(Date.UTC(2013, 6, 9))).oaDate(), false, 1.2], ["null", null, null, null]]; +describe('Simple Excel xlsx Export', function () { + describe('Export', function () { + it('returns xlsx', function () { + var conf = {} + conf.name = 'xxxxxx' + conf.cols = [{ + caption: 'string', + type: 'string' + }, + { + caption: 'date', + type: 'date' + }, + { + caption: 'bool', + type: 'bool' + }, + { + caption: 'number 2', + type: 'number' + }] + conf.rows = [['pi', dateUtils.oaDate(new Date(Date.UTC(2013, 4, 1))), true, 3.14], ['e', dateUtils.oaDate(new Date(2012, 4, 1)), false, 2.7182], ["M&M<>'", dateUtils.oaDate(new Date(Date.UTC(2013, 6, 9))), false, 1.2], ['null', null, null, null]] - var result = nodeExcel.execute(conf); - //console.log(result); + var result = nodeExcel.execute(conf) + // console.log(result); - var fs = require('fs'); - fs.writeFileSync('single.xlsx', result, 'binary'); - }); - it('returns multisheet xlsx', function() { - var confs = []; - var conf = {}; - conf.cols = [{ - caption: 'string', - type: 'string' - }, - { - caption: 'date', - type: 'date' - }, - { - caption: 'bool', - type: 'bool' - }, - { - caption: 'number 2', - type: 'number' - }]; - conf.rows = [['hahai', (new Date(Date.UTC(2013, 4, 1))).oaDate(), true, 3.14], ["e", (new Date(2012, 4, 1)).oaDate(), false, 2.7182], ["M&M<>'", (new Date(Date.UTC(2013, 6, 9))).oaDate(), false, 1.2], ["null", null, null, null]]; - for (var i = 0; i < 3; i++) { - conf = JSON.parse(JSON.stringify(conf)); //clone - conf.name = 'sheet'+i; - confs.push(conf); - } - var result = nodeExcel.execute(confs), - fs = require('fs'); - fs.writeFileSync('multi.xlsx', result, 'binary'); - }) - }); -}); + var fs = require('fs') + fs.writeFileSync('single.xlsx', result, 'binary') + }) + it('returns multisheet xlsx', function () { + var confs = [] + var conf = {} + conf.cols = [{ + caption: 'string', + type: 'string' + }, + { + caption: 'date', + type: 'date' + }, + { + caption: 'bool', + type: 'bool' + }, + { + caption: 'number 2', + type: 'number' + }] + conf.rows = [['hahai', dateUtils.oaDate(new Date(Date.UTC(2013, 4, 1))), true, 3.14], ['e', dateUtils.oaDate(new Date(2012, 4, 1)), false, 2.7182], ["M&M<>'", dateUtils.oaDate(new Date(Date.UTC(2013, 6, 9))), false, 1.2], ['null', null, null, null]] + for (var i = 0; i < 3; i++) { + conf = JSON.parse(JSON.stringify(conf)) // clone + conf.name = 'sheet' + i + confs.push(conf) + } + var result = nodeExcel.execute(confs), + fs = require('fs') + fs.writeFileSync('multi.xlsx', result, 'binary') + }) + }) +})