/** Abstract theme representation. @license MIT. Copyright (c) 2015 James Devlin / FluentDesk. */ (function() { var FS = require('fs') , extend = require('../utils/extend') , validator = require('is-my-json-valid') , _ = require('underscore') , PATH = require('path') , EXTEND = require('../utils/extend') , moment = require('moment') , RECURSIVE_READ_DIR = require('recursive-readdir-sync'); /** The Theme class is a representation of a FluentCV theme asset. @class Theme */ function Theme() { } /** Open and parse the specified theme. */ Theme.prototype.open = function( themeFolder ) { // Open the [theme-name].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' ) ); var that = this; // Move properties from the theme JSON file to the theme object EXTEND( true, this, themeInfo ); // Set up a formats has for the theme var formatsHash = { }; // Check for an explicit "formats" entry in the theme JSON. If it has one, // then this theme declares its files explicitly. if( !!this.formats ) { formatsHash = loadExplicit.call( this ); } else { formatsHash = loadImplicit.call( this ); } // Add freebie formats every theme gets formatsHash[ 'json' ] = { title: 'json', outFormat: 'json', pre: 'json', ext: 'json', path: null, data: null }; formatsHash[ 'yml' ] = { title: 'yaml', outFormat: 'yml', pre: 'yml', ext: 'yml', path: null, data: null }; // Cache this.formats = formatsHash; // Set the official theme name this.name = PATH.parse( this.folder ).name; return this; }; /** Determine if the theme supports the specified output format. */ Theme.prototype.hasFormat = function( fmt ) { return _.has( this.formats, fmt ); }; /** Determine if the theme supports the specified output format. */ Theme.prototype.getFormat = function( fmt ) { return this.formats[ fmt ]; }; function loadImplicit() { // Set up a hash of formats supported by this theme. var formatsHash = { }; // Establish the base theme folder var tplFolder = PATH.join( this.folder, 'src' ); // Iterate over all files in the theme folder, producing an array, fmts, // containing info for each file. While we're doing that, also build up // the formatsHash object. var fmts = RECURSIVE_READ_DIR( tplFolder ).map( function( absPath ) { // If this file lives in a specific format folder within the theme, // such as "/latex" or "/html", then that format is the output format // for all files within the folder. var pathInfo = PATH.parse(absPath); var outFmt = ''; var portion = pathInfo.dir.replace(tplFolder,''); if( portion && portion.trim() ) { var reg = /^(?:\/|\\)(html|latex|doc|pdf)(?:\/|\\)?/ig; var res = reg.exec( portion ); res && (outFmt = res[1]); } // Otherwise, the output format is inferred from the filename, as in // compact-[outputformat].[extension], for ex, compact-pdf.html. if( !outFmt ) { var idx = pathInfo.name.lastIndexOf('-'); outFmt = ( idx === -1 ) ? pathInfo.name : pathInfo.name.substr( idx + 1 ) } // We should have a valid output format now. formatsHash[ outFmt ] = formatsHash[outFmt] || { outFormat: outFmt, files: [] }; // Create the file representation object. var obj = { path: absPath, ext: pathInfo.ext.slice(1), title: friendlyName( outFmt ), pre: outFmt, // outFormat: outFmt || pathInfo.name, data: FS.readFileSync( absPath, 'utf8' ), css: null }; // Add this file to the list of files for this format type. formatsHash[ outFmt ].files.push( obj ); return obj; }); // Now, get all the CSS files... (this.cssFiles = fmts.filter(function( fmt ){ return fmt.ext === 'css'; })) .forEach(function( cssf ) { // For each CSS file, get its corresponding HTML file var idx = _.findIndex(fmts, function( fmt ) { return fmt.pre === cssf.pre && fmt.ext === 'html' }); fmts[ idx ].css = cssf.data; fmts[ idx ].cssPath = cssf.path; }); // Remove CSS files from the formats array fmts = fmts.filter( function( fmt) { return fmt.ext !== 'css'; }); return formatsHash; } function loadExplicit() { var that = this; // Set up a hash of formats supported by this theme. var formatsHash = { }; // Establish the base theme folder var tplFolder = PATH.join( this.folder, 'src' ); var act = null; // Iterate over all files in the theme folder, producing an array, fmts, // containing info for each file. While we're doing that, also build up // the formatsHash object. var fmts = RECURSIVE_READ_DIR( tplFolder ).map( function( absPath ) { act = null; // If this file is mentioned in the theme's JSON file under "transforms" var pathInfo = PATH.parse(absPath); var absPathSafe = absPath.trim().toLowerCase(); var outFmt = _.find( Object.keys( that.formats ), function( fmtKey ) { var fmtVal = that.formats[ fmtKey ]; return _.some( fmtVal.transform, function( fpath ) { var absPathB = PATH.join( that.folder, fpath ).trim().toLowerCase(); return absPathB === absPathSafe; }); }); if( outFmt ) { act = 'transform'; } // If this file lives in a specific format folder within the theme, // such as "/latex" or "/html", then that format is the output format // for all files within the folder. if( !outFmt ) { var portion = pathInfo.dir.replace(tplFolder,''); if( portion && portion.trim() ) { var reg = /^(?:\/|\\)(html|latex|doc|pdf)(?:\/|\\)?/ig; var res = reg.exec( portion ); res && (outFmt = res[1]); } } // Otherwise, the output format is inferred from the filename, as in // compact-[outputformat].[extension], for ex, compact-pdf.html. if( !outFmt ) { var idx = pathInfo.name.lastIndexOf('-'); outFmt = ( idx === -1 ) ? pathInfo.name : pathInfo.name.substr( idx + 1 ) } // We should have a valid output format now. formatsHash[ outFmt ] = formatsHash[outFmt] || { outFormat: outFmt, files: [] }; // Create the file representation object. var obj = { action: act, orgPath: PATH.relative(that.folder, absPath), path: absPath, ext: pathInfo.ext.slice(1), title: friendlyName( outFmt ), pre: outFmt, // outFormat: outFmt || pathInfo.name, data: FS.readFileSync( absPath, 'utf8' ), css: null }; // Add this file to the list of files for this format type. formatsHash[ outFmt ].files.push( obj ); return obj; }); // Now, get all the CSS files... (this.cssFiles = fmts.filter(function( fmt ){ return fmt.ext === 'css'; })) .forEach(function( cssf ) { // For each CSS file, get its corresponding HTML file var idx = _.findIndex(fmts, function( fmt ) { return fmt.pre === cssf.pre && fmt.ext === 'html' }); fmts[ idx ].css = cssf.data; fmts[ idx ].cssPath = cssf.path; }); // Remove CSS files from the formats array fmts = fmts.filter( function( fmt) { return fmt.ext !== 'css'; }); // Object.keys( formatsHash ).forEach(function(k){ // formatsHash[ k ].files.forEach(function(xhs){ // console.log(xhs.orgPath); // }); // }); return formatsHash; } function friendlyName( val ) { val = val.trim().toLowerCase(); var friendly = { yml: 'yaml', md: 'markdown', txt: 'text' }; return friendly[val] || val; } module.exports = Theme; }());