mirror of
https://github.com/JuanCanham/HackMyResume.git
synced 2024-11-22 08:20:11 +00:00
Introduce formal generators.
Introduce a shallow hierarchy of simple generator classes, collecting common functionality and allowing for future snap-in generator replacement. Use John Resig's "class"-based inheritance per http://ejohn.org/blog/simple-javascript-inheritance/.
This commit is contained in:
parent
7bf25469de
commit
7363e48019
13
src/gen/base-generator.js
Normal file
13
src/gen/base-generator.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
Base resume generator for FluentCV.
|
||||||
|
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var Class = require( './class' );
|
||||||
|
var BaseGenerator = module.exports = Class.extend({
|
||||||
|
init: function( outputFormat ) {
|
||||||
|
this.format = outputFormat;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}());
|
67
src/gen/class.js
Normal file
67
src/gen/class.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/* Simple JavaScript Inheritance
|
||||||
|
* By John Resig http://ejohn.org/
|
||||||
|
* MIT Licensed.
|
||||||
|
* http://ejohn.org/blog/simple-javascript-inheritance/
|
||||||
|
*/
|
||||||
|
// Inspired by base2 and Prototype
|
||||||
|
(function(){
|
||||||
|
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
||||||
|
|
||||||
|
// The base Class implementation (does nothing)
|
||||||
|
this.Class = function(){};
|
||||||
|
module.exports = Class;
|
||||||
|
|
||||||
|
// Create a new Class that inherits from this class
|
||||||
|
Class.extend = function(prop) {
|
||||||
|
var _super = this.prototype;
|
||||||
|
|
||||||
|
// Instantiate a base class (but only create the instance,
|
||||||
|
// don't run the init constructor)
|
||||||
|
initializing = true;
|
||||||
|
var prototype = new this();
|
||||||
|
initializing = false;
|
||||||
|
|
||||||
|
// Copy the properties over onto the new prototype
|
||||||
|
for (var name in prop) {
|
||||||
|
// Check if we're overwriting an existing function
|
||||||
|
prototype[name] = typeof prop[name] == "function" &&
|
||||||
|
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
|
||||||
|
(function(name, fn){
|
||||||
|
return function() {
|
||||||
|
var tmp = this._super;
|
||||||
|
|
||||||
|
// Add a new ._super() method that is the same method
|
||||||
|
// but on the super-class
|
||||||
|
this._super = _super[name];
|
||||||
|
|
||||||
|
// The method only need to be bound temporarily, so we
|
||||||
|
// remove it when we're done executing
|
||||||
|
var ret = fn.apply(this, arguments);
|
||||||
|
this._super = tmp;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
})(name, prop[name]) :
|
||||||
|
prop[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dummy class constructor
|
||||||
|
function Class() {
|
||||||
|
// All construction is actually done in the init method
|
||||||
|
if ( !initializing && this.init )
|
||||||
|
this.init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate our constructed prototype object
|
||||||
|
Class.prototype = prototype;
|
||||||
|
|
||||||
|
// Enforce the constructor to be what we expect
|
||||||
|
Class.prototype.constructor = Class;
|
||||||
|
|
||||||
|
// And make this class extendable
|
||||||
|
Class.extend = arguments.callee;
|
||||||
|
|
||||||
|
return Class;
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
31
src/gen/html-generator.js
Normal file
31
src/gen/html-generator.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
HTML resume generator for FluentCV.
|
||||||
|
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var TemplateGenerator = require('./template-generator');
|
||||||
|
var FS = require('fs-extra');
|
||||||
|
var HTML = require( 'html' );
|
||||||
|
|
||||||
|
var HtmlGenerator = TemplateGenerator.extend({
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
this._super( 'html' );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generate an HTML resume with optional pretty printing.
|
||||||
|
*/
|
||||||
|
onBeforeSave: function( mk, themeFile, outputFile ) {
|
||||||
|
var cssSrc = themeFile.replace( /.html$/g, '.css' );
|
||||||
|
var cssDst = outputFile.replace( /.html$/g, '.css' );
|
||||||
|
FS.copy( cssSrc, cssDst, function( e ) {
|
||||||
|
if( e ) err( "Couldn't copy CSS file to destination: " + err);
|
||||||
|
});
|
||||||
|
return true ?
|
||||||
|
HTML.prettyPrint( mk, { indent_size: 2 } ) : mk;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = HtmlGenerator;
|
31
src/gen/html-pdf-generator.js
Normal file
31
src/gen/html-pdf-generator.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
HTML-based PDF resume generator for FluentCV.
|
||||||
|
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var TemplateGenerator = require('./template-generator');
|
||||||
|
var FS = require('fs-extra');
|
||||||
|
var HTML = require( 'html' );
|
||||||
|
|
||||||
|
var HtmlPdfGenerator = TemplateGenerator.extend({
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
this._super( 'pdf', 'html' );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generate an HTML resume with optional pretty printing.
|
||||||
|
*/
|
||||||
|
onBeforeSave: function( mk, themeFile, outputFile ) {
|
||||||
|
var cssSrc = themeFile.replace( /.html$/g, '.css' );
|
||||||
|
var cssDst = outputFile.replace( /.html$/g, '.css' );
|
||||||
|
FS.copy( cssSrc, cssDst, function( e ) {
|
||||||
|
if( e ) err( "Couldn't copy CSS file to destination: " + err);
|
||||||
|
});
|
||||||
|
return true ?
|
||||||
|
HTML.prettyPrint( mk, { indent_size: 2 } ) : mk;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = HtmlPdfGenerator;
|
148
src/gen/template-generator.js
Normal file
148
src/gen/template-generator.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
Template-based resume generator base for FluentCV.
|
||||||
|
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
var FS = require( 'fs' );
|
||||||
|
var BaseGenerator = require( './base-generator' );
|
||||||
|
var _ = require( 'underscore' );
|
||||||
|
var MD = require( 'marked' );
|
||||||
|
var XML = require( 'xml-escape' );
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var _opts = {
|
||||||
|
keepBreaks: true,
|
||||||
|
nSym: '&newl;',
|
||||||
|
rSym: '&retn;',
|
||||||
|
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\>|\<\/p\>\s*$/gi, ''); },
|
||||||
|
lower: function( txt ) { return txt.toLowerCase(); }
|
||||||
|
},
|
||||||
|
prettyPrint: true,
|
||||||
|
prettyIndent: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
var TemplateGenerator = 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. */
|
||||||
|
generate: function( rez, f, themeName ) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Get the output file type (pdf, html, txt, etc)
|
||||||
|
var fName = path.basename( f, '.' + this.format );
|
||||||
|
|
||||||
|
// Load the active theme file, including CSS data if req'd
|
||||||
|
var themeFile = path.join( __dirname, '../../../watermark/', themeName, this.format + '.' + this.tplFormat );
|
||||||
|
var cssData = this.tplFormat === 'html' ? FS.readFileSync( path.join( __dirname, '../../../watermark/', themeName, 'html.css' ), 'utf8' ) : null;
|
||||||
|
var mk = FS.readFileSync( themeFile, 'utf8' );
|
||||||
|
|
||||||
|
// Compile and invoke the template!
|
||||||
|
mk = this.single( rez, mk, this.format, cssData, fName );
|
||||||
|
this.onBeforeSave && (mk = this.onBeforeSave( mk, themeFile, f ));
|
||||||
|
|
||||||
|
// Post-process and save the file
|
||||||
|
FS.writeFileSync( f, mk, 'utf8' );
|
||||||
|
return mk;
|
||||||
|
}
|
||||||
|
catch( ex ) {
|
||||||
|
err( ex );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
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, styles, fName ) {
|
||||||
|
|
||||||
|
// Freeze whitespace in the template
|
||||||
|
_opts.keepBreaks && ( jst = freeze(jst) );
|
||||||
|
|
||||||
|
// 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, '');
|
||||||
|
json.display_progress_bar = true;
|
||||||
|
|
||||||
|
// Compile and run the template. TODO: avoid unnecessary recompiles.
|
||||||
|
jst = _.template( jst )({ r: json, css: styles, embedCss: false, cssFile: fName, filt: _opts.filters });
|
||||||
|
|
||||||
|
// Unfreeze whitespace
|
||||||
|
_opts.keepBreaks && ( jst = unfreeze(jst) );
|
||||||
|
|
||||||
|
return jst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
Export the TemplateGenerator function/ctor.
|
||||||
|
*/
|
||||||
|
module.exports = TemplateGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Freeze newlines for protection against errant JST parsers.
|
||||||
|
*/
|
||||||
|
function freeze( markup ) {
|
||||||
|
return markup
|
||||||
|
.replace( _reg.regN, _opts.nSym )
|
||||||
|
.replace( _reg.regR, _opts.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( _opts.nSym, 'g' ),
|
||||||
|
regSymR: new RegExp( _opts.rSym, 'g' )
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}());
|
15
src/gen/text-generator.js
Normal file
15
src/gen/text-generator.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
Plain text resume generator for FluentCV.
|
||||||
|
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var TemplateGenerator = require('./template-generator');
|
||||||
|
var TextGenerator = TemplateGenerator.extend({
|
||||||
|
|
||||||
|
init: function(){
|
||||||
|
this._super( 'txt' );
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = TextGenerator;
|
13
src/gen/word-generator.js
Normal file
13
src/gen/word-generator.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
MS Word resume generator for FluentCV.
|
||||||
|
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var TemplateGenerator = require('./template-generator');
|
||||||
|
var WordGenerator = module.exports = TemplateGenerator.extend({
|
||||||
|
|
||||||
|
init: function(){
|
||||||
|
this._super( 'doc', 'xml' );
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
@ -13,7 +13,11 @@ module.exports = function () {
|
|||||||
, path = require( 'path' )
|
, path = require( 'path' )
|
||||||
, extend = require( './extend' )
|
, extend = require( './extend' )
|
||||||
, _ = require('underscore')
|
, _ = require('underscore')
|
||||||
, Sheet = require('./sheet');
|
, Sheet = require('./sheet')
|
||||||
|
, HtmlGenerator = require('./gen/html-generator')
|
||||||
|
, TextGenerator = require('./gen/text-generator')
|
||||||
|
, HtmlPdfGenerator = require('./gen/html-pdf-generator')
|
||||||
|
, WordGenerator = require('./gen/word-generator');
|
||||||
|
|
||||||
String.prototype.endsWith = function(suffix) {
|
String.prototype.endsWith = function(suffix) {
|
||||||
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
||||||
@ -281,7 +285,12 @@ module.exports = function () {
|
|||||||
generate: hmr,
|
generate: hmr,
|
||||||
transform: single,
|
transform: single,
|
||||||
options: _opts,
|
options: _opts,
|
||||||
formats: _fmts
|
formats: _fmts,
|
||||||
|
Sheet: Sheet,
|
||||||
|
HtmlGenerator: HtmlGenerator,
|
||||||
|
TextGenerator: TextGenerator,
|
||||||
|
HtmlPdfGenerator: HtmlPdfGenerator,
|
||||||
|
WordGenerator: WordGenerator
|
||||||
};
|
};
|
||||||
|
|
||||||
}();
|
}();
|
||||||
|
Loading…
Reference in New Issue
Block a user