<commit_before>'use strict';

var path = require('path');
var util = require('./lib/util');
var tornado = require('./proto');

// export instances with the CLI built-in
//
// Notes:
//  - app -> [tornado][m-tornado] instance
//
// [m-tornado]: (https://github.com/stringparser/tornado)
//
function create(name, o){
  o = util.type(o || name).plainObject || {};
  var app = tornado.create(name, o);

  // have we been here already?
  if(app.repl){ return app; }
  if(app.store.children['--silent'] && !o.repl && !o.input){
    return app;
  }

  /**
   * Provide CLI commands
   */

  // --no-color
  //
  app.set('--silent', function(next){
    if(process.argv.indexOf('--tasks-simple') < 0){
      app.store.log = !this.log;
    }
    console.log('logging %s', app.store.log ? 'enabled' : 'disabled');
    next();
  });

  // --no-color, --color
  //
  app.set(':flag(--no-color|--color)', function(next){
    var wrote = false;
    if(util.color.enabled && this.params.flag === '--no-color'){
      util.color.enabled = false;
    } else if(!util.color.enabled && this.params.flag === '--color'){
      util.color.enabled = true;
    } else if(this.log){
      util.log('color already %s',
        util.color.enabled ? 'enabled' : 'disabled'
      );
      wrote = true;
    }

    if(!this.log || wrote){ return next(); }
    util.log('color %s', util.color.enabled
      ? util.color.bold('enabled') : 'disabled'
    );
    next();
  });

  // --cwd
  //
  app.set('--cwd :dirname([^ ]+)', function(next){
    var cwd = process.cwd();
    var dirname = path.resolve(cwd, next.params.dirname);
    if(dirname !== cwd){
      process.chdir(dirname);
      util.log('Working directory changed to',
        util.color.file(dirname)
      );
    }
    next();
  });

  // --tasks, -T or --tasks-simple
  //
  app.set(':flag(--tasks-simple|--tasks|-T)', function (next){
    if(!util.logTasks){ util.lazy('tasks-flags'); }
    if(next.params.flag === '--tasks-simple'){
      util.logTasksSimple(this.runtime);
    } else {
      util.logTasks(this.runtime);
    }
    next();
  });

  /*
   --require, --gulpfile is the same but changing the cwd
   */
  app.set(':flag(--require|--gulpfile) :file', function (next){

    var cwd = process.cwd();
    var file = path.resolve(cwd, next.params.file);
    var cached = Boolean(require.cache[file]);
    var isGulpfile = /--gulpfile/.test(this.queue);
    if(cached){
      delete require.cache[file];
    }

    try {
      require(file);
    } catch(err){
      var message = 'Could not load ' +
        (isGulpfile ? 'gulpfile' : 'module') + ' ' +
        util.color.file(file);

      if(this.runtime.repl){
        util.log(message);
        util.log(err.stack);
        return next();
      } else {
        throw new Error(message);
      }
    }

    util.log(
      (cached ? 'Reloaded' : 'Loaded'),
      util.color.file(file)
    );

    var dirname = path.dirname(file);
    if(isGulpfile && dirname !== cwd){
      process.chdir(dirname);
      util.log('Working directory changed to',
        util.color.file(dirname)
      );
    }

    next();
  });

  if(o.repl || util.type(o.input).stream){
    app = tornado.repl(name, o);
  }

  return app;
}

exports = module.exports = {
  create: create
};
<commit_msg>simplify initial checks in create<commit_after>'use strict';

var path = require('path');
var util = require('./lib/util');
var tornado = require('./lib/proto');

// export instances with the CLI built-in
//
// Notes:
//  - app -> [tornado][m-tornado] instance
//
// [m-tornado]: (https://github.com/stringparser/tornado)
//
function create(name, o){
  var app = tornado.create(name, o);
  var children = app.store.children;

  o = util.type(o || name).plainObject || {};
  if(app.repl || (children['--silent'] && !o.repl && !o.input)){
    return app;
  }

  /**
   * Provide CLI commands
   */

  // --no-color
  //
  app.set('--silent', function(next){
    if(process.argv.indexOf('--tasks-simple') < 0){
      app.store.log = !this.log;
    }
    console.log('logging %s', app.store.log ? 'enabled' : 'disabled');
    next();
  });

  // --no-color, --color
  //
  app.set(':flag(--no-color|--color)', function(next){
    var wrote = false;
    if(util.color.enabled && this.params.flag === '--no-color'){
      util.color.enabled = false;
    } else if(!util.color.enabled && this.params.flag === '--color'){
      util.color.enabled = true;
    } else if(this.log){
      util.log('color already %s',
        util.color.enabled ? 'enabled' : 'disabled'
      );
      wrote = true;
    }

    if(!this.log || wrote){ return next(); }
    util.log('color %s', util.color.enabled
      ? util.color.bold('enabled') : 'disabled'
    );
    next();
  });

  // --cwd
  //
  app.set('--cwd :dirname([^ ]+)', function(next){
    var cwd = process.cwd();
    var dirname = path.resolve(cwd, next.params.dirname);
    if(dirname !== cwd){
      process.chdir(dirname);
      util.log('Working directory changed to',
        util.color.file(dirname)
      );
    }
    next();
  });

  // --tasks, -T or --tasks-simple
  //
  app.set(':flag(--tasks-simple|--tasks|-T)', function (next){
    if(!util.logTasks){ util.lazy('tasks-flags'); }
    if(next.params.flag === '--tasks-simple'){
      util.logTasksSimple(this.runtime);
    } else {
      util.logTasks(this.runtime);
    }
    next();
  });

  /*
   --require, --gulpfile is the same but changing the cwd
   */
  app.set(':flag(--require|--gulpfile) :file', function (next){

    var cwd = process.cwd();
    var file = path.resolve(cwd, next.params.file);
    var cached = Boolean(require.cache[file]);
    var isGulpfile = /--gulpfile/.test(this.queue);
    if(cached){
      delete require.cache[file];
    }

    try {
      require(file);
    } catch(err){
      var message = 'Could not load ' +
        (isGulpfile ? 'gulpfile' : 'module') + ' ' +
        util.color.file(file);

      if(this.runtime.repl){
        util.log(message);
        util.log(err.stack);
        return next();
      } else {
        throw new Error(message);
      }
    }

    util.log(
      (cached ? 'Reloaded' : 'Loaded'),
      util.color.file(file)
    );

    var dirname = path.dirname(file);
    if(isGulpfile && dirname !== cwd){
      process.chdir(dirname);
      util.log('Working directory changed to',
        util.color.file(dirname)
      );
    }

    next();
  });

  if(o.repl || util.type(o.input).stream){
    app = tornado.repl(name, o);
  }

  return app;
}

exports = module.exports = {
  create: create
};
<|endoftext|>


<commit_before>$(document).ready(function() {
  $('[data-hover-show]').hide();

  $('[data-hover-effect]').hover(function() {
    $(this).find('[data-hover-show]').fadeToggle(200);
  });

  $('[data-add-to-cart]').on('click', function(e) {
    e.preventDefault();
    var id = $(this).closest('.product').data('id');

    $.post('cart.php', { 'product_id': id }, function(update) {
      var cart = $('.cart');
      var affectedLineItem = function() {
        return cart.find('tr[data-product-id=' + id + ']');
      };

      switch (update.cartUpdateType) {
        case 'add':
          if (cart.find('tbody').length === 0)
            cart.load('cart.php');
          else
            cart.find('tbody:last').append(update.html);

          break;
        case 'update':
          affectedLineItem().replaceWith(update.html);
          break;
      }

      cart.find('tfoot tr').replaceWith(update.totalPriceHtml);

    }, 'json');
  });

  $('.cart').on('click', '[data-empty-cart]', function(e) {
    e.preventDefault();
    $.post('cart.php', { 'empty': true }, function(data) {
      $('.cart').empty().append(data);
    });
  });

  $('.cart').on('click', '[data-checkout-cart]', function() {
    document.location.pathname = "checkout.php";
  });
});<commit_msg>Streamlined some javascript.<commit_after>$(document).ready(function() {
  $('[data-hover-show]').hide();

  $('[data-hover-effect]').hover(function() {
    $(this).find('[data-hover-show]').fadeToggle(200);
  });

  $('[data-add-to-cart]').on('click', function(e) {
    e.preventDefault();
    var id = $(this).closest('.product').data('id');

    $.post('cart.php', { 'product_id': id }, function(update) {
      var cart = $('.cart');

      switch (update.cartUpdateType) {
        case 'add':
          if (cart.find('tbody').length === 0)
            cart.load('cart.php');
          else
            cart.find('tbody:last').append(update.html);

          break;
        case 'update':
          cart.find('tr[data-product-id="' + id + '"]').replaceWith(update.html);
          break;
      }

      cart.find('tfoot tr').replaceWith(update.totalPriceHtml);
    }, 'json');
  });

  $('.cart').on('click', '[data-empty-cart]', function() {
    $.post('cart.php', { 'empty': true }, function(data) {
      $('.cart').empty().append(data);
    });
  });

  $('.cart').on('click', '[data-checkout-cart]', function() {
    document.location.pathname = "checkout.php";
  });
});<|endoftext|>


<commit_before>/** Sample login test against Facebook with data-driven test framework */

// :TODO: Figure out a way to make this script reusable by pulling code
//  out of it block into a module and auto-magically load it

//Module inclusions
var testObj = require('./library/testUtils');       //Custom library to read config and data files
var login = require('./library/loginPage');                 //Custom library to hold login information

//Constants
var CONFIG_FILE = "testConfig.json";        //Full path to test configuration
var TEST_DATA_FILE = "testData.json";       //Full path to test data
var TIMEOUT = 15000;                        //Used to adjust script timeout

var testInfo;                               //Imported test config and data

/** Main test script function
* @param {string} testDesc - Test suite description
* @param {string} testKey - Test case description used to find associated data in testInfo object
*/
function runTestScript(testDesc, testKey) {
    describe(testDesc, function(){
        this.timeout(TIMEOUT);

        //This is where the actual test steps take place
        it(testKey, function(done){
            //Create a new test object
            var loginPage = new login.loginPage(testInfo, testKey);

            //Load the page and attempt to login with given credentials
            loginPage.loadPage();
            loginPage.attemptLogin();
            loginPage.close();

            done();
        });
    })
}

//Import the test config and test data into the testInfo hash
testInfo = new testObj.testClass(CONFIG_FILE, TEST_DATA_FILE);

//Run the test script for each test data set
for (var k in testInfo.testData) {
    runTestScript(testInfo.testDesc, k);
} <commit_msg>Moved test setup and teardown into respective hooks<commit_after>/** Sample login test against Facebook with data-driven test framework */

// :TODO: Figure out a way to make this script reusable by pulling code
//  out of it block into a module and auto-magically load it

//Module inclusions
var testObj = require('./library/testUtils');       //Custom library to read config and data files
var login = require('./library/loginPage');         //Custom library to hold login information

//Constants
var CONFIG_FILE = "testConfig.json";        //Full path to test configuration
var TEST_DATA_FILE = "testData.json";       //Full path to test data
var TIMEOUT = 15000;                        //Used to adjust script timeout

var testInfo;                               //Imported test config and data
var loginPage;                              //Instance of login page class

