<commit_before>/*
 * Iris
 * (c) 2016+ Buenos Aires City Government
 * License: https://opensource.org/licenses/MIT
 *
 */

'use strict';

const Flow = require('./lib/Flow');
const BaseDock = require('./lib/bases/Dock');
const BaseHandler = require('./lib/bases/Handler');
const BaseHook = require('./lib/bases/Hook');
const dispatcher = require('./lib/Dispatcher');
const validator = require('propchecker');
const shortid = require('shortid');
const ora = require('ora');
const chalk = require('chalk');

class Iris {
    constructor() {
        this._config = {};
        this._flows = [];
        this._modules = [];
    }

    get flows() {
        return this._flows;
    }

    get config() {
        return this._config;
    }

    get modules() {
        return this._modules;
    }

    get Dock() {
        return BaseDock;
    }

    get Handler() {
        return BaseHandler;
    }

    get Hook() {
        return BaseHook;
    }

    set config(options) {
        const config = options;

        this._checkConfig(config);
        config.logLevel = config.logLevel || 'info';

        if (config.events) {
            config.events.dispatcher = config.events.dispatcher && true;
            config.events.docks = config.events.docks && true;
            config.events.handlers = config.events.handlers && true;
            config.events.hooks = config.events.hooks && true;
        }

        if (config.vantage) {
            config.vantage.enabled = config.vantage.enabled || false;
        }

        Object.assign(this._config, options);
    }

    set(key, value) {
        this._config[key] = value;
    }

    flow(name, options) {
        const config = options;

        this._spinner = ora(`Validating '${name}'`).start();
        this._checkFlowOptions(config);
        config.inputHooks = config.inputHooks || [];
        config.outputHooks = config.outputHooks || [];

        const flow = new Flow(name, config);

        this._validateDocks(flow, this._spinner);
        this._validateHooks(flow, this._spinner);
        this._validateHandler(flow, this._spinner);
        this._spinner.succeed();
        this._flows.push(flow);
    }

    _checkConfig(config) {
        const schema = {
            threads: validator.isNumber,
            logLevel: validator.isString,
            events: {
                dispatcher: validator.isBoolean,
                docks: validator.isBoolean,
                handlers: validator.isBoolean,
                hooks: validator.isBoolean
            },
            vantage: {
                enabled: validator.isBoolean,
                port: validator.isNumber
            }
        };

        validator.validate(config, schema, this._handleErrors);
    }

    _checkFlowOptions(options) {
        const flowSchema = {
            tag: [validator.isRequired, validator.isString],
            docks: [validator.isRequired, validator.isArray],
            handler: validator.isRequired,
            inputHooks: validator.isArray,
            outputHooks: validator.isArray
        };

        validator.validate(options, flowSchema, this._handleErrors);
    }

    _validateDocks(flow) {
        const dockSchema = {
            name: [validator.isRequired, validator.isString],
            protocol: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            validate: [validator.isRequired, validator.isFunction],
            parse: [validator.isRequired, validator.isFunction],
            process: [validator.isRequired, validator.isFunction],
            reply: [validator.isRequired, validator.isFunction],
            encode: [validator.isRequired, validator.isFunction],
            listen: [validator.isRequired, validator.isFunction],
            port: [validator.isRequired, validator.isNumber],
            send: [validator.isFunction]
        };

        flow.docks.forEach((dock) => {
            if (!dock.id) {
                validator.validate(dock, dockSchema, this._handleErrors);
                dock.id = shortid.generate();
                dock.config = Object.assign(dock.config, {
                    events: this._config.events.docks
                });
                this._modules.push(dock.path);
                this._startDock(dock);
            }
        }, this);
    }

    _validateHooks(flow) {
        const hookSchema = {
            name: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            process: [validator.isRequired, validator.isFunction],
            run: [validator.isRequired, validator.isFunction]
        };

        flow.inputHooks.forEach(function (hook) {
            if (!hook.validated) {
                validator.validate(hook, hookSchema, this._handleErrors);
                this._configureComponent(hook, this.config.events.hooks);
            }
        }, this);

        flow.outputHooks.forEach(function (hook) {
            if (!hook.validated) {
                validator.validate(hook, hookSchema, this._handleErrors);
                this._configureComponent(hook, this.config.events.hooks);
            }
        }, this);
    }

    _validateHandler(flow) {
        const handlerSchema = {
            name: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            process: [validator.isRequired, validator.isFunction],
            handle: [validator.isRequired, validator.isFunction]
        };

        if (!flow.handler.validated) {
            validator.validate(flow.handler, handlerSchema, this._handleErrors);
            this._configureComponent(flow.handler, this.config.events.handlers);
        }
    }

    _configureComponent(component, eventConfig) {
        component.validated = true;
        component.config = Object.assign(component.config, {
            events: eventConfig
        });
        this._modules.push(component.path);
    }

    _handleErrors(errors) {
        this._spinner.fail();

        errors.forEach(function (error) {
            console.error(chalk.red(error));
        }, this);

        process.exit(1);
    }

    _startDock(dock) {
        let id = shortid.generate();

        dock.id = id;
        dock.dispatcher = dispatcher;
        dock.listen(dock.config.port);
    }
}

module.exports = new Iris();<commit_msg>Revert "Cleanup"<commit_after>/*
 * Iris
 * (c) 2016+ Buenos Aires City Government
 * License: https://opensource.org/licenses/MIT
 *
 */

'use strict';

const Flow = require('./lib/Flow');
const BaseDock = require('./lib/bases/Dock');
const BaseHandler = require('./lib/bases/Handler');
const BaseHook = require('./lib/bases/Hook');
const dispatcher = require('./lib/Dispatcher');
const validator = require('propchecker');
const shortid = require('shortid');
const ora = require('ora');
const chalk = require('chalk');

class Iris {
    constructor() {
        this._config = {};
        this._flows = [];
        this._modules = [];
    }

    get flows() {
        return this._flows;
    }

    get config() {
        return this._config;
    }

    get modules() {
        return this._modules;
    }

    get Dock() {
        return BaseDock;
    }

    get Handler() {
        return BaseHandler;
    }

    get Hook() {
        return BaseHook;
    }

    set config(options) {
        const config = options;

        this._checkConfig(config);
        config.logLevel = config.logLevel || 'info';

        if (config.events) {
            config.events.dispatcher = config.events.dispatcher && true;
            config.events.docks = config.events.docks && true;
            config.events.handlers = config.events.handlers && true;
            config.events.hooks = config.events.hooks && true;
        }

        if (config.vantage) {
            config.vantage.enabled = config.vantage.enabled || false;
        }

        Object.assign(this._config, options);
    }

    set(key, value) {
        this._config[key] = value;
    }

    flow(name, options) {
        const config = options;

        this._spinner = ora(`Validating '${name}'`).start();
        this._checkFlowOptions(config);
        config.inputHooks = config.inputHooks || [];
        config.outputHooks = config.outputHooks || [];

        const flow = new Flow(name, config);

        this._validateDocks(flow, this._spinner);
        this._validateHooks(flow, this._spinner);
        this._validateHandler(flow, this._spinner);
        this._spinner.succeed();
        this._flows.push(flow);
    }

    _checkConfig(config, spinner) {
        const schema = {
            threads: validator.isNumber,
            logLevel: validator.isString,
            events: {
                dispatcher: validator.isBoolean,
                docks: validator.isBoolean,
                handlers: validator.isBoolean,
                hooks: validator.isBoolean
            },
            vantage: {
                enabled: validator.isBoolean,
                port: validator.isNumber
            }
        };

        validator.validate(config, schema, this._handleErrors);
    }

    _checkFlowOptions(options, spinner) {
        const schema = {
            tag: [validator.isRequired, validator.isString],
            docks: [validator.isRequired, validator.isArray],
            handler: validator.isRequired,
            inputHooks: validator.isArray,
            outputHooks: validator.isArray
        };

        validator.validate(options, schema, this._handleErrors);
    }

    _validateDocks(flow, spinner) {
        const dockSchema = {
            name: [validator.isRequired, validator.isString],
            protocol: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            validate: [validator.isRequired, validator.isFunction],
            parse: [validator.isRequired, validator.isFunction],
            process: [validator.isRequired, validator.isFunction],
            reply: [validator.isRequired, validator.isFunction],
            encode: [validator.isRequired, validator.isFunction],
            listen: [validator.isRequired, validator.isFunction],
            port: [validator.isRequired, validator.isNumber],
            send: [validator.isFunction]
        };

        flow.docks.forEach((dock) => {
            if (!dock.id) {
                validator.validate(dock, dockSchema, this._handleErrors);
                dock.id = shortid.generate();
                dock.config = Object.assign(dock.config, {
                    events: this._config.events.docks
                });
                this._modules.push(dock.path);
                this._startDock(dock);
            }
        }, this);
    }

    _validateHooks(flow, spinner) {
        const hookSchema = {
            name: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            process: [validator.isRequired, validator.isFunction],
            run: [validator.isRequired, validator.isFunction]
        };

        flow.inputHooks.forEach(function (hook) {
            if (!hook.validated) {
                validator.validate(hook, hookSchema, this._handleErrors);
                this._configureComponent(hook, this.config.events.hooks);
            }
        }, this);

        flow.outputHooks.forEach(function (hook) {
            if (!hook.validated) {
                validator.validate(hook, hookSchema, this._handleErrors);
                this._configureComponent(hook, this.config.events.hooks);
            }
        }, this);
    }

