New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

carbone

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

carbone - npm Package Compare versions

Comparing version 1.0.1 to 1.1.0

23

CHANGELOG.md

@@ -0,1 +1,24 @@

### v1.1.0
- Release February 26, 2018
- Fix: should find markers even if there is a opening bracket `{` before the markers
- Fix: accept to nest arrays in XML whereas these arrays are not nested in JSON
- Fix: markers were not parsed if formatters were used directly on `d` or `c` like this `{d:ifEmpty('yeah')}` ...
- Fix: keep the first element of the array if the custom iterator is constant
- Fix a lot of strange bugs when using a filter without iterators in arrays (ex. `{d.cities[i=0].temperature}`)
- Optimization: gain x10 when sorting 1 Million of rows
- Add formatter `unaccent` to remove accent from string
- `carbone.set` does not overwrite user-defined translations
- Accepts iteration on non-XML. Example: `{d[i].brand} , {d[i+1].brand}`
- Add new formatters
- `unaccent()` to remove accent from string
- `count()` to print a counter in loops. Usage: `{d[i].name:count()}`
- `convCRLF()` to convert text, which contains `\r\n` or `\n`, into "real" carriage return in odt or docx document
- Formatters which have the property `canInjectXML = true` can inject XML in documents
- Return an error in render callback when LibreOffice is not detected
- Get the last object of an array using negative values when filtering with `i` iterator
- `{d.cities[i=-1].temperature}` shows the temperature (if the array is not empty) of the last city
- `{d.cities[i=-2].temperature}` shows the temperature of the city before the last
- ...
### V1.0.1

@@ -2,0 +25,0 @@ - Release October 13, 2017

18

formatters/array.js

@@ -88,5 +88,21 @@

/**
* Count and print row (or column) number
*
* @param {String} d Array passed by carbone
* @param {Integer} loopId Loop ID generated by carbone
* @param {String} start Number to start with (default: 1)
* @return {String} Counter value
*/
function count (d, loopId, start) {
if (start === undefined) {
start = 1;
}
return '__COUNT_' + loopId + '_' + start + '__';
}
module.exports = {
arrayJoin : arrayJoin,
arrayMap : arrayMap
arrayMap : arrayMap,
count : count
};

@@ -0,1 +1,7 @@

const LINEBREAK = {
odt : '<text:line-break/>',
docx : '</w:t><w:br/><w:t>'
};
/**

@@ -123,2 +129,50 @@ * Lower case all letters

/**
* Removes accents from text
*
* @example [ "crème brulée" ]
* @example [ "CRÈME BRULÉE" ]
* @example [ "être" ]
* @example [ "éùïêèà" ]
*
* @param {String} d string to parse
* @return {String} string without accent
*/
function unaccent (d) {
if (typeof d === 'string') {
return d.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
return d;
}
/**
* Convert carriage return `\r\n` and line feed `\n` to XML-specific code in rendered document
*
* Compatible with odt, and docx (beta)
*
* @exampleContext { "extension" : "odt" }
* @example [ "my text \n contains Line Feed" , "my text <text:line-break/> contains Line Feed" ]
* @example [ "my text \r\n contains Carriage Return" , "my text <text:line-break/> contains Line Feed" ]
*
* @exampleContext { "extension" : "docx" }
* @example [ "my text \n contains Line Feed" , "my text </w:t><w:br/><w:t> contains Line Feed" ]
* @example [ "my text \r\n contains Carriage Return" , "my text </w:t><w:br/><w:t> contains Line Feed" ]
*
* @param {Integer|String} d
* @return {String} return "XML carriage return" for odt and docx
*/
function convCRLF (d) {
if (typeof d === 'string') {
var _lineBreak = LINEBREAK[this.extension];
if (_lineBreak) {
return d.replace(/\r?\n/g, _lineBreak);
}
}
return d;
}
// this formatter is separately to inject code
convCRLF.canInjectXML = true;
module.exports = {

@@ -130,3 +184,5 @@ lowerCase : lowerCase,

convEnum : convEnum,
convCRLF : convCRLF,
unaccent : unaccent,
print : print
};

140

lib/builder.js
var extracter = require('./extracter');
var parser = require('./parser');
var helper = require('./helper');
var timSort = require('timsort');

@@ -17,2 +18,3 @@ var builder = {

* 'existingVariables' : existing variables (Array returned by parser.findVariables())
* 'extension' : template file extension
* }

@@ -26,2 +28,3 @@ * @param {Function} callback

}
// find translation markers {t()} and translate