/** Main test script function
* @param {string} testDesc - Test suite description
* @param {string} testKey - Test case description used to find associated data in testInfo object
*/
function runTestScript(testDesc, testKey) {
    describe(testDesc, function(){
        this.timeout(TIMEOUT);

        before(function(){
            //Create a new test object
            loginPage = new login.loginPage(testInfo, testKey);
        })

        after(function(){
            //Close the browser
            loginPage.close();
        })

        //This is where the actual test steps take place
        it(testKey, function(done){

            //Load the page and attempt to login with given credentials
            loginPage.loadPage();
            loginPage.attemptLogin();

            done();
        });
    })
}

//Import the test config and test data into the testInfo hash
testInfo = new testObj.testClass(CONFIG_FILE, TEST_DATA_FILE);

//Run the test script for each test data set
for (var k in testInfo.testData) {
    runTestScript(testInfo.testDesc, k);
} <|endoftext|>


<commit_before>var request = require('request'),
    http = require('http');

var BASE_URL = 'https://spreadsheets.google.com/feeds/';

// Utility functions

// Get value from a nested structure or null.
function getIn(o, keys) {
  var k = keys[0],
      ks = keys.slice(1);
  if (!o.hasOwnProperty(k)) return null;
  return ks.length ? getIn(o[k], ks) : o[k];
}

// Fetching

function fetch(params, callback) {
  var url = BASE_URL + params.join('/') + '/public/values?alt=json';
  request.get({
    url: url,
    json: true
  }, function(err, resp, body) {
    if (err) return callback(err);
    if (!resp) return callback(new Error('No response.'));
    if (resp.statusCode >= 400) return callback(new Error('Google Spreadsheets responded with HTTP error ' + resp.statusCode + ' (' + http.STATUS_CODES[resp.statusCode] + '). Are you sure the spreadsheet exists and is published?'));
    if (!body.feed) return callback(new Error('No feed was returned'));
    callback(null, body.feed);
  });
}

function fetchSpreadsheet(key, callback) {
  fetch(['worksheets', key], function(err, feed) {
    if (err) return callback(err);
    callback(null, parseSpreadsheetFeed(feed));
  });
}

function fetchWorksheetById(key, worksheetId, callback) {
  fetch(['cells', key, worksheetId], function(err, feed) {
    if (err) return callback(err);
    callback(null, parseWorksheetFeed(feed));
  });
}

function fetchWorksheetByTitle(key, worksheetTitle, callback) {
  fetchSpreadsheet(key, function(err, spreadsheet) {
    if (err) return callback(err);
    var worksheet = spreadsheet.worksheets.filter(function(d) { return d.title === worksheetTitle; })[0];
    if (!worksheet) return callback(new Error('No worksheet with title \'' + worksheetTitle + '\' found.'));
    fetchWorksheetById(key, worksheet.id, callback);
  });
}

// Parsing

function parseWorksheetIdInSpreadsheetFeed(uri) {
  var re = /.*\/(.+)$/,
      matches = re.exec(uri);
  return matches ? matches[1] : null;
}

function parseWorksheet(worksheet) {
  return {
    id: parseWorksheetIdInSpreadsheetFeed(getIn(worksheet, ['id', '$t'])),
    title: getIn(worksheet, ['title', '$t'])
  };
}

function parseSpreadsheetFeed(feed) {
  return {
    updated: getIn(feed, ['updated', '$t']),
    worksheets: feed.entry ? feed.entry.map(parseWorksheet) : null
  };
}

function parseWorksheetFeed(feed) {
  return {
    updated: getIn(feed, ['updated', '$t']),
    title: getIn(feed, ['title', '$t']),
    data: feed.entry ? parseCellsIntoRows(feed.entry) : null
  };
}

function getCellData(cell) {
  var data = cell.gs$cell;
  return {
    col: +data.col,
    row: +data.row,
    value: data.numericValue ? +data.numericValue : data.$t
  };
}

function createRowFromHeaders(headers) {
  return headers.reduce(function(row, key) {
    row[key] = null;
    return row;
  }, {});
}

function parseCellsIntoRows(cells) {
  var cellsData = cells.map(getCellData),
      headerCells = cellsData.filter(function(d) { return d.row === 1; }),
      headers = headerCells.map(function(d) { return d.value; }),
      headersByCol = headerCells.reduce(function(byCol, d) {
        byCol[d.col] = d.value;
        return byCol;
      }, {}),
      bodyCells = cellsData.filter(function(d) { return d.row !== 1; }),
      bodyByRow = bodyCells.reduce(function(byRow, d) {
        var row = byRow[d.row] || createRowFromHeaders(headers),
            key = headersByCol[d.col];
        row[key] = d.value;
        byRow[d.row] = row;
        return byRow;
      }, {});

  return Object.keys(bodyByRow)
    .sort(function(a, b) { return +a - +b; })
    .map(function(row) { return bodyByRow[row]; });
}

// Public API

module.exports = {
  sheet: fetchWorksheetByTitle,
  sheets: fetchSpreadsheet
};<commit_msg>Rename API<commit_after>var request = require('request'),
    http = require('http');

var BASE_URL = 'https://spreadsheets.google.com/feeds/';

// Utility functions

// Get value from a nested structure or null.
function getIn(o, keys) {
  var k = keys[0],
      ks = keys.slice(1);
  if (!o.hasOwnProperty(k)) return null;
  return ks.length ? getIn(o[k], ks) : o[k];
}

// Fetching

function fetch(params, callback) {
  var url = BASE_URL + params.join('/') + '/public/values?alt=json';
  request.get({
    url: url,
    json: true
  }, function(err, resp, body) {
    if (err) return callback(err);
    if (!resp) return callback(new Error('No response.'));
    if (resp.statusCode >= 400) return callback(new Error('Google Spreadsheets responded with HTTP error ' + resp.statusCode + ' (' + http.STATUS_CODES[resp.statusCode] + '). Are you sure the spreadsheet exists and is published?'));
    if (!body.feed) return callback(new Error('No feed was returned'));
    callback(null, body.feed);
  });
}

function fetchSpreadsheet(key, callback) {
  fetch(['worksheets', key], function(err, feed) {
    if (err) return callback(err);
    callback(null, parseSpreadsheetFeed(feed));
  });
}

function fetchWorksheetById(key, worksheetId, callback) {
  fetch(['cells', key, worksheetId], function(err, feed) {
    if (err) return callback(err);
    callback(null, parseWorksheetFeed(feed));
  });
}

function fetchWorksheetByTitle(key, worksheetTitle, callback) {
  fetchSpreadsheet(key, function(err, spreadsheet) {
    if (err) return callback(err);
    var worksheet = spreadsheet.worksheets.filter(function(d) { return d.title === worksheetTitle; })[0];
    if (!worksheet) return callback(new Error('No worksheet with title \'' + worksheetTitle + '\' found.'));
    fetchWorksheetById(key, worksheet.id, callback);
  });
}

// Parsing

function parseWorksheetIdInSpreadsheetFeed(uri) {
  var re = /.*\/(.+)$/,
      matches = re.exec(uri);
  return matches ? matches[1] : null;
}

function parseWorksheet(worksheet) {
  return {
    id: parseWorksheetIdInSpreadsheetFeed(getIn(worksheet, ['id', '$t'])),
    title: getIn(worksheet, ['title', '$t'])
  };
}

function parseSpreadsheetFeed(feed) {
  return {
    updated: getIn(feed, ['updated', '$t']),
    worksheets: feed.entry ? feed.entry.map(parseWorksheet) : null
  };
}

function parseWorksheetFeed(feed) {
  return {
    updated: getIn(feed, ['updated', '$t']),
    title: getIn(feed, ['title', '$t']),
    data: feed.entry ? parseCellsIntoRows(feed.entry) : null
  };
}

function getCellData(cell) {
  var data = cell.gs$cell;
  return {
    col: +data.col,
    row: +data.row,
    value: data.numericValue ? +data.numericValue : data.$t
  };
}

function createRowFromHeaders(headers) {
  return headers.reduce(function(row, key) {
    row[key] = null;
    return row;
  }, {});
}

function parseCellsIntoRows(cells) {
  var cellsData = cells.map(getCellData),
      headerCells = cellsData.filter(function(d) { return d.row === 1; }),
      headers = headerCells.map(function(d) { return d.value; }),
      headersByCol = headerCells.reduce(function(byCol, d) {
        byCol[d.col] = d.value;
        return byCol;
      }, {}),
      bodyCells = cellsData.filter(function(d) { return d.row !== 1; }),
      bodyByRow = bodyCells.reduce(function(byRow, d) {
        var row = byRow[d.row] || createRowFromHeaders(headers),
            key = headersByCol[d.col];
        row[key] = d.value;
        byRow[d.row] = row;
        return byRow;
      }, {});

  return Object.keys(bodyByRow)
    .sort(function(a, b) { return +a - +b; })
    .map(function(row) { return bodyByRow[row]; });
}

// Public API

module.exports = {
  getWorksheetByTitle: fetchWorksheetByTitle,
  getWorksheet: fetchWorksheetById,
  listWorksheets: fetchSpreadsheet
};<|endoftext|>


<commit_before>/**
 * Copyright 2014-2016 David Herron
 *
 * The MIT License (MIT)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

'use strict';

var cheerio = require('cheerio');
var util    = require('util');
var async   = require('async');

var configCheerio;

exports.config = function(_configCheerio) {
    configCheerio = _configCheerio;
};

/**
 * Simply parse the text, returning $ so the caller can do whatever they want.
 */
exports.parse = function(text) {
    return configCheerio ? cheerio.load(text, configCheerio) : cheerio.load(text);
};

exports.Mahafunc = class Mahafunc {
    get selector() { throw new Error("The 'selector' getter must be overridden"); }

    findElements($) {
        var ret = [];
        $(this.selector).each(function(i, elem) { ret.push(elem); });
        return ret;
    }

    process() { throw new Error("The 'process' function must be overridden"); }
    processAll() { throw new Error("The 'processAll' function must be overridden"); }
}

/**
 * Implements an HTML-ish element that is replaced with
 * some other HTML.  For example, <embed-video> might take
 * an href= and other attributes to describe a video from
 * a known service, the process function discerns the HTML code
 * to use for the player, rendering that into the output.
 */
exports.CustomElement = class CustomElement extends exports.Mahafunc {

    get elementName() { throw new Error("The 'elementName' getter must be overridden"); }
	get selector() { return this.elementName; }

    process($element, metadata, setDirty, done) {
        throw new Error("The 'process' function must be overridden");
    }

    processAll($, metadata, setDirty, done) {
        return new Promise((resolve, reject) => {
            // console.log(`CustomElement processAll ${this.elementName}`);
            var elements = this.findElements($);
            if (elements.length <= 0) return resolve();
            async.eachSeries(elements, (element, next) => {
                this.process($(element), metadata, setDirty)
                .then(replaceWith => {
                    // console.log(`CustomElement ${this.elementName} replaceWith ${replaceWith}`);
                    $(element).replaceWith(replaceWith);
                    next();
                })
                .catch(err => { next(err); });
            },
            err => {
                // log(`after ak-stylesheets ${metadata.document.path} ${$.html()}`);
                if (err) {
                    console.error(`CustomElement ${this.elementName} Errored with ${util.inspect(err)}`);
                    reject(err);
                } else resolve();
            });
        });
    }
}

exports.ElementTweaker = class ElementTweaker extends exports.Mahafunc {
    process() {
        throw new Error("The 'process' function must be overridden")
    }
}

exports.Munger = class Munger extends exports.Mahafunc {
    process($, $element, metadata, setDirty, done) {
        throw new Error("The 'process' function must be overridden")
    }
    processAll($, metadata, setDirty) {
        return new Promise((resolve, reject) => {
            // console.log(`Munger processAll`);
            var elements = this.findElements($);
            // console.log(`Munger for ${this.selector} found ${elements.length} items to process`);
            if (elements.length <= 0) return resolve();
            async.eachSeries(elements, (element, next) => {
                this.process($, $(element), metadata, setDirty)
                .then(() => { next(); })
                .catch(err => { next(err); });
            },
            err => {
                // log(`after ak-stylesheets ${metadata.document.path} ${$.html()}`);
                if (err) {
                    console.error(`${this.selector} Errored with ${util.inspect(err)}`);
                    reject(err);
                } else {
                    // console.log(`Munger finished with ${this.selector}`);
                    resolve();
                }
            });
        });
    }
}

exports.MahafuncArray = class MahafuncArray {

    constructor(name, config) {
        this._functions = [];
        this._name = name;
        this._config = config;
    }

    get name() { return this._name; }

    addMahafunc(func) {
        if (!(func instanceof exports.Mahafunc
           || func instanceof exports.MahafuncArray
           || Array.isArray(func))) {
            throw new Error("Improper addition "+ util.inspect(func));
        } else {
            this._functions.push(func);
        }
    }

    setMahafuncArray(functions) {
        if (!(Array.isArray(func))) {
            throw new Error("Improper mahafunction array "+ util.inspect(func));
        } else {
            this._functions = functions;
        }
    }

    process($, metadata, dirty) {
        return new Promise((resolve, reject) => {
            async.eachSeries(this._functions, (mahafunc, next) => {
                // util.log(util.inspect(mahafunc));
                if (mahafunc instanceof exports.CustomElement) {
                    // console.log(`Mahabhuta calling CustomElement ${mahafunc.elementName}`);
                    mahafunc.processAll($, metadata, dirty)
                    .then(() => { next(); })
                    .catch(err => { next(err); });
                } else if (mahafunc instanceof exports.Munger) {
                    // console.log(`Mahabhuta calling Munger ${mahafunc.selector}`);
                    mahafunc.processAll($, metadata, dirty)
                    .then(() => { next(); })
                    .catch(err => { next(err); });
                } else if (mahafunc instanceof exports.MahafuncArray) {
                    mahafunc.process($, metadata, dirty)
                    .then(() => { next(); })
                    .catch(err => { next(err); });
                } else if (typeof mahafunc === 'function') {
                    mahafunc($, metadata, dirty, next);
                } else if (Array.isArray(mahafunc)) {
                    let mhObj = new exports.MahafuncArray("inline", this._config);
                    mhObj.setMahafuncArray(mahafunc);
                    mhObj.process($, metadata, dirty)
                    .then(() => { next(); })
                    .catch(err => { next(err); });
                } else {
                    console.error("BAD MAHAFUNC "+ util.inspect(mahafunc));
                    next();
                }
            },
            err => {
                // console.log(`runMahaArray finished with ${err}`);
                if (err) reject(err);
                else resolve();
            });
        });
    }
}

/**
 * Process the text using functions supplied in the array mahabhutaFuncs.
 */
exports.process = function(text, metadata, mahabhutaFuncs, done) {

	// If we were on a Synchronous platform, this might be
	//
	// var cleanOrDirty = 'first-time';
	// while (dirtyOrClean !== 'dirty' && dirtyOrClean !== 'first-time') {
	// 		cleanOrDirty = 'clean';
	//		mahabhutaFuncs.forEach(function(mahafunc) {
	//			mahafunc($, metadata, setDirty, function(err) { ... });
	//      }
	// }

    if (!mahabhutaFuncs || mahabhutaFuncs.length < 0) mahabhutaFuncs = [];

	var cleanOrDirty = 'first-time';
	var setDirty = function() { cleanOrDirty = 'dirty'; };

    // Allow a pre-parsed context to be passed in
    var $ = typeof text === 'function' ? text : exports.parse(text);

    // Keep running the functions until the page is clean
    var runMahaFuncs = function() {
        // console.log(`START RUNMAHAFUNCS`);
    	if (cleanOrDirty === 'dirty' || cleanOrDirty === 'first-time') {
    		cleanOrDirty = 'clean';
            var mhObj;
            if (Array.isArray(mahabhutaFuncs)) {
                mhObj = new exports.MahafuncArray("master", {});
                mhObj.setMahafuncArray(mahabhutaFuncs);
            } else if (mahabhutaFuncs instanceof exports.MahafuncArray) {
                mhObj = mahabhutaFuncs;
            } else {
                done(new Error(`Bad mahabhutaFuncs object supplied`));
            }
            mhObj.process($, metadata, setDirty)
            .then(() => { runMahaFuncs(); })
            .catch(err => { console.error(`runMahaFuncs finished with ERROR ${err.stack}`); done(err); });
		} else {
            // console.log(`runMahaFuncs finished normally`);
			done(undefined, $.html());
		}
	};
	runMahaFuncs();
};

exports.process1 = function(text, metadata, mahafunc, done) {
	exports.process(text, metadata, [ mahafunc ], done);
};

exports.builtin = require('./built-in');

/**
 * The beginnings of Express integration for Mahabhuta.  The only unclarity is
 * the source for the function array.
 * /
exports.express = function(filePath, options, callback) {
	fs.readFile(filePath, function (err, content) {
		if (err) callback(new Error(err));
		else {
			exports.process(content, options, "TBD - FUNCTIONS", function(err, html) {
				if (err) callback(err);
				else callback(null, html);
			});
		}
	})
};*/
<commit_msg>Fixes<commit_after>/**
 * Copyright 2014-2016 David Herron
 *
 * The MIT License (MIT)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

'use strict';

var cheerio = require('cheerio');
var util    = require('util');
var async   = require('async');

var configCheerio;

exports.config = function(_configCheerio) {
    configCheerio = _configCheerio;
};

/**
 * Simply parse the text, returning $ so the caller can do whatever they want.
 */
exports.parse = function(text) {
    return configCheerio ? cheerio.load(text, configCheerio) : cheerio.load(text);
};

exports.Mahafunc = class Mahafunc {
    get selector() { throw new Error("The 'selector' getter must be overridden"); }

    findElements($) {
        var ret = [];
        $(this.selector).each(function(i, elem) { ret.push(elem); });
        return ret;
    }

    process() { throw new Error("The 'process' function must be overridden"); }
    processAll() { throw new Error("The 'processAll' function must be overridden"); }
}

/**
 * Implements an HTML-ish element that is replaced with
 * some other HTML.  For example, <embed-video> might take
 * an href= and other attributes to describe a video from
 * a known service, the process function discerns the HTML code
 * to use for the player, rendering that into the output.
 */
exports.CustomElement = class CustomElement extends exports.Mahafunc {

    get elementName() { throw new Error("The 'elementName' getter must be overridden"); }
	get selector() { return this.elementName; }

    process($element, metadata, setDirty, done) {
        throw new Error("The 'process' function must be overridden");
    }

    processAll($, metadata, setDirty, done) {
        return new Promise((resolve, reject) => {
            // console.log(`CustomElement processAll ${this.elementName}`);
            var elements = this.findElements($);
            if (elements.length <= 0) return resolve();
            async.eachSeries(elements, (element, next) => {
                this.process($(element), metadata, setDirty)
                .then(replaceWith => {
                    // console.log(`CustomElement ${this.elementName} replaceWith ${replaceWith}`);
                    $(element).replaceWith(replaceWith);
                    next();
                })
                .catch(err => { next(err); });
            },
            err => {
                // log(`after ak-stylesheets ${metadata.document.path} ${$.html()}`);
                if (err) {
                    console.error(`CustomElement ${this.elementName} Errored with ${util.inspect(err)}`);
                    reject(err);
                } else resolve();
            });
        });
    }
}

exports.ElementTweaker = class ElementTweaker extends exports.Mahafunc {
    process() {
        throw new Error("The 'process' function must be overridden")
    }
}

exports.Munger = class Munger extends exports.Mahafunc {
    process($, $element, metadata, setDirty, done) {
        throw new Error("The 'process' function must be overridden")
    }
    processAll($, metadata, setDirty) {
        return new Promise((resolve, reject) => {
            // console.log(`Munger processAll`);
            var elements = this.findElements($);
            // console.log(`Munger for ${this.selector} found ${elements.length} items to process`);
            if (elements.length <= 0) return resolve();
            async.eachSeries(elements, (element, next) => {
                this.process($, $(element), metadata, setDirty)
                .then(() => { next(); })
                .catch(err => { next(err); });
            },
            err => {
                // log(`after ak-stylesheets ${metadata.document.path} ${$.html()}`);
                if (err) {
                    console.error(`${this.selector} Errored with ${util.inspect(err)}`);
                    reject(err);
                } else {
                    // console.log(`Munger finished with ${this.selector}`);
                    resolve();
                }
            });
        });
    }
}

exports.MahafuncArray = class MahafuncArray {

    constructor(name, config) {
        this._functions = [];
        this._name = name;
        this._config = config;
    }

    get name() { return this._name; }

    addMahafunc(func) {
        if (!(func instanceof exports.Mahafunc
           || func instanceof exports.MahafuncArray
           || typeof func === 'function'
           || Array.isArray(func))) {
            throw new Error("Improper addition "+ util.inspect(func));
        } else {
            this._functions.push(func);
        }
    }

    setMahafuncArray(functions) {
        if (!(Array.isArray(functions))) {
            throw new Error("Improper mahafunction array "+ util.inspect(functions));
        } else {
            this._functions = functions;
        }
    }

    process($, metadata, dirty) {
        return new Promise((resolve, reject) => {
            async.eachSeries(this._functions, (mahafunc, next) => {
                // util.log(util.inspect(mahafunc));
                if (mahafunc instanceof exports.CustomElement) {
                    // console.log(`Mahabhuta calling CustomElement ${mahafunc.elementName}`);
                    mahafunc.processAll($, metadata, dirty)
                    .then(() => { next(); })
                    .catch(err => { next(err); });
                } else if (mahafunc instanceof exports.Munger) {
                    // console.log(`Mahabhuta calling Munger ${mahafunc.selector}`);
                    mahafunc.processAll($, metadata, dirty)
                    .then(() => { next(); })
                    .catch(err => { next(err); });
                } else if (mahafunc instanceof exports.MahafuncArray) {
                    mahafunc.process($, metadata, dirty)
                    .then(() => { next(); })
                    .catch(err => { next(err); });
                } else if (typeof mahafunc === 'function') {
                    mahafunc($, metadata, dirty, next);
                } else if (Array.isArray(mahafunc)) {
                    let mhObj = new exports.MahafuncArray("inline", this._config);
                    mhObj.setMahafuncArray(mahafunc);
                    mhObj.process($, metadata, dirty)
                    .then(() => { next(); })
                    .catch(err => { next(err); });
                } else {
                    console.error("BAD MAHAFUNC "+ util.inspect(mahafunc));
                    next();
                }
            },
            err => {
                // console.log(`runMahaArray finished with ${err}`);
                if (err) reject(err);
                else resolve();
            });
        });
    }
}

/**
 * Process the text using functions supplied in the array mahabhutaFuncs.
 */
exports.process = function(text, metadata, mahabhutaFuncs, done) {

	// If we were on a Synchronous platform, this might be
	//
	// var cleanOrDirty = 'first-time';
	// while (dirtyOrClean !== 'dirty' && dirtyOrClean !== 'first-time') {
	// 		cleanOrDirty = 'clean';
	//		mahabhutaFuncs.forEach(function(mahafunc) {
	//			mahafunc($, metadata, setDirty, function(err) { ... });
	//      }
	// }

    if (!mahabhutaFuncs || mahabhutaFuncs.length < 0) mahabhutaFuncs = [];

	var cleanOrDirty = 'first-time';
	var setDirty = function() { cleanOrDirty = 'dirty'; };

    // Allow a pre-parsed context to be passed in
    var $ = typeof text === 'function' ? text : exports.parse(text);

    // Keep running the functions until the page is clean
    var runMahaFuncs = function() {
        // console.log(`START RUNMAHAFUNCS`);
    	if (cleanOrDirty === 'dirty' || cleanOrDirty === 'first-time') {
    		cleanOrDirty = 'clean';
            var mhObj;
            if (Array.isArray(mahabhutaFuncs)) {
                mhObj = new exports.MahafuncArray("master", {});
                mhObj.setMahafuncArray(mahabhutaFuncs);
            } else if (mahabhutaFuncs instanceof exports.MahafuncArray) {
                mhObj = mahabhutaFuncs;
            } else {
                return done(new Error(`Bad mahabhutaFuncs object supplied`));
            }
            mhObj.process($, metadata, setDirty)
            .then(() => { runMahaFuncs(); })
            .catch(err => { console.error(`runMahaFuncs finished with ERROR ${err.stack}`); done(err); });
		} else {
            // console.log(`runMahaFuncs finished normally`);
			done(undefined, $.html());
		}
	};
	runMahaFuncs();
};

exports.process1 = function(text, metadata, mahafunc, done) {
	exports.process(text, metadata, [ mahafunc ], done);
};

exports.builtin = require('./built-in');

/**
 * The beginnings of Express integration for Mahabhuta.  The only unclarity is
 * the source for the function array.
 * /
exports.express = function(filePath, options, callback) {
	fs.readFile(filePath, function (err, content) {
		if (err) callback(new Error(err));
		else {
			exports.process(content, options, "TBD - FUNCTIONS", function(err, html) {
				if (err) callback(err);
				else callback(null, html);
			});
		}
	})
};*/
<|endoftext|>


<commit_before>/**
 *  http-route-proxy
 *  http://github.com/switer/http-route-proxy
 *
 *  Copyright (c) 2013 "switer" guankaishe
 *  Licensed under the MIT license.
 *  https://github.com/switer/http-route-proxy/blob/master/LICENSE
 */

var httpProxy = require('http-proxy'), // base on nodejitsu proxy server
    staticServer = require('./lib/static'), // static server
    util = require('./lib/util'),
    colors = require('colors'), // use to pretty console log
    path = require('path'),
    _ = require('underscore'); // use each method.....

var server = {

    /**
     *  request proxy route match status code
     */
    status: {

        STATIC: 0,
        FORWARD: 1,
        UNMATCHED: 2
    },
    /**
     *   id for each proxy server
     */
    serverId: 1,
    /**
     *   mapping root
     */
    rules: {},
    /**
     *   save proxy host
     */
    proxies: {
        /**
         *  from: Object {hostname, name}
         *  rules: Object {static, forward, rewrite}
         */
    },
    /**
     *   defualt port when config port is not pass in
     */
    defaultPort: 80,
    /**
     *  defualt port for https
     */
    defualtHttpsPort: 443,
    /**
     *   module regexps
     */
    regexps: {
        // validate host string
        HOST: /^[0-9a-zA-Z\.]+\:?[0-9]*$/,
        ROUTE_FORWARD: /^([A-Z]+:)?\/.*$/,
        // static request must be GET method
        ROUTE_STATIC: /^!([A-Z]+:)?\/.*$/
        // ROUTE_REWRITE: /^\^.*/
    },
    /**
     *  for creating request route matching rule
     */
    str: {
        ROUTE_METHOD_PREFIX: '([A-Z]+:)?'
    },
    /**
     *  Proxy enter api
     */
    proxy: function (hosts) {

        // handle each proxy config
        _.each(hosts, function (hostConfig) {

            var hostObj = {
                from: this.parseHost(hostConfig.from),
                routes: this.parseRouteRule(hostConfig)
            }

            var serverId = this.create(hostObj.from, hostObj.routes, hostConfig);
            this.saveHost(hostObj.from, hostObj.routes, serverId);

        }.bind(this));
    },
    /**
     *  support as express connect middleware
     */
    connect: function (config) {

        var proxy = new httpProxy.RoutingProxy(),
            connectConfig = {
                rules: this.parseRouteRule(config)
            },
            _this = this;

        return function (req, res, next) {

            _this.proxyMiddleware(req, res, proxy, connectConfig, function (proxyStatus) {
                // processing in express
                if (proxyStatus.status !== _this.status.FORWARD) {
                    next();
                }
                // forward request
                else {
                    console.log(proxyStatus.message);
                }
            });
        }
    },
    /**
     *   create a server for the one proxy config
     */
    create: function (from, routes, options) {

        var serverId = this.serverId, // for serverid hoisting
            // proxy server options
            proxyOptions = {
                // use client origin
                changeOrigin: true,
                target: {
                    // https: true,
                    // rejectUnauthorized: false
                }
            },
            _this = this; // for hoisting context of `this`
        // nessary for fixing node-http-proxy rejectUnauthorized option not work bug
        process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
        // create proxy server
        var server = httpProxy.createServer(
            /**
             *  middleware
             *  Cross-Domain-Access
             */
            function (req, res, next) {
                // TODO
                next();
            },
            function (req, res, proxy) {

                _this.proxyMiddleware(req, res, proxy, _this.proxies[serverId], function (statusObj) {
                    console.log(statusObj.message);
                });

            /**
             *  must listen hostname, otherwise it will be fail when repeat listening
             *  localhost in the some port
             */
            }).listen(from.port, from.hostname)
              .on('error', function (e) {
                // catch server listen error
                console.log('Create proxy error,'.red.grey + "cause by can't listen host from " +
                    from.hostname.yellow.grey + ' : ' + from.port.toString().blue.grey);
              });

        console.log('Listen from ' + from.hostname.green.grey + ' : ' + from.port.toString().blue.grey);

        // Custom error
        server.proxy.on('proxyError', function (err, req, res) {
            res.writeHead(500, {
                'Content-Type': 'text/plain'
            });
            res.end('Something went wrong. And we are reporting a custom error message.\n' + err);
        });

        return this.serverId ++;
    },
    /**
     *  Proxy middle for handling request and response
     */
    proxyMiddleware: function (req, res, proxy, config, next) {
        var from = this.parseHost(req.headers.host),
            method = req.method,
            // rewrite url to originUrl for proxy agent
            requestURL = req.url = req.originalUrl,
            url = method.toUpperCase() + ':' + req.originalUrl,
            forwardRouteObj = null;

        // get proxy config by proxy server id
        var proxyConfig = config;

        if (this.staticMatched(url, proxyConfig.rules.static)) {
            // match route is static file response
            var directory = path.resolve(process.cwd(), '.');

            // send static files without server
            staticServer.sendfile(req, res, directory);

            next({
                status: this.status.STATIC,
                message: method.blue.grey + '  ' + requestURL + ' from '.green.grey +  from.hostname + ':' + from.port.toString().blue.grey
            });

        } else if (forwardRouteObj = this.forwardMatched(url, proxyConfig.rules.forward)) {
            // set headers of config
            this.setHeaders(req, res, forwardRouteObj.options.headers);

            var forwardObj = forwardRouteObj.forward,
                // forward options
                proxyForwardOptions = {
                    changeOrigin: true,
                    host: forwardObj.hostname,
                    port: forwardObj.port
                };

            if (forwardRouteObj.options.https) {
                // set https forward options
                proxyForwardOptions = _.extend(proxyForwardOptions, {
                    target: {
                        https: true,
                        rejectUnauthorized: false
                    }
                });
            }

            // forward to remote server
            proxy.proxyRequest(req, res, proxyForwardOptions);

            next({
                status: this.status.FORWARD,
                message: 'forward '.yellow.grey + method.blue.grey + '  ' + requestURL + ' from '.green.grey +  from.hostname + ':' +
                    from.port.toString().blue.grey +' to '.green.grey + forwardObj.protocal + '://' + forwardObj.hostname + ':' +
                    forwardObj.port.toString().blue.grey + requestURL
            });
        }
        else {
            next({
                status: this.status.UNMATCHED,
                message: "Sorry proxy error, http-route-proxy can't match any forward rule, please check your proxy config".red
            });
        }
    },
    /**
     *  Set headers by proxy config
     */
    setHeaders: function (req, res, headers) {
        var reqHeaders = headers && headers.req ? headers.req: {},
            resHeaders = headers && headers.res ? headers.res: {},
            origin = req.headers.origin || req.headers.host;

        // if exist Origin, use "Cross-Domain-Access"
        if (origin) {
            // currently, we only support receiving http request
            res.setHeader('access-control-allow-origin', 'http://' + origin);
            res.setHeader('access-control-allow-credentials', true);
        }
        //rewrite header on config
        if (headers) {
            _.each(reqHeaders, function (value, key) {
                req.headers[key] = value;
            });
            _.each(resHeaders, function (value, key) {
                res.setHeader(key, value);
            });
        }
        // avoid node-http-proxy to rewrite headers
        var setHeader = res.setHeader;
        res.setHeader = function (key, value) {
            if (!resHeaders[key]) setHeader.call(res, key, value);
        }

    },
    /**
     *   host is a string, should be parse to hostname + port object format
     */
    parseHost: function (host, options) {
        options = options || {};

        var hostChunks = host.match(/^([a-z]+)\:\/\/(.*)$/),
            protocal = hostChunks? hostChunks[1]: '',
            hostSection = hostChunks? hostChunks[2]: host,
            hostname = hostSection.split(':')[0],
            port = hostSection.split(':')[1],
            protocalObj = this.protocal(protocal),
            protocalOptions = protocalObj.unknow ? options: protocalObj;

        return {
            protocal: this.options2protocal(protocalOptions),
            protocalOption: protocalOptions,
            hostname: hostname,
            port: port ? parseInt(port) : (protocalOptions.https ? this.defualtHttpsPort : this.defaultPort)
        };
    },
    /**
     *   save each host config
     */
    saveHost: function (fromObj, routeRules, serverId) {

        // save proxy config
        this.proxies[serverId] = {

            from: fromObj,
            rules: routeRules

        };

    },
    /**
     *  Check route if has default action , if has not, put it on first
     */
    checkDefaultAction: function (routes) {

        var isContainDefaultAction = false;

        _.each(routes, function (item) {
            if (item == '/' || item.action == '/') {
                isContainDefaultAction = true;
                return true;
            }
        });
        // put default action on the first tuples
        if (!isContainDefaultAction) routes.unshift('/');
        return routes;
    },
    /**
     *   validate host string
     */
    validHost: function (host) {
        return this.regexps.HOST.exec(host) ? true : false;
    },
    /**
     *
     *  @return Object
     */
    protocal: function (protocal) {
        var protocalObj = {
            http: false,
            https: false,
            websocket: false,
            unknow: false
        };
        if (protocal === 'http') {
            protocalObj.http = true;
        } else if (protocal === 'https') {
            protocalObj.https = true;
        } else if (protocal === 'ws') {
            protocalObj.websocket = true;
        } else {
            protocalObj.unknow = true;
        }
        return protocalObj;
    },
    /**
     *  protocal option to protocal name
     */
    options2protocal: function (options) {

        if (options.https) {
            return 'https';
        } else if (options.websocket) {
            return 'ws';
        } else if (options.http) {
            return 'http';
        } else {
            return 'http';
        }
    },
    /**
     *   parse each route rule to url matched rule
     */
    parseRouteRule: function (options) {
        options = util.copy(options);

        var routes = options.route || [];
        // if route config is empty, give it default
        routes = this.checkDefaultAction(routes);

        var _this = this,
            forwards = [], // forward route rules
            // rewrites = [], // currently it's useless
            statices = [], // static route rules
            forwardObj = null;

        _.each(routes, function (route) {

            // route detail config
            if (_.isObject(route)) {

                var routeOptions = route,
                    forwardObj = _this.parseHost(routeOptions.forward);

                routeOptions = _.extend(routeOptions, forwardObj.protocalOption);

                forwards.push({
                    rule: _this.forwardRouteRule2Regexp(routeOptions.action),
                    forward: forwardObj,
                    options: routeOptions
                });

            }
            // forward route match, route rule in shorthand
            else if (_this.regexps.ROUTE_FORWARD.exec(route)) {
                // parse forward host
                var forwardObj = _this.parseHost(options.to, options);
                // set foward options
                options = _.extend(options, forwardObj.protocalOption);
                // save config
                forwards.push({
                    rule: _this.forwardRouteRule2Regexp(route),
                    forward: forwardObj,
                    options: options
                });

            }
            // static route match, route rule in shorthand
            else if (_this.regexps.ROUTE_STATIC.exec(route)) {
                statices.push(_this.staticRouteRule2Regexp(route));
            }
        });

        return {
            forward: forwards,
            // rewrite: rewrites,
            static: statices
        };
    },
    /**
     *   url route rule to regexp
     */
    string2Regexp: function (rule) {
        rule = rule.replace('/','\\/');
        rule = '^' + rule + '.*$';
        return new RegExp(rule);
    },
    /**
     *  find the rule whether is static route rule and return regexpess rule.
     */
    staticRouteRule2Regexp: function (rule) {
        var matches = this.regexps.ROUTE_STATIC.exec(rule);

        rule = rule.replace(/^!/, '');

        if (!matches[1]) {
            rule = this.str.ROUTE_METHOD_PREFIX + rule;
        }
        return this.string2Regexp(rule);
    },
    /**
     *  find the rule whether is forward route rule and return regexpess rule.
     */
    forwardRouteRule2Regexp: function (rule) {
        var matches = this.regexps.ROUTE_FORWARD.exec(rule);

        if (!matches[1]) {
            rule = this.str.ROUTE_METHOD_PREFIX + '(@)'.replace('@', rule);
        }
        return this.string2Regexp(rule);
    },
    /**
     *  Match url by forward route rule
     *  @return {Object} routeRule
     */
    forwardMatched: function (url, forwardRules) {
        var matchedRoute = null;

        _.each(forwardRules, function (ruleObj) {
            if (ruleObj.rule.exec(url)) {
                matchedRoute = ruleObj;
                return true;
            }
        });

        return matchedRoute;
    },
    /**
     *  Match url by static route rule
     *  @return <Boolean> isMatched
     */
    staticMatched: function (url, staticRules) {
        var isMatched = false;

        _.each(staticRules, function (rule) {
            if (rule.exec(url)) {
                isMatched = true;
                return true;
            }
        });

        return isMatched;
    }
}

module.exports = server;<commit_msg>Fixed route match in node version above 0.x<commit_after>/**
 *  http-route-proxy
 *  http://github.com/switer/http-route-proxy
 *
 *  Copyright (c) 2013 "switer" guankaishe
 *  Licensed under the MIT license.
 *  https://github.com/switer/http-route-proxy/blob/master/LICENSE
 */

var httpProxy = require('http-proxy'), // base on nodejitsu proxy server
    staticServer = require('./lib/static'), // static server
    util = require('./lib/util'),
    colors = require('colors'), // use to pretty console log
    path = require('path'),
    Url = require('url'),
    _ = require('underscore'); // use each method.....

var server = {

    /**
     *  request proxy route match status code
     */
    status: {

        STATIC: 0,
        FORWARD: 1,
        UNMATCHED: 2
    },
    /**
     *   id for each proxy server
     */
    serverId: 1,
    /**
     *   mapping root
     */
    rules: {},
    /**
     *   save proxy host
     */
    proxies: {
        /**
         *  from: Object {hostname, name}
         *  rules: Object {static, forward, rewrite}
         */
    },
    /**
     *   defualt port when config port is not pass in
     */
    defaultPort: 80,
    /**
     *  defualt port for https
     */
    defualtHttpsPort: 443,
    /**
     *   module regexps
     */
    regexps: {
        // validate host string
        HOST: /^[0-9a-zA-Z\.]+\:?[0-9]*$/,
        ROUTE_FORWARD: /^([A-Z]+:)?\/.*$/,
        // static request must be GET method
        ROUTE_STATIC: /^!([A-Z]+:)?\/.*$/
        // ROUTE_REWRITE: /^\^.*/
    },
    /**
     *  for creating request route matching rule
     */
    str: {
        ROUTE_METHOD_PREFIX: '([A-Z]+:)?'
    },
    /**
     *  Proxy enter api
     */
    proxy: function (hosts) {

        // handle each proxy config
        _.each(hosts, function (hostConfig) {

            var hostObj = {
                from: this.parseHost(hostConfig.from),
                routes: this.parseRouteRule(hostConfig)
            }

            var serverId = this.create(hostObj.from, hostObj.routes, hostConfig);
            this.saveHost(hostObj.from, hostObj.routes, serverId);

        }.bind(this));
    },
    /**
     *  support as express connect middleware
     */
    connect: function (config) {

        var proxy = new httpProxy.RoutingProxy(),
            connectConfig = {
                rules: this.parseRouteRule(config)
            },
            _this = this;

        return function (req, res, next) {

            _this.proxyMiddleware(req, res, proxy, connectConfig, function (proxyStatus) {
                // processing in express
                if (proxyStatus.status !== _this.status.FORWARD) {
                    next();
                }
                // forward request
                else {
                    console.log(proxyStatus.message);
                }
            });
        }
    },
    /**
     *   create a server for the one proxy config
     */
    create: function (from, routes, options) {

        var serverId = this.serverId, // for serverid hoisting
            // proxy server options
            proxyOptions = {
                // use client origin
                changeOrigin: true,
                target: {
                    // https: true,
                    // rejectUnauthorized: false
                }
            },
            _this = this; // for hoisting context of `this`
        // nessary for fixing node-http-proxy rejectUnauthorized option not work bug
        process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
        // create proxy server
        var server = httpProxy.createServer(
            /**
             *  middleware
             *  Cross-Domain-Access
             */
            function (req, res, next) {
                // TODO
                next();
            },
            function (req, res, proxy) {

                _this.proxyMiddleware(req, res, proxy, _this.proxies[serverId], function (statusObj) {
                    console.log(statusObj.message);
                });

            /**
             *  must listen hostname, otherwise it will be fail when repeat listening
             *  localhost in the some port
             */
            }).listen(from.port, from.hostname)
              .on('error', function (e) {
                // catch server listen error
                console.log('Create proxy error,'.red.grey + "cause by can't listen host from " +
                    from.hostname.yellow.grey + ' : ' + from.port.toString().blue.grey);
              });

        console.log('Listen from ' + from.hostname.green.grey + ' : ' + from.port.toString().blue.grey);

        // Custom error
        server.proxy.on('proxyError', function (err, req, res) {
            res.writeHead(500, {
                'Content-Type': 'text/plain'
            });
            res.end('Something went wrong. And we are reporting a custom error message.\n' + err);
        });

        return this.serverId ++;
    },
    /**
     *  Proxy middle for handling request and response
     */
    proxyMiddleware: function (req, res, proxy, config, next) {

        var from = this.parseHost(req.headers.host),
            method = req.method,
            // rewrite url to originUrl for proxy agent
            requestURL = req.url = req.url || req.originalUrl,
            url = method.toUpperCase() + ':' + requestURL,
            forwardRouteObj = null;

        // get proxy config by proxy server id
        var proxyConfig = config;

        if (this.staticMatched(url, proxyConfig.rules.static)) {
            // match route is static file response
            var directory = path.resolve(process.cwd(), '.');

            // send static files without server
            staticServer.sendfile(req, res, directory);

            next({
                status: this.status.STATIC,
                message: method.blue.grey + '  ' + requestURL + ' from '.green.grey +  from.hostname + ':' + from.port.toString().blue.grey
            });

        } else if (forwardRouteObj = this.forwardMatched(url, proxyConfig.rules.forward)) {
            // set headers of config
            this.setHeaders(req, res, forwardRouteObj.options.headers);

            var forwardObj = forwardRouteObj.forward,
                // forward options
                proxyForwardOptions = {
                    changeOrigin: true,
                    host: forwardObj.hostname,
                    port: forwardObj.port
                };

            if (forwardRouteObj.options.https) {
                // set https forward options
                proxyForwardOptions = _.extend(proxyForwardOptions, {
                    target: {
                        https: true,
                        rejectUnauthorized: false
                    }
                });
            }

            // forward to remote server
            proxy.proxyRequest(req, res, proxyForwardOptions);

            next({
                status: this.status.FORWARD,
                message: 'forward '.yellow.grey + method.blue.grey + '  ' + requestURL + ' from '.green.grey +  from.hostname + ':' +
                    from.port.toString().blue.grey +' to '.green.grey + forwardObj.protocal + '://' + forwardObj.hostname + ':' +
                    forwardObj.port.toString().blue.grey + requestURL
            });
        }
        else {
            next({
                status: this.status.UNMATCHED,
                message: "Sorry proxy error, http-route-proxy can't match any forward rule, please check your proxy config".red
            });
        }
    },
    /**
     *  Set headers by proxy config
     */
    setHeaders: function (req, res, headers) {
        var reqHeaders = headers && headers.req ? headers.req: {},
            resHeaders = headers && headers.res ? headers.res: {},
            origin = req.headers.origin || req.headers.host;

        // if exist Origin, use "Cross-Domain-Access"
        if (origin) {
            // currently, we only support receiving http request
            res.setHeader('access-control-allow-origin', 'http://' + origin);
            res.setHeader('access-control-allow-credentials', true);
        }
        //rewrite header on config
        if (headers) {
            _.each(reqHeaders, function (value, key) {
                req.headers[key] = value;
            });
            _.each(resHeaders, function (value, key) {
                res.setHeader(key, value);
            });
        }
        // avoid node-http-proxy to rewrite headers
        var setHeader = res.setHeader;
        res.setHeader = function (key, value) {
            if (!resHeaders[key]) setHeader.call(res, key, value);
        }

    },
    /**
     *   host is a string, should be parse to hostname + port object format
     */
    parseHost: function (host, options) {
        options = options || {};

        var hostChunks = host.match(/^([a-z]+)\:\/\/(.*)$/),
            protocal = hostChunks? hostChunks[1]: '',
            hostSection = hostChunks? hostChunks[2]: host,
            hostname = hostSection.split(':')[0],
            port = hostSection.split(':')[1],
            protocalObj = this.protocal(protocal),
            protocalOptions = protocalObj.unknow ? options: protocalObj;

        return {
            protocal: this.options2protocal(protocalOptions),
            protocalOption: protocalOptions,
            hostname: hostname,
            port: port ? parseInt(port) : (protocalOptions.https ? this.defualtHttpsPort : this.defaultPort)
        };
    },
    /**
     *   save each host config
     */
    saveHost: function (fromObj, routeRules, serverId) {

        // save proxy config
        this.proxies[serverId] = {

            from: fromObj,
            rules: routeRules

        };

    },
    /**
     *  Check route if has default action , if has not, put it on first
     */
    checkDefaultAction: function (routes) {

        var isContainDefaultAction = false;

        _.each(routes, function (item) {
            if (item == '/' || item.action == '/') {
                isContainDefaultAction = true;
                return true;
            }
        });
        // put default action on the first tuples
        if (!isContainDefaultAction) routes.unshift('/');
        return routes;
    },
    /**
     *   validate host string
     */
    validHost: function (host) {
        return this.regexps.HOST.exec(host) ? true : false;
    },
    /**
     *
     *  @return Object
     */
    protocal: function (protocal) {
        var protocalObj = {
            http: false,
            https: false,
            websocket: false,
            unknow: false
        };
        if (protocal === 'http') {
            protocalObj.http = true;
        } else if (protocal === 'https') {
            protocalObj.https = true;
        } else if (protocal === 'ws') {
            protocalObj.websocket = true;
        } else {
            protocalObj.unknow = true;
        }
        return protocalObj;
    },
    /**
     *  protocal option to protocal name
     */
    options2protocal: function (options) {

        if (options.https) {
            return 'https';
        } else if (options.websocket) {
            return 'ws';
        } else if (options.http) {
            return 'http';
        } else {
            return 'http';
        }
    },
    /**
     *   parse each route rule to url matched rule
     */
    parseRouteRule: function (options) {
        options = util.copy(options);

        var routes = options.route || [];
        // if route config is empty, give it default
        routes = this.checkDefaultAction(routes);

        var _this = this,
            forwards = [], // forward route rules
            // rewrites = [], // currently it's useless
            statices = [], // static route rules
            forwardObj = null;

        _.each(routes, function (route) {

            // route detail config
            if (_.isObject(route)) {

                var routeOptions = route,
                    forwardObj = _this.parseHost(routeOptions.forward);

                routeOptions = _.extend(routeOptions, forwardObj.protocalOption);

                forwards.push({
                    rule: _this.forwardRouteRule2Regexp(routeOptions.action),
                    forward: forwardObj,
                    options: routeOptions
                });

            }
            // forward route match, route rule in shorthand
            else if (_this.regexps.ROUTE_FORWARD.exec(route)) {
                // parse forward host
                var forwardObj = _this.parseHost(options.to, options);
                // set foward options
                options = _.extend(options, forwardObj.protocalOption);
                // save config
                forwards.push({
                    rule: _this.forwardRouteRule2Regexp(route),
                    forward: forwardObj,
                    options: options
                });

            }
            // static route match, route rule in shorthand
            else if (_this.regexps.ROUTE_STATIC.exec(route)) {
                statices.push(_this.staticRouteRule2Regexp(route));
            }
        });

        return {
            forward: forwards,
            // rewrite: rewrites,
            static: statices
        };
    },
    /**
     *   url route rule to regexp
     */
    string2Regexp: function (rule) {
        rule = rule.replace('/','\\/');
        rule = '^' + rule + '.*$';
        return new RegExp(rule);
    },
    /**
     *  find the rule whether is static route rule and return regexpess rule.
     */
    staticRouteRule2Regexp: function (rule) {
        var matches = this.regexps.ROUTE_STATIC.exec(rule);

        rule = rule.replace(/^!/, '');

        if (!matches[1]) {
            rule = this.str.ROUTE_METHOD_PREFIX + rule;
        }
        return this.string2Regexp(rule);
    },
    /**
     *  find the rule whether is forward route rule and return regexpess rule.
     */
    forwardRouteRule2Regexp: function (rule) {
        var matches = this.regexps.ROUTE_FORWARD.exec(rule);

        if (!matches[1]) {
            rule = this.str.ROUTE_METHOD_PREFIX + '(@)'.replace('@', rule);
        }
        return this.string2Regexp(rule);
    },
    /**
     *  Match url by forward route rule
     *  @return {Object} routeRule
     */
    forwardMatched: function (url, forwardRules) {
        var matchedRoute = null;

        forwardRules.some(function (ruleObj) {
            if (ruleObj.rule.exec(url)) {
                matchedRoute = ruleObj;
                return true;
            }
        });

        return matchedRoute;
    },
    /**
     *  Match url by static route rule
     *  @return <Boolean> isMatched
     */
    staticMatched: function (url, staticRules) {
        var isMatched = false;

        _.each(staticRules, function (rule) {
            if (rule.exec(url)) {
                isMatched = true;
                return true;
            }
        });

        return isMatched;
    }
}