    _validateHandler(flow, spinner) {
        const handlerSchema = {
            name: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            process: [validator.isRequired, validator.isFunction],
            handle: [validator.isRequired, validator.isFunction]
        };

        if (!flow.handler.validated) {
            validator.validate(flow.handler, handlerSchema, this._handleErrors);
            this._configureComponent(flow.handler, this.config.events.handlers);
        }
    }

    _configureComponent(component, eventConfig) {
        component.validated = true;
        component.config = Object.assign(component.config, {
            events: eventConfig
        });
        this._modules.push(component.path);
    }

    _handleErrors(errors) {
        this._spinner.fail();

        errors.forEach(function (error) {
            console.error(chalk.red(error));
        }, this);

        process.exit(1);
    }

    _startDock(dock) {
        let id = shortid.generate();

        dock.id = id;
        dock.dispatcher = dispatcher;
        dock.listen(dock.config.port);
    }
}

module.exports = new Iris();<|endoftext|>


<commit_before>'use strict';

const got = require('got');

const replace = arg => {
	return arg.replace('/s320x320', '');
};

const highDefinition = username => {
	if (typeof username !== 'string') {
		throw new TypeError(`Expected a string, got ${typeof username}`);
	}

	const url = `https://www.instagram.com/${username}/?__a=1`;

	return got(`${url}`, {json: true}).then(res => {
		return replace(res.body.user.profile_pic_url_hd) || '';
	});
};

const instaProfileImage = module.exports = username => highDefinition(username);

instaProfileImage.medium = username => {
	if (typeof username !== 'string') {
		throw new TypeError(`Expected a string, got ${typeof username}`);
	}

	const url = `https://www.instagram.com/${username}/?__a=1`;

	return got(`${url}`, {json: true}).then(res => {
		return res.body.user.profile_pic_url_hd || '';
	});
};

instaProfileImage.small = username => {
	if (typeof username !== 'string') {
		throw new TypeError(`Expected a string, got ${typeof username}`);
	}

	const url = `https://www.instagram.com/${username}/?__a=1`;

	return got(`${url}`, {json: true}).then(res => {
		return res.body.user.profile_pic_url || '';
	});
};

instaProfileImage.image = source => {
	if (typeof source !== 'string') {
		throw new TypeError(`Expected a string, got ${typeof source}`);
	}

	const url = source.split('?')[0];

	return got(`${url}?__a=1`, {json: true}).then(res => {
		return res.body.graphql.shortcode_media.display_url || '';
	});
};

instaProfileImage.video = source => {
	if (typeof source !== 'string') {
		throw new TypeError(`Epxected a string, got ${typeof source}`);
	}

	const url = source.split('?')[0];

	return got(`${url}?__a=1`, {json: true}).then(res => {
		return res.body.graphql.shortcode_media.video_url || '';
	});
};
<commit_msg>fix issue #1<commit_after>'use strict';

const got = require('got');

const cdn = 'https://scontent-lga3-1.cdninstagram.com/';

const replaceCDN = link => {
	return `${cdn}${link.split('/vp/')[1].replace('/s320x320/', '/s1080x1080/')}`;
};

const highDefinition = username => {
	if (typeof username !== 'string') {
		throw new TypeError(`Expected a string, got ${typeof username}`);
	}

	const url = `https://www.instagram.com/${username}/?__a=1`;

	return got(`${url}`, {json: true}).then(res => {
		return replaceCDN(res.body.graphql.user.profile_pic_url_hd) || '';
	});
};

const instaProfileImage = module.exports = username => highDefinition(username);

instaProfileImage.medium = username => {
	if (typeof username !== 'string') {
		throw new TypeError(`Expected a string, got ${typeof username}`);
	}

	const url = `https://www.instagram.com/${username}/?__a=1`;

	return got(`${url}`, {json: true}).then(res => {
		return res.body.graphql.user.profile_pic_url_hd || '';
	});
};

instaProfileImage.small = username => {
	if (typeof username !== 'string') {
		throw new TypeError(`Expected a string, got ${typeof username}`);
	}

	const url = `https://www.instagram.com/${username}/?__a=1`;

	return got(`${url}`, {json: true}).then(res => {
		return res.body.graphql.user.profile_pic_url || '';
	});
};

instaProfileImage.image = source => {
	if (typeof source !== 'string') {
		throw new TypeError(`Expected a string, got ${typeof source}`);
	}

	const url = source.split('?')[0];

	return got(`${url}?__a=1`, {json: true}).then(res => {
		return res.body.graphql.shortcode_media.display_url || '';
	});
};

instaProfileImage.video = source => {
	if (typeof source !== 'string') {
		throw new TypeError(`Epxected a string, got ${typeof source}`);
	}

	const url = source.split('?')[0];

	return got(`${url}?__a=1`, {json: true}).then(res => {
		return res.body.graphql.shortcode_media.video_url || '';
	});
};
<|endoftext|>


<commit_before>'use strict';
/**
 * @file structure test
 * @module mongodb-backup
 * @subpackage test
 * @version 0.0.1
 * @author hex7c0 <hex7c0@gmail.com>
 * @license GPLv3
 */

/*
 * initialize module
 */
var backup = require('..');
var assert = require('assert');
var fs = require('fs');
var extname = require('path').extname;
var URI = process.env.URI;

/*
 * test module
 */
describe('structure', function() {

  describe('with query', function() {

    var ROOT = __dirname + '/dump';

    describe('collections', function() {

      it('should build 1 directory (*.json)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          collections: [ 'logins', 'auths' ],
          parser: 'json',
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              var second = fs.readdirSync(database);
              assert.equal(second.length, 2);
              assert.equal(second[0], 'auths');
              assert.equal(second[1], 'logins');

              var collection = database + '/' + second[0];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.json');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              var collection = database + '/' + second[1];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.json');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
      it('should build 1 directory (*.bson)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          collections: [ 'logins', 'auths' ],
          parser: 'bson',
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              var second = fs.readdirSync(database);
              assert.equal(second.length, 2);
              assert.equal(second[0], 'auths');
              assert.equal(second[1], 'logins');

              var collection = database + '/' + second[0];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.bson');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              var collection = database + '/' + second[1];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.bson');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
    });

    describe('parser', function() {

      it('should build any directories (*.json)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          parser: 'json',
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              fs.readdirSync(database).forEach(function(second) { // collection

                var collection = database + '/' + second;
                if (fs.statSync(collection).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(collection).forEach(function(third) { // document

                  assert.equal(extname(third), '.json');
                  var document = collection + '/' + third;
                  fs.unlinkSync(document);
                });
                fs.rmdirSync(collection);
              });
              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
      it('should build any directories (*.bson)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          parser: 'bson',
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              fs.readdirSync(database).forEach(function(second) { // collection

                var collection = database + '/' + second;
                if (fs.statSync(collection).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(collection).forEach(function(third) { // document

                  assert.equal(extname(third), '.bson');
                  var document = collection + '/' + third;
                  fs.unlinkSync(document);
                });
                fs.rmdirSync(collection);
              });
              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
    });

    describe('tar', function() {

      var path = ROOT + '/t1.tar';
      it('should check that tar file not exist before test', function(done) {

        assert.equal(fs.existsSync(path), false);
        done();
      });
      it('should make a tar file', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          tar: 't1.tar',
          callback: function() {

            assert.equal(fs.existsSync(path), true);
            fs.unlink(path, done);
          }
        });
      });
      it('should check that buffer dir not exist', function(done) {

        var paths = __dirname + '/../dump';
        assert.equal(fs.existsSync(paths), true); // stay alive
        assert.equal(fs.readdirSync(paths).length, 0, 'empty dir');
        done();
      });
    });

    describe('logger', function() {

      var l = 'l1.log';
      it('should check that log file not exist before test', function(done) {

        assert.equal(fs.existsSync(l), false);
        done();
      });
      it('should make a log file', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          logger: l,
          callback: function() {

            assert.equal(fs.existsSync(l), true);
            fs.unlink(l, done);
          }
        });
      });
      it('should remove dirs', function(done) {

        fs.readdirSync(ROOT).forEach(function(first) { // database

          var database = ROOT + '/' + first;
          if (fs.statSync(database).isDirectory() === false) {
            return;
          }
          fs.readdirSync(database).forEach(function(second) { // collection

            var collection = database + '/' + second;
            if (fs.statSync(collection).isDirectory() === false) {
              return;
            }
            fs.readdirSync(collection).forEach(function(third) { // document

              assert.equal(extname(third), '.bson');
              var document = collection + '/' + third;
              fs.unlinkSync(document);
            });
            fs.rmdirSync(collection);
          });
          fs.rmdirSync(database);
        });
        done();
      });
    });
  });

  describe('with parallelCollectionScan', function() {

    var ROOT = __dirname + '/dump';

    describe('collections', function() {

      it('should build 1 directory (*.json)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          collections: [ 'logins', 'auths' ],
          parser: 'json',
          numCursors: 2,
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              var second = fs.readdirSync(database);
              assert.equal(second.length, 2);
              assert.equal(second[0], 'auths');
              assert.equal(second[1], 'logins');

              var collection = database + '/' + second[0];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.json');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              var collection = database + '/' + second[1];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.json');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
      it('should build 1 directory (*.bson)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          collections: [ 'logins', 'auths' ],
          parser: 'bson',
          numCursors: 2,
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              var second = fs.readdirSync(database);
              assert.equal(second.length, 2);
              assert.equal(second[0], 'auths');
              assert.equal(second[1], 'logins');

              var collection = database + '/' + second[0];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.bson');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              var collection = database + '/' + second[1];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.bson');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
    });

    describe('parser', function() {

      it('should build any directories (*.json)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          parser: 'json',
          numCursors: 2,
          callback: function() {

            setTimeout(function() {

              fs.readdirSync(ROOT).forEach(function(first) { // database

                var database = ROOT + '/' + first;
                if (fs.statSync(database).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(database).forEach(function(second) { // collection

                  var collection = database + '/' + second;
                  if (fs.statSync(collection).isDirectory() === false) {
                    return;
                  }
                  fs.readdirSync(collection).forEach(function(third) { // document

                    assert.equal(extname(third), '.json');
                    var document = collection + '/' + third;
                    fs.unlinkSync(document);
                  });
                  fs.rmdirSync(collection);
                });
                fs.rmdirSync(database);
              });
              done();
            }, 100);
          }
        });
      });
      it('should build any directories (*.bson)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          parser: 'bson',
          numCursors: 2,
          callback: function() {

            setTimeout(function() {

              fs.readdirSync(ROOT).forEach(function(first) { // database

                var database = ROOT + '/' + first;
                if (fs.statSync(database).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(database).forEach(function(second) { // collection

                  var collection = database + '/' + second;
                  if (fs.statSync(collection).isDirectory() === false) {
                    return;
                  }
                  fs.readdirSync(collection).forEach(function(third) { // document

                    assert.equal(extname(third), '.bson');
                    var document = collection + '/' + third;
                    fs.unlinkSync(document);
                  });
                  fs.rmdirSync(collection);
                });
                fs.rmdirSync(database);
              });
              done();
            }, 100);
          }
        });
      });
    });

    describe('tar', function() {

      var path = ROOT + '/t2.tar';
      it('should check that tar file not exist before test', function(done) {

        assert.equal(fs.existsSync(path), false);
        done();
      });
      it('should make a tar file', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          tar: 't2.tar',
          numCursors: 2,
          callback: function() {

            assert.equal(fs.existsSync(path), true);
            fs.unlink(path, done);
          }
        });
      });
      it('should check that buffer dir not exist', function(done) {

        var paths = __dirname + '/../dump';
        assert.equal(fs.existsSync(paths), true); // stay alive
        assert.equal(fs.readdirSync(paths).length, 0, 'empty dir');
        done();
      });
    });

    describe('logger', function() {

      var l = 'l2.log';
      it('should check that log file not exist before test', function(done) {

        assert.equal(fs.existsSync(l), false);
        done();
      });
      it('should make a log file', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          logger: l,
          numCursors: 2,
          callback: function() {

            assert.equal(fs.existsSync(l), true);
            fs.unlink(l, done);
          }
        });
      });
      it('should remove dirs', function(done) {

        fs.readdirSync(ROOT).forEach(function(first) { // database

          var database = ROOT + '/' + first;
          if (fs.statSync(database).isDirectory() === false) {
            return;
          }
          fs.readdirSync(database).forEach(function(second) { // collection

            var collection = database + '/' + second;
            if (fs.statSync(collection).isDirectory() === false) {
              return;
            }
            fs.readdirSync(collection).forEach(function(third) { // document

              assert.equal(extname(third), '.bson');
              var document = collection + '/' + third;
              fs.unlinkSync(document);
            });
            fs.rmdirSync(collection);
          });
          fs.rmdirSync(database);
        });
        done();
      });
    });
  });
});
<commit_msg>test: fix previous file after callback<commit_after>'use strict';
/**
 * @file structure test
 * @module mongodb-backup
 * @subpackage test
 * @version 0.0.1
 * @author hex7c0 <hex7c0@gmail.com>
 * @license GPLv3
 */

