diff --git a/src/core/theme.js b/src/core/theme.js index 9ade483..b971f39 100644 --- a/src/core/theme.js +++ b/src/core/theme.js @@ -10,10 +10,11 @@ Abstract theme representation. , validator = require('is-my-json-valid') , _ = require('underscore') , PATH = require('path') + , EXTEND = require('../utils/extend') , moment = require('moment'); /** - The Theme class represents a specific presentation of a resume. + The Theme class is a representation of a FluentCV theme asset. @class Theme */ function Theme() { @@ -31,8 +32,12 @@ Abstract theme representation. return friendly[val] || val; } - // Remember the theme folder; might be custom + // Open the theme.json file; should have the same name as folder this.folder = themeFolder; + var pathInfo = PATH.parse( themeFolder ); + var themeFile = PATH.join( themeFolder, pathInfo.base + '.json' ); + var themeInfo = JSON.parse( FS.readFileSync( themeFile, 'utf8' ) ); + EXTEND( true, this, themeInfo ); // Iterate over all files in the theme folder, producing an array, fmts, // containing info for each file. diff --git a/src/eng/handlebars-generator.js b/src/eng/handlebars-generator.js new file mode 100644 index 0000000..807c891 --- /dev/null +++ b/src/eng/handlebars-generator.js @@ -0,0 +1,18 @@ +/** +Handlebars template generate for FluentCV. +@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk. +*/ + +(function() { + + var _ = require('underscore'); + var HANDLEBARS = require('handlebars'); + + module.exports = function( json, jst, format, cssInfo, opts ) { + + var template = HANDLEBARS.compile(jst); + return template( { r: json, filt: opts.filters, cssInfo: cssInfo, headFragment: opts.headFragment || '' } ) + + }; + +}()); diff --git a/src/eng/underscore-generator.js b/src/eng/underscore-generator.js new file mode 100644 index 0000000..f744153 --- /dev/null +++ b/src/eng/underscore-generator.js @@ -0,0 +1,37 @@ +/** +Underscore template generate for FluentCV. +@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk. +*/ + +(function() { + + var _ = require('underscore'); + + module.exports = function( json, jst, format, cssInfo, opts ) { + + // Tweak underscore's default template delimeters + _.templateSettings = opts.template; + + // Convert {{ someVar }} to {% print(filt.out(someVar) %} + // Convert {{ someVar|someFilter }} to {% print(filt.someFilter(someVar) %} + jst = jst.replace( _.templateSettings.interpolate, function replace(m, p1) { + if( p1.indexOf('|') > -1 ) { + var terms = p1.split('|'); + return '{% print( filt.' + terms[1] + '( ' + terms[0] + ' )) %}'; + } + else { + return '{% print( filt.out(' + p1 + ') ) %}'; + } + }); + + // Strip {# comments #} + jst = jst.replace( _.templateSettings.comment, ''); + + // Compile and run the template. TODO: avoid unnecessary recompiles. + jst = _.template(jst)({ r: json, filt: opts.filters, cssInfo: cssInfo, headFragment: opts.headFragment || '' }); + + return jst; + + }; + +}()); diff --git a/src/gen/template-generator.js b/src/gen/template-generator.js index 121402d..609b624 100644 --- a/src/gen/template-generator.js +++ b/src/gen/template-generator.js @@ -1,176 +1,164 @@ /** Template-based resume generator base for FluentCV. -@license Copyright (c) 2015 | James M. Devlin +@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk. */ -var FS = require( 'fs' ) - , _ = require( 'underscore' ) - , MD = require( 'marked' ) - , XML = require( 'xml-escape' ) - , PATH = require('path') - , BaseGenerator = require( './base-generator' ) - , EXTEND = require('../utils/extend') - , Theme = require('../core/theme'); +(function() { -// Default options. -var _defaultOpts = { - keepBreaks: true, - freezeBreaks: true, - nSym: '&newl;', // newline entity - rSym: '&retn;', // return entity - template: { - interpolate: /\{\{(.+?)\}\}/g, - escape: /\{\{\=(.+?)\}\}/g, - evaluate: /\{\%(.+?)\%\}/g, - comment: /\{\#(.+?)\#\}/g - }, - filters: { - out: function( txt ) { return txt; }, - raw: function( txt ) { return txt; }, - xml: function( txt ) { return XML(txt); }, - md: function( txt ) { return MD( txt || '' ); }, - mdin: function( txt ) { return MD(txt || '' ).replace(/^\s*\|\<\/p\>\s*$/gi, ''); }, - lower: function( txt ) { return txt.toLowerCase(); }, - link: function( name, url ) { return url ? - '' + name + '' : name } - }, - prettify: { // ← See https://github.com/beautify-web/js-beautify#options - indent_size: 2, - unformatted: ['em','strong','a'], - max_char: 80, // ← See lib/html.js in above-linked repo - //wrap_line_length: 120, <-- Don't use this - } -}; + var FS = require( 'fs' ) + , _ = require( 'underscore' ) + , MD = require( 'marked' ) + , XML = require( 'xml-escape' ) + , PATH = require('path') + , BaseGenerator = require( './base-generator' ) + , EXTEND = require('../utils/extend') + , Theme = require('../core/theme'); -/** -TemplateGenerator performs resume generation via Underscore-style template -expansion and is appropriate for text-based formats like HTML, plain text, -and XML versions of Microsoft Word, Excel, and OpenOffice. -*/ -var TemplateGenerator = module.exports = BaseGenerator.extend({ - - /** outputFormat: html, txt, pdf, doc - templateFormat: html or txt - **/ - init: function( outputFormat, templateFormat, cssFile ){ - this._super( outputFormat ); - this.tplFormat = templateFormat || outputFormat; - }, - - /** Default generation method for template-based generators. */ - invoke: function( rez, themeMarkup, cssInfo, opts ) { - - // Compile and invoke the template! - this.opts = EXTEND( true, {}, _defaultOpts, opts ); - mk = this.single( rez, themeMarkup, this.format, cssInfo, { } ); - this.onBeforeSave && (mk = this.onBeforeSave( mk, themeFile, f )); - return mk; - - }, - - /** Default generation method for template-based generators. */ - generate: function( rez, f, opts ) { - - // Carry over options - this.opts = EXTEND( true, { }, _defaultOpts, opts ); - - // Verify the specified theme name/path - var tFolder = PATH.join( - PATH.parse( require.resolve('fluent-themes') ).dir, - this.opts.theme - ); - var exists = require('../utils/file-exists'); - if (!exists( tFolder )) { - tFolder = PATH.resolve( this.opts.theme ); - if (!exists( tFolder )) { - throw { fluenterror: this.codes.themeNotFound, data: this.opts.theme }; - } + // Default options. + var _defaultOpts = { + engine: 'underscore', + keepBreaks: true, + freezeBreaks: true, + nSym: '&newl;', // newline entity + rSym: '&retn;', // return entity + template: { + interpolate: /\{\{(.+?)\}\}/g, + escape: /\{\{\=(.+?)\}\}/g, + evaluate: /\{\%(.+?)\%\}/g, + comment: /\{\#(.+?)\#\}/g + }, + filters: { + out: function( txt ) { return txt; }, + raw: function( txt ) { return txt; }, + xml: function( txt ) { return XML(txt); }, + md: function( txt ) { return MD( txt || '' ); }, + mdin: function( txt ) { return MD(txt || '' ).replace(/^\s*\|\<\/p\>\s*$/gi, ''); }, + lower: function( txt ) { return txt.toLowerCase(); }, + link: function( name, url ) { return url ? + '' + name + '' : name } + }, + prettify: { // ← See https://github.com/beautify-web/js-beautify#options + indent_size: 2, + unformatted: ['em','strong','a'], + max_char: 80, // ← See lib/html.js in above-linked repo + //wrap_line_length: 120, <-- Don't use this } - - // Load the theme - var theme = opts.themeObj || new Theme().open( tFolder ); - - // Load theme and CSS data - var tplFolder = PATH.join( tFolder, 'templates' ); - var curFmt = theme.getFormat( this.format ); - var cssInfo = { file: curFmt.css ? curFmt.cssPath : null, data: curFmt.css || null }; - - // Compile and invoke the template! - var mk = this.single( rez, curFmt.data, this.format, cssInfo, opts ); - this.onBeforeSave && (mk = this.onBeforeSave( { mk: mk, theme: theme, outputFile: f } )); - FS.writeFileSync( f, mk, { encoding: 'utf8', flags: 'w' } ); - - }, + }; /** - Perform a single resume JSON-to-DEST resume transformation. Exists as a - separate function in order to expose string-based transformations to clients - who don't have access to filesystem resources (in-browser, etc.). + TemplateGenerator performs resume generation via local Handlebar or Underscore + style template expansion and is appropriate for text-based formats like HTML, + plain text, and XML versions of Microsoft Word, Excel, and OpenOffice. */ - single: function( json, jst, format, cssInfo, opts ) { + var TemplateGenerator = module.exports = BaseGenerator.extend({ - // Freeze whitespace in the template. - this.opts.freezeBreaks && ( jst = freeze(jst) ); + /** outputFormat: html, txt, pdf, doc + templateFormat: html or txt + **/ + init: function( outputFormat, templateFormat, cssFile ){ + this._super( outputFormat ); + this.tplFormat = templateFormat || outputFormat; + }, - // Tweak underscore's default template delimeters - _.templateSettings = this.opts.template; + /** Default generation method for template-based generators. */ + invoke: function( rez, themeMarkup, cssInfo, opts ) { - // Convert {{ someVar }} to {% print(filt.out(someVar) %} - // Convert {{ someVar|someFilter }} to {% print(filt.someFilter(someVar) %} - jst = jst.replace( _.templateSettings.interpolate, function replace(m, p1) { - if( p1.indexOf('|') > -1 ) { - var terms = p1.split('|'); - return '{% print( filt.' + terms[1] + '( ' + terms[0] + ' )) %}'; + // Compile and invoke the template! + this.opts = EXTEND( true, {}, _defaultOpts, opts ); + mk = this.single( rez, themeMarkup, this.format, cssInfo, { } ); + this.onBeforeSave && (mk = this.onBeforeSave( mk, themeFile, f )); + return mk; + + }, + + /** Default generation method for template-based generators. */ + generate: function( rez, f, opts ) { + + // Carry over options + this.opts = EXTEND( true, { }, _defaultOpts, opts ); + + // Verify the specified theme name/path + var tFolder = PATH.join( + PATH.parse( require.resolve('fluent-themes') ).dir, + this.opts.theme + ); + var exists = require('../utils/file-exists'); + if (!exists( tFolder )) { + tFolder = PATH.resolve( this.opts.theme ); + if (!exists( tFolder )) { + throw { fluenterror: this.codes.themeNotFound, data: this.opts.theme }; + } } - else { - return '{% print( filt.out(' + p1 + ') ) %}'; - } - }); - // Strip {# comments #} - jst = jst.replace( _.templateSettings.comment, ''); + // Load the theme + var theme = opts.themeObj || new Theme().open( tFolder ); - // Compile and run the template. TODO: avoid unnecessary recompiles. - jst = _.template(jst)({ r: json, filt: this.opts.filters, cssInfo: cssInfo, headFragment: this.opts.headFragment || '' }); + // Load theme and CSS data + var tplFolder = PATH.join( tFolder, 'templates' ); + var curFmt = theme.getFormat( this.format ); + var cssInfo = { file: curFmt.css ? curFmt.cssPath : null, data: curFmt.css || null }; - // Unfreeze whitespace - this.opts.freezeBreaks && ( jst = unfreeze(jst) ); + // Compile and invoke the template! + var mk = this.single( rez, curFmt.data, this.format, cssInfo, this.opts ); + this.onBeforeSave && (mk = this.onBeforeSave( { mk: mk, theme: theme, outputFile: f } )); + FS.writeFileSync( f, mk, { encoding: 'utf8', flags: 'w' } ); - return jst; + }, + + /** + Perform a single resume JSON-to-DEST resume transformation. Exists as a + separate function in order to expose string-based transformations to clients + who don't have access to filesystem resources (in-browser, etc.). + */ + single: function( json, jst, format, cssInfo, opts ) { + + // Freeze whitespace in the template. + this.opts.freezeBreaks && ( jst = freeze(jst) ); + + // Apply the template. + var eng = require( '../eng/' + opts.themeObj.engine + '-generator' ); + var result = eng( json, jst, format, cssInfo, opts ); + + // Unfreeze whitespace. + this.opts.freezeBreaks && ( result = unfreeze(result) ); + + return result; + } + + + }); + + /** + Export the TemplateGenerator function/ctor. + */ + module.exports = TemplateGenerator; + + /** + Freeze newlines for protection against errant JST parsers. + */ + function freeze( markup ) { + return markup + .replace( _reg.regN, _defaultOpts.nSym ) + .replace( _reg.regR, _defaultOpts.rSym ); } + /** + Unfreeze newlines when the coast is clear. + */ + function unfreeze( markup ) { + return markup + .replace( _reg.regSymR, '\r' ) + .replace( _reg.regSymN, '\n' ); + } -}); + /** + Regexes for linebreak preservation. + */ + var _reg = { + regN: new RegExp( '\n', 'g' ), + regR: new RegExp( '\r', 'g' ), + regSymN: new RegExp( _defaultOpts.nSym, 'g' ), + regSymR: new RegExp( _defaultOpts.rSym, 'g' ) + }; -/** -Export the TemplateGenerator function/ctor. -*/ -module.exports = TemplateGenerator; - -/** -Freeze newlines for protection against errant JST parsers. -*/ -function freeze( markup ) { - return markup - .replace( _reg.regN, _defaultOpts.nSym ) - .replace( _reg.regR, _defaultOpts.rSym ); -} - -/** -Unfreeze newlines when the coast is clear. -*/ -function unfreeze( markup ) { - return markup - .replace( _reg.regSymR, '\r' ) - .replace( _reg.regSymN, '\n' ); -} - -/** -Regexes for linebreak preservation. -*/ -var _reg = { - regN: new RegExp( '\n', 'g' ), - regR: new RegExp( '\r', 'g' ), - regSymN: new RegExp( _defaultOpts.nSym, 'g' ), - regSymR: new RegExp( _defaultOpts.rSym, 'g' ) -}; +}());