module.exports = server;<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var tools = require('./tools');
var through = require('through2');
var chalk = require('chalk');
var gutil = require('gulp-util');

module.exports = function(options) {

    options = options || {};
    var first = true;

    return through.obj(function (file, enc, callback) {

        if (options.rootDir === undefined) options.rootDir = file.base;

        if (first) {
            gutil.log('gulp-rev-all:', 'Root directory [', options.rootDir, ']');
            first = !first;
        }

        if (file.isNull()) {
            callback(null, file);
            return;
        } else if (file.isStream()) {
            throw new Error('Streams are not supported!');
            callback(null, file);
            return;
        }

        // Only process references in these types of files, otherwise we'll corrupt images etc
        switch(path.extname(file.path)) {
            case '.js':
            case '.css':
            case '.html':
                tools.revReferencesInFile(file, options.rootDir);
        }

        var filenameReved = path.basename(tools.revFile(file.path));
        var base = path.dirname(file.path);
        file.path = path.join(base, filenameReved);

        callback(null, file);

    });

};
<commit_msg>Skip rev of a filename that ends in html<commit_after>var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var tools = require('./tools');
var through = require('through2');
var chalk = require('chalk');
var gutil = require('gulp-util');

module.exports = function(options) {

    options = options || {};
    var first = true;

    return through.obj(function (file, enc, callback) {

        if (options.rootDir === undefined) options.rootDir = file.base;

        if (first) {
            gutil.log('gulp-rev-all:', 'Root directory [', options.rootDir, ']');
            first = !first;
        }

        if (file.isNull()) {
            callback(null, file);
            return;
        } else if (file.isStream()) {
            throw new Error('Streams are not supported!');
            callback(null, file);
            return;
        }

        // Only process references in these types of files, otherwise we'll corrupt images etc
        switch(path.extname(file.path)) {
            case '.js':
            case '.css':
            case '.html':
                tools.revReferencesInFile(file, options.rootDir);
        }

        if (path.extname(file.path) !== '.html') {
            var filenameReved = path.basename(tools.revFile(file.path));
            var base = path.dirname(file.path);
            file.path = path.join(base, filenameReved);
        }

        callback(null, file);

    });

};
<|endoftext|>


<commit_before>var bops = require('bops')

//TODO: handle reviver/dehydrate function like normal
//and handle indentation, like normal.
//if anyone needs this... please send pull request.

exports.stringify = function stringify (o) {
  if(o && bops.is(o))
    return JSON.stringify(':base64:' + bops.to(o, 'base64'))

  if(o && o.toJSON)
    o =  o.toJSON()

  if(o && 'object' === typeof o) {
    var s = ''
    var array = Array.isArray(o)
    s = array ? '[' : '{'
    var first = true

    for(var k in o) {
      var isFunction = 'function' == typeof o[k]
      if(Object.hasOwnProperty.call(o, k) && o[k] !== void(0) && !isFunction) {
        if(!first)
          s += ','
        first = false
        s += array ? stringify(o[k]) : stringify(k) + ':' + stringify(o[k])
      }
    }

    s += array ? ']' : '}'

    return s
  } else if ('string' === typeof o) {
    return JSON.stringify(/^:/.test(o) ? ':' + o : o)
  } else
    return JSON.stringify(o)
}

exports.parse = function (s) {
  return JSON.parse(s, function (key, value) {
    if('string' === typeof value) {
      if(/^:base64:/.test(value))
        return bops.from(value.substring(8), 'base64')
      else
        return /^:/.test(value) ? value.substring(1) : value
    }
    return value
  })
}
<commit_msg>undefineds in arrays should be null<commit_after>var bops = require('bops')

//TODO: handle reviver/dehydrate function like normal
//and handle indentation, like normal.
//if anyone needs this... please send pull request.

exports.stringify = function stringify (o) {
  if(o && bops.is(o))
    return JSON.stringify(':base64:' + bops.to(o, 'base64'))

  if(o && o.toJSON)
    o =  o.toJSON()

  if(o && 'object' === typeof o) {
    var s = ''
    var array = Array.isArray(o)
    s = array ? '[' : '{'
    var first = true

    for(var k in o) {
      var isFunction = 'function' == typeof o[k]
      if(Object.hasOwnProperty.call(o, k) && !isFunction) {
        if(!first)
          s += ','
        first = false
        if (array) {
          s += stringify(o[k])
        } else if (o[k] !== void(0)) {
          s += stringify(k) + ':' + stringify(o[k])
        }
      }
    }

    s += array ? ']' : '}'

    return s
  } else if ('string' === typeof o) {
    return JSON.stringify(/^:/.test(o) ? ':' + o : o)
  } else if ('undefined' === typeof o) {
    return 'null';
  } else
    return JSON.stringify(o)
}

exports.parse = function (s) {
  return JSON.parse(s, function (key, value) {
    if('string' === typeof value) {
      if(/^:base64:/.test(value))
        return bops.from(value.substring(8), 'base64')
      else
        return /^:/.test(value) ? value.substring(1) : value
    }
    return value
  })
}
<|endoftext|>


<commit_before>'use strict'

module.exports = [
  '0.0.1'
, '0.0.2'
, '0.0.3'
, '0.0.4'
, '0.0.5'
, '0.0.6'
, '0.1.0'
, '0.1.1'
, '0.1.2'
, '0.1.3'
, '0.1.4'
, '0.1.5'
, '0.1.6'
, '0.1.7'
, '0.1.8'
, '0.1.9'
, '0.1.10'
, '0.1.11'
, '0.1.12'
, '0.1.13'
, '0.1.14'
, '0.1.15'
, '0.1.16'
, '0.1.17'
, '0.1.18'
, '0.1.19'
, '0.1.20'
, '0.1.21'
, '0.1.22'
, '0.1.23'
, '0.1.24'
, '0.1.25'
, '0.1.26'
, '0.1.27'
, '0.1.28'
, '0.1.29'
, '0.1.30'
, '0.1.31'
, '0.1.32'
, '0.1.33'
, '0.1.90'
, '0.1.91'
, '0.1.92'
, '0.1.93'
, '0.1.94'
, '0.1.95'
, '0.1.96'
, '0.1.97'
, '0.1.98'
, '0.1.99'
, '0.1.100'
, '0.1.101'
, '0.1.102'
, '0.1.103'
, '0.1.104'
, '0.2.0'
, '0.2.1'
, '0.2.2'
, '0.2.3'
, '0.2.4'
, '0.2.5'
, '0.2.6'
, '0.3.0'
, '0.3.1'
, '0.3.2'
, '0.3.3'
, '0.3.4'
, '0.3.5'
, '0.3.6'
, '0.3.7'
, '0.3.8'
, '0.4.0'
, '0.4.1'
, '0.4.2'
, '0.4.3'
, '0.4.4'
, '0.4.5'
, '0.4.6'
, '0.4.7'
, '0.4.8'
, '0.4.9'
, '0.4.10'
, '0.4.11'
, '0.4.12'
, '0.5.0'
, '0.5.1'
, '0.5.2'
, '0.5.3'
, '0.5.4'
, '0.5.5'
, '0.5.6'
, '0.5.7'
, '0.5.8'
, '0.5.9'
, '0.5.10'
, '0.6.0'
, '0.6.1'
, '0.6.2'
, '0.6.3'
, '0.6.4'
, '0.6.5'
, '0.6.6'
, '0.6.7'
, '0.6.8'
, '0.6.9'
, '0.6.10'
, '0.6.11'
, '0.6.12'
, '0.6.13'
, '0.6.14'
, '0.6.15'
, '0.6.16'
, '0.6.17'
, '0.6.18'
, '0.6.19'
, '0.6.20'
, '0.6.21'
, '0.7.0'
, '0.7.1'
, '0.7.2'
, '0.7.3'
, '0.7.4'
, '0.7.5'
, '0.7.6'
, '0.7.7'
, '0.7.8'
, '0.7.9'
, '0.7.10'
, '0.7.11'
, '0.7.12'
, '0.8.0'
, '0.8.1'
, '0.8.2'
, '0.8.3'
, '0.8.4'
, '0.8.5'
, '0.8.6'
, '0.8.7'
, '0.8.8'
, '0.8.9'
, '0.8.10'
, '0.8.11'
, '0.8.12'
, '0.8.13'
, '0.8.14'
, '0.8.15'
, '0.8.16'
, '0.8.17'
, '0.8.18'
, '0.8.19'
, '0.8.20'
, '0.8.21'
, '0.8.22'
, '0.8.23'
, '0.8.24'
, '0.8.25'
, '0.8.26'
, '0.8.27'
, '0.8.28'
, '0.9.0'
, '0.9.1'
, '0.9.2'
, '0.9.3'
, '0.9.4'
, '0.9.5'
, '0.9.6'
, '0.9.7'
, '0.9.8'
, '0.9.9'
, '0.9.10'
, '0.9.11'
, '0.9.12'
, '0.10.0'
, '0.10.1'
, '0.10.2'
, '0.10.3'
, '0.10.4'
, '0.10.5'
, '0.10.6'
, '0.10.7'
, '0.10.8'
, '0.10.9'
, '0.10.10'
, '0.10.11'
, '0.10.12'
, '0.10.13'
, '0.10.14'
, '0.10.15'
, '0.10.16'
, '0.10.17'
, '0.10.18'
, '0.10.19'
, '0.10.20'
, '0.10.21'
, '0.10.22'
, '0.10.23'
, '0.10.24'
, '0.10.25'
, '0.10.26'
, '0.10.27'
, '0.10.28'
, '0.10.29'
, '0.10.30'
, '0.10.31'
, '0.10.32'
, '0.10.33'
, '0.10.34'
, '0.10.35'
, '0.10.36'
, '0.10.37'
, '0.10.38'
, '0.10.39'
, '0.10.40'
, '0.10.41'
, '0.10.42'
, '0.10.43'
, '0.10.44'
, '0.10.45'
, '0.10.46'
, '0.10.47'
, '0.10.48'
, '0.11.0'
, '0.11.1'
, '0.11.2'
, '0.11.3'
, '0.11.4'
, '0.11.5'
, '0.11.6'
, '0.11.7'
, '0.11.8'
, '0.11.9'
, '0.11.10'
, '0.11.11'
, '0.11.12'
, '0.11.13'
, '0.11.14'
, '0.11.15'
, '0.11.16'
, '0.12.0'
, '0.12.1'
, '0.12.2'
, '0.12.3'
, '0.12.4'
, '0.12.5'
, '0.12.6'
, '0.12.7'
, '0.12.8'
, '0.12.9'
, '0.12.10'
, '0.12.11'
, '0.12.12'
, '0.12.13'
, '0.12.14'
, '0.12.15'
, '0.12.16'
, '0.12.17'
, '1.0.0'
, '1.0.1'
, '1.0.2'
, '1.0.3'
, '1.0.4'
, '1.1.0'
, '1.2.0'
, '1.3.0'
, '1.4.1'
, '1.4.2'
, '1.4.3'
, '1.5.0'
, '1.5.1'
, '1.6.0'
, '1.6.1'
, '1.6.2'
, '1.6.3'
, '1.6.4'
, '1.7.0'
, '1.7.1'
, '1.8.1'
, '1.8.2'
, '1.8.3'
, '1.8.4'
, '2.0.0'
, '2.0.1'
, '2.0.2'
, '2.1.0'
, '2.2.0'
, '2.2.1'
, '2.3.0'
, '2.3.1'
, '2.3.2'
, '2.3.3'
, '2.3.4'
, '2.4.0'
, '2.5.0'
, '3.0.0'
, '3.1.0'
, '3.2.0'
, '3.3.0'
, '3.3.1'
, '4.0.0'
, '4.1.0'
, '4.1.1'
, '4.1.2'
, '4.2.0'
, '4.2.1'
, '4.2.2'
, '4.2.3'
, '4.2.4'
, '4.2.5'
, '4.2.6'
, '4.3.0'
, '4.3.1'
, '4.3.2'
, '4.4.0'
, '4.4.1'
, '4.4.2'
, '4.4.3'
, '4.4.4'
, '4.4.5'
, '4.4.6'
, '4.4.7'
, '4.5.0'
, '4.6.0'
, '4.6.1'
, '4.6.2'
, '5.0.0'
, '5.1.0'
, '5.1.1'
, '5.2.0'
, '5.3.0'
, '5.4.0'
, '5.4.1'
, '5.5.0'
, '5.6.0'
, '5.7.0'
, '5.7.1'
, '5.8.0'
, '5.9.0'
, '5.9.1'
, '5.10.0'
, '5.10.1'
, '5.11.0'
, '5.11.1'
, '5.12.0'
, '6.0.0'
, '6.1.0'
, '6.2.0'
, '6.2.1'
, '6.2.2'
, '6.3.0'
, '6.3.1'
, '6.4.0'
, '6.5.0'
, '6.6.0'
, '6.7.0'
, '6.8.0'
, '6.8.1'
, '6.9.0'
, '6.9.1'
, '7.0.0'
, '7.1.0'
]
<commit_msg>add v4.7.0<commit_after>'use strict'

module.exports = [
  '0.0.1'
, '0.0.2'
, '0.0.3'
, '0.0.4'
, '0.0.5'
, '0.0.6'
, '0.1.0'
, '0.1.1'
, '0.1.2'
, '0.1.3'
, '0.1.4'
, '0.1.5'
, '0.1.6'
, '0.1.7'
, '0.1.8'
, '0.1.9'
, '0.1.10'
, '0.1.11'
, '0.1.12'
, '0.1.13'
, '0.1.14'
, '0.1.15'
, '0.1.16'
, '0.1.17'
, '0.1.18'
, '0.1.19'
, '0.1.20'
, '0.1.21'
, '0.1.22'
, '0.1.23'
, '0.1.24'
, '0.1.25'
, '0.1.26'
, '0.1.27'
, '0.1.28'
, '0.1.29'
, '0.1.30'
, '0.1.31'
, '0.1.32'
, '0.1.33'
, '0.1.90'
, '0.1.91'
, '0.1.92'
, '0.1.93'
, '0.1.94'
, '0.1.95'
, '0.1.96'
, '0.1.97'
, '0.1.98'
, '0.1.99'
, '0.1.100'
, '0.1.101'
, '0.1.102'
, '0.1.103'
, '0.1.104'
, '0.2.0'
, '0.2.1'
, '0.2.2'
, '0.2.3'
, '0.2.4'
, '0.2.5'
, '0.2.6'
, '0.3.0'
, '0.3.1'
, '0.3.2'
, '0.3.3'
, '0.3.4'
, '0.3.5'
, '0.3.6'
, '0.3.7'
, '0.3.8'
, '0.4.0'
, '0.4.1'
, '0.4.2'
, '0.4.3'
, '0.4.4'
, '0.4.5'
, '0.4.6'
, '0.4.7'
, '0.4.8'
, '0.4.9'
, '0.4.10'
, '0.4.11'
, '0.4.12'
, '0.5.0'
, '0.5.1'
, '0.5.2'
, '0.5.3'
, '0.5.4'
, '0.5.5'
, '0.5.6'
, '0.5.7'
, '0.5.8'
, '0.5.9'
, '0.5.10'
, '0.6.0'
, '0.6.1'
, '0.6.2'
, '0.6.3'
, '0.6.4'
, '0.6.5'
, '0.6.6'
, '0.6.7'
, '0.6.8'
, '0.6.9'
, '0.6.10'
, '0.6.11'
, '0.6.12'
, '0.6.13'
, '0.6.14'
, '0.6.15'
, '0.6.16'
, '0.6.17'
, '0.6.18'
, '0.6.19'
, '0.6.20'
, '0.6.21'
, '0.7.0'
, '0.7.1'
, '0.7.2'
, '0.7.3'
, '0.7.4'
, '0.7.5'
, '0.7.6'
, '0.7.7'
, '0.7.8'
, '0.7.9'
, '0.7.10'
, '0.7.11'
, '0.7.12'
, '0.8.0'
, '0.8.1'
, '0.8.2'
, '0.8.3'
, '0.8.4'
, '0.8.5'
, '0.8.6'
, '0.8.7'
, '0.8.8'
, '0.8.9'
, '0.8.10'
, '0.8.11'
, '0.8.12'
, '0.8.13'
, '0.8.14'
, '0.8.15'
, '0.8.16'
, '0.8.17'
, '0.8.18'
, '0.8.19'
, '0.8.20'
, '0.8.21'
, '0.8.22'
, '0.8.23'
, '0.8.24'
, '0.8.25'
, '0.8.26'
, '0.8.27'
, '0.8.28'
, '0.9.0'
, '0.9.1'
, '0.9.2'
, '0.9.3'
, '0.9.4'
, '0.9.5'
, '0.9.6'
, '0.9.7'
, '0.9.8'
, '0.9.9'
, '0.9.10'
, '0.9.11'
, '0.9.12'
, '0.10.0'
, '0.10.1'
, '0.10.2'
, '0.10.3'
, '0.10.4'
, '0.10.5'
, '0.10.6'
, '0.10.7'
, '0.10.8'
, '0.10.9'
, '0.10.10'
, '0.10.11'
, '0.10.12'
, '0.10.13'
, '0.10.14'
, '0.10.15'
, '0.10.16'
, '0.10.17'
, '0.10.18'
, '0.10.19'
, '0.10.20'
, '0.10.21'
, '0.10.22'
, '0.10.23'
, '0.10.24'
, '0.10.25'
, '0.10.26'
, '0.10.27'
, '0.10.28'
, '0.10.29'
, '0.10.30'
, '0.10.31'
, '0.10.32'
, '0.10.33'
, '0.10.34'
, '0.10.35'
, '0.10.36'
, '0.10.37'
, '0.10.38'
, '0.10.39'
, '0.10.40'
, '0.10.41'
, '0.10.42'
, '0.10.43'
, '0.10.44'
, '0.10.45'
, '0.10.46'
, '0.10.47'
, '0.10.48'
, '0.11.0'
, '0.11.1'
, '0.11.2'
, '0.11.3'
, '0.11.4'
, '0.11.5'
, '0.11.6'
, '0.11.7'
, '0.11.8'
, '0.11.9'
, '0.11.10'
, '0.11.11'
, '0.11.12'
, '0.11.13'
, '0.11.14'
, '0.11.15'
, '0.11.16'
, '0.12.0'
, '0.12.1'
, '0.12.2'
, '0.12.3'
, '0.12.4'
, '0.12.5'
, '0.12.6'
, '0.12.7'
, '0.12.8'
, '0.12.9'
, '0.12.10'
, '0.12.11'
, '0.12.12'
, '0.12.13'
, '0.12.14'
, '0.12.15'
, '0.12.16'
, '0.12.17'
, '1.0.0'
, '1.0.1'
, '1.0.2'
, '1.0.3'
, '1.0.4'
, '1.1.0'
, '1.2.0'
, '1.3.0'
, '1.4.1'
, '1.4.2'
, '1.4.3'
, '1.5.0'
, '1.5.1'
, '1.6.0'
, '1.6.1'
, '1.6.2'
, '1.6.3'
, '1.6.4'
, '1.7.0'
, '1.7.1'
, '1.8.1'
, '1.8.2'
, '1.8.3'
, '1.8.4'
, '2.0.0'
, '2.0.1'
, '2.0.2'
, '2.1.0'
, '2.2.0'
, '2.2.1'
, '2.3.0'
, '2.3.1'
, '2.3.2'
, '2.3.3'
, '2.3.4'
, '2.4.0'
, '2.5.0'
, '3.0.0'
, '3.1.0'
, '3.2.0'
, '3.3.0'
, '3.3.1'
, '4.0.0'
, '4.1.0'
, '4.1.1'
, '4.1.2'
, '4.2.0'
, '4.2.1'
, '4.2.2'
, '4.2.3'
, '4.2.4'
, '4.2.5'
, '4.2.6'
, '4.3.0'
, '4.3.1'
, '4.3.2'
, '4.4.0'
, '4.4.1'
, '4.4.2'
, '4.4.3'
, '4.4.4'
, '4.4.5'
, '4.4.6'
, '4.4.7'
, '4.5.0'
, '4.6.0'
, '4.6.1'
, '4.6.2'
, '4.7.0'
, '5.0.0'
, '5.1.0'
, '5.1.1'
, '5.2.0'
, '5.3.0'
, '5.4.0'
, '5.4.1'
, '5.5.0'
, '5.6.0'
, '5.7.0'
, '5.7.1'
, '5.8.0'
, '5.9.0'
, '5.9.1'
, '5.10.0'
, '5.10.1'
, '5.11.0'
, '5.11.1'
, '5.12.0'
, '6.0.0'
, '6.1.0'
, '6.2.0'
, '6.2.1'
, '6.2.2'
, '6.3.0'
, '6.3.1'
, '6.4.0'
, '6.5.0'
, '6.6.0'
, '6.7.0'
, '6.8.0'
, '6.8.1'
, '6.9.0'
, '6.9.1'
, '7.0.0'
, '7.1.0'
]
<|endoftext|>