/*
 * initialize module
 */
var backup = require('..');
var assert = require('assert');
var fs = require('fs');
var extname = require('path').extname;
var URI = process.env.URI;

/*
 * test module
 */
describe('structure', function() {

  describe('with query', function() {

    var ROOT = __dirname + '/dump';

    describe('collections', function() {

      it('should build 1 directory (*.json)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          collections: [ 'logins', 'auths' ],
          parser: 'json',
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              var second = fs.readdirSync(database);
              assert.equal(second.length, 2);
              assert.equal(second[0], 'auths');
              assert.equal(second[1], 'logins');

              var collection = database + '/' + second[0];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.json');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              var collection = database + '/' + second[1];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.json');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
      it('should build 1 directory (*.bson)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          collections: [ 'logins', 'auths' ],
          parser: 'bson',
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              var second = fs.readdirSync(database);
              assert.equal(second.length, 2);
              assert.equal(second[0], 'auths');
              assert.equal(second[1], 'logins');

              var collection = database + '/' + second[0];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.bson');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              var collection = database + '/' + second[1];
              if (fs.statSync(collection).isDirectory() === false) {
                return;
              }
              fs.readdirSync(collection).forEach(function(third) { // document

                assert.equal(extname(third), '.bson');
                var document = collection + '/' + third;
                fs.unlinkSync(document);
              });
              fs.rmdirSync(collection);

              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
    });

    describe('parser', function() {

      it('should build any directories (*.json)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          parser: 'json',
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              fs.readdirSync(database).forEach(function(second) { // collection

                var collection = database + '/' + second;
                if (fs.statSync(collection).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(collection).forEach(function(third) { // document

                  assert.equal(extname(third), '.json');
                  var document = collection + '/' + third;
                  fs.unlinkSync(document);
                });
                fs.rmdirSync(collection);
              });
              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
      it('should build any directories (*.bson)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          parser: 'bson',
          callback: function() {

            fs.readdirSync(ROOT).forEach(function(first) { // database

              var database = ROOT + '/' + first;
              if (fs.statSync(database).isDirectory() === false) {
                return;
              }
              fs.readdirSync(database).forEach(function(second) { // collection

                var collection = database + '/' + second;
                if (fs.statSync(collection).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(collection).forEach(function(third) { // document

                  assert.equal(extname(third), '.bson');
                  var document = collection + '/' + third;
                  fs.unlinkSync(document);
                });
                fs.rmdirSync(collection);
              });
              fs.rmdirSync(database);
            });
            done();
          }
        });
      });
    });

    describe('tar', function() {

      var path = ROOT + '/t1.tar';
      it('should check that tar file not exist before test', function(done) {

        assert.equal(fs.existsSync(path), false);
        done();
      });
      it('should make a tar file', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          tar: 't1.tar',
          callback: function() {

            assert.equal(fs.existsSync(path), true);
            fs.unlink(path, done);
          }
        });
      });
      it('should check that buffer dir not exist', function(done) {

        var paths = __dirname + '/../dump';
        assert.equal(fs.existsSync(paths), true); // stay alive
        assert.equal(fs.readdirSync(paths).length, 0, 'empty dir');
        done();
      });
    });

    describe('logger', function() {

      var l = 'l1.log';
      it('should check that log file not exist before test', function(done) {

        assert.equal(fs.existsSync(l), false);
        done();
      });
      it('should make a log file', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          logger: l,
          callback: function() {

            assert.equal(fs.existsSync(l), true);
            fs.unlink(l, done);
          }
        });
      });
      it('should remove dirs', function(done) {

        fs.readdirSync(ROOT).forEach(function(first) { // database

          var database = ROOT + '/' + first;
          if (fs.statSync(database).isDirectory() === false) {
            return;
          }
          fs.readdirSync(database).forEach(function(second) { // collection

            var collection = database + '/' + second;
            if (fs.statSync(collection).isDirectory() === false) {
              return;
            }
            fs.readdirSync(collection).forEach(function(third) { // document

              assert.equal(extname(third), '.bson');
              var document = collection + '/' + third;
              fs.unlinkSync(document);
            });
            fs.rmdirSync(collection);
          });
          fs.rmdirSync(database);
        });
        done();
      });
    });
  });

  describe('with parallelCollectionScan', function() {

    var ROOT = __dirname + '/dump';

    describe('collections', function() {

      it('should build 1 directory (*.json)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          collections: [ 'logins', 'auths' ],
          parser: 'json',
          numCursors: 2,
          callback: function() {

            setTimeout(function() {

              fs.readdirSync(ROOT).forEach(function(first) { // database

                var database = ROOT + '/' + first;
                if (fs.statSync(database).isDirectory() === false) {
                  return;
                }
                var second = fs.readdirSync(database);
                assert.equal(second.length, 2);
                assert.equal(second[0], 'auths');
                assert.equal(second[1], 'logins');

                var collection = database + '/' + second[0];
                if (fs.statSync(collection).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(collection).forEach(function(third) { // document

                  assert.equal(extname(third), '.json');
                  var document = collection + '/' + third;
                  fs.unlinkSync(document);
                });
                fs.rmdirSync(collection);

                var collection = database + '/' + second[1];
                if (fs.statSync(collection).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(collection).forEach(function(third) { // document

                  assert.equal(extname(third), '.json');
                  var document = collection + '/' + third;
                  fs.unlinkSync(document);
                });
                fs.rmdirSync(collection);

                fs.rmdirSync(database);
              });
              done();
            }, 100);
          }
        });
      });
      it('should build 1 directory (*.bson)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          collections: [ 'logins', 'auths' ],
          parser: 'bson',
          numCursors: 2,
          callback: function() {

            setTimeout(function() {

              fs.readdirSync(ROOT).forEach(function(first) { // database

                var database = ROOT + '/' + first;
                if (fs.statSync(database).isDirectory() === false) {
                  return;
                }
                var second = fs.readdirSync(database);
                assert.equal(second.length, 2);
                assert.equal(second[0], 'auths');
                assert.equal(second[1], 'logins');

                var collection = database + '/' + second[0];
                if (fs.statSync(collection).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(collection).forEach(function(third) { // document

                  assert.equal(extname(third), '.bson');
                  var document = collection + '/' + third;
                  fs.unlinkSync(document);
                });
                fs.rmdirSync(collection);

                var collection = database + '/' + second[1];
                if (fs.statSync(collection).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(collection).forEach(function(third) { // document

                  assert.equal(extname(third), '.bson');
                  var document = collection + '/' + third;
                  fs.unlinkSync(document);
                });
                fs.rmdirSync(collection);

                fs.rmdirSync(database);
              });
              done();
            }, 100);
          }
        });
      });
    });

    describe('parser', function() {

      it('should build any directories (*.json)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          parser: 'json',
          numCursors: 2,
          callback: function() {

            setTimeout(function() {

              fs.readdirSync(ROOT).forEach(function(first) { // database

                var database = ROOT + '/' + first;
                if (fs.statSync(database).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(database).forEach(function(second) { // collection

                  var collection = database + '/' + second;
                  if (fs.statSync(collection).isDirectory() === false) {
                    return;
                  }
                  fs.readdirSync(collection).forEach(function(third) { // document

                    assert.equal(extname(third), '.json');
                    var document = collection + '/' + third;
                    fs.unlinkSync(document);
                  });
                  fs.rmdirSync(collection);
                });
                fs.rmdirSync(database);
              });
              done();
            }, 100);
          }
        });
      });
      it('should build any directories (*.bson)', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          parser: 'bson',
          numCursors: 2,
          callback: function() {

            setTimeout(function() {

              fs.readdirSync(ROOT).forEach(function(first) { // database

                var database = ROOT + '/' + first;
                if (fs.statSync(database).isDirectory() === false) {
                  return;
                }
                fs.readdirSync(database).forEach(function(second) { // collection

                  var collection = database + '/' + second;
                  if (fs.statSync(collection).isDirectory() === false) {
                    return;
                  }
                  fs.readdirSync(collection).forEach(function(third) { // document

                    assert.equal(extname(third), '.bson');
                    var document = collection + '/' + third;
                    fs.unlinkSync(document);
                  });
                  fs.rmdirSync(collection);
                });
                fs.rmdirSync(database);
              });
              done();
            }, 100);
          }
        });
      });
    });

    describe('tar', function() {

      var path = ROOT + '/t2.tar';
      it('should check that tar file not exist before test', function(done) {

        assert.equal(fs.existsSync(path), false);
        done();
      });
      it('should make a tar file', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          tar: 't2.tar',
          numCursors: 2,
          callback: function() {

            assert.equal(fs.existsSync(path), true);
            fs.unlink(path, done);
          }
        });
      });
      it('should check that buffer dir not exist', function(done) {

        var paths = __dirname + '/../dump';
        assert.equal(fs.existsSync(paths), true); // stay alive
        assert.equal(fs.readdirSync(paths).length, 0, 'empty dir');
        done();
      });
    });

    describe('logger', function() {

      var l = 'l2.log';
      it('should check that log file not exist before test', function(done) {

        assert.equal(fs.existsSync(l), false);
        done();
      });
      it('should make a log file', function(done) {

        backup({
          uri: URI,
          root: ROOT,
          logger: l,
          numCursors: 2,
          callback: function() {

            assert.equal(fs.existsSync(l), true);
            fs.unlink(l, done);
          }
        });
      });
      it('should remove dirs', function(done) {

        fs.readdirSync(ROOT).forEach(function(first) { // database

          var database = ROOT + '/' + first;
          if (fs.statSync(database).isDirectory() === false) {
            return;
          }
          fs.readdirSync(database).forEach(function(second) { // collection

            var collection = database + '/' + second;
            if (fs.statSync(collection).isDirectory() === false) {
              return;
            }
            fs.readdirSync(collection).forEach(function(third) { // document

              assert.equal(extname(third), '.bson');
              var document = collection + '/' + third;
              fs.unlinkSync(document);
            });
            fs.rmdirSync(collection);
          });
          fs.rmdirSync(database);
        });
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>var clone = require("./lib/clone.js")
var executeScripts = require("./lib/execute-scripts.js")

var forEachEls = require("./lib/foreach-els.js")

var newUid = require("./lib/uniqueid.js")

var on = require("./lib/events/on.js")
// var off = require("./lib/events/on.js")
var trigger = require("./lib/events/trigger.js")

var defaultSwitches = require("./lib/switches")

var Pjax = function(options) {
    this.state = {
      numPendingSwitches: 0,
      href: null,
      options: null
    }

    var parseOptions = require("./lib/proto/parse-options.js");
    parseOptions.apply(this,[options])
    this.log("Pjax options", this.options)

    this.maxUid = this.lastUid = newUid()

    this.parseDOM(document)

    on(window, "popstate", function(st) {
      if (st.state) {
        var opt = clone(this.options)
        opt.url = st.state.url
        opt.title = st.state.title
        opt.history = false
        opt.requestOptions = {};
        opt.scrollPos = st.state.scrollPos
        if (st.state.uid < this.lastUid) {
          opt.backward = true
        }
        else {
          opt.forward = true
        }
        this.lastUid = st.state.uid

        // @todo implement history cache here, based on uid
        this.loadUrl(st.state.url, opt)
      }
    }.bind(this))
  }

Pjax.switches = defaultSwitches

Pjax.prototype = {
  log: require("./lib/proto/log.js"),

  getElements: require("./lib/proto/get-elements.js"),

  parseDOM: require("./lib/proto/parse-dom.js"),

  refresh: require("./lib/proto/refresh.js"),

  reload: require("./lib/reload.js"),

  attachLink: require("./lib/proto/attach-link.js"),

  attachForm: require("./lib/proto/attach-form.js"),

  forEachSelectors: function(cb, context, DOMcontext) {
    return require("./lib/foreach-selectors.js").bind(this)(this.options.selectors, cb, context, DOMcontext)
  },

  switchSelectors: function(selectors, fromEl, toEl, options) {
    return require("./lib/switches-selectors.js").bind(this)(this.options.switches, this.options.switchesOptions, selectors, fromEl, toEl, options)
  },

  // too much problem with the code below
  // + it’s too dangerous
  //   switchFallback: function(fromEl, toEl) {
  //     this.switchSelectors(["head", "body"], fromEl, toEl)
  //     // execute script when DOM is like it should be
  //     Pjax.executeScripts(document.querySelector("head"))
  //     Pjax.executeScripts(document.querySelector("body"))
  //   }

  latestChance: function(href) {
    window.location = href
  },

  onSwitch: function() {
    trigger(window, "resize scroll")

    this.state.numPendingSwitches--

    // debounce calls, so we only run this once after all switches are finished.
    if (this.state.numPendingSwitches === 0) {
      this.afterAllSwitches()
    }
  },

  loadContent: function(html, options) {
    var tmpEl = document.implementation.createHTMLDocument("pjax")

    // parse HTML attributes to copy them
    // since we are forced to use documentElement.innerHTML (outerHTML can't be used for <html>)
    var htmlRegex = /<html[^>]+>/gi
    var htmlAttribsRegex = /\s?[a-z:]+(?:\=(?:\'|\")[^\'\">]+(?:\'|\"))*/gi
    var matches = html.match(htmlRegex)
    if (matches && matches.length) {
      matches = matches[0].match(htmlAttribsRegex)
      if (matches.length) {
        matches.shift()
        matches.forEach(function(htmlAttrib) {
          var attr = htmlAttrib.trim().split("=")
          if (attr.length === 1) {
            tmpEl.documentElement.setAttribute(attr[0], true)
          }
          else {
            tmpEl.documentElement.setAttribute(attr[0], attr[1].slice(1, -1))
          }
        })
      }
    }

    tmpEl.documentElement.innerHTML = html
    this.log("load content", tmpEl.documentElement.attributes, tmpEl.documentElement.innerHTML.length)

    // Clear out any focused controls before inserting new page contents.
    // we clear focus on non form elements
    if (document.activeElement && !document.activeElement.value) {
      try {
        document.activeElement.blur()
      } catch (e) { }
    }

    // try {
    this.switchSelectors(this.options.selectors, tmpEl, document, options)

    // }
    // catch(e) {
    //   if (this.options.debug) {
    //     this.log("Pjax switch fail: ", e)
    //   }
    //   this.switchFallback(tmpEl, document)
    // }
  },

  abortRequest: require("./lib/abort-request.js"),

  doRequest: require("./lib/send-request.js"),

  loadUrl: function(href, options) {
    this.log("load href", href, options)

    // Abort any previous request
    this.abortRequest(this.request)

    trigger(document, "pjax:send", options);

    // Do the request
    options.requestOptions.timeout = this.options.timeout
    this.request = this.doRequest(href, options.requestOptions, function(html, request) {
      // Fail if unable to load HTML via AJAX
      if (html === false) {
        trigger(document,"pjax:complete pjax:error", options)

        return
      }

      // push scroll position to history
      var currentState = window.history.state || {}
      window.history.replaceState({
          url: currentState.url || window.location.href,
          title: currentState.title || document.title,
          uid: currentState.uid || newUid(),
          scrollPos: [document.documentElement.scrollLeft || document.body.scrollLeft,
            document.documentElement.scrollTop || document.body.scrollTop]
        },
        document.title, window.location)

      // Clear out any focused controls before inserting new page contents.
      document.activeElement.blur()

      var oldHref = href
      if (request.responseURL) {
        if (href !== request.responseURL) {
          href = request.responseURL
        }
      }
      else if (request.getResponseHeader("X-PJAX-URL")) {
        href = request.getResponseHeader("X-PJAX-URL")
      }
      else if (request.getResponseHeader("X-XHR-Redirected-To")) {
        href = request.getResponseHeader("X-XHR-Redirected-To")
      }

      // Add back the hash if it was removed
      var a = document.createElement("a")
      a.href = oldHref
      var oldHash = a.hash
      a.href = href
      if (oldHash && !a.hash) {
        a.hash = oldHash
        href = a.href
      }

      this.state.href = href
      this.state.options = clone(options)

      try {
        this.loadContent(html, options)
      }
      catch (e) {
        if (!this.options.debug) {
          if (console && console.error) {
            console.error("Pjax switch fail: ", e)
          }
          this.latestChance(href)
          return
        }
        else {
          throw e
        }
      }
    }.bind(this))
  },

  afterAllSwitches: function() {
    // FF bug: Won’t autofocus fields that are inserted via JS.
    // This behavior is incorrect. So if theres no current focus, autofocus
    // the last field.
    //
    // http://www.w3.org/html/wg/drafts/html/master/forms.html
    var autofocusEl = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop()
    if (autofocusEl && document.activeElement !== autofocusEl) {
      autofocusEl.focus();
    }

    // execute scripts when DOM have been completely updated
    this.options.selectors.forEach(function(selector) {
      forEachEls(document.querySelectorAll(selector), function(el) {
        executeScripts(el)
      })
    })

    var state = this.state

    if (state.options.history) {
      if (!window.history.state) {
        this.lastUid = this.maxUid = newUid()
        window.history.replaceState({
            url: window.location.href,
            title: document.title,
            uid: this.maxUid,
            scrollPos: [0, 0]
          },
          document.title)
      }

      // Update browser history
      this.lastUid = this.maxUid = newUid()

      window.history.pushState({
          url: state.href,
          title: state.options.title,
          uid: this.maxUid,
          scrollPos: [0, 0]
        },
        state.options.title,
        state.href)
    }

    this.forEachSelectors(function(el) {
      this.parseDOM(el)
    }, this)

    // Fire Events
    trigger(document,"pjax:complete pjax:success", state.options)

    state.options.analytics()

    if (state.options.history) {
      // First parse url and check for hash to override scroll
      var a = document.createElement("a")
      a.href = this.state.href
      if (a.hash) {
        var name = a.hash.slice(1)
        name = decodeURIComponent(name)

        var curtop = 0
        var target = document.getElementById(name) || document.getElementsByName(name)[0]
        if (target) {
          // http://stackoverflow.com/questions/8111094/cross-browser-javascript-function-to-find-actual-position-of-an-element-in-page
          if (target.offsetParent) {
            do {
              curtop += target.offsetTop

              target = target.offsetParent
            } while (target)
          }
        }
        window.scrollTo(0, curtop);
      }
      else if (state.options.scrollTo !== false) {
        // Scroll page to top on new page load
        if (state.options.scrollTo.length > 1) {
          window.scrollTo(state.options.scrollTo[0], state.options.scrollTo[1])
        }
        else {
          window.scrollTo(0, state.options.scrollTo)
        }
      }
    }
    else if (state.options.scrollRestoration && state.options.scrollPos) {
      window.scrollTo(state.options.scrollPos[0], state.options.scrollPos[1])
    }

    this.state = {
      numPendingSwitches: 0,
      href: null,
      options: null
    }
  }
}

Pjax.isSupported = require("./lib/is-supported.js");

// arguably could do `if( require("./lib/is-supported.js")()) {` but that might be a little to simple
if (Pjax.isSupported()) {
  module.exports = Pjax
}
// if there isn’t required browser functions, returning stupid api
else {
  var stupidPjax = function() {}
  for (var key in Pjax.prototype) {
    if (Pjax.prototype.hasOwnProperty(key) && typeof Pjax.prototype[key] === "function") {
      stupidPjax[key] = stupidPjax
    }
  }

  module.exports = stupidPjax
}
<commit_msg>Make use of new `noop` module<commit_after>var clone = require("./lib/clone.js")
var executeScripts = require("./lib/execute-scripts.js")

var forEachEls = require("./lib/foreach-els.js")

var newUid = require("./lib/uniqueid.js")

var noop = require("./lib/util/noop")

var on = require("./lib/events/on.js")
// var off = require("./lib/events/on.js")
var trigger = require("./lib/events/trigger.js")

var defaultSwitches = require("./lib/switches")

var Pjax = function(options) {
    this.state = {
      numPendingSwitches: 0,
      href: null,
      options: null
    }

    var parseOptions = require("./lib/proto/parse-options.js");
    parseOptions.apply(this,[options])
    this.log("Pjax options", this.options)

    this.maxUid = this.lastUid = newUid()

    this.parseDOM(document)

    on(window, "popstate", function(st) {
      if (st.state) {
        var opt = clone(this.options)
        opt.url = st.state.url
        opt.title = st.state.title
        opt.history = false
        opt.requestOptions = {};
        opt.scrollPos = st.state.scrollPos
        if (st.state.uid < this.lastUid) {
          opt.backward = true
        }
        else {
          opt.forward = true
        }
        this.lastUid = st.state.uid

        // @todo implement history cache here, based on uid
        this.loadUrl(st.state.url, opt)
      }
    }.bind(this))
  }

Pjax.switches = defaultSwitches

Pjax.prototype = {
  log: require("./lib/proto/log.js"),

  getElements: require("./lib/proto/get-elements.js"),

  parseDOM: require("./lib/proto/parse-dom.js"),

  refresh: require("./lib/proto/refresh.js"),

  reload: require("./lib/reload.js"),

  attachLink: require("./lib/proto/attach-link.js"),

  attachForm: require("./lib/proto/attach-form.js"),

  forEachSelectors: function(cb, context, DOMcontext) {
    return require("./lib/foreach-selectors.js").bind(this)(this.options.selectors, cb, context, DOMcontext)
  },

  switchSelectors: function(selectors, fromEl, toEl, options) {
    return require("./lib/switches-selectors.js").bind(this)(this.options.switches, this.options.switchesOptions, selectors, fromEl, toEl, options)
  },

  // too much problem with the code below
  // + it’s too dangerous
  //   switchFallback: function(fromEl, toEl) {
  //     this.switchSelectors(["head", "body"], fromEl, toEl)
  //     // execute script when DOM is like it should be
  //     Pjax.executeScripts(document.querySelector("head"))
  //     Pjax.executeScripts(document.querySelector("body"))
  //   }

  latestChance: function(href) {
    window.location = href
  },

  onSwitch: function() {
    trigger(window, "resize scroll")

    this.state.numPendingSwitches--

    // debounce calls, so we only run this once after all switches are finished.
    if (this.state.numPendingSwitches === 0) {
      this.afterAllSwitches()
    }
  },

  loadContent: function(html, options) {
    var tmpEl = document.implementation.createHTMLDocument("pjax")

    // parse HTML attributes to copy them
    // since we are forced to use documentElement.innerHTML (outerHTML can't be used for <html>)
    var htmlRegex = /<html[^>]+>/gi
    var htmlAttribsRegex = /\s?[a-z:]+(?:\=(?:\'|\")[^\'\">]+(?:\'|\"))*/gi
    var matches = html.match(htmlRegex)
    if (matches && matches.length) {
      matches = matches[0].match(htmlAttribsRegex)
      if (matches.length) {
        matches.shift()
        matches.forEach(function(htmlAttrib) {
          var attr = htmlAttrib.trim().split("=")
          if (attr.length === 1) {
            tmpEl.documentElement.setAttribute(attr[0], true)
          }
          else {
            tmpEl.documentElement.setAttribute(attr[0], attr[1].slice(1, -1))
          }
        })
      }
    }

    tmpEl.documentElement.innerHTML = html
    this.log("load content", tmpEl.documentElement.attributes, tmpEl.documentElement.innerHTML.length)

    // Clear out any focused controls before inserting new page contents.
    // we clear focus on non form elements
    if (document.activeElement && !document.activeElement.value) {
      try {
        document.activeElement.blur()
      } catch (e) { }
    }

    // try {
    this.switchSelectors(this.options.selectors, tmpEl, document, options)

    // }
    // catch(e) {
    //   if (this.options.debug) {
    //     this.log("Pjax switch fail: ", e)
    //   }
    //   this.switchFallback(tmpEl, document)
    // }
  },

  abortRequest: require("./lib/abort-request.js"),

  doRequest: require("./lib/send-request.js"),

  loadUrl: function(href, options) {
    this.log("load href", href, options)

    // Abort any previous request
    this.abortRequest(this.request)

    trigger(document, "pjax:send", options);

    // Do the request
    options.requestOptions.timeout = this.options.timeout
    this.request = this.doRequest(href, options.requestOptions, function(html, request) {
      // Fail if unable to load HTML via AJAX
      if (html === false) {
        trigger(document,"pjax:complete pjax:error", options)

        return
      }

      // push scroll position to history
      var currentState = window.history.state || {}
      window.history.replaceState({
          url: currentState.url || window.location.href,
          title: currentState.title || document.title,
          uid: currentState.uid || newUid(),
          scrollPos: [document.documentElement.scrollLeft || document.body.scrollLeft,
            document.documentElement.scrollTop || document.body.scrollTop]
        },
        document.title, window.location)

      // Clear out any focused controls before inserting new page contents.
      document.activeElement.blur()

      var oldHref = href
      if (request.responseURL) {
        if (href !== request.responseURL) {
          href = request.responseURL
        }
      }
      else if (request.getResponseHeader("X-PJAX-URL")) {
        href = request.getResponseHeader("X-PJAX-URL")
      }
      else if (request.getResponseHeader("X-XHR-Redirected-To")) {
        href = request.getResponseHeader("X-XHR-Redirected-To")
      }

      // Add back the hash if it was removed
      var a = document.createElement("a")
      a.href = oldHref
      var oldHash = a.hash
      a.href = href
      if (oldHash && !a.hash) {
        a.hash = oldHash
        href = a.href
      }

      this.state.href = href
      this.state.options = clone(options)

      try {
        this.loadContent(html, options)
      }
      catch (e) {
        if (!this.options.debug) {
          if (console && console.error) {
            console.error("Pjax switch fail: ", e)
          }
          this.latestChance(href)
          return
        }
        else {
          throw e
        }
      }
    }.bind(this))
  },

  afterAllSwitches: function() {
    // FF bug: Won’t autofocus fields that are inserted via JS.
    // This behavior is incorrect. So if theres no current focus, autofocus
    // the last field.
    //
    // http://www.w3.org/html/wg/drafts/html/master/forms.html
    var autofocusEl = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop()
    if (autofocusEl && document.activeElement !== autofocusEl) {
      autofocusEl.focus();
    }

    // execute scripts when DOM have been completely updated
    this.options.selectors.forEach(function(selector) {
      forEachEls(document.querySelectorAll(selector), function(el) {
        executeScripts(el)
      })
    })

    var state = this.state

    if (state.options.history) {
      if (!window.history.state) {
        this.lastUid = this.maxUid = newUid()
        window.history.replaceState({
            url: window.location.href,
            title: document.title,
            uid: this.maxUid,
            scrollPos: [0, 0]
          },
          document.title)
      }

      // Update browser history
      this.lastUid = this.maxUid = newUid()

      window.history.pushState({
          url: state.href,
          title: state.options.title,
          uid: this.maxUid,
          scrollPos: [0, 0]
        },
        state.options.title,
        state.href)
    }

    this.forEachSelectors(function(el) {
      this.parseDOM(el)
    }, this)

    // Fire Events
    trigger(document,"pjax:complete pjax:success", state.options)

    state.options.analytics()

    if (state.options.history) {
      // First parse url and check for hash to override scroll
      var a = document.createElement("a")
      a.href = this.state.href
      if (a.hash) {
        var name = a.hash.slice(1)
        name = decodeURIComponent(name)

        var curtop = 0
        var target = document.getElementById(name) || document.getElementsByName(name)[0]
        if (target) {
          // http://stackoverflow.com/questions/8111094/cross-browser-javascript-function-to-find-actual-position-of-an-element-in-page
          if (target.offsetParent) {
            do {
              curtop += target.offsetTop

              target = target.offsetParent
            } while (target)
          }
        }
        window.scrollTo(0, curtop);
      }
      else if (state.options.scrollTo !== false) {
        // Scroll page to top on new page load
        if (state.options.scrollTo.length > 1) {
          window.scrollTo(state.options.scrollTo[0], state.options.scrollTo[1])
        }
        else {
          window.scrollTo(0, state.options.scrollTo)
        }
      }
    }
    else if (state.options.scrollRestoration && state.options.scrollPos) {
      window.scrollTo(state.options.scrollPos[0], state.options.scrollPos[1])
    }

    this.state = {
      numPendingSwitches: 0,
      href: null,
      options: null
    }
  }
}

Pjax.isSupported = require("./lib/is-supported.js");

// arguably could do `if( require("./lib/is-supported.js")()) {` but that might be a little to simple
if (Pjax.isSupported()) {
  module.exports = Pjax
}
// if there isn’t required browser functions, returning stupid api
else {
  var stupidPjax = noop
  for (var key in Pjax.prototype) {
    if (Pjax.prototype.hasOwnProperty(key) && typeof Pjax.prototype[key] === "function") {
      stupidPjax[key] = noop
    }
  }

  module.exports = stupidPjax
}
<|endoftext|>


<commit_before>var css = require('./css')
  , less = require('./less')
  , fs = require('fs')
  , path = require('path')
  , mkdirp = require('mkdirp')
  , writer = require('write-to-path')
  , rework = require('rework')
  , assets = require('rework-assets')
  , isLess

module.exports = function (opts, cb) {
  if (typeof opts === 'string') opts = {entry: opts};
  if (typeof cb === 'string') opts.output = cb;

  if (opts.entry.substr(-4) === 'less') {
    isLess = true
    less(opts, complete)
  } else {
    css(opts, complete)
  }

  function complete (err, src) {
    if (opts.transform && !err) src = opts.transform(src)

    if (isLess && opts.assets) {
      var resolvedPath = path.resolve(process.cwd(), opts.output)

      src = rework(src)
        .use(assets({
          src: path.dirname(resolvedPath)
          , dest: opts.assets.dest || ''
          , prefix: opts.assets.prefix || ''
        }))
        .toString({
          sourcemap: opts.debug || opts.sourcemap
          , compress: opts.compress
        })
    }

    if (opts.output) {
      // we definitely have to write the file
      var outputPath = path.resolve(process.cwd(), opts.output)
        , outputDir = path.dirname(outputPath)
        , writeFile = writer(outputPath, {debug: opts.debug})

      if (!fs.existsSync(outputDir)) mkdirp.sync(outputDir)

      // we might need to call a callback also
      if (typeof cb === 'function') {
        var _cb = cb
        cb = function (err, src) {
          writeFile(err, src)
          _cb(err, src)
        }
      } else {
        cb = writeFile
      }
    }

    cb(err, src)
  }
}
<commit_msg>Revert LESS assets as post bundle step<commit_after>var css = require('./css')
  , less = require('./less')
  , fs = require('fs')
  , path = require('path')
  , mkdirp = require('mkdirp')
  , writer = require('write-to-path')

module.exports = function (opts, cb) {
  if (typeof opts === 'string') opts = {entry: opts};
  if (typeof cb === 'string') opts.output = cb;

  if (opts.entry.substr(-4) === 'less') {
    less(opts, complete)
  } else {
    css(opts, complete)
  }

  function complete (err, src) {
    if (opts.transform && !err) src = opts.transform(src)

    if (opts.output) {
      // we definitely have to write the file
      var outputPath = path.resolve(process.cwd(), opts.output)
        , outputDir = path.dirname(outputPath)
        , writeFile = writer(outputPath, {debug: opts.debug})

      if (!fs.existsSync(outputDir)) mkdirp.sync(outputDir)

      // we might need to call a callback also
      if (typeof cb === 'function') {
        var _cb = cb
        cb = function (err, src) {
          writeFile(err, src)
          _cb(err, src)
        }
      } else {
        cb = writeFile
      }
    }

    cb(err, src)
  }
}
<|endoftext|>


<commit_before>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2015, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */

(function(_http, _path, _url, _fs, _qs, _multipart, _cookies, _api, _vfs)
{
  /**
   * Globals and default settings etc.
   */
  var ISWIN   = /^win/.test(process.platform);
  var HANDLER = null;
  var ROOTDIR = _path.join(_path.dirname(__filename), '/../../');
  var DISTDIR = (process && process.argv.length > 2) ? process.argv[2] : 'dist';
  var API     = {};
  var CONFIG  = {
    port:       8000,
    directory:  null, // Automatic
    appdirs:    null, // Automatic, but overrideable
    vfs:        {
      'homes':   _path.join(ROOTDIR, 'vfs/home'),
      'tmp':     _path.join(ROOTDIR, 'vfs/tmp'),
      'public':  _path.join(ROOTDIR, 'vfs/public')
    },
    repodir:    _path.join(ROOTDIR, 'src/packages'),
    distdir:    _path.join(ROOTDIR, DISTDIR),
    mimes:      {}
  };

  /////////////////////////////////////////////////////////////////////////////
  // HELPERS
  /////////////////////////////////////////////////////////////////////////////

  /**
   * HTTP Output
   */
  var respond = function(data, mime, response, headers, code) {
    data    = data    || '';
    headers = headers || [];
    mime    = mime    || "text/html; charset=utf-8";
    code    = code    || 200;

    //console.log(">>>", 'respond()', mime, data.length);

    for ( var i = 0; i < headers.length; i++ ) {
      response.writeHead.apply(response, headers[i]);
    }

    response.writeHead(code, {"Content-Type": mime});
    response.write(data);

    if ( HANDLER && HANDLER.onRequestEnd ) {
      HANDLER.onRequestEnd(null, response);
    }

    response.end();
  };

  var respondJSON = function(data, response, headers) {
    data = JSON.stringify(data);
    console.log(">>>", 'application/json', data.length || 0);
    respond(data, 'application/json', response, headers);
  };

  /**
   * File Output
   */
  var respondFile = function(path, request, response, jpath) {
    var fullPath = jpath ? _path.join(CONFIG.directory, path) : _vfs.getRealPath(path, CONFIG, request).root;
    _fs.exists(fullPath, function(exists) {
      if ( exists ) {
        _fs.readFile(fullPath, function(error, data) {
          if ( error ) {
            console.log(">>>", '500', fullPath);
            console.warn(error);
            respond("500 Internal Server Error", null, response, null, 500);
          } else {
            var mime = _vfs.getMime(fullPath, CONFIG);
            console.log(">>>", '200', mime, fullPath, data.length);
            respond(data, mime, response);
          }
        });
      } else {
        console.log('!!!', '404', fullPath);
        respond("404 Not Found", null, response, null, 404);
      }
    });
  };

  var readConfig = function(filename) {
    var path = _path.join(ROOTDIR, filename);
    if ( _fs.existsSync(path) ) {
      try {
        console.info('-->', 'Found configuration', filename);
        var str = _fs.readFileSync(path).toString();
        var droot = ROOTDIR.replace(/\/$/, '');

        if ( ISWIN ) {
          str = str.replace(/%DROOT%/g,       droot.replace(/(["\s'$`\\])/g,'\\$1'));
        } else {
          str = str.replace(/%DROOT%/g,       droot);
        }

        return JSON.parse(str);
      } catch ( e ) {
        console.warn('!!!', 'Failed to parse configuration', filename, e);
      }
    } else {
      console.warn('!!!', 'Did not find configuration', path);
    }
    return false;
  };

  var checkPrivilege = function(request, response, privilege) {
    if ( typeof privilege !== 'boolean' ) {
      if ( !privilege ) privilege = [];
      if ( !(privilege instanceof Array) && privilege ) privilege = [privilege];
    }

    function check() {
      var groups = [];
      try {
        groups = JSON.parse(request.cookies.get('groups'));
      } catch ( e ) {
        groups = [];
      }

      if ( !('admin' in groups) ) {
        var allowed = true;
        privilege.forEach(function(p) {
          if ( groups.indexOf(p) < 0 ) {
            allowed = false;
          }
          return allowed;
        });
        return allowed;
      }

      return false;
    }

    var uname = request.cookies.get('username');
    if ( !uname ) {
      respond('You have no OS.js Session, please log in!', "text/plain", response, null, 500);
      return false;
    }

    if ( privilege.length && !check() ) {
      respond('You are not allowed to use this API function!', "text/plain", response, null, 403);
      return false;
    }

    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  // API
  /////////////////////////////////////////////////////////////////////////////

  /**
   * OS.js API
   */
  var api = {
    FileGET : function(path, request, response, arg) {
      if ( !arg ) {
        console.log('---', 'FileGET', path);
        if ( !HANDLER.checkPrivilege(request, response, 'vfs') ) {
          return;
        }
      }

      respondFile(unescape(path), request, response, arg);
    },

    FilePOST : function(fields, files, request, response) {
      if ( !HANDLER.checkPrivilege(request, response, 'upload') ) {
        return;
      }

      var srcPath = files.upload.path;
      var tmpPath = (fields.path + '/' + files.upload.name).replace('////', '///'); // FIXME
      var dstPath = _vfs.getRealPath(tmpPath, CONFIG, request).root;

      _fs.exists(srcPath, function(exists) {
        if ( exists ) {
          _fs.exists(dstPath, function(exists) {
            if ( exists ) {
              respond('Target already exist!', "text/plain", response, null, 500);
            } else {
              _fs.rename(srcPath, dstPath, function(error, data) {
                if ( error ) {
                  respond('Error renaming/moving: ' + error, "text/plain", response, null, 500);
                } else {
                  respond("1", "text/plain", response);
                }
              });
            }
          });
        } else {
          respond('Source does not exist!', "text/plain", response, null, 500);
        }
      });
    },

    CoreAPI : function(url, path, POST, request, response) {
                /*
      if ( !HANDLER.checkPrivilege(request, response, 'upload') ) {
        return;
      }
      */

      if ( path.match(/^\/API/) ) {
        try {
          var data   = JSON.parse(POST);
          var method = data.method;
          var args   = data['arguments'] || {}

          console.log('---', 'CoreAPI', method, args);
          if ( API[method] ) {
            API[method](args, function(error, result) {
              respondJSON({result: result, error: error}, response);
            }, request, response, POST);
          } else {
            throw "Invalid method: " + method;
          }
        } catch ( e ) {
          console.error("!!! Caught exception", e);
          console.warn(e.stack);

          respondJSON({result: null, error: "500 Internal Server Error: " + e}, response);
        }
        return true;
      }
      return false;
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  // MAIN
  /////////////////////////////////////////////////////////////////////////////

  console.log('***');
  console.log('***', 'THIS IS A WORK IN PROGRESS!!!');
  console.log('***');

  /**
   * Initialize config
   */
  (function() {

    var settConfig = readConfig("src/server-node/settings.json");
    if ( settConfig !== false ) {
      for ( var i in settConfig ) {
        if ( settConfig.hasOwnProperty(i) && CONFIG.hasOwnProperty(i) ) {
          CONFIG[i] = settConfig[i];
        }
      }
    }

    var mimeConfig = readConfig("src/mime.json");
    if ( mimeConfig !== false ) {
      CONFIG.mimes = mimeConfig.mapping;
    }

    var repoConfig = readConfig("src/packages/repositories.json");
    if ( repoConfig !== false ) {
      CONFIG.appdirs = repoConfig;
    }

    if ( !CONFIG.directory ) {
      CONFIG.directory = _fs.realpathSync('.');
    }

    HANDLER = require(_path.join(ROOTDIR, 'src', 'server-node', 'handlers', settConfig.handler , 'handler.js'));
    if ( !HANDLER.checkPrivilege ) {
      HANDLER.checkPrivilege = checkPrivilege;
    }

    _api.register(CONFIG, API, HANDLER);
    if ( settConfig.extensions ) {
      var exts = JSON.parse(settConfig.extensions);
      exts.forEach(function(f) {
        console.info('-->', 'Registering external API methods', f);
        require(ROOTDIR + f).register(settConfig, API, HANDLER);
      });
    }

  })();

  console.log(JSON.stringify(CONFIG, null, 2));
  if ( !HANDLER ) {
    console.log("Invalid handler %s defined", CONFIG.handler);
    return;
  }
  HANDLER.register(CONFIG, API, HANDLER);

  if ( HANDLER.onServerStart ) {
    HANDLER.onServerStart(CONFIG);
  }
  process.on("exit", function() {
    if ( HANDLER.onServerEnd ) {
      HANDLER.onServerEnd(CONFIG);
    }
  });

  /**
   * Server instance
   */
  _http.createServer(function(request, response) {

      var url     = _url.parse(request.url, true),
          path    = decodeURIComponent(url.pathname),
          cookies = new _cookies(request, response);

      request.cookies = cookies;

      if ( path === "/" ) path += "index.html";
      console.log('<<<', path);

      if ( HANDLER && HANDLER.onRequestStart ) {
        HANDLER.onRequestStart(request, response);
      }

      if ( request.method == 'POST' )
      {
        // File Uploads
        if ( path.match(/^\/FS$/) ) {
          var form = new _multipart.IncomingForm();
          form.parse(request, function(err, fields, files) {
            api.FilePOST(fields, files, request, response);
          });
        }

        // API Calls
        else {
          var body = '';
          request.on('data', function (data) {
            body += data;
          });

          request.on('end', function () {
            if ( !api.CoreAPI(url, path, body, request, response) ) {
              console.log(">>>", '404', path);
              respond("404 Not Found", null, response, [[404, {}]]);
            }
          });
        }
      }

      // File Gets
      else {
        if ( path.match(/^\/FS/) ) {
          api.FileGET(path.replace(/^\/FS/, ''), request, response, false);
        } else {
          api.FileGET(_path.join(DISTDIR, path), request, response, true);
        }
      }
  }).listen(CONFIG.port);

})(
  require("http"),
  require("path"),
  require("url"),
  require("node-fs-extra"),
  require("querystring"),
  require("formidable"),
  require("cookies"),
  require("./api.js"),
  require("./vfs.js")
);
<commit_msg>server-node: Now serving files with stream piping<commit_after>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2015, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */

(function(_http, _path, _url, _fs, _qs, _multipart, _cookies, _api, _vfs)
{
  /**
   * Globals and default settings etc.
   */
  var ISWIN   = /^win/.test(process.platform);
  var HANDLER = null;
  var ROOTDIR = _path.join(_path.dirname(__filename), '/../../');
  var DISTDIR = (process && process.argv.length > 2) ? process.argv[2] : 'dist';
  var API     = {};
  var CONFIG  = {
    port:       8000,
    directory:  null, // Automatic
    appdirs:    null, // Automatic, but overrideable
    vfs:        {
      'homes':   _path.join(ROOTDIR, 'vfs/home'),
      'tmp':     _path.join(ROOTDIR, 'vfs/tmp'),
      'public':  _path.join(ROOTDIR, 'vfs/public')
    },
    repodir:    _path.join(ROOTDIR, 'src/packages'),
    distdir:    _path.join(ROOTDIR, DISTDIR),
    mimes:      {}
  };

  /////////////////////////////////////////////////////////////////////////////
  // HELPERS
  /////////////////////////////////////////////////////////////////////////////

  /**
   * HTTP Output
   */
  var respond = function(data, mime, response, headers, code, pipeFile) {
    data    = data    || '';
    headers = headers || [];
    mime    = mime    || "text/html; charset=utf-8";
    code    = code    || 200;

    //console.log(">>>", 'respond()', mime, data.length);

    function _end() {
      if ( HANDLER && HANDLER.onRequestEnd ) {
        HANDLER.onRequestEnd(null, response);
      }

      response.end();
    }

    for ( var i = 0; i < headers.length; i++ ) {
      response.writeHead.apply(response, headers[i]);
    }

    response.writeHead(code, {"Content-Type": mime});

    if ( pipeFile ) {
      var stream = _fs.createReadStream(pipeFile, {bufferSize: 64 * 1024});
      stream.on('end', function() {
        _end();
      });
      stream.pipe(response);
    } else {
      response.write(data);
      _end();
    }

  };

  var respondJSON = function(data, response, headers) {
    data = JSON.stringify(data);
    console.log(">>>", 'application/json', data.length || 0);
    respond(data, 'application/json', response, headers);
  };

  /**
   * File Output
   */
  var respondFile = function(path, request, response, jpath) {
    var fullPath = jpath ? _path.join(CONFIG.directory, path) : _vfs.getRealPath(path, CONFIG, request).root;
    _fs.exists(fullPath, function(exists) {
      if ( exists ) {

        var mime = _vfs.getMime(fullPath, CONFIG);
        respond(null, mime, response, null, null, fullPath);
        /*

        _fs.readFile(fullPath, function(error, data) {
          if ( error ) {
            console.log(">>>", '500', fullPath);
            console.warn(error);
            respond("500 Internal Server Error", null, response, null, 500);
          } else {
            var mime = _vfs.getMime(fullPath, CONFIG);
            console.log(">>>", '200', mime, fullPath, data.length);
            respond(data, mime, response);
          }
        });
        */
      } else {
        console.log('!!!', '404', fullPath);
        respond("404 Not Found", null, response, null, 404);
      }
    });
  };

  var readConfig = function(filename) {
    var path = _path.join(ROOTDIR, filename);
    if ( _fs.existsSync(path) ) {
      try {
        console.info('-->', 'Found configuration', filename);
        var str = _fs.readFileSync(path).toString();
        var droot = ROOTDIR.replace(/\/$/, '');

        if ( ISWIN ) {
          str = str.replace(/%DROOT%/g,       droot.replace(/(["\s'$`\\])/g,'\\$1'));
        } else {
          str = str.replace(/%DROOT%/g,       droot);
        }

        return JSON.parse(str);
      } catch ( e ) {
        console.warn('!!!', 'Failed to parse configuration', filename, e);
      }
    } else {
      console.warn('!!!', 'Did not find configuration', path);
    }
    return false;
  };

  var checkPrivilege = function(request, response, privilege) {
    if ( typeof privilege !== 'boolean' ) {
      if ( !privilege ) privilege = [];
      if ( !(privilege instanceof Array) && privilege ) privilege = [privilege];
    }

    function check() {
      var groups = [];
      try {
        groups = JSON.parse(request.cookies.get('groups'));
      } catch ( e ) {
        groups = [];
      }

      if ( !('admin' in groups) ) {
        var allowed = true;
        privilege.forEach(function(p) {
          if ( groups.indexOf(p) < 0 ) {
            allowed = false;
          }
          return allowed;
        });
        return allowed;
      }

      return false;
    }

    var uname = request.cookies.get('username');
    if ( !uname ) {
      respond('You have no OS.js Session, please log in!', "text/plain", response, null, 500);
      return false;
    }

    if ( privilege.length && !check() ) {
      respond('You are not allowed to use this API function!', "text/plain", response, null, 403);
      return false;
    }

    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  // API
  /////////////////////////////////////////////////////////////////////////////

  /**
   * OS.js API
   */
  var api = {
    FileGET : function(path, request, response, arg) {
      if ( !arg ) {
        console.log('---', 'FileGET', path);
        if ( !HANDLER.checkPrivilege(request, response, 'vfs') ) {
          return;
        }
      }

      respondFile(unescape(path), request, response, arg);
    },

    FilePOST : function(fields, files, request, response) {
      if ( !HANDLER.checkPrivilege(request, response, 'upload') ) {
        return;
      }

      var srcPath = files.upload.path;
      var tmpPath = (fields.path + '/' + files.upload.name).replace('////', '///'); // FIXME
      var dstPath = _vfs.getRealPath(tmpPath, CONFIG, request).root;

      _fs.exists(srcPath, function(exists) {
        if ( exists ) {
          _fs.exists(dstPath, function(exists) {
            if ( exists ) {
              respond('Target already exist!', "text/plain", response, null, 500);
            } else {
              _fs.rename(srcPath, dstPath, function(error, data) {
                if ( error ) {
                  respond('Error renaming/moving: ' + error, "text/plain", response, null, 500);
                } else {
                  respond("1", "text/plain", response);
                }
              });
            }
          });
        } else {
          respond('Source does not exist!', "text/plain", response, null, 500);
        }
      });
    },

    CoreAPI : function(url, path, POST, request, response) {
                /*
      if ( !HANDLER.checkPrivilege(request, response, 'upload') ) {
        return;
      }
      */

      if ( path.match(/^\/API/) ) {
        try {
          var data   = JSON.parse(POST);
          var method = data.method;
          var args   = data['arguments'] || {}

          console.log('---', 'CoreAPI', method, args);
          if ( API[method] ) {
            API[method](args, function(error, result) {
              respondJSON({result: result, error: error}, response);
            }, request, response, POST);
          } else {
            throw "Invalid method: " + method;
          }
        } catch ( e ) {
          console.error("!!! Caught exception", e);
          console.warn(e.stack);

          respondJSON({result: null, error: "500 Internal Server Error: " + e}, response);
        }
        return true;
      }
      return false;
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  // MAIN
  /////////////////////////////////////////////////////////////////////////////

  console.log('***');
  console.log('***', 'THIS IS A WORK IN PROGRESS!!!');
  console.log('***');

  /**
   * Initialize config
   */
  (function() {

    var settConfig = readConfig("src/server-node/settings.json");
    if ( settConfig !== false ) {
      for ( var i in settConfig ) {
        if ( settConfig.hasOwnProperty(i) && CONFIG.hasOwnProperty(i) ) {
          CONFIG[i] = settConfig[i];
        }
      }
    }

    var mimeConfig = readConfig("src/mime.json");
    if ( mimeConfig !== false ) {
      CONFIG.mimes = mimeConfig.mapping;
    }

    var repoConfig = readConfig("src/packages/repositories.json");
    if ( repoConfig !== false ) {
      CONFIG.appdirs = repoConfig;
    }

    if ( !CONFIG.directory ) {
      CONFIG.directory = _fs.realpathSync('.');
    }

    HANDLER = require(_path.join(ROOTDIR, 'src', 'server-node', 'handlers', settConfig.handler , 'handler.js'));
    if ( !HANDLER.checkPrivilege ) {
      HANDLER.checkPrivilege = checkPrivilege;
    }

    _api.register(CONFIG, API, HANDLER);
    if ( settConfig.extensions ) {
      var exts = JSON.parse(settConfig.extensions);
      exts.forEach(function(f) {
        console.info('-->', 'Registering external API methods', f);
        require(ROOTDIR + f).register(settConfig, API, HANDLER);
      });
    }

  })();

  console.log(JSON.stringify(CONFIG, null, 2));
  if ( !HANDLER ) {
    console.log("Invalid handler %s defined", CONFIG.handler);
    return;
  }
  HANDLER.register(CONFIG, API, HANDLER);

  if ( HANDLER.onServerStart ) {
    HANDLER.onServerStart(CONFIG);
  }
  process.on("exit", function() {
    if ( HANDLER.onServerEnd ) {
      HANDLER.onServerEnd(CONFIG);
    }
  });

  /**
   * Server instance
   */
  _http.createServer(function(request, response) {

      var url     = _url.parse(request.url, true),
          path    = decodeURIComponent(url.pathname),
          cookies = new _cookies(request, response);

      request.cookies = cookies;

      if ( path === "/" ) path += "index.html";
      console.log('<<<', path);

      if ( HANDLER && HANDLER.onRequestStart ) {
        HANDLER.onRequestStart(request, response);
      }

      if ( request.method == 'POST' )
      {
        // File Uploads
        if ( path.match(/^\/FS$/) ) {
          var form = new _multipart.IncomingForm();
          form.parse(request, function(err, fields, files) {
            api.FilePOST(fields, files, request, response);
          });
        }

        // API Calls
        else {
          var body = '';
          request.on('data', function (data) {
            body += data;
          });

          request.on('end', function () {
            if ( !api.CoreAPI(url, path, body, request, response) ) {
              console.log(">>>", '404', path);
              respond("404 Not Found", null, response, [[404, {}]]);
            }
          });
        }
      }

      // File Gets
      else {
        if ( path.match(/^\/FS/) ) {
          api.FileGET(path.replace(/^\/FS/, ''), request, response, false);
        } else {
          api.FileGET(_path.join(DISTDIR, path), request, response, true);
        }
      }
  }).listen(CONFIG.port);

})(
  require("http"),
  require("path"),
  require("url"),
  require("node-fs-extra"),
  require("querystring"),
  require("formidable"),
  require("cookies"),
  require("./api.js"),
  require("./vfs.js")
);
<|endoftext|>