| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 | 
/* content.coffee */(function() {  var ContentPlugin, ContentTree, StaticFile, async, chalk, fs, loadContent, minimatch, minimatchOptions, path, setImmediate, url,    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },    hasProp = {}.hasOwnProperty,    slice = [].slice;  async = require('async');  fs = require('fs');  path = require('path');  url = require('url');  chalk = require('chalk');  minimatch = require('minimatch');  minimatchOptions = {    dot: false  };  if (typeof setImmediate === "undefined" || setImmediate === null) {    setImmediate = process.nextTick;  }  ContentPlugin = (function() {    /* The mother of all plugins */    function ContentPlugin() {}    ContentPlugin.property = function(name, getter) {      /* Define read-only property with *name*. */      var get;      if (typeof getter === 'string') {        get = function() {          return this[getter].call(this);        };      } else {        get = function() {          return getter.call(this);        };      }      return Object.defineProperty(this.prototype, name, {        get: get,        enumerable: true      });    };    ContentPlugin.property('view', 'getView');    ContentPlugin.prototype.getView = function() {      /* Return a view that renders the plugin. Either a string naming a exisitng view or a function:          `(env, locals, contents, templates, callback) ->`          Where *environment* is the current wintersmith environment, *contents* is the content-tree          and *templates* is a map of all templates as: {filename: templateInstance}. *callback* should be          called with a stream/buffer or null if this plugin instance should not be rendered.       */      throw new Error('Not implemented.');    };    ContentPlugin.property('filename', 'getFilename');    ContentPlugin.prototype.getFilename = function() {      /* Return filename for this content. This is where the result of the plugin's view will be written to. */      throw new Error('Not implemented.');    };    ContentPlugin.property('url', 'getUrl');    ContentPlugin.prototype.getUrl = function(base) {      /* Return url for this content relative to *base*. */      var filename;      filename = this.getFilename();      if (base == null) {        base = this.__env.config.baseUrl;      }      if (!base.match(/\/$/)) {        base += '/';      }      if (process.platform === 'win32') {        filename = filename.replace(/\\/g, '/');      }      return url.resolve(base, filename);    };    ContentPlugin.property('pluginColor', 'getPluginColor');    ContentPlugin.prototype.getPluginColor = function() {      /* Return vanity color used to identify the plugin when printing the content tree          choices are: bold, italic, underline, inverse, yellow, cyan, white, magenta,          green, red, grey, blue, rainbow, zebra or none.       */      return 'cyan';    };    ContentPlugin.property('pluginInfo', 'getPluginInfo');    ContentPlugin.prototype.getPluginInfo = function() {      /* Return plugin information. Also displayed in the content tree printout. */      return "url: " + this.url;    };    return ContentPlugin;  })();  ContentPlugin.fromFile = function(filepath, callback) {    /* Calls *callback* with an instance of class. Where *filepath* is an object containing        both the absolute and realative paths for the file. e.g.        {full: "/home/foo/mysite/contents/somedir/somefile.ext",         relative: "somedir/somefile.ext"}     */    throw new Error('Not implemented.');  };  StaticFile = (function(superClass) {    extend(StaticFile, superClass);    /* Static file handler, simply serves content as-is. Last in chain. */    function StaticFile(filepath1) {      this.filepath = filepath1;    }    StaticFile.prototype.getView = function() {      return function() {        var args, callback, error, j, rs;        args = 2 <= arguments.length ? slice.call(arguments, 0, j = arguments.length - 1) : (j = 0, []), callback = arguments[j++];        try {          rs = fs.createReadStream(this.filepath.full);        } catch (error1) {          error = error1;          return callback(error);        }        return callback(null, rs);      };    };    StaticFile.prototype.getFilename = function() {      return this.filepath.relative;    };    StaticFile.prototype.getPluginColor = function() {      return 'none';    };    return StaticFile;  })(ContentPlugin);  StaticFile.fromFile = function(filepath, callback) {    return callback(null, new StaticFile(filepath));  };  loadContent = function(env, filepath, callback) {    /* Helper that loads content plugin found in *filepath*. */    var i, j, plugin, ref;    env.logger.silly("loading " + filepath.relative);    plugin = {      "class": StaticFile,      group: 'files'    };    for (i = j = ref = env.contentPlugins.length - 1; j >= 0; i = j += -1) {      if (minimatch(filepath.relative, env.contentPlugins[i].pattern, minimatchOptions)) {        plugin = env.contentPlugins[i];        break;      }    }    return plugin["class"].fromFile(filepath, function(error, instance) {      if (error != null) {        error.message = filepath.relative + ": " + error.message;      }      if (instance != null) {        instance.__env = env;      }      if (instance != null) {        instance.__plugin = plugin;      }      if (instance != null) {        instance.__filename = filepath.full;      }      return callback(error, instance);    });  };  ContentTree = function(filename, groupNames) {    var groups, j, len, name, parent;    if (groupNames == null) {      groupNames = [];    }    parent = null;    groups = {      directories: [],      files: []    };    for (j = 0, len = groupNames.length; j < len; j++) {      name = groupNames[j];      groups[name] = [];    }    Object.defineProperty(this, '__groupNames', {      get: function() {        return groupNames;      }    });    Object.defineProperty(this, '_', {      get: function() {        return groups;      }    });    Object.defineProperty(this, 'filename', {      get: function() {        return filename;      }    });    Object.defineProperty(this, 'index', {      get: function() {        var item, key, ref;        ref = this;        for (key in ref) {          item = ref[key];          if (key.slice(0, 6) === 'index.') {            return item;          }        }      }    });    return Object.defineProperty(this, 'parent', {      get: function() {        return parent;      },      set: function(val) {        return parent = val;      }    });  };  ContentTree.fromDirectory = function(env, directory, callback) {    /* Recursively scan *directory* and build a ContentTree with enviroment *env*.        Calls *callback* with a nested ContentTree or an error if something went wrong.     */    var createInstance, createInstances, filterIgnored, readDirectory, reldir, resolveFilenames, tree;    reldir = env.relativeContentsPath(directory);    tree = new ContentTree(reldir, env.getContentGroups());    env.logger.silly("creating content tree from " + directory);    readDirectory = function(callback) {      return fs.readdir(directory, callback);    };    resolveFilenames = function(filenames, callback) {      filenames.sort();      return async.map(filenames, function(filename, callback) {        var relname;        relname = path.join(reldir, filename);        return callback(null, {          full: path.join(env.contentsPath, relname),          relative: relname        });      }, callback);    };    filterIgnored = function(filenames, callback) {      /* Exclude *filenames* matching ignore patterns in environment config. */      if (env.config.ignore.length > 0) {        return async.filter(filenames, function(filename, callback) {          var include, j, len, pattern, ref;          include = true;          ref = env.config.ignore;          for (j = 0, len = ref.length; j < len; j++) {            pattern = ref[j];            if (minimatch(filename.relative, pattern, minimatchOptions)) {              env.logger.verbose("ignoring " + filename.relative + " (matches: " + pattern + ")");              include = false;              break;            }          }          return callback(null, include);        }, callback);      } else {        return callback(null, filenames);      }    };    createInstance = function(filepath, callback) {      /* Create plugin or subtree instance for *filepath*. */      return setImmediate(function() {        return async.waterfall([          async.apply(fs.stat, filepath.full), function(stats, callback) {            var basename;            basename = path.basename(filepath.relative);            if (stats.isDirectory()) {              return ContentTree.fromDirectory(env, filepath.full, function(error, result) {                result.parent = tree;                tree[basename] = result;                tree._.directories.push(result);                return callback(error);              });            } else if (stats.isFile()) {              return loadContent(env, filepath, function(error, instance) {                if (!error) {                  instance.parent = tree;                  tree[basename] = instance;                  tree._[instance.__plugin.group].push(instance);                }                return callback(error);              });            } else {              return callback(new Error("Invalid file " + filepath.full + "."));            }          }        ], callback);      });    };    createInstances = function(filenames, callback) {      return async.forEachLimit(filenames, env.config._fileLimit, createInstance, callback);    };    return async.waterfall([readDirectory, resolveFilenames, filterIgnored, createInstances], function(error) {      return callback(error, tree);    });  };  ContentTree.inspect = function(tree, depth) {    var cfn, i, j, k, keys, l, len, pad, ref, rv, s, v;    if (depth == null) {      depth = 0;    }    /* Return a pretty formatted string representing the content *tree*. */    if (typeof tree === 'number') {      return '[Function: ContentTree]';    }    rv = [];    pad = '';    for (i = j = 0, ref = depth; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {      pad += '  ';    }    keys = Object.keys(tree).sort(function(a, b) {      var ad, bd;      ad = tree[a] instanceof ContentTree;      bd = tree[b] instanceof ContentTree;      if (ad !== bd) {        return bd - ad;      }      if (a < b) {        return -1;      }      if (a > b) {        return 1;      }      return 0;    });    for (l = 0, len = keys.length; l < len; l++) {      k = keys[l];      v = tree[k];      if (v instanceof ContentTree) {        s = (chalk.bold(k)) + "/\n";        s += ContentTree.inspect(v, depth + 1);      } else {        cfn = function(s) {          return s;        };        if (v.pluginColor !== 'none') {          if (!(cfn = chalk[v.pluginColor])) {            throw new Error("Plugin " + k + " specifies invalid pluginColor: " + v.pluginColor);          }        }        s = (cfn(k)) + " (" + (chalk.grey(v.pluginInfo)) + ")";      }      rv.push(pad + s);    }    return rv.join('\n');  };  ContentTree.flatten = function(tree) {    /* Return all the items in the *tree* as an array of content plugins. */    var key, rv, value;    rv = [];    for (key in tree) {      value = tree[key];      if (value instanceof ContentTree) {        rv = rv.concat(ContentTree.flatten(value));      } else {        rv.push(value);      }    }    return rv;  };  ContentTree.merge = function(root, tree) {    /* Merge *tree* into *root* tree. */    var item, key;    for (key in tree) {      item = tree[key];      if (item instanceof ContentPlugin) {        root[key] = item;        item.parent = root;        root._[item.__plugin.group].push(item);      } else if (item instanceof ContentTree) {        if (root[key] == null) {          root[key] = new ContentTree(key, item.__groupNames);          root[key].parent = root;          root[key].parent._.directories.push(root[key]);        }        if (root[key] instanceof ContentTree) {          ContentTree.merge(root[key], item);        }      } else {        throw new Error("Invalid item in tree for '" + key + "'");      }    }  };  /* Exports */  module.exports = {    ContentTree: ContentTree,    ContentPlugin: ContentPlugin,    StaticFile: StaticFile,    loadContent: loadContent  };}).call(this);
 |