<commit_before>var config = require('./config');
var pocket = require('pocket-api');
var Q = require('q');
var request = require('request');
var express = require('express');
var session = require('express-session');

var app = express();

var pocketUrl = {
	redirect: 'https://getpocket.com/auth/authorize',
	get: 'https://getpocket.com/v3/get'
};
var headers = {
	"content-type": "application/x-www-form-urlencoded",
	"X-Accept": "application/json"
};

app.set('json spaces', 4);
app.use(session({
	secret: config.secret,
	cookie: {
		maxAge: 60000
	}
}));

app.get('/', function(req, res) {
	constructRedirectUrl(req.session).then(function(url) {
		res.send('Authorize');
	});
});

app.get('/response', function(req, res) {
	getAccessToken(req.session)
	.then(getFavorites)
	.then(function(favorites) {
		res.setHeader('Content-Disposition', 'attachment; filename="' + req.session.username + '-Reading-List.md"');
res.setHeader('Content-type', 'text/plain');
		res.write(formatFavorites(favorites, req.session));
		res.end();
	});
});

app.listen(config.port);

function constructRedirectUrl(session) {
	return Q.fcall(function() {
		return getRequestToken(session);
	})
	.then(function() {
		return pocketUrl.redirect + '?request_token=' + session.request_token + '&redirect_uri=' + config.url + ':' + config.port + '/response';
	});
}

function getRequestToken(session) {
	var d = Q.defer();
	pocket.getRequestToken(config.api_key, function(data) {
		session.request_token = data.code;
		d.resolve();
	});
	return d.promise;
}

function getAccessToken(session) {
	var d = Q.defer();
	pocket.getAccessToken(config.api_key, session.request_token, function(data) {
		session.username = data.username;
		session.access_token = data.access_token;
		d.resolve(session);
	});
	return d.promise;
}

function getFavorites(session) {
	var d = Q.defer();
	var options = {
		headers: headers,
		url: pocketUrl.get,
		body: 'consumer_key=' + config.api_key + '&access_token=' + session.access_token + '&favorite=1&detailType=complete'
	};

	request.post(options, function(error, response, body) {
		var list = JSON.parse(body).list;
		var favorites = Object.keys(list).map(function(key) {
			return list[key];
		});
		d.resolve(favorites);
	});
	return d.promise;
}

function formatFavorites(favorites, session) {
	var tags = getTags(favorites);
	var mkdn = '# ' + session.username + '\'s Reading List\n\n';
	mkdn += tags.map(function(tag) {
		return formatTag(favorites, tag);
	}).join('\n');
	return mkdn;
}

function getTags(favorites) {
	return [].concat.apply([], favorites.map(function(item) {
		return getItemTags(item);
	})).filter(onlyUnique).sort();
}

function getItemTags(item) {
	var tagKeys = Object.keys(item.tags);
	return tagKeys.map(function(tagKey) {
		return item.tags[tagKey].tag;
	});
}

function onlyUnique(value, index, self) {
	return self.indexOf(value) === index;
}

function formatTag(favorites, tag) {
	var filteredFavorites = filterFavorites(favorites, tag);
	var mkdn = '## ' + tag + '\n';
	mkdn += '\n' + filteredFavorites.map(function(item) {
		return '- [' + item.resolved_title + '](' + item.resolved_url + ') - ' + item.excerpt + '\n';
	}).join('');
	return mkdn;
}

function filterFavorites(favorites, tag) {
	return favorites.filter(function(item) {
		return getItemTags(item).indexOf(tag) != -1;
	}).sort(function(item1, item2) {
		return item1.resolved_title.localeCompare(item2.resolved_title);
	});
}
<commit_msg>Add tag capitalization<commit_after>var config = require('./config');
var pocket = require('pocket-api');
var Q = require('q');
var request = require('request');
var express = require('express');
var session = require('express-session');

var app = express();

var pocketUrl = {
	redirect: 'https://getpocket.com/auth/authorize',
	get: 'https://getpocket.com/v3/get'
};
var headers = {
	"content-type": "application/x-www-form-urlencoded",
	"X-Accept": "application/json"
};

app.set('json spaces', 4);
app.use(session({
	secret: config.secret,
	cookie: {
		maxAge: 60000
	}
}));

app.get('/', function(req, res) {
	constructRedirectUrl(req.session).then(function(url) {
		res.send('Authorize');
	});
});

app.get('/response', function(req, res) {
	getAccessToken(req.session)
	.then(getFavorites)
	.then(function(favorites) {
		res.setHeader('Content-Disposition', 'attachment; filename="' + req.session.username + '-Reading-List.md"');
res.setHeader('Content-type', 'text/plain');
		res.write(formatFavorites(favorites, req.session));
		res.end();
	});
});

app.listen(config.port);

function constructRedirectUrl(session) {
	return Q.fcall(function() {
		return getRequestToken(session);
	})
	.then(function() {
		return pocketUrl.redirect + '?request_token=' + session.request_token + '&redirect_uri=' + config.url + ':' + config.port + '/response';
	});
}

function getRequestToken(session) {
	var d = Q.defer();
	pocket.getRequestToken(config.api_key, function(data) {
		session.request_token = data.code;
		d.resolve();
	});
	return d.promise;
}

function getAccessToken(session) {
	var d = Q.defer();
	pocket.getAccessToken(config.api_key, session.request_token, function(data) {
		session.username = data.username;
		session.access_token = data.access_token;
		d.resolve(session);
	});
	return d.promise;
}

function getFavorites(session) {
	var d = Q.defer();
	var options = {
		headers: headers,
		url: pocketUrl.get,
		body: 'consumer_key=' + config.api_key + '&access_token=' + session.access_token + '&favorite=1&detailType=complete'
	};

	request.post(options, function(error, response, body) {
		var list = JSON.parse(body).list;
		var favorites = Object.keys(list).map(function(key) {
			return list[key];
		});
		d.resolve(favorites);
	});
	return d.promise;
}

function formatFavorites(favorites, session) {
	var tags = getTags(favorites);
	var mkdn = '# ' + session.username + '\'s Reading List\n\n';
	mkdn += tags.map(function(tag) {
		return formatTag(favorites, tag);
	}).join('\n');
	return mkdn;
}

function getTags(favorites) {
	return [].concat.apply([], favorites.map(function(item) {
		return getItemTags(item);
	})).filter(onlyUnique).sort();
}

function getItemTags(item) {
	var tagKeys = Object.keys(item.tags);
	return tagKeys.map(function(tagKey) {
		return item.tags[tagKey].tag;
	});
}

function onlyUnique(value, index, self) {
	return self.indexOf(value) === index;
}

function formatTag(favorites, tag) {
	var filteredFavorites = filterFavorites(favorites, tag);
	var mkdn = '## ' + tag.split(' ').map(capitalize).join(' ') + '\n';
	mkdn += '\n' + filteredFavorites.map(function(item) {
		return '- [' + item.resolved_title + '](' + item.resolved_url + ') - ' + item.excerpt + '\n';
	}).join('');
	return mkdn;
}

function filterFavorites(favorites, tag) {
	return favorites.filter(function(item) {
		return getItemTags(item).indexOf(tag) != -1;
	}).sort(function(item1, item2) {
		return item1.resolved_title.localeCompare(item2.resolved_title);
	});
}

function capitalize(word) {
	if (word.length == 1) {
		return word[0].toUpperCase();
	}
	return word[0].toUpperCase() + word.slice(1);
}
<|endoftext|>