@@ -70,7 +73,9 @@ parser.translate(xml, options, function (err, xmlWithoutTranslation) {

* @param {Object} existingFormatters
* @param {Boolean} onlyFormatterWhichInjectXML : if undefined|false, it returns only formatters which do not inject XML
* if true, it returns only formatters which do inject XML. These formatters have
* the property canInjectXML = true
* @return {String}. Example 'toFixed(int(d.number), 2)'
*/
getFormatterString : function (varName, contextName, formatters, existingFormatters) {
getFormatterString : function (varName, contextName, formatters, existingFormatters, onlyFormatterWhichInjectXML) {
var _lineOfCodes = [];
var _ifClosingBraces = [];
for (var i = 0; i < formatters.length; i++) {

@@ -96,11 +101,13 @@ var _formatter = formatters[i];

}
if (existingFormatters && existingFormatters[_functionStr] === undefined) {
if (existingFormatters[_functionStr] === undefined) {
var _alternativeFnName = helper.findClosest(_functionStr, existingFormatters);
throw Error('Formatter "'+_functionStr+'" does not exist. Do you mean "'+_alternativeFnName+'"?');
}
_lineOfCodes.push(varName +' = formatters.' + _functionStr + '.call(' + contextName + ', ' + varName + _argumentStr + ');\n');
_ifClosingBraces.push('}');
if ( (existingFormatters[_functionStr].canInjectXML === true && onlyFormatterWhichInjectXML === true)
|| (existingFormatters[_functionStr].canInjectXML !== true && onlyFormatterWhichInjectXML !== true)) {
_lineOfCodes.push(varName +' = formatters.' + _functionStr + '.call(' + contextName + ', ' + varName + _argumentStr + ');\n');
}
}
var _str = _lineOfCodes.join('if('+contextName+'.stopPropagation === false){\n');
var _nbClosingBraces = formatters.length - 1;
var _nbClosingBraces = _lineOfCodes.length - 1;
while (_nbClosingBraces-- > 0) {

@@ -115,3 +122,2 @@ _str += '}';

*
* @param {string} objName : Example 'd.number'
* @param {array} conditions : array of conditions. Example [{'left':'sort', 'operator':'>', 'right':'10'}]

@@ -121,5 +127,6 @@ * @param {string} codeIfTrue : code to insert if the condition is true. Example 'row = true'

* @param {boolean} inverseCondition (optional, default false) : if true, it will inverse the condition
* @param {string} forceObjectTested (optional) : use forceObjectTested instead of _condition.left.parent
* @return {string}. Example : 'if(d.number['sort'] > 10) { row = true }'
*/
getFilterString : function (conditions, codeIfTrue, prefix, inverseCondition) {
getFilterString : function (conditions, codeIfTrue, prefix, inverseCondition, forceObjectTested) {
prefix = prefix || '';

@@ -138,2 +145,5 @@ if (!codeIfTrue || !(conditions instanceof Array) || conditions.length ===0) {

var _objName = _condition.left.parent;
if (forceObjectTested) {
_objName = forceObjectTested;
}
// if the left part is an object

@@ -144,9 +154,15 @@ var _objectSeparatorIndex = _attr.indexOf('.');

_attr = _attr.slice(_objectSeparatorIndex+1);
_objName = '_'+prefix+_obj+'_filter';
if (_alreadyDeclaredFilter[_objName] === undefined) {
_declareStr += 'var '+_objName+'='+_condition.left.parent+'["'+_obj+'"];\n';
var _subObjName = '_'+prefix+_obj+'_filter';
if (_alreadyDeclaredFilter[_subObjName] === undefined) {
_declareStr += 'var '+_subObjName+'='+_objName+'["'+_obj+'"];\n';
}
_objName = _subObjName;
}
if (_attr === 'i') {
_str += _objName + '_i '+_condition.operator+_condition.right;
var _rightOperator = _condition.right;
var _arrayName = _condition.left.parent;
if (parseInt(_condition.right, 10) < 0) {
_rightOperator = _arrayName + '_array_length ' + _condition.right;
}
_str += _arrayName + '_i '+_condition.operator + _rightOperator;
}

@@ -180,7 +196,9 @@ else {

var _res = '';
builder.sortXmlParts(arrayOfStringPart, sortDepth);
builder.sortXmlParts(arrayOfStringPart);
var _rowInfo = [];
var _prevDepth = 0;
var _prevPos = [];
var _prevPart = {pos : []};
var _arrayLevel = 0;
var _loopIds = {};
// TODO MANAGE sortDepth

@@ -197,4 +215,7 @@ sortDepth = (sortDepth) ? sortDepth : 1;

var _part = arrayOfStringPart[i];
if (isArrayEqual(_prevPos, _part.pos)===false) {
_prevPos = _part.pos;
// Get count value if needed
builder.getLoopIteration(_loopIds, _part);
// keep parts if positions are not the same as the last one OR if the it the beginning of an array
if ((_prevPart.rowStart === true && _part.rowStart !== true) || isArrayEqual(_prevPart.pos, _part.pos) === false) {
_prevPart = _part;
}

@@ -215,3 +236,3 @@ else {

// TODO. Protection is sortDepth is too short
// TODO. Protection if sortDepth is too short
if (_rowInfo[_arrayLevel] === undefined) {

@@ -239,2 +260,27 @@ _rowInfo[_arrayLevel] = {

/**
* Replace __COUNT_*_*__ by the right value (part.str)
* INTERNAL USE ONLY (USED ONLY BY assembleXmlParts)
*
* @param {Object} loopIds Stored loop IDs with their start value
* @param {Object} part Xml part
*/
getLoopIteration : function (loopIds, part) {
var _rowColumnRegex = /__COUNT_(\d*?)_(\d*?)__/;
var _rowColumnMatch = _rowColumnRegex.exec(part.str);
// If we match a COUNT marker
if (_rowColumnMatch !== null && part.rowShow === true) {
var _loopId = _rowColumnMatch[1];
var _loopStart = _rowColumnMatch[2];
// Define loop ID if does not exist
if (loopIds[_loopId] === undefined) {
loopIds[_loopId] = _loopStart;
}
// And replace by the good value
part.str = part.str.replace(_rowColumnMatch[0], loopIds[_loopId]++);
}
},
/**
* Sort the special array which is generated by the function returned by getBuilderFunction.

@@ -244,13 +290,11 @@ * INTERNAL USE ONLY (USED ONLY BY assembleXmlParts)

* @param {array} arrayToSort : array of objects
* @param {integer} sortDepth
*/
sortXmlParts : function (arrayToSort, sortDepth) {
if (sortDepth === undefined) {
sortDepth = 1;
}
arrayToSort.sort(function (a, b) {
sortXmlParts : function (arrayToSort) {
timSort.sort(arrayToSort, function (a, b) {
var i = 0;
var _a = a.pos[i];
var _b = b.pos[i];
while (_a === _b && !(_a === undefined && _b === undefined) && i < sortDepth) {
var _aLength = a.pos.length;
var _bLength = b.pos.length;
while (_a === _b && i < _aLength && i < _bLength) {
i++;

@@ -260,17 +304,9 @@ _a = a.pos[i];

}
if (_a === undefined && _b === undefined && a.rowShow === true && b.rowShow === false) {
return -1;
if (_a !== undefined && _b !== undefined) {
return (_a > _b) ? 1 : -1;
}
else if (_a === undefined && _b === undefined && a.rowShow === false && b.rowShow === true) {
return 1;
if (_a === undefined && _b === undefined && a.rowShow !== b.rowShow) {
return (a.rowShow === false && b.rowShow === true) ? 1 : -1;
}
else if (_a === undefined) {
return -1;
}
else if (_b === undefined) {
return 1;
}
else {
return (_a > _b) ? 1 : -1;
}
return _aLength - _bLength;
});

@@ -300,2 +336,6 @@ },

var _lastArrayName = currentlyVisitedArrays[currentlyVisitedArrays.length-1];
// do not close current array loop if the next array is nested in the former
if (objDependencyDescriptor[nextAttrName].type === 'array' && objDependencyDescriptor[nextAttrName].depth > objDependencyDescriptor[_lastArrayName].depth) {
return;
}
while (_firstParentOfTypeArray !== _lastArrayName && currentlyVisitedArrays.length>0) {

@@ -339,3 +379,3 @@ execute(_lastArrayName);

_code.add('init', 'var _xmlPos = [0];\n');
_code.add('init', 'var _formatterOptions = { lang : options.lang, stopPropagation : false, enum : options.enum };\n');
_code.add('init', 'var _formatterOptions = { lang : options.lang, stopPropagation : false, enum : options.enum, extension : options.extension };\n');
_code.add('init', 'var formatters = options.formatters;\n'); // used by getFormatterString

@@ -408,2 +448,15 @@

}
else if (_type === 'objectInArray') {
var _arrayOfObjectName = _objName+'_array';
var _arrayOfObjectIndexName = _objName+'_i';
_code.add('prev','main', 'var ' + _arrayOfObjectName+'='+'('+_objParentName+' !== undefined)? '+_objParentName+'[\''+_realObjName+'\']:[];\n');
var _conditionToFindObject = _dynamicData[_objName].conditions || [];
var _objNameTemp = _objName+'_temp';
_code.add('prev', 'main', 'var '+_arrayOfObjectName+'_length = ('+_arrayOfObjectName+' instanceof Array)?'+_arrayOfObjectName+'.length : 0;\n');
_code.add('prev', 'main', 'for (var '+_arrayOfObjectIndexName+' = 0; '+_arrayOfObjectIndexName+' < '+_arrayOfObjectName+'_length ; '+_arrayOfObjectIndexName+'++) {\n');
_code.add('prev', 'main', ' var '+_objNameTemp+' = '+_arrayOfObjectName+'['+_arrayOfObjectIndexName+'];\n');
_code.add('prev', 'main', that.getFilterString(_conditionToFindObject, _objName+'='+_arrayOfObjectName+'['+_arrayOfObjectIndexName+'];\n break;\n', _objNameTemp, false, _objNameTemp));
_code.add('prev', 'main', '}\n');
}
// Array type

@@ -451,3 +504,3 @@ else if (_type === 'array') {

if (_containSpecialIterator===true) {
_code.add( 'main', ' | '+_missingIteratorArrayName+'.length>0');
_code.add( 'main', ' || '+_missingIteratorArrayName+'.length>0');
}

@@ -538,3 +591,3 @@ _code.add('prev','main', ' ; '+_arrayIndexName+'++) {\n');

_code.add('main', '_formatterOptions.stopPropagation = false;\n');
_code.add('main', that.getFormatterString('_str', '_formatterOptions', _formatters, existingFormatters));
_code.add('main', that.getFormatterString('_str', '_formatterOptions', _formatters, existingFormatters, false));
// replace null or undefined value by an empty string

@@ -548,2 +601,5 @@ _code.add('main', 'if(_str === null || _str === undefined) {\n');

_code.add('main', '};\n');
// insert formatters which can inject XML, so after .replace(/</g, '&lt;') ... etc
_code.add('main', that.getFormatterString('_str', '_formatterOptions', _formatters, existingFormatters, true));
_code.add('main', "_strPart.str += (_strPart.rowShow !== false)?_str:''"+';\n');

@@ -559,3 +615,3 @@ _code.add('main', '}\n');

_code.add('main', '_strParts.push(_strPart);\n');
// when no iterator only push when the part position is new or rowshow is true
// when no iterator only push when the part position is new or rowshow is true
}

@@ -621,3 +677,3 @@ else {

}
for (var i = 0; i < _arrALength; i++) {
for (var i = _arrALength - 1; i >= 0; i--) {
if (arrA[i] !== arrB[i]) {

@@ -624,0 +680,0 @@ return false;

@@ -19,2 +19,3 @@ var path = require('path');

var isLibreOfficeFound = false;

@@ -60,5 +61,5 @@ var converterOptions = {

}
// restart Factory automatically if it crashes.
// restart Factory automatically if it crashes.
isAutoRestartActive = true;
// if we must start all factory now

@@ -82,3 +83,3 @@ if (params.startFactory === true) {

else {
// else, start LibreOffice when needed
// else, start LibreOffice when needed
if (callback) {

@@ -131,3 +132,3 @@ callback();

/**
* Convert a document
* Convert a document
*

@@ -137,5 +138,10 @@ * @param {string} inputFile : absolute path to the source document

* @param {string} formatOptions : options passed to convert
* @param {function} callback : function(buffer) result
* @param {function} callback : function(buffer) result
*/
convertFile : function (inputFile, outputType, formatOptions, callback, returnLink, outputFile) {
if (isLibreOfficeFound === false) {
return callback('Cannot find LibreOffice. Document conversion cannot be used');
}
var _output = helper.getUID();

@@ -188,3 +194,3 @@ var _job = {

var _uniqueName = helper.getUID();
// generate a unique path to a fake user profile. We cannot start multiple instances of LibreOffice if it uses the same user cache

@@ -245,3 +251,3 @@ var _userCachePath = path.join(params.tempPath, '_office_' + _uniqueName);

return function (error) {
var _processName = '';
var _processName = '';
var _otherThreadToKill = null;

@@ -272,3 +278,3 @@

debug('process '+_processName+' of factory '+factoryID+' died ' + error);
// if both processes Python and Office are off...

@@ -282,3 +288,3 @@ if (_factory.pythonThread === null && _factory.officeThread === null) {

else {
_otherThreadToKill.kill('SIGKILL');
_otherThreadToKill.kill('SIGKILL');
}

@@ -290,3 +296,3 @@ };

/**
* Manage factory restart ot shutdown when a factory is completly off
* Manage factory restart ot shutdown when a factory is completly off
* @param {Object} factory factory description

@@ -297,3 +303,3 @@ */

if (isAutoRestartActive === true) {
// if there is an error while converting a document, let's try another time
// if there is an error while converting a document, let's try another time
var _job = factory.currentJob;

@@ -315,4 +321,4 @@ if (_job) {

// else if Carbone is shutting down and there is an exitCallback
else {
// delete office files synchronously (we do not care because Carbone is shutting down) when office is dead
else {
// delete office files synchronously (we do not care because Carbone is shutting down) when office is dead
helper.rmDirRecursive(factory.userCachePath);

@@ -433,12 +439,9 @@ if (factory.exitCallback) {

if (fs.existsSync('/Applications/LibreOffice.app/Contents/' + _path + '/python') === true) {
isLibreOfficeFound = true;
converterOptions.pythonExecPath = '/Applications/LibreOffice.app/Contents/' + _path + '/python';
converterOptions.sofficeExecPath = '/Applications/LibreOffice.app/Contents/MacOS/soffice';
}
else {
debug('cannot find LibreOffice');
}
}
else if (process.platform === 'linux') {
var directories = fs.readdirSync('/opt');
var isLibreOfficeFound = false;
var patternLibreOffice = /libreoffice\d+\.\d+/;

@@ -455,5 +458,2 @@ for (var i = 0; i < directories.length; i++) {

}
if (isLibreOfficeFound === false) {
debug('cannot find LibreOffice. Document conversion cannot be used');
}
}

@@ -463,2 +463,6 @@ else {

}
if (isLibreOfficeFound === false) {
debug('cannot find LibreOffice. Document conversion cannot be used');
}
}

@@ -473,2 +477,2 @@

module.exports = converter;
module.exports = converter;
var parser = require('./parser');
var helper = require('./helper');

@@ -34,2 +35,4 @@ var extracter = {

var _conditions = [];
var _isIteratorUsedInCurrentArray = false;
var _conditionsToFindObjectInArray = [];
// Loop on each char.

@@ -71,2 +74,3 @@ // We do not use regular expressions because it is a lot easier like that.

else if (_char==='[') {
_isIteratorUsedInCurrentArray = false;
_isArray = true;

@@ -143,2 +147,5 @@ _uniqueMarkerName += _word;

}
else if (_word.length > 1 && (_char===',' || _char===']')) {
_isIteratorUsedInCurrentArray = true; // +1 or not
}
// detect array iteration

@@ -156,3 +163,2 @@ if (_word.substr(-2)==='+1') {

}
// if we exit the bracket of an array

@@ -182,2 +188,23 @@ if (_char===']' && _isArray===true) {

}
if (_isIteratorUsedInCurrentArray === false) {
var _filterUniqueName = '';
var h = _conditions.length - 1;
for (; h >= 0 ; h--) {
var _conditionToFindObject = _conditions[h];
_filterUniqueName += helper.cleanJavascriptVariable(_conditionToFindObject.left.attr + '__' + _conditionToFindObject.right);
// consider only conditions of this array, thus only last rows which match with unique array name
if (_conditionToFindObject.left.parent !== _uniqueMarkerName ) {
break;
}
}
// extract conditions to find this object
_conditionsToFindObjectInArray = _conditions.slice(h + 1, _conditions.length);
_conditions = _conditions.slice(0, h + 1);
// generate a new object
_uniqueMarkerName += _filterUniqueName;
// correction of previously detected conditions
for (var m = 0; m < _conditionsToFindObjectInArray.length ; m++) {
_conditionsToFindObjectInArray[m].left.parent += _filterUniqueName;
}
}
// create the new array

@@ -187,6 +214,6 @@ if (!_res[_uniqueMarkerName]) {

name : _arrayName,
type : 'array',
type : (_isIteratorUsedInCurrentArray === true) ? 'array' : 'objectInArray',
parent : _prevMarker,
parents : _parents.slice(0),
position : {start : _markerPos},
position : {},
iterators : [],

@@ -196,2 +223,9 @@ xmlParts : []

}
if (_isIteratorUsedInCurrentArray === true && _res[_uniqueMarkerName].position.start === undefined) {
_res[_uniqueMarkerName].position.start = _markerPos;
}
if (_isIteratorUsedInCurrentArray === false) {
_res[_uniqueMarkerName].conditions = _conditionsToFindObjectInArray;
_conditionsToFindObjectInArray = [];
}
_prevMarker = _uniqueMarkerName;

@@ -207,5 +241,5 @@ }

var _iteratorStr = _iterator.str;
// if the iterator is in a sub-object. Ex: movie.sort+1
if (_prevIteratorStr !== _iteratorStr) {
var _iteratorInfo = {};
// if the iterator is in a sub-object. Ex: movie.sort+1
if (/\./.test(_iteratorStr)===true) {

@@ -258,3 +292,3 @@ var _splitIterator = _iteratorStr.split('.');

};
if (_conditions.length>0) {
if (_conditions.length > 0) {
_xmlPart.conditions = _conditions;

@@ -327,2 +361,3 @@ }

// And sort them by ascendant positions

@@ -367,2 +402,6 @@ extracter.sortXmlParts(_allSortedParts);

var _partParents = descriptor[_part.obj].parents;
// if the part belongs to the current visited array, keep it in this array.
if (_part.pos <= _arrayPos.end && (_part.depth === _arrayDepth && _lastVisitedArrayName === _part.obj || _part.array === 'start' || _part.array === 'end' )) {
break;
}
if (_part.pos > _arrayPos.start && _part.pos < _arrayPos.end && _partParents.indexOf(_lastVisitedArrayName) === -1 && _lastVisitedArrayName !== _part.obj) {

@@ -565,3 +604,3 @@ _part.moveTo = _lastVisitedArrayName;

parents : [], // TODO use _obj.parents
hasOnlyObjects : (_obj.type === 'object') ? true : false,
hasOnlyObjects : (_obj.type !== 'array') ? true : false,
branchName : ''

@@ -590,3 +629,3 @@ };

var _objParent = descriptor.dynamicData[_objParentName];
if (_objParent.type !== 'object') {
if (_objParent.type === 'array') {
_obj.hasOnlyObjects = false;

@@ -634,6 +673,6 @@ }

// if the depth is the same, put objects before arrays
if (a.type === 'object' && b.type === 'array') {
if (a.type !== 'array' && b.type === 'array') {
return -1;
}
else if (a.type === 'array' && b.type === 'object') {
else if (a.type === 'array' && b.type !== 'array') {
return 1;

@@ -640,0 +679,0 @@ }

@@ -67,2 +67,6 @@ var fs = require('fs');

cleanJavascriptVariable : function(attributeName) {
return attributeName.replace(/[^a-zA-Z0-9$_]/g, '_');
},
/**

@@ -69,0 +73,0 @@ * Remove a directory and all its content

@@ -45,3 +45,5 @@ var fs = require('fs');

}
translator.loadTranslations(params.templatePath);
if (options.translations === undefined) {
translator.loadTranslations(params.templatePath);
}
}

@@ -154,2 +156,3 @@ if (options.tempPath !== undefined && fs.existsSync(params.tempPath) === false) {

// open the template (unzip if necessary)
options.extension = path.extname(templatePath).toLowerCase().slice(1);
file.openTemplate(templatePath, function (err, template) {

@@ -159,3 +162,3 @@ if (err) {

}
if (path.extname(templatePath).toLowerCase().slice(1) === options.convertTo) {
if (options.extension === options.convertTo) {
options.convertTo = null; // avoid calling LibreOffice if the output file type is the same as the input file type

@@ -238,2 +241,3 @@ }

convertTo : options.convertTo,
extension : options.extension,
formatters : carbone.formatters,

@@ -240,0 +244,0 @@ existingVariables : variables

@@ -20,3 +20,3 @@ var helper = require('./helper');

// "?:" avoiding capturing
var _cleanedXml = xml.replace(/(\r\n|\n|\r)/g,' ').replace(/\{([\s\S]+?)\}/g, function (m, text, offset) {
var _cleanedXml = xml.replace(/(\r\n|\n|\r)/g,' ').replace(/\{([^{]+?)\}/g, function (m, text, offset) {
// clean the marker

@@ -27,3 +27,3 @@ var _marker = that.extractMarker(text);

var _cleanedMarker = that.removeWhitespace(that.cleanMarker(_marker));
if (/^(?:d\.|d\[|c\.|c\[|\$)/.test(_cleanedMarker) === false) {
if (/^(?:d[\.\[:]|c[\.\[:]|\$)/.test(_cleanedMarker) === false) {
return m;

@@ -287,2 +287,3 @@ }

}
parser.assignLoopId(markers);
callback(null, markers);

@@ -292,2 +293,39 @@ },

/**
* Assign loop IDs for count formatter
*
* @param {Array} markers Array of markers
* @return {Array} Array of markers with loop ID in count parameters
*/
assignLoopId : function (markers) {
var _loopWithRowNumberRegex = /.*?:count(\((.*?)\))?/g;
var match;
for (var _key in markers) {
var _marker = markers[_key];
// If the marker has a count formatter
if (match = _loopWithRowNumberRegex.exec(_marker.name)) {
var _parameters = '()';
var _before = '(';
var _after = ', ' + match[2] + ')';
var _loopId = _key + _marker.pos;
// If parameters are given, we store it
if (match[1]) {
_parameters = match[1];
}
// If no parameters are given, _after is set to )
if (match[2] === undefined || match[2] === '') {
_after = ')';
}
// Now we can concatenate _before, _loopId and _after
// _after are the given parameters
_parameters = _parameters.replace(/\(.*?\)/, _before + _loopId + _after);
// And we can replace _marker.name by the new one
_marker.name = _marker.name.replace(/count(\(.*?\))?/, 'count' + _parameters);
}
}
},
/**
* Find position of the opening tag which matches with the last tag of the xml string

@@ -325,3 +363,3 @@ *

if (_openingTagIndex[_tagCount] === undefined) {
return -1;
return 0;
}

@@ -332,3 +370,3 @@ else if (_openingTagIndex[_tagCount] < indexWhereToStopSearch) {

else {
return (_lastOpeningTagIndexBeforeStop[_tagCount]!==undefined)? _lastOpeningTagIndexBeforeStop[_tagCount] : -1 ;
return (_lastOpeningTagIndexBeforeStop[_tagCount]!==undefined)? _lastOpeningTagIndexBeforeStop[_tagCount] : 0 ;
}

@@ -393,5 +431,13 @@ },

var _highestTagCount = 0;
var _hasAChanceToContainAtLeastOnePivot = false;
// capture all tags
var _tagRegex = new RegExp('<(\/)?([^\/|^>| ]*)[^\/|^>]*(\/)?>','g');
var _tag = _tagRegex.exec(partialXml);
if (_tag === null) {
// when there is no XML, return the end of the string as the pivot
return {
part1End : {tag : '', pos : partialXml.length, selfClosing : true},
part2Start : {tag : '', pos : partialXml.length, selfClosing : true}
};
}
while (_tag !== null) {

@@ -411,2 +457,5 @@ var _tagType = '';

}
if (_tagCount > 0) {
_hasAChanceToContainAtLeastOnePivot = true;
}
if (_tagCount > _highestTagCount) {

@@ -432,3 +481,3 @@ _highestTagCount = _tagCount;

}
if (_highestTags.length===0 || (_highestTags.length===1 && _highestTags[0].type!=='x') || _highestTags[0].type==='<') {
if ( _tagCount !== 0 && (_highestTags.length===0 || (_highestTags.length===1 && _highestTags[0].type!=='x') || _highestTags[0].type==='<')) {
return null;

@@ -452,2 +501,10 @@ }

}
// TODO, this code could be simplified and generalized when there is no XML
// if it contains a flat XML structure, considers it is a selfClosing tag
if ( _tagCount === 0 && _hasAChanceToContainAtLeastOnePivot === false) {
_pivot = {
part1End : {tag : _lastTag.tag, pos : _lastTag.posEnd, selfClosing : true},
part2Start : {tag : _lastTag.tag, pos : _lastTag.posEnd, selfClosing : true}
};
}
return _pivot;

@@ -454,0 +511,0 @@ },

@@ -33,3 +33,3 @@ var path = require('path');

},
/**

@@ -36,0 +36,0 @@ * [XLSX] Convert shared string to inline string in Excel format in order to be compatible with Carbone algorithm

{
"name": "carbone",
"description": "Fast, Simple and Powerful report generator. Injects JSON and produces PDF, DOCX, XLSX, ODT, PPTX, ODS, ...!",
"version": "1.0.1",
"version": "1.1.0",
"bin": "bin/carbone",

@@ -24,2 +24,3 @@ "main": "./lib",

"moxie-zip": "=0.0.3",
"timsort": "=0.3.0",
"yauzl": "=2.8.0"

@@ -26,0 +27,0 @@ },

@@ -17,2 +17,3 @@ Carbone.io

- [Minimum Requirements](#minimum-requirements)
- [Optional](#optional)
- [Getting started](#getting-started)

@@ -42,3 +43,3 @@ - [Basic sample](#basic-sample)

- **Unlimited design** : The limit is your document editor: pagination, headers, footers, tables...
- **Convert documents** : thanks to the integrated document converter (LibreOffice must be installed)
- **Convert documents** : thanks to the integrated document converter
- **Unique template engine** : Insert JSON-like markers `{d.companyName}` directly in your document

@@ -66,6 +67,9 @@ - **Flexible** : Use any XML documents as a template: docx, odt, ods, xlsx, html, pptx, odp, custom xml files...

- NodeJS 4.x+
- LibreOffice, if you want to use the document converter and generate PDF
- Runs on OSX, Linux (servers and desktop), and coming soon on Windows
#### Optional
- LibreOffice server if you want to use the document converter and generate PDF. Without LibreOffice, you can still generate docx, xlsx, pptx, odt, ods, odp, html as long as your template is in the same format.
## Getting started

@@ -442,2 +446,6 @@

- Fabien Bigant
- Maxime Magne
- Vincent Bertin
- Léo Labruyère
- Aurélien Kermabon

@@ -444,0 +452,0 @@ Thanks to all French citizens (Crédit Impôt Recherche, Jeune Entreprise Innovante, BPI)!

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc