<commit_before>const {EventEmitter} = require('promise-events')
const GitHubApi = require('github')
const Bottleneck = require('bottleneck')
const express = require('express')
const Context = require('./context')

/**
 * The `robot` parameter available to plugins
 *
 * @property {logger} log - A logger
 */
class Robot {
  constructor ({app, cache, logger, router, catchErrors} = {}) {
    this.events = new EventEmitter()
    this.app = app
    this.cache = cache
    this.router = router || new express.Router()
    this.log = wrapLogger(logger)
    this.catchErrors = catchErrors
  }

  async receive (event) {
    return this.events.emit('*', event).then(() => {
      return this.events.emit(event.event, event)
    })
  }

  /**
   * Get an {@link http://expressjs.com|express} router that can be used to
   * expose HTTP endpoints
   *
   * @example
   * module.exports = robot => {
   *   // Get an express router to expose new HTTP endpoints
   *   const app = robot.route('/my-plugin');
   *
   *   // Use any middleware
   *   app.use(require('express').static(__dirname + '/public'));
   *
   *   // Add a new route
   *   app.get('/hello-world', (req, res) => {
   *     res.end('Hello World');
   *   });
   * };
   *
   * @param {string} path - the prefix for the routes
   * @returns {@link http://expressjs.com/en/4x/api.html#router|express.Router}
   */
  route (path) {
    if (path) {
      const router = new express.Router()
      this.router.use(path, router)
      return router
    } else {
      return this.router
    }
  }

  /**
   * Listen for [GitHub webhooks](https://developer.github.com/webhooks/),
   * which are fired for almost every significant action that users take on
   * GitHub.
   *
   * @param {string} event - the name of the [GitHub webhook
   * event](https://developer.github.com/webhooks/#events). Most events also
   * include an "action". For example, the * [`issues`](
   * https://developer.github.com/v3/activity/events/types/#issuesevent)
   * event has actions of `assigned`, `unassigned`, `labeled`, `unlabeled`,
   * `opened`, `edited`, `milestoned`, `demilestoned`, `closed`, and `reopened`.
   * Often, your bot will only care about one type of action, so you can append
   * it to the event name with a `.`, like `issues.closed`.
   *
   * @param {Robot~webhookCallback} callback - a function to call when the
   * webhook is received.
   *
   * @example
   *
   * robot.on('push', context => {
   *   // Code was just pushed.
   * });
   *
   * robot.on('issues.opened', context => {
   *   // An issue was just opened.
   * });
   */
  on (event, callback) {
    if (event.constructor === Array) {
      event.forEach(e => this.on(e, callback));
      return;
    }
    if (callback.length === 2) {
      const caller = (new Error()).stack.split('\n')[2]
      console.warn('DEPRECATED: Event callbacks now only take a single `context` argument. This feature will be removed in version 0.10')
      console.warn(caller)
    }

    const [name, action] = event.split('.')

    return this.events.on(name, async event => {
      if (!action || action === event.payload.action) {
        try {
          const github = await this.auth(event.payload.installation.id)
          const context = new Context(event, github)
          await callback(context, context /* DEPRECATED: for backward compat */)
          // This will be removed in version 0.10 and will become:
          // await callback(context)
        } catch (err) {
          this.log.error({err, event})
          if (!this.catchErrors) {
            throw err
          }
        }
      }
    })
  }

  /**
   * Authenticate and get a GitHub client that can be used to make API calls.
   *
   * You'll probably want to use `context.github` instead.
   *
   * **Note**: `robot.auth` is asynchronous, so it needs to be prefixed with a
   * [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)
   * to wait for the magic to happen.
   *
   * @example
   *
   *  module.exports = function(robot) {
   *    robot.on('issues.opened', async context => {
   *      const github = await robot.auth();
   *    });
   *  };
   *
   * @param {number} [id] - ID of the installation, which can be extracted from
   * `context.payload.installation.id`. If called without this parameter, the
   * client wil authenticate [as the app](https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/#authenticating-as-a-github-app)
   * instead of as a specific installation, which means it can only be used for
   * [app APIs](https://developer.github.com/v3/apps/).
   *
   * @returns {Promise<github>} - An authenticated GitHub API client
   * @private
   */
  async auth (id) {
    let github

    if (id) {
      const res = await this.cache.wrap(`app:${id}:token`, () => {
        this.log.trace(`creating token for installation ${id}`)
        return this.app.createToken(id)
      }, {ttl: 60 * 59}) // Cache for 1 minute less than GitHub expiry

      github = new GitHubApi({debug: process.env.LOG_LEVEL === 'trace'})
      github.authenticate({type: 'token', token: res.data.token})
    } else {
      github = await this.app.asApp()
    }

    return probotEnhancedClient(github)
  }
}

function probotEnhancedClient (github) {
  github = rateLimitedClient(github)

  github.paginate = require('./paginate')

  return github
}

// Hack client to only allow one request at a time with a 1s delay
// https://github.com/mikedeboer/node-github/issues/526
function rateLimitedClient (github) {
  const limiter = new Bottleneck(1, 1000)
  const oldHandler = github.handler
  github.handler = (msg, block, callback) => {
    limiter.submit(oldHandler.bind(github), msg, block, callback)
  }
  return github
}

// Return a function that defaults to "debug" level, and has properties for
// other levels:
//
//     robot.log("debug")
//     robot.log.trace("verbose details");
//
function wrapLogger (logger) {
  const fn = logger ? logger.debug.bind(logger) : function () { };

  // Add level methods on the logger
  ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(level => {
    fn[level] = logger ? logger[level].bind(logger) : function () { }
  })

  return fn
}

module.exports = (...args) => new Robot(...args)

/**
* Do the thing
* @callback Robot~webhookCallback
* @param {Context} context - the context of the event that was triggered,
*   including `context.payload`, and helpers for extracting information from
*   the payload, which can be passed to GitHub API calls.
*
*  ```js
*  module.exports = robot => {
*    robot.on('push', context => {
*      // Code was pushed to the repo, what should we do with it?
*      robot.log(context);
*    });
*  };
*  ```
*/

/**
 * A [GitHub webhook event](https://developer.github.com/webhooks/#events) payload
 *
 * @typedef payload
 */

/**
 * the [github Node.js module](https://github.com/mikedeboer/node-github),
 * which wraps the [GitHub API](https://developer.github.com/v3/) and allows
 * you to do almost anything programmatically that you can do through a web
 * browser.
 * @typedef github
 * @see {@link https://github.com/mikedeboer/node-github}
 */

 /**
  * A logger backed by [bunyan](https://github.com/trentm/node-bunyan)
  *
  * The default log level is `debug`, but you can change it by setting the
  * `LOG_LEVEL` environment variable to `trace`, `info`, `warn`, `error`, or
  * `fatal`.
  *
  * @typedef logger
  *
  * @example
  *
  * robot.log("This is a debug message");
  * robot.log.debug("…so is this");
  * robot.log.trace("Now we're talking");
  * robot.log.info("I thought you should know…");
  * robot.log.warn("Woah there");
  * robot.log.error("ETOOMANYLOGS");
  * robot.log.fatal("Goodbye, cruel world!");
  */
<commit_msg>Fix lint error by removing semicolons<commit_after>const {EventEmitter} = require('promise-events')
const GitHubApi = require('github')
const Bottleneck = require('bottleneck')
const express = require('express')
const Context = require('./context')

/**
 * The `robot` parameter available to plugins
 *
 * @property {logger} log - A logger
 */
class Robot {
  constructor ({app, cache, logger, router, catchErrors} = {}) {
    this.events = new EventEmitter()
    this.app = app
    this.cache = cache
    this.router = router || new express.Router()
    this.log = wrapLogger(logger)
    this.catchErrors = catchErrors
  }

  async receive (event) {
    return this.events.emit('*', event).then(() => {
      return this.events.emit(event.event, event)
    })
  }

  /**
   * Get an {@link http://expressjs.com|express} router that can be used to
   * expose HTTP endpoints
   *
   * @example
   * module.exports = robot => {
   *   // Get an express router to expose new HTTP endpoints
   *   const app = robot.route('/my-plugin');
   *
   *   // Use any middleware
   *   app.use(require('express').static(__dirname + '/public'));
   *
   *   // Add a new route
   *   app.get('/hello-world', (req, res) => {
   *     res.end('Hello World');
   *   });
   * };
   *
   * @param {string} path - the prefix for the routes
   * @returns {@link http://expressjs.com/en/4x/api.html#router|express.Router}
   */
  route (path) {
    if (path) {
      const router = new express.Router()
      this.router.use(path, router)
      return router
    } else {
      return this.router
    }
  }

  /**
   * Listen for [GitHub webhooks](https://developer.github.com/webhooks/),
   * which are fired for almost every significant action that users take on
   * GitHub.
   *
   * @param {string} event - the name of the [GitHub webhook
   * event](https://developer.github.com/webhooks/#events). Most events also
   * include an "action". For example, the * [`issues`](
   * https://developer.github.com/v3/activity/events/types/#issuesevent)
   * event has actions of `assigned`, `unassigned`, `labeled`, `unlabeled`,
   * `opened`, `edited`, `milestoned`, `demilestoned`, `closed`, and `reopened`.
   * Often, your bot will only care about one type of action, so you can append
   * it to the event name with a `.`, like `issues.closed`.
   *
   * @param {Robot~webhookCallback} callback - a function to call when the
   * webhook is received.
   *
   * @example
   *
   * robot.on('push', context => {
   *   // Code was just pushed.
   * });
   *
   * robot.on('issues.opened', context => {
   *   // An issue was just opened.
   * });
   */
  on (event, callback) {
    if (event.constructor === Array) {
      event.forEach(e => this.on(e, callback))
      return
    }
    if (callback.length === 2) {
      const caller = (new Error()).stack.split('\n')[2]
      console.warn('DEPRECATED: Event callbacks now only take a single `context` argument. This feature will be removed in version 0.10')
      console.warn(caller)
    }

    const [name, action] = event.split('.')

    return this.events.on(name, async event => {
      if (!action || action === event.payload.action) {
        try {
          const github = await this.auth(event.payload.installation.id)
          const context = new Context(event, github)
          await callback(context, context /* DEPRECATED: for backward compat */)
          // This will be removed in version 0.10 and will become:
          // await callback(context)
        } catch (err) {
          this.log.error({err, event})
          if (!this.catchErrors) {
            throw err
          }
        }
      }
    })
  }

  /**
   * Authenticate and get a GitHub client that can be used to make API calls.
   *
   * You'll probably want to use `context.github` instead.
   *
   * **Note**: `robot.auth` is asynchronous, so it needs to be prefixed with a
   * [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)
   * to wait for the magic to happen.
   *
   * @example
   *
   *  module.exports = function(robot) {
   *    robot.on('issues.opened', async context => {
   *      const github = await robot.auth();
   *    });
   *  };
   *
   * @param {number} [id] - ID of the installation, which can be extracted from
   * `context.payload.installation.id`. If called without this parameter, the
   * client wil authenticate [as the app](https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/#authenticating-as-a-github-app)
   * instead of as a specific installation, which means it can only be used for
   * [app APIs](https://developer.github.com/v3/apps/).
   *
   * @returns {Promise<github>} - An authenticated GitHub API client
   * @private
   */
  async auth (id) {
    let github

    if (id) {
      const res = await this.cache.wrap(`app:${id}:token`, () => {
        this.log.trace(`creating token for installation ${id}`)
        return this.app.createToken(id)
      }, {ttl: 60 * 59}) // Cache for 1 minute less than GitHub expiry

      github = new GitHubApi({debug: process.env.LOG_LEVEL === 'trace'})
      github.authenticate({type: 'token', token: res.data.token})
    } else {
      github = await this.app.asApp()
    }

    return probotEnhancedClient(github)
  }
}

function probotEnhancedClient (github) {
  github = rateLimitedClient(github)

  github.paginate = require('./paginate')

  return github
}

// Hack client to only allow one request at a time with a 1s delay
// https://github.com/mikedeboer/node-github/issues/526
function rateLimitedClient (github) {
  const limiter = new Bottleneck(1, 1000)
  const oldHandler = github.handler
  github.handler = (msg, block, callback) => {
    limiter.submit(oldHandler.bind(github), msg, block, callback)
  }
  return github
}

// Return a function that defaults to "debug" level, and has properties for
// other levels:
//
//     robot.log("debug")
//     robot.log.trace("verbose details");
//
function wrapLogger (logger) {
  const fn = logger ? logger.debug.bind(logger) : function () { };

  // Add level methods on the logger
  ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(level => {
    fn[level] = logger ? logger[level].bind(logger) : function () { }
  })

  return fn
}

module.exports = (...args) => new Robot(...args)

/**
* Do the thing
* @callback Robot~webhookCallback
* @param {Context} context - the context of the event that was triggered,
*   including `context.payload`, and helpers for extracting information from
*   the payload, which can be passed to GitHub API calls.
*
*  ```js
*  module.exports = robot => {
*    robot.on('push', context => {
*      // Code was pushed to the repo, what should we do with it?
*      robot.log(context);
*    });
*  };
*  ```
*/

/**
 * A [GitHub webhook event](https://developer.github.com/webhooks/#events) payload
 *
 * @typedef payload
 */

/**
 * the [github Node.js module](https://github.com/mikedeboer/node-github),
 * which wraps the [GitHub API](https://developer.github.com/v3/) and allows
 * you to do almost anything programmatically that you can do through a web
 * browser.
 * @typedef github
 * @see {@link https://github.com/mikedeboer/node-github}
 */

 /**
  * A logger backed by [bunyan](https://github.com/trentm/node-bunyan)
  *
  * The default log level is `debug`, but you can change it by setting the
  * `LOG_LEVEL` environment variable to `trace`, `info`, `warn`, `error`, or
  * `fatal`.
  *
  * @typedef logger
  *
  * @example
  *
  * robot.log("This is a debug message");
  * robot.log.debug("…so is this");
  * robot.log.trace("Now we're talking");
  * robot.log.info("I thought you should know…");
  * robot.log.warn("Woah there");
  * robot.log.error("ETOOMANYLOGS");
  * robot.log.fatal("Goodbye, cruel world!");
  */
<|endoftext|>


<commit_before>'use strict';

module.exports = {
    createSourceError,
    replace,
    count
};

const padStart = require('pad-start');
const repeat = require('repeat-string');

const POINTER = '> ';

/**
 * An alternate to template strings.
 *
 * @example
 *   replace('Unexpected token "{token}"', { token: 'for' });
 *   // 'Unexpected token "for"'
 *
 * @param  {String} data
 * @param  {Object} replacers
 * @return {String}
 */

function replace(data, replacers) {
    for (let str in replacers) {
        data = data.replace(new RegExp(`{${str}}`, 'g'), replacers[str])
    }

    return data;
}

/**
 * Creates an error that points to the line and column the error occured.
 * If the `line` option is not passed, only the error message passed will
 * be shown.
 *
 * @param  {Object} options
 * @param  {Object} options.name - The error objects name
 * @param  {Object} [options.line]
 * @param  {Object} [options.column]
 * @param  {Object} [options.filename]
 * @return {Error}
 */

const toLine = num => num + 1;

function createSourceError(options) {
    const { name,
            line: _line,
            column: _column,
            source,
            message='',
            filename='[Source]' } = options;

    const column = _column + 1;
    const isLineDefined  = (typeof _line === 'number' && isFinite(_line));
    const isColumnNumber = typeof column === 'number' && column === column;
    const tolerance = 3;
    let errorMessage;

    if (isLineDefined) {
        // TODO: Might want to optimize this in case the string is large
        const lines  = source.split('\n');
        const length = lines.length;

        // Restrict the line to be between 0 and the total number of lines
        const line = Math.min(lines.length, Math.max(_line, 1));

        const _start = Math.min(length - tolerance, Math.max(line - tolerance - 1));
        const start  = Math.max(_start, 0);
        const end    = Math.min(length, line + tolerance);

        // Pointer line can not be more or less than the start
        const pointerLine = Math.max(start, Math.min(line, end));

        const linecol     = isLineDefined ? ` (${pointerLine}:${_column})` : '';
        const header      = `${filename}: ${message}${linecol}\n`;
        const padding     = lines.length.toString().length + POINTER.length;

        errorMessage = header +
            lines.slice(start, end).map(function (text, index) {
                const currentLine = start + index + 1;
                let beginning = String(currentLine);
                let leadingSpace;
                let arrowSpacing;

                if (currentLine === pointerLine) {
                    beginning = POINTER + beginning;

                    if (isColumnNumber) {
                        leadingSpace = repeat(' ', padding + 1);
                        arrowSpacing = repeat(' ', Math.max(0, column));
                        text =
                        `${text}\n${leadingSpace} ${arrowSpacing}^`;
                    }
                }

                return `${padStart(beginning, padding, ' ')} | ${text}\n`;
            })
            .join('');
    } else {
        errorMessage = message;
    }

    const err = new Error(errorMessage);
    err.message = errorMessage;
    err.name = name;
    err.line = _line;
    err.column = column;
    return err;
}

/**
 * Counts the number of occurences of a string.
 *
 * @param {String} str The string to count the occurrences.
 */

function count(str, substr) {
    let index = str.indexOf(substr);
    let occurrences = 0;

    while (index !== -1) {
        index = str.indexOf(substr, index + 1);
        occurrences = occurrences + 1;
    }

    return occurrences;
}
<commit_msg>Update utils<commit_after>'use strict';

module.exports = {
    createSourceError,
    replace,
    count
};

const padStart = require('pad-start');
const repeat = require('repeat-string');

const POINTER = '> ';

/**
 * An alternate to template strings.
 *
 * @example
 *   replace('Unexpected token "{token}"', { token: 'for' });
 *   // 'Unexpected token "for"'
 *
 * @param  {String} data
 * @param  {Object} replacers
 * @return {String}
 */

function replace(data, replacers) {
    for (let str in replacers) {
        data = data.replace(new RegExp(`{${str}}`, 'g'), replacers[str])
    }

    return data;
}

/**
 * Creates an error that points to the line and column the error occured.
 * If the `line` option is not passed, only the error message passed will
 * be shown.
 *
 * @param  {Object} options
 * @param  {Object} options.name - The error objects name
 * @param  {Object} [options.line]
 * @param  {Object} [options.column]
 * @param  {Object} [options.filename]
 * @return {Error}
 */

const toLine = num => num + 1;

function createSourceError(options) {
    const { name,
            line: _line,
            column: _column,
            source,
            message='',
            filename='[Source]' } = options;

    const column = _column + 1;
    const isLineNumber  = (typeof _line === 'number' && isFinite(_line));
    const isColumnNumber = typeof column === 'number' && column === column;
    const tolerance = 3;
    let errorMessage;

    if (isLineNumber) {
        // TODO: Might want to optimize this in case the string is large
        const lines  = source.split('\n');
        const length = lines.length;

        // Restrict the line to be between 0 and the total number of lines
        const line   = Math.min(lines.length, Math.max(_line, 1));

        const _start = Math.min(length - tolerance, Math.max(line - tolerance - 1));
        const start  = Math.max(_start, 0);
        const end    = Math.min(length, line + tolerance);

        // Pointer line can not be more or less than the start
        const pointerLine = Math.max(start, Math.min(line, end));

        const linecol =
            isLineNumber && isColumnNumber
                ? ` (${pointerLine}:${_column})`
                : '';

        const header  = `${filename}:${message ? ' ' + message : ''}${linecol}\n`;
        const padding = lines.length.toString().length + POINTER.length;

        errorMessage = header +
            lines.slice(start, end).map(function (text, index) {
                const currentLine = start + index + 1;
                let beginning = String(currentLine);
                let leadingSpace;
                let arrowSpacing;

                if (currentLine === pointerLine) {
                    beginning = POINTER + beginning;

                    if (isColumnNumber) {
                        leadingSpace = repeat(' ', padding + 1);
                        arrowSpacing = repeat(' ', Math.max(0, column));
                        text =
                        `${text}\n${leadingSpace} ${arrowSpacing}^`;
                    }
                }

                return `${padStart(beginning, padding, ' ')} | ${text}\n`;
            })
            .join('');
    } else {
        errorMessage = message;
    }

    const err = new Error(errorMessage);
    err.message = errorMessage;
    err.name = name;
    err.line = _line;
    err.column = column;
    return err;
}

/**
 * Counts the number of occurences of a string.
 *
 * @param {String} str The string to count the occurrences.
 */

function count(str, substr) {
    let index = str.indexOf(substr);
    let occurrences = 0;

    while (index !== -1) {
        index = str.indexOf(substr, index + 1);
        occurrences = occurrences + 1;
    }

    return occurrences;
}
<|endoftext|>


<commit_before>var controllerDir;
var appName;

function getAppName() {
    var parts = __dirname.replace(/\\/g, '/').split('/');
    return parts[parts.length - 2].split('.')[0];
}

// Get js-controller directory to load libs
function getControllerDir(isInstall) {
    var fs = require('fs');
    // Find the js-controller location
    var controllerDir = __dirname.replace(/\\/g, '/');
    controllerDir = controllerDir.split('/');
    if (controllerDir[controllerDir.length - 3] === 'adapter') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
    } else if (controllerDir[controllerDir.length - 3] === 'node_modules') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
        if (fs.existsSync(controllerDir + '/node_modules/' + appName + '.js-controller')) {
            controllerDir += '/node_modules/' + appName + '.js-controller';
        } else if (fs.existsSync(controllerDir + '/node_modules/' + appName.toLowerCase() + '.js-controller')) {
            controllerDir += '/node_modules/' + appName.toLowerCase() + '.js-controller';
        } else if (!fs.existsSync(controllerDir + '/controller.js')) {
            if (!isInstall) {
                console.log('Cannot find js-controller');
                process.exit(10);
            } else {
                process.exit();
            }
        }
    } else if (fs.existsSync(__dirname + '/../../node_modules/' + appName.toLowerCase() + '.js-controller')) {
        controllerDir.splice(controllerDir.length - 2, 2);
        return controllerDir.join('/') + '/node_modules/' + appName.toLowerCase() + '.js-controller';
    } else {
        if (!isInstall) {
            console.log('Cannot find js-controller');
            process.exit(10);
        } else {
            process.exit();
        }
    }
    return controllerDir;
}

// Read controller configuration file
function getConfig() {
    var fs = require('fs');
    if (fs.existsSync(controllerDir + '/conf/' + appName + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName + '.json'));
    } else if (fs.existsSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json'));
    } else {
        throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
    }
}
appName       = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);

exports.controllerDir = controllerDir;
exports.getConfig =     getConfig;
exports.Adapter =       require(controllerDir + '/lib/adapter.js');
exports.appName =       appName;
<commit_msg>update utils.js<commit_after>'use strict';

const fs = require('fs');
const path = require('path');

let controllerDir;
let appName;

/**
 * returns application name
 *
 * The name of the application can be different and this function finds it out.
 *
 * @returns {string}
 */
 function getAppName() {
    const parts = __dirname.replace(/\\/g, '/').split('/');
    return parts[parts.length - 2].split('.')[0];
}

/**
 * looks for js-controller home folder
 *
 * @param {boolean} isInstall
 * @returns {string}
 */
function getControllerDir(isInstall) {
    // Find the js-controller location
    const possibilities = [
        'iobroker.js-controller',
        'ioBroker.js-controller',
    ];
    /** @type {string} */
    let controllerPath;
    for (const pkg of possibilities) {
        try {
            const possiblePath = require.resolve(pkg);
            if (fs.existsSync(possiblePath)) {
                controllerPath = possiblePath;
                break;
            }
        } catch (e) { /* not found */ }
    }
    if (controllerPath == null) {
        if (!isInstall) {
            console.log('Cannot find js-controller');
            process.exit(10);
        } else {
            process.exit();
        }
    }
    // we found the controller
    return path.dirname(controllerPath);
}

/**
 * reads controller base settings
 *
 * @alias getConfig
 * @returns {object}
 */
 function getConfig() {
    let configPath;
    if (fs.existsSync(
        configPath = path.join(controllerDir, 'conf', appName + '.json')
    )) {
        return JSON.parse(fs.readFileSync(configPath, 'utf8'));
    } else if (fs.existsSync(
        configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json')
    )) {
        return JSON.parse(fs.readFileSync(configPath, 'utf8'));
    } else {
        throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
    }
}
appName       = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
const adapter = require(path.join(controllerDir, 'lib/adapter.js'));

exports.controllerDir = controllerDir;
exports.getConfig =     getConfig;
exports.Adapter =       adapter;
exports.appName =       appName;
<|endoftext|>


<commit_before>var request = require( 'request' );
var async = require( 'async' );
var _ = require( 'underscore' );
var moment = require( 'moment' );
var parseString = require('xml2js').parseString;

var utils = module.exports;

utils.apiRequest = function ( options, done ) {
    request( options, function ( err, res, body ) {
        if ( !err && res.statusCode === 200 ) {
            return done( null, body );
        }
        return done( err, body );
    } );
};

utils.convertXML = function ( xml, done ) {

    var formatNodes = function ( data ) {

        var keys = Object.keys( data );
        var reDateTime = new RegExp("[0-9]{8} [0-9]{2}:[0-9]{2}$");
        var reDateTimeSeconds = new RegExp("[0-9]{8} [0-9]{2}:[0-9]{2}:[0-9]{2}$");

        for ( var k = 0; k < keys.length; k++ ) {

            var key = keys[ k ];

            if ( Array.isArray( data[ key ] ) && data[ key ].length === 1 ) {

                data[ key ] = data[ key ][ 0 ];

                if ( reDateTimeSeconds.test( data[ key ] ) ) {
                    data[ key ] = moment( data[ key ], 'YYYYMMDD HH:mm:ss' ).format();
                }

                if ( reDateTime.test( data[ key ] ) ) {
                    data[ key ] = moment( data[ key ], 'YYYYMMDD HH:mm' ).format();
                }

            }

            if ( typeof data[ key ] === "object" ) {
                data[ key ] = formatNodes( data[ key ] );
            }

        }

        return data;

    };

    parseString( xml, function ( err, result ) {

        var data = formatNodes( result[ "bustime-response" ] );

        if ( err ) {
            return done( err, data );
        }

        return done( null, data );

    } );

};
<commit_msg>One less dependency<commit_after>var request = require( 'request' );
var async = require( 'async' );
var moment = require( 'moment' );
var parseString = require('xml2js').parseString;

var utils = module.exports;

utils.apiRequest = function ( options, done ) {
    request( options, function ( err, res, body ) {
        if ( !err && res.statusCode === 200 ) {
            return done( null, body );
        }
        return done( err, body );
    } );
};

utils.convertXML = function ( xml, done ) {

    var formatNodes = function ( data ) {

        var keys = Object.keys( data );
        var reDateTime = new RegExp("[0-9]{8} [0-9]{2}:[0-9]{2}$");
        var reDateTimeSeconds = new RegExp("[0-9]{8} [0-9]{2}:[0-9]{2}:[0-9]{2}$");

        for ( var k = 0; k < keys.length; k++ ) {

            var key = keys[ k ];

            if ( Array.isArray( data[ key ] ) && data[ key ].length === 1 ) {

                data[ key ] = data[ key ][ 0 ];

                if ( reDateTimeSeconds.test( data[ key ] ) ) {
                    data[ key ] = moment( data[ key ], 'YYYYMMDD HH:mm:ss' ).format();
                }

                if ( reDateTime.test( data[ key ] ) ) {
                    data[ key ] = moment( data[ key ], 'YYYYMMDD HH:mm' ).format();
                }

            }

            if ( typeof data[ key ] === "object" ) {
                data[ key ] = formatNodes( data[ key ] );
            }

        }

        return data;

    };

    parseString( xml, function ( err, result ) {

        var data = formatNodes( result[ "bustime-response" ] );

        if ( err ) {
            return done( err, data );
        }

        return done( null, data );

    } );

};
<|endoftext|>


<commit_before>var _ = require("underscore"),
    sanitizer = require("caja-sanitizer"),
    toobusy = require("toobusy"),
    options = require("./options"),
    logger = require("./logging").getLogger();

// Configure maximum latency in the event loop.  See
// https://github.com/lloyd/node-toobusy for more.
toobusy.maxLag(options.MAX_EVENT_LOOP_LAG);

module.exports = {
    // Provide this access to our maxLag-configured instance of `toobusy`.
    toobusy: function() {
        return toobusy();
    },
    // Return 503's if we're over capacity.
    tooBusyMiddleware: function() {
        return function(req, res, next) {
            // whitelist URLs involved in setting up and maintaining sesions,
            // to ensure that the hangout app starts in those.
            if (/^\/(facilitator|session|hangout)/.test(req.path)) {
                return next();
            }
            if (toobusy()) {
                res._errorReason = "over capacity";
                res.send(503, "Sorry, we're over capacity! Please try back later.");
            } else {
                next();
            }
        }
    },
    // redirect to login if unauthenticated.
    ensureAuthenticated: function (req, res, next) {
        if (req.isAuthenticated()) { return next(); }
        req.session["post-auth-path"] = req.path;
        res.redirect('/auth/google');
    },
    // send Permission Denied if unauthenticated.
    rejectUnlessAuthenticated: function (req, res, next) {
        return req.isAuthenticated() ? next() : res.send(403);
    },
    rejectUnlessSuperuser: function (req, res, next) {
        return req.user && req.user.isSuperuser() ? next() : res.send(403);
    },
    ensureSuperuser: function (req, res, next) {
        if(req.user.isSuperuser()) {
            next();
        } else {
            res._errorReason = "Not a superuser";
            res.send(401, "Permission denied");
        }
    },
    ensureAdmin: function (req, res, next) {
        if(req.user.isSuperuser() || req.user.isAdminOfSomeEvent()) {
            next();
        } else {
            res._errorReason = "Not an admin";
            res.send(401, "Permission denied");
        }
    },
    ensurePerm: function(perm) {
        return function(req, res, next) {
            if (req.isAuthenticated() && req.user.hasPerm(perm)) {
                next();
            } else {
                res._errorReason = "Lacks permission `" + perm + "`.";
                res.send(401, "Permission denied");
            }
        };
    },
    // Use this if we want to add CORS support for some reason in the future.
    // We used to do it for HTTP POST polling strategy for hangout session
    // apps, but not anymore (we now use socket connections).
    allowCrossDomain: function(domain) {
        return function(req, res, next) {
            res.header('Access-Control-Allow-Origin', domain);
            res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
            res.header('Access-Control-Allow-Headers', 'Content-Type');

            next();
        }
    },
    getSession: function(sessionKey, events, permalinkSessions) {
        // this is a bit silly, but we don't maintain a separate dedicated list of sessions,
        // and there's no easy way to map back from a session id to that session's event.
        // so, create a temporary list of sessions to look up against.
        // logger.debug("sessionKey: " + sessionKey);
        var allSessions = _.flatten(events.map(function(event) {
            var arr = event.get("sessions").toArray();
            if (event.get("hoa")) {
                arr.push(event.get("hoa"));
            }
            return arr;
        })).concat(permalinkSessions.toArray());

        return _.find(allSessions, function(session) {
            return session.get("session-key") === sessionKey;
        });
    },
    // Sanitize user-supplied HTML.  This uses a node packaging of Google's
    // Caja sanitizer.
    sanitize: function(dirty) {
        return sanitizer.sanitize(dirty,
              // Allow all URI's, just pass-through.
              function uriPolicy(uri, effects, ltype, hints) {
                  // "effects" is a description of whether the URI loads in the
                  // same document, or a new document, or doesn't get loaded at
                  // all.  If it's the same document (e.g. an img embed)
                  // enforce that we don't mess up SSL with mixed content.
                  // effects values defined in html4.ueffects = {
                  //   'NOT_LOADED': 0,
                  //   'SAME_DOCUMENT': 1,
                  //   'NEW_DOCUMENT': 2
                  // };
                  if (effects == 1 && options.UNHANGOUT_USE_SSL && uri.getScheme() != "https") {
                      return null;
                  }
                  return uri;
              },
              // Prefix id/name/class tokens so that we don't break our local ID's.
              function tokenPolicy(tokens) {
                  return tokens.replace(/([^\s]+)(\s+|$)/g, function(_, id, spaces) {
                      return "userhtml-" + id + (spaces ? ' ' : '');
                  });
              },
              function logPolicy(msg, detail) {
                  if (detail.change === "removed") {
                      logger.warn("HTML sanitizer: " + msg, detail);
                  }
              }
        );
    }
}

<commit_msg>Excempt superusers from over-capacity messages<commit_after>var _ = require("underscore"),
    sanitizer = require("caja-sanitizer"),
    toobusy = require("toobusy"),
    options = require("./options"),
    logger = require("./logging").getLogger();

// Configure maximum latency in the event loop.  See
// https://github.com/lloyd/node-toobusy for more.
toobusy.maxLag(options.MAX_EVENT_LOOP_LAG);

module.exports = {
    // Provide this access to our maxLag-configured instance of `toobusy`.
    toobusy: function() {
        return toobusy();
    },
    // Return 503's if we're over capacity.
    tooBusyMiddleware: function() {
        return function(req, res, next) {
            // whitelist URLs involved in setting up and maintaining sesions,
            // to ensure that the hangout app starts in those.
            if (/^\/(facilitator|session|hangout)/.test(req.path)) {
                return next();
            }
            if (toobusy() && (!req.user || !req.user.isSuperuser())) {
                res._errorReason = "over capacity";
                res.send(503, "Sorry, we're over capacity! Please try back later.");
            } else {
                next();
            }
        }
    },
    // redirect to login if unauthenticated.
    ensureAuthenticated: function (req, res, next) {
        if (req.isAuthenticated()) { return next(); }
        req.session["post-auth-path"] = req.path;
        res.redirect('/auth/google');
    },
    // send Permission Denied if unauthenticated.
    rejectUnlessAuthenticated: function (req, res, next) {
        return req.isAuthenticated() ? next() : res.send(403);
    },
    rejectUnlessSuperuser: function (req, res, next) {
        return req.user && req.user.isSuperuser() ? next() : res.send(403);
    },
    ensureSuperuser: function (req, res, next) {
        if(req.user.isSuperuser()) {
            next();
        } else {
            res._errorReason = "Not a superuser";
            res.send(401, "Permission denied");
        }
    },
    ensureAdmin: function (req, res, next) {
        if(req.user.isSuperuser() || req.user.isAdminOfSomeEvent()) {
            next();
        } else {
            res._errorReason = "Not an admin";
            res.send(401, "Permission denied");
        }
    },
    ensurePerm: function(perm) {
        return function(req, res, next) {
            if (req.isAuthenticated() && req.user.hasPerm(perm)) {
                next();
            } else {
                res._errorReason = "Lacks permission `" + perm + "`.";
                res.send(401, "Permission denied");
            }
        };
    },
    // Use this if we want to add CORS support for some reason in the future.
    // We used to do it for HTTP POST polling strategy for hangout session
    // apps, but not anymore (we now use socket connections).
    allowCrossDomain: function(domain) {
        return function(req, res, next) {
            res.header('Access-Control-Allow-Origin', domain);
            res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
            res.header('Access-Control-Allow-Headers', 'Content-Type');

            next();
        }
    },
    getSession: function(sessionKey, events, permalinkSessions) {
        // this is a bit silly, but we don't maintain a separate dedicated list of sessions,
        // and there's no easy way to map back from a session id to that session's event.
        // so, create a temporary list of sessions to look up against.
        // logger.debug("sessionKey: " + sessionKey);
        var allSessions = _.flatten(events.map(function(event) {
            var arr = event.get("sessions").toArray();
            if (event.get("hoa")) {
                arr.push(event.get("hoa"));
            }
            return arr;
        })).concat(permalinkSessions.toArray());

        return _.find(allSessions, function(session) {
            return session.get("session-key") === sessionKey;
        });
    },
    // Sanitize user-supplied HTML.  This uses a node packaging of Google's
    // Caja sanitizer.
    sanitize: function(dirty) {
        return sanitizer.sanitize(dirty,
              // Allow all URI's, just pass-through.
              function uriPolicy(uri, effects, ltype, hints) {
                  // "effects" is a description of whether the URI loads in the
                  // same document, or a new document, or doesn't get loaded at
                  // all.  If it's the same document (e.g. an img embed)
                  // enforce that we don't mess up SSL with mixed content.
                  // effects values defined in html4.ueffects = {
                  //   'NOT_LOADED': 0,
                  //   'SAME_DOCUMENT': 1,
                  //   'NEW_DOCUMENT': 2
                  // };
                  if (effects == 1 && options.UNHANGOUT_USE_SSL && uri.getScheme() != "https") {
                      return null;
                  }
                  return uri;
              },
              // Prefix id/name/class tokens so that we don't break our local ID's.
              function tokenPolicy(tokens) {
                  return tokens.replace(/([^\s]+)(\s+|$)/g, function(_, id, spaces) {
                      return "userhtml-" + id + (spaces ? ' ' : '');
                  });
              },
              function logPolicy(msg, detail) {
                  if (detail.change === "removed") {
                      logger.warn("HTML sanitizer: " + msg, detail);
                  }
              }
        );
    }
}

<|endoftext|>


<commit_before>const _ = require('underscore-plus')
const {Range} = require('atom')
const dedent = require('dedent')

const TOC_END = '<!-- TOC END -->'
const TOC_START_REGEXP = /<!\-\- TOC START (.*)?\-\->/i
const TOC_END_REGEXP = new RegExp(`${_.escapeRegExp(TOC_END)}`, 'i')

// TOC generation
// -------------------------
function linkFor (text) {
  return text
    .toLowerCase()
    .replace(/\s/g, '-')
    .replace(/\<(.*?)>(.+)<\/\1>/g, '$2') // e.g. exract 'a' from <kbd>a<kbd>
    .replace(/[^\w-]/g, '') // Remove non-(alphanumeric or '-') char.
}

function titleFor (text) {
  return text
    .replace(/!\[.*?\]\(https?:\/\/.*?\)/g, '') // Remove img link
    .replace(/\[(.*?)\]\(https?:\/\/.*?\)/g, '$1') // extract link txt
    .trim()
}

// Set title and link
function injectTitleAndLink (headers) {
  const countByLink = {}

  for (const header of headers) {
    let linkSuffix
    const title = titleFor(header.subject)
    const link = linkFor(title)

    if (!(link in countByLink)) {
      countByLink[link] = 0
      linkSuffix = ''
    } else {
      countByLink[link] += 1
      linkSuffix = '-' + countByLink[link]
    }
    header.title = title
    header.link = link + linkSuffix
  }
}

function buildTocText (headers, options) {
  injectTitleAndLink(headers)

  const indentBase = '  '
  return headers
    .filter(header => options.min <= header.level && header.level <= options.max)
    .map(header => {
      const indent = indentBase.repeat(header.level - options.min)
      if (options.link) {
        return `${indent}- [${header.title}](#${header.link})`
      } else {
        return `${indent}- [${header.title}]`
      }
    })
    .join('\n')
}

// Extract markdown headers from editor
// -------------------------
function isMarkdownHeader (editor, bufferPosition) {
  const {scopes} = editor.scopeDescriptorForBufferPosition(bufferPosition)
  const scope = scopes[1]
  return (scope != null && scope.startsWith('markup.heading')) || scope.startsWith('heading.markup.md')
}

const MARKDOWN_HEADER_REGEXP = /^(#+)\s*(.*$)$/g
function scanHeaders (editor) {
  const headers = []
  editor.scan(MARKDOWN_HEADER_REGEXP, ({match, range}) => {
    if (!isMarkdownHeader(editor, range.start)) {
      return
    }
    const level = match[1].length
    const subject = match[2]
    headers.push({level, subject})
  })
  return headers
}

// Misc
// -------------------------
function deserializeTocOptions (text) {
  const options = {}
  for (const param of text.trim().split(/\s+/)) {
    let [key, value] = param.split(':')
    switch (key) {
      case 'min':
      case 'max': // integer
        const number = Number(value)
        if (number >= 1) options[key] = number
        break
      case 'link':
      case 'update': // boolean
        if (['true', 'false'].includes(value)) {
          options[key] = value === 'true'
        }
        break
    }
  }
  return options
}

function serializeTocOptions (tocOptions) {
  return JSON.stringify(tocOptions)
    .replace(/[{"}]/g, '')
    .replace(/,/g, ' ')
}

function getDefaultTocOptions () {
  return {
    min: atom.config.get('markdown-toc-auto.min'),
    max: atom.config.get('markdown-toc-auto.max'),
    link: atom.config.get('markdown-toc-auto.link'),
    update: atom.config.get('markdown-toc-auto.update')
  }
}

// Public
// -------------------------
function insertToc (editor, range, options = {}) {
  range = Range.fromObject(range)
  options = Object.assign(getDefaultTocOptions(), options)

  const list = []
  let toc = ''
  toc += `<!-- TOC START ${serializeTocOptions(options)} -->\n`
  toc += buildTocText(scanHeaders(editor), options) + '\n\n'
  toc += TOC_END + (range.isEmpty() ? '\n\n' : '')

  const bufferPositionByCursor = new Map()
  // Save original cursor position for the cusor which point will change.
  for (const cursor of editor.getCursors()) {
    const point = cursor.getBufferPosition()
    if (range.containsPoint(point)) {
      bufferPositionByCursor.set(cursor, point)
    }
  }

  editor.setTextInBufferRange(range, toc, {undo: 'skip'})

  // Restore oiginal cursor position
  for (const cursor of editor.getCursors()) {
    const point = bufferPositionByCursor.get(cursor)
    if (point) {
      cursor.setBufferPosition(point)
    }
  }
}

function insertTocAtPoint (editor, point) {
  insertToc(editor, [point, point])
}

function updateToc (editor) {
  const range = findTocRange(editor)
  if (range) {
    const tocStartRowText = editor.lineTextForBufferRow(range.start.row)
    const match = tocStartRowText.match(TOC_START_REGEXP)
    if (match) {
      const options = deserializeTocOptions(match[1])
      if (options.update) {
        insertToc(editor, range, options)
      }
    }
  }
}

function findTocRange (editor) {
  const tocRange = []
  const scanRange = new Range([0, 0], editor.getEofBufferPosition())
  editor.scanInBufferRange(TOC_START_REGEXP, scanRange, ({range}) => tocRange.push(range))

  if (tocRange.length === 0) {
    return
  }

  scanRange.start = tocRange[0].end
  editor.scanInBufferRange(TOC_END_REGEXP, scanRange, ({range}) => tocRange.push(range))
  if (tocRange.length === 2) {
    return new Range(tocRange[0].start, tocRange[1].end)
  }
}

module.exports = {
  updateToc,
  deserializeTocOptions,
  serializeTocOptions
}
<commit_msg>fix some error<commit_after>const _ = require('underscore-plus')
const {Range} = require('atom')

const TOC_END = '<!-- TOC END -->'
const TOC_START_REGEXP = /<!-- TOC START (.*)?-->/i
const TOC_END_REGEXP = new RegExp(`${_.escapeRegExp(TOC_END)}`, 'i')

// TOC generation
// -------------------------
function linkFor (text) {
  return text
    .toLowerCase()
    .replace(/\s/g, '-')
    .replace(/<(.*?)>(.+)<\/\1>/g, '$2') // e.g. exract 'a' from <kbd>a<kbd>
    .replace(/[^\w-]/g, '') // Remove non-(alphanumeric or '-') char.
}

function titleFor (text) {
  return text
    .replace(/!\[.*?\]\(https?:\/\/.*?\)/g, '') // Remove img link
    .replace(/\[(.*?)\]\(https?:\/\/.*?\)/g, '$1') // extract link txt
    .trim()
}

// Set title and link
function injectTitleAndLink (headers) {
  const countByLink = {}

  for (const header of headers) {
    let linkSuffix
    const title = titleFor(header.subject)
    const link = linkFor(title)

    if (!(link in countByLink)) {
      countByLink[link] = 0
      linkSuffix = ''
    } else {
      countByLink[link] += 1
      linkSuffix = '-' + countByLink[link]
    }
    header.title = title
    header.link = link + linkSuffix
  }
}

function buildTocText (headers, options) {
  injectTitleAndLink(headers)

  const indentBase = '  '
  return headers
    .filter(header => options.min <= header.level && header.level <= options.max)
    .map(header => {
      const indent = indentBase.repeat(header.level - options.min)
      if (options.link) {
        return `${indent}- [${header.title}](#${header.link})`
      } else {
        return `${indent}- [${header.title}]`
      }
    })
    .join('\n')
}

// Extract markdown headers from editor
// -------------------------
function isMarkdownHeader (editor, bufferPosition) {
  const {scopes} = editor.scopeDescriptorForBufferPosition(bufferPosition)
  const scope = scopes[1]
  return (scope != null && scope.startsWith('markup.heading')) || scope.startsWith('heading.markup.md')
}

const MARKDOWN_HEADER_REGEXP = /^(#+)\s*(.*$)$/g
function scanHeaders (editor) {
  const headers = []
  editor.scan(MARKDOWN_HEADER_REGEXP, ({match, range}) => {
    if (!isMarkdownHeader(editor, range.start)) {
      return
    }
    const level = match[1].length
    const subject = match[2]
    headers.push({level, subject})
  })
  return headers
}

// Misc
// -------------------------
function deserializeTocOptions (text) {
  const options = {}
  for (const param of text.trim().split(/\s+/)) {
    let [key, value] = param.split(':')
    switch (key) {
      case 'min':
      case 'max': // integer
        const number = Number(value)
        if (number >= 1) options[key] = number
        break
      case 'link':
      case 'update': // boolean
        if (['true', 'false'].includes(value)) {
          options[key] = value === 'true'
        }
        break
    }
  }
  return options
}

function serializeTocOptions (tocOptions) {
  return JSON.stringify(tocOptions)
    .replace(/[{"}]/g, '')
    .replace(/,/g, ' ')
}

function getDefaultTocOptions () {
  return {
    min: atom.config.get('markdown-toc-auto.min'),
    max: atom.config.get('markdown-toc-auto.max'),
    link: atom.config.get('markdown-toc-auto.link'),
    update: atom.config.get('markdown-toc-auto.update')
  }
}

// Public
// -------------------------
function insertToc (editor, range, options = {}) {
  range = Range.fromObject(range)
  options = Object.assign(getDefaultTocOptions(), options)

  let toc = ''
  toc += `<!-- TOC START ${serializeTocOptions(options)} -->\n`
  toc += buildTocText(scanHeaders(editor), options) + '\n\n'
  toc += TOC_END + (range.isEmpty() ? '\n\n' : '')

  const bufferPositionByCursor = new Map()
  // Save original cursor position for the cusor which point will change.
  for (const cursor of editor.getCursors()) {
    const point = cursor.getBufferPosition()
    if (range.containsPoint(point)) {
      bufferPositionByCursor.set(cursor, point)
    }
  }

  editor.setTextInBufferRange(range, toc, {undo: 'skip'})

  // Restore oiginal cursor position
  for (const cursor of editor.getCursors()) {
    const point = bufferPositionByCursor.get(cursor)
    if (point) {
      cursor.setBufferPosition(point)
    }
  }
}

function insertTocAtPoint (editor, point) {
  insertToc(editor, [point, point])
}

function updateToc (editor) {
  const range = findTocRange(editor)
  if (range) {
    const tocStartRowText = editor.lineTextForBufferRow(range.start.row)
    const match = tocStartRowText.match(TOC_START_REGEXP)
    if (match) {
      const options = deserializeTocOptions(match[1])
      if (options.update) {
        insertToc(editor, range, options)
      }
    }
  }
}

function findTocRange (editor) {
  const tocRange = []
  const scanRange = new Range([0, 0], editor.getEofBufferPosition())
  editor.scanInBufferRange(TOC_START_REGEXP, scanRange, ({range}) => tocRange.push(range))

  if (tocRange.length === 0) {
    return
  }

  scanRange.start = tocRange[0].end
  editor.scanInBufferRange(TOC_END_REGEXP, scanRange, ({range}) => tocRange.push(range))
  if (tocRange.length === 2) {
    return new Range(tocRange[0].start, tocRange[1].end)
  }
}

module.exports = {
  updateToc,
  insertTocAtPoint,
  deserializeTocOptions,
  serializeTocOptions
}
<|endoftext|>


<commit_before>"use strict";

var utf8 = require('./utf8.js');

module.exports = (function(env) {
	var mod = new Object();

	var normalize_data = function(data) {
		if(typeof data === 'object') {
			if(data.type == "Atom") {
				if(data.value == "undefined") {
					return undefined;
				} else {
					return data.value;
				}
			} else if(data.type == "Tuple") {
				var arr = [];
				for(var i=0 ; i<data.length ; ++i) {
					arr[i] = normalize_data(data[i]);
				}
				return arr;
			} else {
				return data.value;
			}
		} else {
			return data;
		}
	};

	var utf8_decode = function(x) {
		if(!x) return x;
		return utf8.decode(x);
	};

	var translate_state = function(arr) {
		var r = {};
		r.ts = arr[0];

		var name = arr[1];
		r.name = name;

		r.data = {}

		switch(name) {
		case 'voicemail':
			r.data.target_type = arr[2];
			r.data.target_id = arr[3];
			r.data.target_name = arr[4];
			break;
		case 'checking_voicemail':
			r.data.target_name = arr[2];
			break;
		case 'calling':
			var target_location = arr[2];
			var target_type = arr[3];
			var target_data = arr[4];

			r.data.target_location = target_location;
			r.data.target_type = target_type;

			if(target_type == 'user') {
				r.data.user_id = target_data[0];
			} else if(target_type == 'group') {
				r.data.group_type = target_data[0];
				r.data.group_id = target_data[1];
				if(r.data.group_type == 'ring') {
					r.data.group_queue_id = target_data[4];
				}
			} else if(target_type == 'pstn') {
				r.data.address = target_data[0];
			} else if(target_type == 'bridged_endpoint') {
				r.data.address = target_data[0] + "@" + target_data[1];
			}
			break;
		case 'calling_siptermination':
			r.data.url = arr[2];
			break;
		case 'ivr':
			r.data.ivr_id = arr[2];
			break;
		case 'system_operation':
			r.data.operation = arr[2];
			break;
		case 'conference':
			r.data.room_id = arr[2];
			break;
		case 'xml_ivr':
			break;
		case 'park':
			r.data.slot = arr[2];
			r.data.parker_id = arr[4];
			r.data.parker_name = arr[5];
			break;
		case 'bridged':
			break;
		case 'eavesdrop':
			r.data.uuid = arr[2];
			r.data.eavesdrop_state = arr[3];
		default:
			break;
		}

		return r;
	};

	var translate_target = function(arr) {
		var r = {}
		r.type = arr[0];
		r.id = arr[1];
		r.name = arr[2];
		r.address = arr[3];

		return r;
	};

	var adjust_avatar = function(avatar, is_user, base_url) {
		if(!avatar || avatar == "") {
			return base_url + (is_user ? "static/icons/user.png" : "static/icons/end_user.png");
		} else if(avatar.startsWith("https:") || avatar.startsWith("http://")) {
			return avatar;
		} else {
			return base_url + "basix/avatars/" + avatar;
		}
	};

	var translate_end_user = function(s, opts) {
		var tokens = s.split("*");
		tokens = tokens.map(function(token) {
			return decodeURIComponent(token.replace(/\+/g, '%20'));
		})
		var avatar = tokens[3];
		if(opts && opts.base_url) {
			avatar = adjust_avatar(avatar, false, opts.base_url);
		}
		return {
			id: tokens[0],
			name: tokens[1],
			organization: tokens[2],
			avatar: avatar,
		};
	}

	mod.create_entity_info = function(entity_name, BertEntityInfo, info_fields, opts) {
		var entity_info = {};
		var fields = info_fields[entity_name];
		for(var i=0; i<fields.length; ++i) {
			var key = fields[i];
			var data = normalize_data(BertEntityInfo[i]);
			entity_info[key] = data;
		}

		if(entity_name == 'channel') {
			if(entity_info.other_info) {
				var data = entity_info.other_info
				var type = data[0]
				entity_info.other_info = {
					type: type,
					direction: data[2],
					peer_location: data[3],
					address: data[4],
					offer_timestamp: data[5],
				}
				if(type == 'user') {
					entity_info.other_info.user_id = data[1]
					entity_info.other_info.group_id = data[6]
				} else {
					entity_info.other_info.end_user = data[1] ? translate_end_user(data[1], opts) : null
				}
			}
		}

		if(['channel', 'queued_channel', 'parked_channel'].includes(entity_name)) {
			if(entity_info.state) {
				entity_info.state = translate_state(entity_info.state);
			}
			if(entity_info.target) {
				entity_info.target = translate_target(entity_info.target);
			}
			if(entity_info.end_user) {
				entity_info.end_user = translate_end_user(entity_info.end_user, opts);
			}
			if(entity_info.origination) {
				entity_info.origination = entity_info.origination.split("*").map(decodeURIComponent);
			}
		} else if(entity_name == 'user') {
			entity_info.label = utf8_decode(entity_info.label);
			if(opts && opts.base_url) {
				entity_info.avatar = adjust_avatar(entity_info.avatar, true, opts.base_url);
			}
		} else if(entity_name == 'group') {
			entity_info.label = utf8_decode(entity_info.label);
		}

		return entity_info;
	};

	return mod;
})();
<commit_msg>Bug #6889 (Must support end_user with _MANY_:): done<commit_after>"use strict";

var utf8 = require('./utf8.js');

module.exports = (function(env) {
	var mod = new Object();

	var normalize_data = function(data) {
		if(typeof data === 'object') {
			if(data.type == "Atom") {
				if(data.value == "undefined") {
					return undefined;
				} else {
					return data.value;
				}
			} else if(data.type == "Tuple") {
				var arr = [];
				for(var i=0 ; i<data.length ; ++i) {
					arr[i] = normalize_data(data[i]);
				}
				return arr;
			} else {
				return data.value;
			}
		} else {
			return data;
		}
	};

	var utf8_decode = function(x) {
		if(!x) return x;
		return utf8.decode(x);
	};

	var translate_state = function(arr) {
		var r = {};
		r.ts = arr[0];

		var name = arr[1];
		r.name = name;

		r.data = {}

		switch(name) {
		case 'voicemail':
			r.data.target_type = arr[2];
			r.data.target_id = arr[3];
			r.data.target_name = arr[4];
			break;
		case 'checking_voicemail':
			r.data.target_name = arr[2];
			break;
		case 'calling':
			var target_location = arr[2];
			var target_type = arr[3];
			var target_data = arr[4];

			r.data.target_location = target_location;
			r.data.target_type = target_type;

			if(target_type == 'user') {
				r.data.user_id = target_data[0];
			} else if(target_type == 'group') {
				r.data.group_type = target_data[0];
				r.data.group_id = target_data[1];
				if(r.data.group_type == 'ring') {
					r.data.group_queue_id = target_data[4];
				}
			} else if(target_type == 'pstn') {
				r.data.address = target_data[0];
			} else if(target_type == 'bridged_endpoint') {
				r.data.address = target_data[0] + "@" + target_data[1];
			}
			break;
		case 'calling_siptermination':
			r.data.url = arr[2];
			break;
		case 'ivr':
			r.data.ivr_id = arr[2];
			break;
		case 'system_operation':
			r.data.operation = arr[2];
			break;
		case 'conference':
			r.data.room_id = arr[2];
			break;
		case 'xml_ivr':
			break;
		case 'park':
			r.data.slot = arr[2];
			r.data.parker_id = arr[4];
			r.data.parker_name = arr[5];
			break;
		case 'bridged':
			break;
		case 'eavesdrop':
			r.data.uuid = arr[2];
			r.data.eavesdrop_state = arr[3];
		default:
			break;
		}

		return r;
	};

	var translate_target = function(arr) {
		var r = {}
		r.type = arr[0];
		r.id = arr[1];
		r.name = arr[2];
		r.address = arr[3];

		return r;
	};

	var adjust_avatar = function(avatar, is_user, base_url) {
		if(!avatar || avatar == "") {
			return base_url + (is_user ? "static/icons/user.png" : "static/icons/end_user.png");
		} else if(avatar.startsWith("https:") || avatar.startsWith("http://")) {
			return avatar;
		} else {
			return base_url + "basix/avatars/" + avatar;
		}
	};

	var translate_end_user_single = function(s, opts) {
		var tokens = s.split("*");
		tokens = tokens.map(function(token) {
			return decodeURIComponent(token.replace(/\+/g, '%20'));
		})
		var avatar = tokens[3];
		if(opts && opts.base_url) {
			avatar = adjust_avatar(avatar, false, opts.base_url);
		}
		return {
			id: tokens[0],
			name: tokens[1],
			organization: tokens[2],
			avatar: avatar,
		};
	}

	var translate_end_user = function(s, opts) {
		if (s.startsWith("_MANY_:")) {
			var tokens = s.split("|")
			var arr = tokens[0].split(":")
			var count = parseInt(arr[1])
			return {
				count: count,
				items: tokens.slice(1).map(token => {
					return translate_end_user_single(token, opts)
				})
			}
		} else {
			return translate_end_user_single(s, opts)
		}
	}

	mod.create_entity_info = function(entity_name, BertEntityInfo, info_fields, opts) {
		var entity_info = {};
		var fields = info_fields[entity_name];
		for(var i=0; i<fields.length; ++i) {
			var key = fields[i];
			var data = normalize_data(BertEntityInfo[i]);
			entity_info[key] = data;
		}

		if(entity_name == 'channel') {
			if(entity_info.other_info) {
				var data = entity_info.other_info
				var type = data[0]
				entity_info.other_info = {
					type: type,
					direction: data[2],
					peer_location: data[3],
					address: data[4],
					offer_timestamp: data[5],
				}
				if(type == 'user') {
					entity_info.other_info.user_id = data[1]
					entity_info.other_info.group_id = data[6]
				} else {
					entity_info.other_info.end_user = data[1] ? translate_end_user(data[1], opts) : null
				}
			}
		}

		if(['channel', 'queued_channel', 'parked_channel'].includes(entity_name)) {
			if(entity_info.state) {
				entity_info.state = translate_state(entity_info.state);
			}
			if(entity_info.target) {
				entity_info.target = translate_target(entity_info.target);
			}
			if(entity_info.end_user) {
				entity_info.end_user = translate_end_user(entity_info.end_user, opts);
			}
			if(entity_info.origination) {
				entity_info.origination = entity_info.origination.split("*").map(decodeURIComponent);
			}
		} else if(entity_name == 'user') {
			entity_info.label = utf8_decode(entity_info.label);
			if(opts && opts.base_url) {
				entity_info.avatar = adjust_avatar(entity_info.avatar, true, opts.base_url);
			}
		} else if(entity_name == 'group') {
			entity_info.label = utf8_decode(entity_info.label);
		}

		return entity_info;
	};

	return mod;
})();
<|endoftext|>


<commit_before>var
path = require('path'),
zlib = require('zlib'),
http = require('http'),
https = require('https'),
fs = require('fs'),
url = require('url'),
qs = require('querystring'),
picoObj= require('pico').export('pico/obj')

// to prevent ssl error. see http://stackoverflow.com/questions/11091974/ssl-error-in-nodejs
https.globalAgent.options.secureProtocol = 'SSLv3_method'

module.exports = {

    zip: function(str, cb){
        if ('string' !== typeof(str)) return cb('zip err: no payload: '+typeof(str))
        zlib.deflateRaw(str, (err, buf)=>{
            cb(err, err || buf.toString('base64'))
        })
    },

    unzip: function(str, cb){
        if ('string' !== typeof(str) || '' === str) return cb('unzip err: no payload: '+typeof(str))
        zlib.inflateRaw(new Buffer(str, 'base64'), (err, buf)=>{
            cb(err, err || buf.toString())
        })
    },
    // params can be an object or an array of objects
    // if it is an array, objects will be merged, overlapped key will be overrided by later object
    ajax: function(method, href, params, headers, cb, userData){
        cb = cb || ((err)=>{if (err) console.error(method, href, params, headers, userData, err)})
        if (!href) return cb('url not defined')

        var
        options = url.parse(href),
        protocol

        switch(options.protocol){
        case 'http:': protocol=http; break
        case 'https:': protocol=https; break
        default: fs.readFile(href, 'utf8', (err, data)=>{ cb(err, 4, data, userData) }); return
        }

        var
        isGet = 'GET' === (options.method = method.toUpperCase()),
        body = ''

        if (params && 'object' === typeof params){
            if (undefined === params.length){
                body = qs.stringify(params)
            }else{
                var
                merged = {},
                opt = {tidy:1, mergeArr:1}
                for(var i=0,l=params.length; i<l; i++){
                    merged = extend(merged, params[i], opt)
                }
                body = qs.stringify(merged)
            }
        }

        if (isGet){
            options.path += body ? '?' + body : body
            if (headers) options.headers = headers
        }else{
            options.headers = extend(headers || {}, {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': body ? body.length : 0
            })
        }

        var req = protocol.request(options, (res)=>{
            var st=res.statusCode
            if (-1 < [301,302,303,305,306,307,308].indexOf(st)) return module.exports.ajax(method,res.headers.location,params,headers,cb,userData)
            res.setEncoding('utf8')
            var
            data = '',
            status=(200 === st || !st) ? null : {error:res.statusMessage,code:st}
            res.on('data', (chunk)=>{
                data += chunk
                cb(status, 3, data, userData)
            })
            res.on('end', ()=>{
                cb(status, 4, data, userData)
            })
        })

        req.on('error', (err)=>{
            cb(err, null, null, userData)
        })

        if (!isGet){
            req.write(body)
        }
        req.end()
    }
}
<commit_msg>standardised ajax error format<commit_after>var
path = require('path'),
zlib = require('zlib'),
http = require('http'),
https = require('https'),
fs = require('fs'),
url = require('url'),
qs = require('querystring'),
picoObj= require('pico').export('pico/obj')

// to prevent ssl error. see http://stackoverflow.com/questions/11091974/ssl-error-in-nodejs
https.globalAgent.options.secureProtocol = 'SSLv3_method'

module.exports = {

    zip: function(str, cb){
        if ('string' !== typeof(str)) return cb('zip err: no payload: '+typeof(str))
        zlib.deflateRaw(str, (err, buf)=>{
            cb(err, err || buf.toString('base64'))
        })
    },

    unzip: function(str, cb){
        if ('string' !== typeof(str) || '' === str) return cb('unzip err: no payload: '+typeof(str))
        zlib.inflateRaw(new Buffer(str, 'base64'), (err, buf)=>{
            cb(err, err || buf.toString())
        })
    },
    // params can be an object or an array of objects
    // if it is an array, objects will be merged, overlapped key will be overrided by later object
    ajax: function(method, href, params, headers, cb, userData){
        cb = cb || ((err)=>{if (err) console.error(method, href, params, headers, userData, err)})
        if (!href) return cb('url not defined')

        var
        options = url.parse(href),
        protocol

        switch(options.protocol){
        case 'http:': protocol=http; break
        case 'https:': protocol=https; break
        default: fs.readFile(href, 'utf8', (err, data)=>{ cb(err, 4, data, userData) }); return
        }

        var
        isGet = 'GET' === (options.method = method.toUpperCase()),
        body = ''

        if (params && 'object' === typeof params){
            if (undefined === params.length){
                body = qs.stringify(params)
            }else{
                var
                merged = {},
                opt = {tidy:1, mergeArr:1}
                for(var i=0,l=params.length; i<l; i++){
                    merged = extend(merged, params[i], opt)
                }
                body = qs.stringify(merged)
            }
        }

        if (isGet){
            options.path += body ? '?' + body : body
            if (headers) options.headers = headers
        }else{
            options.headers = extend(headers || {}, {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': body ? body.length : 0
            })
        }

        var req = protocol.request(options, (res)=>{
            var st=res.statusCode
            if (-1 < [301,302,303,305,306,307,308].indexOf(st)) return module.exports.ajax(method,res.headers.location,params,headers,cb,userData)
            res.setEncoding('utf8')
            var
            data = '',
            status=(200 === st || !st) ? null : {error:res.statusMessage,code:st}
            res.on('data', (chunk)=>{
                data += chunk
                cb(status, 3, data, userData)
            })
            res.on('end', ()=>{
                cb(status, 4, data, userData)
            })
        })

        req.on('error', (err)=>{
            cb({error:err.message,code:500}, 4, null, userData)
        })

        if (!isGet){
            req.write(body)
        }
        req.end()
    }
}
<|endoftext|>


<commit_before>var path = require('path');

module.exports = {

	/**
	 *
	 * @param {string} message – custom message
	 * @param {error} originalError – error object
	 * @param {string} [optionalMessage] displayed below the original error
	 * @returns {{c: string, web: string, code: string}}
	 */
	error: function(message, originalError, optionalMessage) {
		var  err
			,originalMessage
		;

		message         = message || '';
		originalMessage = originalError && ' \nReason: ' + originalError.message || '';
		optionalMessage = optionalMessage && ' \n' + optionalMessage || '';

		err             = message + originalMessage + optionalMessage;

		return {
			 c: err
			,web: '\n'+err.replace(' \n', '
')
			,code: originalError ? originalError.code : ''
		};
	},

	/**
	 * Add indentation to a multi-line string
	 * @param {string} sInput
	 * @param {number} levels – How many levels of indentation to apply
	 * @param {string} [indentString] – Character to use for indentation, defaults to tab char.
	 * @returns {string}
	 */
	indent: function(sInput, levels, indentString) {
		var indentedLineBreak;

		indentString = indentString || '\t';
		indentedLineBreak = '\n'+ ( new Array( levels + 1 ).join( indentString ) );

		return indentedLineBreak + sInput.split('\n').join(indentedLineBreak);
	},

	/**
	 * Construct an URI to the web view of the file or directory in a repository
	 * @param {string} baseUri – e.g. 'https://github.com/MarcDiethelm/xtc'
	 * @param {string} branch
	 * @param {string} filePath
	 * @param {string} [fileName]
	 * @returns {string} – URI to the web view of the file or directory in the repository
	 */
	getRepoWebViewUri: function(baseUri, branch, filePath, fileName) {
		var varPath = fileName ? 'blob/' : 'tree/'
		   ,branch = branch ? branch + '/' : 'develop'
		   ,filePath = path.relative(process.cwd(), filePath) + '/'
		;

		fileName = fileName || '';

		return baseUri ? baseUri + varPath + branch + filePath + fileName : null;
	}
}
<commit_msg>Precede xtc error messages with a \n<commit_after>var path = require('path');

module.exports = {

	/**
	 *
	 * @param {string} message – custom message
	 * @param {error} originalError – error object
	 * @param {string} [optionalMessage] displayed below the original error
	 * @returns {{c: string, web: string, code: string}}
	 */
	error: function(message, originalError, optionalMessage) {
		var  err
			,originalMessage
		;

		message         = '\n'+ message || '';
		originalMessage = originalError && ' \nReason: ' + originalError.message || '';
		optionalMessage = optionalMessage && ' \n' + optionalMessage || '';

		err             = message + originalMessage + optionalMessage;

		return {
			 c: err
			,web: '\n'+err.replace(' \n', '
')
			,code: originalError ? originalError.code : ''
		};
	},

	/**
	 * Add indentation to a multi-line string
	 * @param {string} sInput
	 * @param {number} levels – How many levels of indentation to apply
	 * @param {string} [indentString] – Character to use for indentation, defaults to tab char.
	 * @returns {string}
	 */
	indent: function(sInput, levels, indentString) {
		var indentedLineBreak;

		indentString = indentString || '\t';
		indentedLineBreak = '\n'+ ( new Array( levels + 1 ).join( indentString ) );

		return indentedLineBreak + sInput.split('\n').join(indentedLineBreak);
	},

	/**
	 * Construct an URI to the web view of the file or directory in a repository
	 * @param {string} baseUri – e.g. 'https://github.com/MarcDiethelm/xtc'
	 * @param {string} branch
	 * @param {string} filePath
	 * @param {string} [fileName]
	 * @returns {string} – URI to the web view of the file or directory in the repository
	 */
	getRepoWebViewUri: function(baseUri, branch, filePath, fileName) {
		var varPath = fileName ? 'blob/' : 'tree/'
		   ,branch = branch ? branch + '/' : 'develop'
		   ,filePath = path.relative(process.cwd(), filePath) + '/'
		;

		fileName = fileName || '';

		return baseUri ? baseUri + varPath + branch + filePath + fileName : null;
	}
}
<|endoftext|>


<commit_before>module.exports = {
   /**
    * Converts `t` to a Unix timestamp from a Date object unless it's already
    * a number.
    */
   toUnixTime: function(date) {
      return (date && (typeof date) === 'object') ? date.getTime() / 1000 : date;
   },

   /**
    * Converts `t` to a Date object from a Unix timestamp unless it's not a
    * number.
    */
   fromUnixTime: function(t) {
      return (typeof t) === 'number' ? new Date(t * 1000) : t;
   },

   /**
    * Converts `str` to a Date object from a Date string (or null).
    * Returns null if str is falsy.
    */
   fromDateString: function(str) {
      return str ? new Date(str) : null;
   },
};
<commit_msg>utils.fromDateString(): deal with date object<commit_after>module.exports = {
   /**
    * Converts `t` to a Unix timestamp from a Date object unless it's already
    * a number.
    */
   toUnixTime: function(date) {
      return (date && (typeof date) === 'object') ? date.getTime() / 1000 : date;
   },

   /**
    * Converts `t` to a Date object from a Unix timestamp unless it's not a
    * number.
    */
   fromUnixTime: function(t) {
      return (typeof t) === 'number' ? new Date(t * 1000) : t;
   },

   /**
    * Converts `str` to a Date object from a Date string (or null).
    * Returns null if str is falsy.
    */
   fromDateString: function(str) {
      return str ? ((str instanceof Date) ? str : new Date(str)) : null;
   },
};
<|endoftext|>


<commit_before>var Entity = require('/entity.js').Entity;
var Utils = require('/utils.js').Utils;
var PerspectiveCamera = require('/camera.js').PerspectiveCamera;
var PhongMaterial = require('/material.js').PhongMaterial;

function Scene(window) {
    Entity.call(this);
    this.entities = [];
    this.camera = new PerspectiveCamera(window.width/window.height);
    this.window = window;
    this.graphics = window.graphics;
}

Utils.extend(Scene, Entity);

Scene.prototype.add = function (entity) {
    this.entities.push(entity);
};

Scene.prototype.update = function(elapsed) {
    Entity.prototype.update.call(this, elapsed);
    for (var i=0; i<this.entities.length; i++) {
        this.entities[i].update(elapsed);
    }
    this.camera.update(elapsed);
};

Scene.prototype.draw = function() {
    PhongMaterial.camera(this.camera);
    Entity.prototype.draw.call(this);
    for (var i=0; i<this.entities.length; i++) {
        this.entities[i].draw();
    }
};

module.exports.Scene = Scene;
<commit_msg>Added support for layers and sprites.<commit_after>var Entity = require('/entity.js').Entity;
var Utils = require('/utils.js').Utils;
var PerspectiveCamera = require('/camera.js').PerspectiveCamera;
var PhongMaterial = require('/material.js').PhongMaterial;
var SpriteComponent = require('/sprite.js').SpriteComponent;

function Scene(window) {
    Entity.call(this);
    this.entities = [];
    this.camera = new PerspectiveCamera(window.width/window.height);
    this.window = window;
    this.graphics = window.graphics;
    this.layers = {};
}

Utils.extend(Scene, Entity);

Scene.prototype.add = function (entity, layer) {
    layer = layer || 0;
    if (!this.layers[layer]) {
        this.layers[layer] = [];
    }
    this.layers[layer].push(entity);
    this.entities.push(entity);
};

Scene.prototype.update = function(elapsed) {
    for (var layer in this.layers) {
        for (var i=0; i<this.layers[layer].length; i++) {
            this.layers[layer][i].update(elapsed);
        }
    }
    this.camera.update(elapsed);
};

Scene.prototype.draw = function() {
    PhongMaterial.camera(this.camera);
    for (var layer in this.layers) {
        for (var i=0; i<this.layers[layer].length; i++) {
            this.layers[layer][i].draw();
        }
        // When sprite components are drawn, the sprites are really just being
        // added to a list. The function below will actually draw the sprites.
        SpriteComponent.draw();
        // When sprites are drawn the depth state is automatically changed,
        // let's reset it here to prepare for the next frame.
        this.graphics.setDepthState('default');
    }
};

module.exports.Scene = Scene;
<|endoftext|>


<commit_before>
/**
 * Lazily required module dependencies
 */

var lazy = module.exports = require('lazy-cache')(require);

lazy('engine-handlebars', 'engine');
lazy('delegate-properties', 'delegate');
lazy('extend-shallow', 'extend');
lazy('through2', 'through');
lazy('parser-front-matter', 'matter');
lazy('vinyl-fs', 'vfs');
lazy('vinyl', 'Vinyl');
lazy('to-vinyl');
lazy('dest');

var utils = lazy;
<commit_msg>async utils<commit_after>
/**
 * Lazily required module dependencies
 */

var lazy = module.exports = require('lazy-cache')(require);

lazy('async');
lazy('engine-handlebars', 'engine');
lazy('delegate-properties', 'delegate');
lazy('extend-shallow', 'extend');
lazy('through2', 'through');
lazy('parser-front-matter', 'matter');
lazy('vinyl-fs', 'vfs');
lazy('vinyl', 'Vinyl');
lazy('to-vinyl');
lazy('dest');

var utils = lazy;

utils.identity = function (obj) {
  return obj;
};

utils.parallel = function (streams, done) {
  utils.async.parallel(handleStream(streams), done);
};

utils.series = function (streams, done) {
  utils.async.series(handleStream(streams), done);
};

function handleStream(streams) {
  streams = Array.isArray(streams) ? streams : [streams];
  return streams.map(function (stream) {
    return function (next) {
      if (typeof stream === 'function') {
        stream = stream();
      }
      stream
        .once('error', next)
        .once('end', next);
    };
  });
}
<|endoftext|>


<commit_before>/**
 * Created by AndrewLiang on 2015/11/24.
 */
'use strict';

/**
 * Module dependencies.
 * @private
 */

var parseUrl     = require('parseurl');
var path         = require('path');
var fs           = require('fs-extra');
var url          = require('url');
var finalhandler = require('finalhandler');
var statuses     = require('statuses');
var _extend      = require('util')._extend;

var config = require('../config');

var index       = config.index || ['index.html', 'default.htm'];
var extend      = config.extend || ['html', 'htm'];
var exclude     = config.exclude || [];
var root        = determineRoot(config.root);
var dist        = config.dist || 'dist';
var indexLen    = index.length;
var indexExtend = extend.length;

var staticServe = require('./static')(root, index);
var engine    = require('./' + config.engine.name)(root, config.engine.setting || {});

/**
 * Module exports.
 * @public
 */

module.exports = serve;

function serve(req, res) {
    var pathname = parseUrl(req).pathname;
    var extName  = path.extname(pathname);

    //console.log("pathname : " + pathname);
    //console.log(" extName : " + extName);

    // dir
    if (pathname === '/' || (pathname && pathname.substr(-1) === '/')) {
        findDefaultFile(pathname, req, res);
        return false;
    }

    // file
    if (extName) {
        distribute(pathname, extName, req, res);
        return false;
    }

    // file no extend
    findExtend(pathname, req, res);
}

/*
 * 如果访问的是目录的话（/ 或者 /xx/ ）， 按照设置的索引文件挨个读取，如果存在此文件，则渲染返回，否则返回 404
 * */
function findDefaultFile(pathname, req, res) {
    var fileDirectory = path.resolve(root + pathname) + '/'; // 当前将要访问文件的目录
    var i = 0;

    function findFile(filePath) {
        if (i >= indexLen) {
            error(res, 404);
            return false;
        }
        filePath = path.resolve(filePath);
        fs.stat(filePath, function (err, stats) {
            if (err) {
                if (err.code === 'ENOENT') {
                    console.log('ENOENT');
                    error(res, 404);
                } else {
                    error(res, 500, '读取索引文件状态出错！' + err);
                    //throw (new Error('读取索引文件状态出错！' + err));
                }
                //return false;
            }
            if (stats && stats.isFile()) {
                render(pathname + path.basename(filePath), req, res);
                //distribute(filePath, extend, req, res);
            } else {
                i++;
                findFile(fileDirectory + index[i]);
            }
        });
    }

    findFile(fileDirectory + index[0]);
}

function findExtend(pathname, req, res) {
    var fileName = path.resolve(root + pathname) + '.'; // 当前将要访问文件
    var i = 0;

    console.log("pathname : " + pathname);

    function findFile(filePath) {
        if (i >= indexExtend) {
            //error(res, 404);
            return false;
        }

        console.log("filePath : " + filePath);

        fs.stat(filePath, function (err, stats) {
            if (err) {
                if (err.code === 'ENOENT') {
                    console.log('ENOENT');
                    //error(res, 404);
                } else {
                    error(res, 500, '读取索引文件状态出错！' + err);
                    //throw (new Error('读取索引文件状态出错！' + err));
                }
                //return false;
            }
            if (stats && stats.isFile()) {
                render("/" + path.basename(filePath), req, res);
            } else {
                i++;
                findFile(fileName + extend[i]);
            }
        });
    }

    findFile(fileName + extend[0]);
}

function distribute(pathname, extName, req, res) {
    var fileName = path.resolve(root + pathname);
    var file     = path.basename(pathname);
    var ext      = extName.replace('.', '');
    var done     = function () {};

    if (extend.indexOf(ext) > -1) {
        fs.stat(fileName, function (err, stats) {
            if (err) {
                if (err.code === 'ENOENT') {
                    error(res, 404);
                } else {
                    error(res, 500, '读取文件状态出错！' + err);
                }
            }
            if (stats && stats.isFile()) {
                render(pathname, req, res);
            }
        });
    } else {
        done = finalhandler(req, res);
        staticServe(req, res, done);
    }
}

function render(fileName, req, res) {
    var data       = preData(req, res);
    var done       = function () {};
    var production = process.env.NODE_ENV === 'production';
    var pathName   = path.resolve(root + fileName);

    console.log(fileName);
    console.log(pathName);

    if (production) {
        fs.stat(pathName, function (err, stats) {
            if (err) {
                if (err.code === 'ENOENT') {
                    error(res, 404);
                } else {
                    error(res, 500, '读取文件状态出错！' + err);
                }
            }
            if (stats && stats.isFile()) {
                // 更改访问的路径，加上 dist 目录
                req.url = req.url + config.dist + '/';
                done = finalhandler(req, res);
                staticServe(req, res, done);
            }
        });

        return false;
    }

    fileName = path.basename(fileName); //fileName.slice(1, fileName.length);

    engine.render(fileName, data, function (err, file) {

        if (err) {
            error(res, 500, err);
            return false;
        }

        writeFile(fileName, file, function(err, file){
            /*res._headers = null;
            res.statusCode = 200;
            res.setHeader('Content-Type', 'text/html; charset=UTF-8');
            res.setHeader('Content-Length', Buffer.byteLength(file));
            res.setHeader('X-Content-Type-Options', 'nosniff');
            res.write(file);
            res.end();*/

            if (err) {
                console.log('文件：' + target + '生成失败，错误详情：' + err);
                return false;
            }

            req.url = config.dist + req.url;

            console.log(req.url);

            done = finalhandler(req, res);
            staticServe(req, res, done);
        });
    });
}

function writeFile(fileName, file, callback) {
    var target = path.resolve(root + '/' + dist + '/' + fileName);
    var has    = false;

    exclude.forEach(function (name, index) {
        if (target.indexOf(name) > -1) {
            has = true;
        }
    });

    if (has) {
        return false;
    }

    fs.outputFile(target, file, function (err) {
        if(callback){
            callback(err, target);
        }
    });

}

function error(res, status, msg) {
    msg          = (msg || statuses[status]).toString();
    res._headers = null;
    // send basic response
    res.statusCode = status;
    res.setHeader('Content-Type', 'text/html; charset=UTF-8');
    res.setHeader('Content-Length', Buffer.byteLength(msg));
    res.setHeader('X-Content-Type-Options', 'nosniff');
    res.write(msg);
    res.end();
    //console.log('nunjucks');
}

function preData(req, res) {
    var query = url.parse(req.url, true).query;

    if (typeof config.data === 'function') {
        return config.data(query);
    } else {
        return _extend(query, config.data);
    }
}

function determineRoot(root) {
    if (!root) {
        //throw (new Error('Root Path is required!'));
        root = path.resolve(__dirname + '/../public');
    }

    return root;
}<commit_msg>调整变量名<commit_after>/**
 * Created by AndrewLiang on 2015/11/24.
 */
'use strict';

/**
 * Module dependencies.
 */

/*
* Define Variable
* fullPath     : "E:\Andrew\serve-komplete\public\pure.html"
* fullDistPath : "E:\Andrew\serve-komplete\public\dist\pure.html"
* fullDotPath  : "E:\Andrew\serve-komplete\public\pure."
* filePath     : "\pure.html"
* fullFilePath : "\dist\pure.html",
* fileName     : "pure",
* fullFileName : "pure.html",
* extName      : "html"
* fullExtName  : ".html"
* */
var parseUrl     = require('parseurl');
var path         = require('path');
var fs           = require('fs-extra');
var url          = require('url');
var finalhandler = require('finalhandler');
var statuses     = require('statuses');
var _extend      = require('util')._extend;

var config = require('../config');

var index       = config.index || ['index.html', 'default.htm'];
var extend      = config.extend || ['html', 'htm'];
var exclude     = config.exclude || [];
var root        = determineRoot(config.root);
var dist        = config.dist || 'dist';
var indexLen    = index.length;
var indexExtend = extend.length;

var staticServe = require('./static')(root, index);
var engine      = require('./' + config.engine.name)(root, config.engine.setting || {});

/**
 * Module exports.
 * @public
 */

module.exports = serve;

function serve(req, res) {
    var filePath     = parseUrl(req).pathname;  // /index.html
    var fullExtName  = path.extname(filePath);  // .html

    console.log("filePath    : " + filePath);
    console.log("fullExtName : " + fullExtName);

    // dir
    if (filePath === '/' || (filePath && filePath.substr(-1) === '/')) {
        findDefaultFile(filePath, req, res);
        return false;
    }

    // file
    if (fullExtName) {
        distribute(filePath, fullExtName, req, res);
        return false;
    }

    // file no extend
    findExtend(filePath, req, res);
}

/*
 * 如果访问的是目录的话（/ 或者 /xx/ ）， 按照设置的索引文件挨个读取，如果存在此文件，则渲染返回，否则返回 404
 * */
function findDefaultFile(filePath, req, res) {
    var fileDirectory = path.resolve(root + filePath) + '/'; // 当前将要访问文件的目录

    var i = 0;

    function findFile(fullPath) {
        if (i >= indexLen) {
            error(res, 404);
            return false;
        }
        fullPath = path.resolve(fullPath);
        fs.stat(fullPath, function (err, stats) {
            if (err) {
                if (err.code === 'ENOENT') {
                    console.log('ENOENT');
                    error(res, 404);
                } else {
                    error(res, 500, '读取索引文件状态出错！' + err);
                    //throw (new Error('读取索引文件状态出错！' + err));
                }
                //return false;
            }
            if (stats && stats.isFile()) {
                render(filePath + path.basename(fullPath), req, res);
            } else {
                i++;
                findFile(fileDirectory + index[i]);
            }
        });
    }

    findFile(fileDirectory + index[0]);
}

function findExtend(filePath, req, res) {
    var fullDotPath = path.resolve(root + filePath) + '.'; // 当前将要访问文件 filePath /pure
    var i = 0;

    function findFile(fullPath) {
        if (i >= indexExtend) {
            return false;
        }

        // E:\Andrew\serve-komplete\public\pure.html
        fs.stat(fullPath, function (err, stats) {
            if (err) {
                if (err.code === 'ENOENT') {
                    console.log('ENOENT');
                } else {
                    error(res, 500, '读取索引文件状态出错！' + err);
                }
            }
            if (stats && stats.isFile()) {
                // /pure.html
                render("/" + path.basename(fullPath), req, res);
            } else {
                i++;
                findFile(fullDotPath + extend[i]);
            }
        });
    }

    findFile(fullDotPath + extend[0]);
}

function distribute(pathname, extName, req, res) {
    var fileName = path.resolve(root + pathname);
    var file     = path.basename(pathname);
    var ext      = extName.replace('.', '');
    var done     = function () {};

    if (extend.indexOf(ext) > -1) {
        fs.stat(fileName, function (err, stats) {
            if (err) {
                if (err.code === 'ENOENT') {
                    error(res, 404);
                } else {
                    error(res, 500, '读取文件状态出错！' + err);
                }
            }
            if (stats && stats.isFile()) {
                render(pathname, req, res);
            }
        });
    } else {
        done = finalhandler(req, res);
        staticServe(req, res, done);
    }
}

function render(fileName, req, res) {
    var data       = preData(req, res);
    var done       = function () {};
    var production = process.env.NODE_ENV === 'production';
    var pathName   = path.resolve(root + fileName); //  E:\Andrew\serve-komplete\public\index.html
    var filePath   = fileName; // /index.html

    if (production) {
        fs.stat(pathName, function (err, stats) {
            if (err) {
                if (err.code === 'ENOENT') {
                    error(res, 404);
                } else {
                    error(res, 500, '读取文件状态出错！' + err);
                }
            }
            if (stats && stats.isFile()) {
                // 更改访问的路径，加上 dist 目录
                req.url = req.url + config.dist + '/';
                done = finalhandler(req, res);
                staticServe(req, res, done);
            }
        });

        return false;
    }

    fileName = path.basename(fileName); //index.html fileName.slice(1, fileName.length);

    engine.render(fileName, data, function (err, file) {

        if (err) {
            error(res, 500, err);
            return false;
        }

        writeFile(fileName, file, function(err, file){
            /*res._headers = null;
            res.statusCode = 200;
            res.setHeader('Content-Type', 'text/html; charset=UTF-8');
            res.setHeader('Content-Length', Buffer.byteLength(file));
            res.setHeader('X-Content-Type-Options', 'nosniff');
            res.write(file);
            res.end();*/

            if (err) {
                console.log('文件：' + target + '生成失败，错误详情：' + err);
                return false;
            }

            req.url = config.dist + filePath; // dist/pure.html

            done = finalhandler(req, res);
            staticServe(req, res, done);
        });
    });
}

function writeFile(fileName, file, callback) {
    var target = path.resolve(root + '/' + dist + '/' + fileName);
    var has    = false;

    exclude.forEach(function (name, index) {
        if (target.indexOf(name) > -1) {
            has = true;
        }
    });

    if (has) {
        return false;
    }

    fs.outputFile(target, file, function (err) {
        if(callback){
            callback(err, target);
        }
    });

}

function error(res, status, msg) {
    msg          = (msg || statuses[status]).toString();
    res._headers = null;
    // send basic response
    res.statusCode = status;
    res.setHeader('Content-Type', 'text/html; charset=UTF-8');
    res.setHeader('Content-Length', Buffer.byteLength(msg));
    res.setHeader('X-Content-Type-Options', 'nosniff');
    res.write(msg);
    res.end();
    //console.log('nunjucks');
}

function preData(req, res) {
    var query = url.parse(req.url, true).query;

    if (typeof config.data === 'function') {
        return config.data(query);
    } else {
        return _extend(query, config.data);
    }
}

function determineRoot(root) {
    if (!root) {
        //throw (new Error('Root Path is required!'));
        root = path.resolve(__dirname + '/../public');
    }

    return root;
}<|endoftext|>


<commit_before>var extend = require('extend');
var fs = require('fs');
var http = require('http');
var mime = require('mime');
var path = require('path');
var url = require('url');
var util = require('util');

function serve(port, host) {
  var options = {
    cwd: process.cwd(),
    scripts: {}
  };

  if (arguments.length === 3) {
    var callback = arguments[2];
  } else {
    var options = extend(options, arguments[2]);
    var callback = arguments[3];
  }

  var server = http.createServer();

  server.on('error', reject);
  server.on('listening', accept);
  server.on('request', handle);

  server.listen(port, host);

  function handle(request, response) {
    var uri = url.parse(request.url);

    if (uri.pathname === '/' || uri.pathname === '/index.html') {
      return index(request, response);
    } else if (uri.pathname === 'favicon.ico') {
      return response.end();
    } else {
      return file(request, response);
    }

    function file(request, response) {
      var filename = path.join(options.cwd, uri.pathname);

      for (var name in options.scripts) {
        if (options.scripts.hasOwnProperty(name)) {
          if (options.scripts[name] === path.relative('/', uri.path)) {
            filename = name;
            break;
          }
        }
      }

      fs.exists(filename, function(exists) {
        if (exists) {
          response.setHeader('content-type', mime.lookup(filename));
          response.writeHead(200);

          var file = fs.createReadStream(filename);
          file.pipe(response);
        } else {
          response.writeHead(404);
          response.end('404');
        }
      });
    }

    function index(request, response) {
      var filename = path.join(options.cwd, 'index.html');

      fs.exists(filename, function(exists) {
        if (exists) {
          return file(request, response);
        } else {
          response.setHeader('content-type', 'text/html');
          response.write('<!doctype html><meta charset="utf-8">');

          for (var name in options.scripts) {
            if (options.scripts.hasOwnProperty(name)) {
              response.write('');
            }
          }

          response.end('');
        }
      });
    }
  }

  function reject(error) {
    callback(error);
  }

  function accept() {
    server.removeListener('error', reject);
    callback(null, server);
  }
}

module.exports = serve;
<commit_msg>Clean up callback failure/success<commit_after>var extend = require('extend');
var fs = require('fs');
var http = require('http');
var mime = require('mime');
var path = require('path');
var url = require('url');
var util = require('util');

function serve(port, host) {
  var options = {
    cwd: process.cwd(),
    scripts: {}
  };

  if (arguments.length === 3) {
    var callback = arguments[2];
  } else {
    var options = extend(options, arguments[2]);
    var callback = arguments[3];
  }

  var server = http.createServer();

  server.on('error', callback);
  server.on('listening', function() {
    server.removeListener('error', callback);
    callback(null, server);
  });

  server.on('request', handle);
  server.listen(port, host);

  function handle(request, response) {
    var uri = url.parse(request.url);

    if (uri.pathname === '/' || uri.pathname === '/index.html') {
      return index(request, response);
    } else if (uri.pathname === 'favicon.ico') {
      return response.end();
    } else {
      return file(request, response);
    }

    function file(request, response) {
      var filename = path.join(options.cwd, uri.pathname);

      for (var name in options.scripts) {
        if (options.scripts.hasOwnProperty(name)) {
          if (options.scripts[name] === path.relative('/', uri.path)) {
            filename = name;
            break;
          }
        }
      }

      fs.exists(filename, function(exists) {
        if (exists) {
          response.setHeader('content-type', mime.lookup(filename));
          response.writeHead(200);

          var file = fs.createReadStream(filename);
          file.pipe(response);
        } else {
          response.writeHead(404);
          response.end('404');
        }
      });
    }

    function index(request, response) {
      var filename = path.join(options.cwd, 'index.html');

      fs.exists(filename, function(exists) {
        if (exists) {
          return file(request, response);
        } else {
          response.setHeader('content-type', 'text/html');
          response.write('<!doctype html><meta charset="utf-8">');

          for (var name in options.scripts) {
            if (options.scripts.hasOwnProperty(name)) {
              response.write('');
            }
          }

          response.end('');
        }
      });
    }
  }
}

module.exports = serve;
<|endoftext|>