<commit_before>(function() {
    'use strict';

    var DIR         = './',
        DIR_LIB     = DIR + '../',
        DIR_JSON    = DIR_LIB + '../json/',

        path        = require('path'),
        root        = require(DIR + 'root'),
        config      = require(DIR + 'config'),
        binom       = require(DIR + 'binom'),

        CloudFunc   = require(DIR_LIB + 'cloudfunc'),

        markdown    = require(DIR + 'markdown'),

        github      = require('faust'),
        jaguar      = require('jaguar'),

        flop        = require('flop'),
        pipe        = require('pipe-io'),
        ponse       = require('ponse'),
        rendy       = require('rendy'),
        copymitter  = require('copymitter'),
        json        = require('jonny'),
        check       = require('checkup'),

        Modules     = require(DIR_JSON + 'modules'),

        isWin32     = process.platform === 'win32';

    /**
     * rest interface
     *
     * @param request
     * @param response
     * @param callback
     */
    module.exports = function(request, response, next) {
        var apiURL, name, is, regExp,
            params  = {
                request     : request,
                response    : response
            };

        check
            .type('next', next, 'function')
            .check({
                request: request,
                response: response
            });

        apiURL  = CloudFunc.apiURL;
        name    = ponse.getPathName(request);
        regExp  = RegExp('^' + apiURL),
        is      = regExp.test(name);

        if (!is) {
            next();
        } else {
            params.name = name.replace(apiURL, '') || '/';

            sendData(params, function(error, options, data) {
                params.gzip = !error;

                if (!data) {
                    data    = options;
                    options = {};
                }

                if (options.name)
                    params.name = options.name;

                if (options.gzip !== undefined)
                    params.gzip = options.gzip;

                if (options.query)
                    params.query = options.query;

                if (error)
                    ponse.sendError(error, params);
                else
                    ponse.send(data, params);
            });
        }
    };

    /**
     * getting data on method and command
     *
     * @param params {name, method, body, requrest, response}
     */
    function sendData(params, callback) {
        var p       = params,
            isMD    = RegExp('^/markdown').test(p.name);

        if (isMD)
            markdown(p.name, p.request, function(error, data) {
                callback(error, data);
            });
        else
            switch(p.request.method) {
            case 'GET':
                onGET(params, callback);
                break;

            case 'PUT':
                pipe.getBody(p.request, function(error, body) {
                    if (error)
                        callback(error);
                    else
                        onPUT(p.name, body, callback);
                });
                break;
            }
    }

    /**
     * process data on GET request
     *
     * @param pParams {method, body, requrest, response}
     */
    function onGET(params, callback) {
        var cmd, json,
            p = params;

        if (p.name[0] === '/')
            cmd = p.name.replace('/', '');

        if (/^pack/.test(cmd)) {
            cmd = cmd.replace(/^pack/, '');
            streamPack(root(cmd), p.response, callback);
        } else {
            switch(cmd) {
            case '':
                p.data = json.stringify({
                    info: 'Cloud Commander API v1'
                });

                callback(null, {name: 'api.json'}, p.data);
                break;

            default:
                json = {
                    message: 'Error: command not found!'
                };

                callback(json);
                break;
            }
        }
    }

    function streamPack(cmd, response, fn) {
        var filename    = cmd.replace(/\.tar\.gz$/, ''),
            dir         = path.dirname(filename),
            names       = [
                path.basename(filename)
            ];

        operation('pack', dir, response, names, fn);
    }

    function auth(code, callback) {
        var storage = binom('storage', Modules),
            gh      = binom('GitHub', storage),
            env     = process.env,

            key     = env.github_key || gh.key,
            secret  = env.github_secret || gh.secret,

            ghAuth  = github(key, secret);

        ghAuth(code, callback);
    }

    /**
     * process data on PUT request
     *
     * @param pParams {command, method, body, requrest, response}
     */
    function onPUT(name, body, callback) {
        var cmd, files, data, msg;

        check
            .type('callback', callback, 'function')
            .check({
                name: name,
                body: body
            });

        if (name[0] === '/')
            cmd = name.replace('/', '');

        files   = json.parse(body);

        switch(cmd) {
        case 'auth':
            auth(body, function(error, token) {
                callback(error, json.stringify({
                    data: token
                }));
            });
            break;

        case 'mv':
            if (!files.from || !files.to) {
                callback(body);
            } else if (isRootAll([files.to, files.from])) {
                callback(getWin32RootMsg());
            } else {
                files.from  = root(files.from);
                files.to    = root(files.to);

                if (files.names)
                    data    = files.names.slice();
                else
                    data    = files;

                copyFiles(files, flop.move, function(error) {
                    var msg = formatMsg('move', data);

                    callback(error, msg);
                });
            }

            break;

        case 'cp':
            if (!files.from || !files.names || !files.to) {
                callback(body);
            } else if (isRootAll([files.to, files.from])) {
                callback(getWin32RootMsg());
            } else {
                files.from  = root(files.from);
                files.to    = root(files.to);

                msg         = formatMsg('copy', files.names);

                copy(files.from, files.to, files.names, function(error) {
                    callback(error, msg);
                });
            }
            break;

        case 'pack':
            if (!files.from)
                callback(body);
            else
                pack(files.from, files.to, files.names, callback);
            break;

        case 'extract':
            if (!files.from)
                callback(body);
            else
                extract(files.from, files.to, callback);

            break;

        default:
            callback();
            break;
        }
    }

    function pack(from, to, names, fn) {
        from    = root(from);
        to      = root(to);

        if (!names) {
            names = [
                path.basename(from)
            ];

            from = path.dirname(from);
        }

        operation('pack', from, to, names, fn);
    }

    function extract(from, to, fn) {
        from    = root(from);

        if (to)
            to  = root(to);
        else
            to = from.replace(/\.tar\.gz$/, '');

        operation('extract', from, to, fn);
    }

    function operation(op, from, to, names, fn) {
        var packer, wasError;

        if (!fn) {
            fn      = names;
            names   = [
                path.basename(from)
            ];
        }

        packer = jaguar[op](from, to, names);

        packer.on('error', function(error) {
            wasError = true;
            fn(error);
        });

        packer.on('progress', function(count) {
            process.stdout.write(rendy('\r{{ operation }} "{{ name }}": {{ count }}%', {
                operation   : op,
                name        : names[0],
                count       : count
            }));
        });

        packer.on('end', function() {
            var name, msg;

            process.stdout.write('\n');

            if (!wasError) {
                name    = path.basename(from),
                msg     = formatMsg(op, name);
                fn(null, msg);
            }
        });
    }

    function copy(from, to, names, fn) {
        var error,
            tmpl    = '\r copy {{ from }} {{ to }} {{ count }}%',
            cp      = copymitter(from, to, names);

        cp.on('error', function(e) {
            error = e;
            cp.abort();
        });

        cp.on('progress', function(count) {
            process.stdout.write(rendy(tmpl, {
                to: to,
                from: from,
                count: count
            }));
        });

        cp.on('end', function() {
            process.stdout.write('\n');
            fn(error);
        });
    }

    function copyFiles(files, processFunc, callback) {
        var names           = files.names,

            copy            = function() {
                var isLast, name,
                    from    = files.from,
                    to      = files.to;

                if (names) {
                    isLast  = !names.length,
                    name    = names.shift(),
                    from    += name;
                    to      += name;
                } else {
                    isLast  = false;
                    names   = [];
                }

                if (isLast)
                    callback();
                else
                    processFunc(from, to, function(error) {
                        if (error)
                            callback(error);
                        else
                            copy();
                    });
            };

        check
            .type('callback', callback, 'function')
            .type('processFunc', processFunc, 'function')
            .check({
                files: files
            });

        copy();
    }

    function isRootWin32(path) {
        var isRoot      = path === '/',
            isConfig    = config('root') === '/';

        return isWin32 && isRoot && isConfig;
    }

    function isRootAll(names) {
        var is = names.some(function(name) {
            return isRootWin32(name);
        });

        return is;
    }

    function getWin32RootMsg() {
        var message     = 'Could not copy from/to root on windows!',
            error       = Error(message);

        return error;
    }

    function formatMsg(msgParam, dataParam, status) {
        var msg, data,
            isObj = typeof dataParam === 'object';

        if (isObj)
            data = json.stringify(dataParam);
        else
            data = dataParam;

        msg = CloudFunc.formatMsg(msgParam, data, status);

        return msg;
    }

})();
<commit_msg>fix(rest) onGET: json.parse<commit_after>(function() {
    'use strict';

    var DIR         = './',
        DIR_LIB     = DIR + '../',
        DIR_JSON    = DIR_LIB + '../json/',

        path        = require('path'),
        root        = require(DIR + 'root'),
        config      = require(DIR + 'config'),
        binom       = require(DIR + 'binom'),

        CloudFunc   = require(DIR_LIB + 'cloudfunc'),

        markdown    = require(DIR + 'markdown'),

        github      = require('faust'),
        jaguar      = require('jaguar'),

        flop        = require('flop'),
        pipe        = require('pipe-io'),
        ponse       = require('ponse'),
        rendy       = require('rendy'),
        copymitter  = require('copymitter'),
        json        = require('jonny'),
        check       = require('checkup'),

        Modules     = require(DIR_JSON + 'modules'),

        isWin32     = process.platform === 'win32';

    /**
     * rest interface
     *
     * @param request
     * @param response
     * @param callback
     */
    module.exports = function(request, response, next) {
        var apiURL, name, is, regExp,
            params  = {
                request     : request,
                response    : response
            };

        check
            .type('next', next, 'function')
            .check({
                request: request,
                response: response
            });

        apiURL  = CloudFunc.apiURL;
        name    = ponse.getPathName(request);
        regExp  = RegExp('^' + apiURL),
        is      = regExp.test(name);

        if (!is) {
            next();
        } else {
            params.name = name.replace(apiURL, '') || '/';

            sendData(params, function(error, options, data) {
                params.gzip = !error;

                if (!data) {
                    data    = options;
                    options = {};
                }

                if (options.name)
                    params.name = options.name;

                if (options.gzip !== undefined)
                    params.gzip = options.gzip;

                if (options.query)
                    params.query = options.query;

                if (error)
                    ponse.sendError(error, params);
                else
                    ponse.send(data, params);
            });
        }
    };

    /**
     * getting data on method and command
     *
     * @param params {name, method, body, requrest, response}
     */
    function sendData(params, callback) {
        var p       = params,
            isMD    = RegExp('^/markdown').test(p.name);

        if (isMD)
            markdown(p.name, p.request, function(error, data) {
                callback(error, data);
            });
        else
            switch(p.request.method) {
            case 'GET':
                onGET(params, callback);
                break;

            case 'PUT':
                pipe.getBody(p.request, function(error, body) {
                    if (error)
                        callback(error);
                    else
                        onPUT(p.name, body, callback);
                });
                break;
            }
    }

    /**
     * process data on GET request
     *
     * @param pParams {method, body, requrest, response}
     */
    function onGET(params, callback) {
        var cmd, p = params;

        if (p.name[0] === '/')
            cmd = p.name.replace('/', '');

        if (/^pack/.test(cmd)) {
            cmd = cmd.replace(/^pack/, '');
            streamPack(root(cmd), p.response, callback);
        } else {
            switch(cmd) {
            case '':
                p.data = json.stringify({
                    info: 'Cloud Commander API v1'
                });

                callback(null, {name: 'api.json'}, p.data);
                break;

            default:
                callback({
                    message: 'Not Found'
                });
                break;
            }
        }
    }

    function streamPack(cmd, response, fn) {
        var filename    = cmd.replace(/\.tar\.gz$/, ''),
            dir         = path.dirname(filename),
            names       = [
                path.basename(filename)
            ];

        operation('pack', dir, response, names, fn);
    }

    function auth(code, callback) {
        var storage = binom('storage', Modules),
            gh      = binom('GitHub', storage),
            env     = process.env,

            key     = env.github_key || gh.key,
            secret  = env.github_secret || gh.secret,

            ghAuth  = github(key, secret);

        ghAuth(code, callback);
    }

    /**
     * process data on PUT request
     *
     * @param pParams {command, method, body, requrest, response}
     */
    function onPUT(name, body, callback) {
        var cmd, files, data, msg;

        check
            .type('callback', callback, 'function')
            .check({
                name: name,
                body: body
            });

        if (name[0] === '/')
            cmd = name.replace('/', '');

        files   = json.parse(body);

        switch(cmd) {
        case 'auth':
            auth(body, function(error, token) {
                callback(error, json.stringify({
                    data: token
                }));
            });
            break;

        case 'mv':
            if (!files.from || !files.to) {
                callback(body);
            } else if (isRootAll([files.to, files.from])) {
                callback(getWin32RootMsg());
            } else {
                files.from  = root(files.from);
                files.to    = root(files.to);

                if (files.names)
                    data    = files.names.slice();
                else
                    data    = files;

                copyFiles(files, flop.move, function(error) {
                    var msg = formatMsg('move', data);

                    callback(error, msg);
                });
            }

            break;

        case 'cp':
            if (!files.from || !files.names || !files.to) {
                callback(body);
            } else if (isRootAll([files.to, files.from])) {
                callback(getWin32RootMsg());
            } else {
                files.from  = root(files.from);
                files.to    = root(files.to);

                msg         = formatMsg('copy', files.names);

                copy(files.from, files.to, files.names, function(error) {
                    callback(error, msg);
                });
            }
            break;

        case 'pack':
            if (!files.from)
                callback(body);
            else
                pack(files.from, files.to, files.names, callback);
            break;

        case 'extract':
            if (!files.from)
                callback(body);
            else
                extract(files.from, files.to, callback);

            break;

        default:
            callback();
            break;
        }
    }

    function pack(from, to, names, fn) {
        from    = root(from);
        to      = root(to);

        if (!names) {
            names = [
                path.basename(from)
            ];

            from = path.dirname(from);
        }

        operation('pack', from, to, names, fn);
    }

    function extract(from, to, fn) {
        from    = root(from);

        if (to)
            to  = root(to);
        else
            to = from.replace(/\.tar\.gz$/, '');

        operation('extract', from, to, fn);
    }

    function operation(op, from, to, names, fn) {
        var packer, wasError;

        if (!fn) {
            fn      = names;
            names   = [
                path.basename(from)
            ];
        }

        packer = jaguar[op](from, to, names);

        packer.on('error', function(error) {
            wasError = true;
            fn(error);
        });

        packer.on('progress', function(count) {
            process.stdout.write(rendy('\r{{ operation }} "{{ name }}": {{ count }}%', {
                operation   : op,
                name        : names[0],
                count       : count
            }));
        });

        packer.on('end', function() {
            var name, msg;

            process.stdout.write('\n');

            if (!wasError) {
                name    = path.basename(from),
                msg     = formatMsg(op, name);
                fn(null, msg);
            }
        });
    }

    function copy(from, to, names, fn) {
        var error,
            tmpl    = '\r copy {{ from }} {{ to }} {{ count }}%',
            cp      = copymitter(from, to, names);

        cp.on('error', function(e) {
            error = e;
            cp.abort();
        });

        cp.on('progress', function(count) {
            process.stdout.write(rendy(tmpl, {
                to: to,
                from: from,
                count: count
            }));
        });

        cp.on('end', function() {
            process.stdout.write('\n');
            fn(error);
        });
    }

    function copyFiles(files, processFunc, callback) {
        var names           = files.names,

            copy            = function() {
                var isLast, name,
                    from    = files.from,
                    to      = files.to;

                if (names) {
                    isLast  = !names.length,
                    name    = names.shift(),
                    from    += name;
                    to      += name;
                } else {
                    isLast  = false;
                    names   = [];
                }

                if (isLast)
                    callback();
                else
                    processFunc(from, to, function(error) {
                        if (error)
                            callback(error);
                        else
                            copy();
                    });
            };

        check
            .type('callback', callback, 'function')
            .type('processFunc', processFunc, 'function')
            .check({
                files: files
            });

        copy();
    }

    function isRootWin32(path) {
        var isRoot      = path === '/',
            isConfig    = config('root') === '/';

        return isWin32 && isRoot && isConfig;
    }

    function isRootAll(names) {
        var is = names.some(function(name) {
            return isRootWin32(name);
        });

        return is;
    }

    function getWin32RootMsg() {
        var message     = 'Could not copy from/to root on windows!',
            error       = Error(message);

        return error;
    }

    function formatMsg(msgParam, dataParam, status) {
        var msg, data,
            isObj = typeof dataParam === 'object';

        if (isObj)
            data = json.stringify(dataParam);
        else
            data = dataParam;

        msg = CloudFunc.formatMsg(msgParam, data, status);

        return msg;
    }

})();
<|endoftext|>


<commit_before>var _ = require('underscore'),
    mongoose = require('mongoose');
require('mongoose-schema-extend');

var Schema = mongoose.Schema;

// Errors //
const ERRORS = {
    need_name_schema: '[Error] The Model need name and schema before compile it',
    no_schema: '[Error] The schema is not defined'
};

var Model = function (attrs, value) {
    var attributes = {};
    var schemaModel = {};

    /**
     * Get model attribute
     *
     * @param {string} key
     * @returns {*}
     */
    this.get = function (key) {
        return attributes[key];
    };

    /**
     * Set model attribute
     *
     * @param {string|object} attr
     * @param value
     * @returns {Model}
     */
    this.set = function (attr, value) {
        if (_.isObject(attr)) {
            _.extend(attributes, attr);
            _.extend(schemaModel, attr);
        } else {
            if (_.isUndefined(value)) {
                return this;
            }
            attributes[attr] = value;
            schemaModel[attr] = value;
        }
        return this;
    };

    /**
     * Check if this model has an attribute
     *
     * @param {string|array} key
     * @returns {boolean}
     */
    this.has = function (key) {
        if (_.isArray(key)) {
            return !_.difference(key, _.keys(attributes)).length;
        }
        return !!attributes[key];
    };

    /**
     * Delete a model attribute
     *
     * @param {string|array} key
     */
    this.unset = function (key) {
        if (!_.isArray(key)) {
            key = [key];
        }
        _.each(key, function (k) {
            delete attributes[k];
        })
    };

    /**
     * Signal that we desire an increment of this documents version.
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model-increment
     */
    this.increment = function () {
        if (!schemaModel) {
            throw new Error(ERRORS.no_schema);
        }
        return schemaModel.increment();
    };

    /**
     * Saves this document.
     *
     * @param {Function} cb
     * @returns {Model}
     * @see http://mongoosejs.com/docs/api.html#model_Model-save
     */
    this.save = function (cb) {
        if (!schemaModel) {
            throw new Error(ERRORS.no_schema);
        }
        schemaModel.save(cb);
        attributes._id = schemaModel._id;
        attributes.__v = schemaModel.__v;
        return this;
    };

    /**
     * Removes this document from the db.
     *
     * @param {Function} cb
     * @returns {Model}
     * @see http://mongoosejs.com/docs/api.html#model_Model-remove
     */
    this.remove = function (cb) {
        if (!schemaModel) {
            throw new Error(ERRORS.no_schema);
        }
        schemaModel.remove(cb);
        return this;
    };

    // if schema is defined, create schema model
    if (this._class.hasSchema()) {
        // If it is the first instance, compile the schema
        if (!this._class._isCompiled()) {
            this._class._compile();
        }

        schemaModel = new this._class.getMongooseModel()();
    }

    // set the constructor params
    this.set(attrs, value);

    // Call the initialize method of each instance
    if (this.initialize && _.isFunction(this.initialize)) {
        this.initialize.apply(this, arguments);
    }
};

/**
 * Based on Backbone.js 1.1.0 extend helper
 *
 * (c) 2010-2011 Jeremy Ashkenas, DocumentCloud Inc.
 * (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 * Backbone may be freely distributed under the MIT license.
 */
Model.extend = function(protoProps, staticProps) {
    var parent = this;
    var child;

    // The constructor function for the new subclass is either defined by you
    // (the "constructor" property in your `extend` definition), or defaulted
    // by us to simply call the parent's constructor.
    if (protoProps && _.has(protoProps, 'constructor')) {
        child = protoProps.constructor;
    } else {
        child = function() {
            var _class = this._class ? this._class : child;
            return parent.apply(_.extend(this, { _class: _class }), arguments);
        };
    }

    // Add static properties to the constructor function, if supplied.
    _.extend(child, parent, staticProps);

    // Set the prototype chain to inherit from `parent`, without calling
    // `parent`'s constructor function.
    var Surrogate = function(){ this.constructor = child; };
    Surrogate.prototype = parent.prototype;
    child.prototype = new Surrogate;

    // Add prototype properties (instance properties) to the subclass,
    // if supplied.
    if (protoProps) _.extend(child.prototype, protoProps);

    // Set a convenience property in case the parent's prototype is needed
    // later.
    child.__super__ = parent.prototype;

    // Add default statics methods
    addStatics(child);

    return child;
};

/**
 * Add statics methods
 *
 * @param self
 */
var addStatics = function (self) {
    _.extend(self, {
        getName: function () {
            return self.prototype.name;
        },
        hasSchema: function () {
            return !!self.prototype.schema;
        }
    });

    // if schema is defined, add support
    if (self.prototype.schema) {
        addMongooseSupport(self);
    }
};

/**
 * Add mongoose support to the model, with statics methods
 *
 * @param self
 */
var addMongooseSupport = function (self) {
    var schema = self.prototype.schema;
    var model;

    var options = _.extend({
        discriminatorKey: '_type',
        collection: self.prototype.name,
        strict: false
    }, self.prototype.schemaOptions);

    // Check for super schema
    if (!_.isEmpty(self.__super__) && self.__super__.schema) {
        options.collection = self.__super__.constructor.getCollectionName();
        schema = self.__super__.constructor.getSchema().extend(schema);
    } else {
        schema = new Schema(schema, options);
    }

    /**
     * Ensure that mongoose query not return parents documents
     *
     * @param conditions
     */
    var ensureCollection = function (conditions) {
        if(!_.isEmpty(self.__super__) && self.__super__.constructor.hasSchema()) {
            if (!conditions[self.__super__.constructor._getDiscriminatorKey()]) {
                conditions[self.__super__.constructor._getDiscriminatorKey()] = self.getName();
            }
        }
        return conditions;
    };

    var getCallbackDocument = function (callback) {
        return function () {
            if (arguments[1]) {
                arguments[1] = new self(arguments[1]);
            }
            callback.apply(null, arguments);
        };
    };

    var getCallbackArrayDocs = function (callback) {
        return function () {
            if (arguments[1]) {
                var models = [];
                arguments[1].forEach(function (doc) {
                    if (doc) {
                        models.push(new self(doc));
                    }
                });
                arguments[1] = models
            }

            callback.apply(null, arguments);
        };
    };

    self.getSchema = function () {
        return schema;
    };

    self.getCollectionName = function () {
        return options.collection;
    };

    self.getMongooseModel = function () {
        return model;
    };

    self._getDiscriminatorKey = function () {
        return options.discriminatorKey;
    };

    self._compile = function () {
        if (!options.collection || !schema) {
            throw new Error(ERRORS.need_name_schema);
        }
        model = mongoose.model(self.getName(), schema);
    };

    self._isCompiled = function () {
        return !!model;
    };

    /**
     * @deprecated use find(callback) instead
     */
    self.getAll = function (callback) {
        return self.find(callback);
    };

    /**
     * Finds documents
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.find
     */
    self.find = function (conditions, fields, options, callback) {
        if (_.isFunction(conditions)) {
            fields = conditions;
            conditions = {};
        }
        if (_.isFunction(callback)) {
            callback = getCallbackArrayDocs(callback);
        } else if (_.isFunction(options)) {
            options = getCallbackArrayDocs(options);
        } else if (_.isFunction(fields)) {
            fields = getCallbackArrayDocs(fields);
        }
        model.find(ensureCollection(conditions), fields, options, callback);
    };

    /**
     * Finds a single document by id
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findById
     */
    self.findById = function (id, fields, options, callback) {
        self.findOne({ _id: id }, fields, options, callback);
    };

    /**
     * Finds one document
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findOne
     */
    self.findOne = function (conditions, fields, options, callback) {
        if (_.isFunction(conditions)) {
            fields = conditions;
            conditions = {};
        }
        if (_.isFunction(options)) {
            options = getCallbackDocument(options);
        } else if (_.isFunction(fields)) {
            fields = getCallbackDocument(fields);
        }
        model.findOne(ensureCollection(conditions), fields, options, callback);
    };

    /**
     * Counts number of matching documents in a database collection.
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.count
     */
    self.count = function (conditions, callback) {
        if (_.isFunction(conditions)) {
            callback = conditions;
            conditions = {};
        }
        return model.count(ensureCollection(conditions), callback);
    };

    /**
     * Creates a Query for a distinct operation.
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.distinct
     */
    self.distinct = function (field, conditions, callback) {
        if (_.isFunction(conditions)) {
            callback = conditions;
            conditions = {};
        }
        return model.distinct(field, ensureCollection(conditions), callback);
    };

    /**
     * Issues a mongodb findAndModify update command.
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
     */
    self.findOneAndUpdate = function (conditions, update, options, callback) {
        if (_.isFunction(callback)) {
            callback = getCallbackDocument(callback);
        } else if (_.isFunction(options)) {
            options = getCallbackDocument(options);
        } else if (_.isFunction(update)) {
            options = getCallbackDocument(update);
            update = conditions;
            conditions = {};
        }
        // if there is only one argument, is update
        if (arguments.length === 1) {
            update = conditions;
            conditions = {};
        }
        model.findOneAndUpdate(ensureCollection(conditions), update, options, callback);
    };

    /**
     * Issues a mongodb findAndModify update command
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
     */
    self.findByIdAndUpdate = function (id, update, options, callback) {
        if (arguments.length === 1) {
            model.findByIdAndUpdate(id);
            return;
        }
        self.findOneAndUpdate({ _id: id }, update, options, callback);
    };

    /**
     * Issue a mongodb findAndModify remove command
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove
     */
    self.findOneAndRemove = function (conditions, options, callback) {
        if (arguments.length === 1 && _.isFunction(conditions)) {
            model.findOneAndRemove(conditions);
            return;
        }
        if (_.isFunction(callback)) {
            callback = getCallbackDocument(callback);
        } else if (_.isFunction(options)) {
            options = getCallbackDocument(options);
        }
        model.findOneAndRemove(ensureCollection(conditions), options, callback);
    };

    /**
     * Issue a mongodb findAndModify remove command by a documents id
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove
     */
    self.findByIdAndRemove = function (id, options, callback) {
        if (arguments.length === 1 && _.isFunction(id)) {
            model.findOneAndRemove(id);
            return;
        }
        self.findOneAndRemove({ _id: id }, options, callback);
    };

    /**
     * Shortcut for creating a new Document that is automatically saved to the db if valid
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.create
     */
    self.create = function (doc, callback) {
        model.create(doc, getCallbackDocument(callback));
    };

    /**
     * Updates documents in the database without returning them
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.update
     */
    self.update = function (conditions, doc, options, callback) {
        ensureCollection(conditions);
        if (_.isFunction(options)) {
            callback = options;
            options = {};
        }
        model.update(conditions, doc, options, getCallbackDocument(callback));
    };

    /**
     * Executes a mapReduce command
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.mapReduce
     */
    self.mapReduce = function (o, callback) {
        model.mapReduce(o, getCallbackArrayDocs(callback));
    };

    /**
     * geoNear support for Mongoose
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.geoNear
     */
    self.geoNear = function (near, options, callback) {
        if (arguments.length === 1) {
            model.geoNear(near);
            return;
        }
        if (_.isFunction(near)) {
            near = getCallbackArrayDocs(near);
        } else if (_.isFunction(options)) {
            options = getCallbackArrayDocs(options);
        } else if (_.isFunction(callback)) {
            callback = getCallbackArrayDocs(callback);
        }
        model.geoNear(near, options, callback);
    };

    /**
     * Performs aggregations on the models collection
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.aggregate
     */
    self.aggregate = function () {
        model.aggregate.apply(null, arguments);
    };

    /**
     * Implements $geoSearch functionality for Mongoose
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.geoSearch
     */
    self.geoSearch = function (conditions, options, callback) {
        if (_.isFunction(conditions)) {
            options = conditions;
            conditions = {};
        }
        if (_.isFunction(options)) {
            options = getCallbackArrayDocs(options);
        } else if (_.isFunction(callback)) {
            callback = getCallbackArrayDocs(callback);
        }
        model.geoSearch(ensureCollection(conditions), options, callback);
    };

    /**
     * Populates document references
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.populate
     */
    self.populate = function (docs, options, callback) {
        if (_.isFunction(options)) {
            v = getCallbackArrayDocs(options);
            options = {};
        } else if (_.isFunction(cb)) {
            callback = getCallbackArrayDocs(callback);
        }
        model.populate(docs, ensureCollection(options), callback);
    };

    return schema;
};

module.exports = Model;<commit_msg>Models now support "super"<commit_after>var _ = require('underscore'),
    mongoose = require('mongoose');
require('mongoose-schema-extend');

var Schema = mongoose.Schema;

// Errors //
const ERRORS = {
    need_name_schema: '[Error] The Model need name and schema before compile it',
    no_schema: '[Error] The schema is not defined'
};

var Model = function (attrs, value) {
    var attributes = {};
    var schemaModel = {};

    /**
     * Get model attribute
     *
     * @param {string} key
     * @returns {*}
     */
    this.get = function (key) {
        return attributes[key];
    };

    /**
     * Set model attribute
     *
     * @param {string|object} attr
     * @param value
     * @returns {Model}
     */
    this.set = function (attr, value) {
        if (_.isObject(attr)) {
            _.extend(attributes, attr);
            _.extend(schemaModel, attr);
        } else {
            if (_.isUndefined(value)) {
                return this;
            }
            attributes[attr] = value;
            schemaModel[attr] = value;
        }
        return this;
    };

    /**
     * Check if this model has an attribute
     *
     * @param {string|array} key
     * @returns {boolean}
     */
    this.has = function (key) {
        if (_.isArray(key)) {
            return !_.difference(key, _.keys(attributes)).length;
        }
        return !!attributes[key];
    };

    /**
     * Delete a model attribute
     *
     * @param {string|array} key
     */
    this.unset = function (key) {
        if (!_.isArray(key)) {
            key = [key];
        }
        _.each(key, function (k) {
            delete attributes[k];
        })
    };

    /**
     * Signal that we desire an increment of this documents version.
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model-increment
     */
    this.increment = function () {
        if (!schemaModel) {
            throw new Error(ERRORS.no_schema);
        }
        return schemaModel.increment();
    };

    /**
     * Saves this document.
     *
     * @param {Function} cb
     * @returns {Model}
     * @see http://mongoosejs.com/docs/api.html#model_Model-save
     */
    this.save = function (cb) {
        if (!schemaModel) {
            throw new Error(ERRORS.no_schema);
        }
        schemaModel.save(cb);
        attributes._id = schemaModel._id;
        attributes.__v = schemaModel.__v;
        return this;
    };

    /**
     * Removes this document from the db.
     *
     * @param {Function} cb
     * @returns {Model}
     * @see http://mongoosejs.com/docs/api.html#model_Model-remove
     */
    this.remove = function (cb) {
        if (!schemaModel) {
            throw new Error(ERRORS.no_schema);
        }
        schemaModel.remove(cb);
        return this;
    };

    // if schema is defined, create schema model
    if (this._class.hasSchema()) {
        // If it is the first instance, compile the schema
        if (!this._class._isCompiled()) {
            this._class._compile();
        }

        schemaModel = new this._class.getMongooseModel()();
    }

    // set the constructor params
    this.set(attrs, value);

    // Call the initialize method of each instance
    if (this.initialize && _.isFunction(this.initialize)) {
        this.initialize.apply(this, arguments);
    }
};

/**
 * Based on Backbone.js 1.1.0 extend helper
 *
 * (c) 2010-2011 Jeremy Ashkenas, DocumentCloud Inc.
 * (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 * Backbone may be freely distributed under the MIT license.
 */
Model.extend = function(protoProps, staticProps) {
    var parent = this;
    var child;

    // The constructor function for the new subclass is either defined by you
    // (the "constructor" property in your `extend` definition), or defaulted
    // by us to simply call the parent's constructor.
    if (protoProps && _.has(protoProps, 'constructor')) {
        child = protoProps.constructor;
    } else {
        child = function() {
            var _class = this._class ? this._class : child;
            return parent.apply(_.extend(this, { _class: _class, super: parent.prototype }), arguments);
        };
    }

    // Add static properties to the constructor function, if supplied.
    _.extend(child, parent, staticProps);

    // Set the prototype chain to inherit from `parent`, without calling
    // `parent`'s constructor function.
    var Surrogate = function(){ this.constructor = child; };
    Surrogate.prototype = parent.prototype;
    child.prototype = new Surrogate;

    // Add prototype properties (instance properties) to the subclass,
    // if supplied.
    if (protoProps) _.extend(child.prototype, protoProps);

    // Set a convenience property in case the parent's prototype is needed
    // later.
    child.__super__ = parent.prototype;

    // Add default statics methods
    addStatics(child);

    return child;
};

/**
 * Add statics methods
 *
 * @param self
 */
var addStatics = function (self) {
    _.extend(self, {
        getName: function () {
            return self.prototype.name;
        },
        hasSchema: function () {
            return !!self.prototype.schema;
        }
    });

    // if schema is defined, add support
    if (self.prototype.schema) {
        addMongooseSupport(self);
    }
};

/**
 * Add mongoose support to the model, with statics methods
 *
 * @param self
 */
var addMongooseSupport = function (self) {
    var schema = self.prototype.schema;
    var model;

    var options = _.extend({
        discriminatorKey: '_type',
        collection: self.prototype.name,
        strict: false
    }, self.prototype.schemaOptions);

    // Check for super schema
    if (!_.isEmpty(self.__super__) && self.__super__.schema) {
        options.collection = self.__super__.constructor.getCollectionName();
        schema = self.__super__.constructor.getSchema().extend(schema);
    } else {
        schema = new Schema(schema, options);
    }

    /**
     * Ensure that mongoose query not return parents documents
     *
     * @param conditions
     */
    var ensureCollection = function (conditions) {
        if(!_.isEmpty(self.__super__) && self.__super__.constructor.hasSchema()) {
            if (!conditions[self.__super__.constructor._getDiscriminatorKey()]) {
                conditions[self.__super__.constructor._getDiscriminatorKey()] = self.getName();
            }
        }
        return conditions;
    };

    var getCallbackDocument = function (callback) {
        return function () {
            if (arguments[1]) {
                arguments[1] = new self(arguments[1]);
            }
            callback.apply(null, arguments);
        };
    };

    var getCallbackArrayDocs = function (callback) {
        return function () {
            if (arguments[1]) {
                var models = [];
                arguments[1].forEach(function (doc) {
                    if (doc) {
                        models.push(new self(doc));
                    }
                });
                arguments[1] = models
            }

            callback.apply(null, arguments);
        };
    };

    self.getSchema = function () {
        return schema;
    };

    self.getCollectionName = function () {
        return options.collection;
    };

    self.getMongooseModel = function () {
        return model;
    };

    self._getDiscriminatorKey = function () {
        return options.discriminatorKey;
    };

    self._compile = function () {
        if (!options.collection || !schema) {
            throw new Error(ERRORS.need_name_schema);
        }
        model = mongoose.model(self.getName(), schema);
    };

    self._isCompiled = function () {
        return !!model;
    };

    /**
     * @deprecated use find(callback) instead
     */
    self.getAll = function (callback) {
        return self.find(callback);
    };

    /**
     * Finds documents
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.find
     */
    self.find = function (conditions, fields, options, callback) {
        if (_.isFunction(conditions)) {
            fields = conditions;
            conditions = {};
        }
        if (_.isFunction(callback)) {
            callback = getCallbackArrayDocs(callback);
        } else if (_.isFunction(options)) {
            options = getCallbackArrayDocs(options);
        } else if (_.isFunction(fields)) {
            fields = getCallbackArrayDocs(fields);
        }
        model.find(ensureCollection(conditions), fields, options, callback);
    };

    /**
     * Finds a single document by id
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findById
     */
    self.findById = function (id, fields, options, callback) {
        self.findOne({ _id: id }, fields, options, callback);
    };

    /**
     * Finds one document
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findOne
     */
    self.findOne = function (conditions, fields, options, callback) {
        if (_.isFunction(conditions)) {
            fields = conditions;
            conditions = {};
        }
        if (_.isFunction(options)) {
            options = getCallbackDocument(options);
        } else if (_.isFunction(fields)) {
            fields = getCallbackDocument(fields);
        }
        model.findOne(ensureCollection(conditions), fields, options, callback);
    };

    /**
     * Counts number of matching documents in a database collection.
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.count
     */
    self.count = function (conditions, callback) {
        if (_.isFunction(conditions)) {
            callback = conditions;
            conditions = {};
        }
        return model.count(ensureCollection(conditions), callback);
    };

    /**
     * Creates a Query for a distinct operation.
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.distinct
     */
    self.distinct = function (field, conditions, callback) {
        if (_.isFunction(conditions)) {
            callback = conditions;
            conditions = {};
        }
        return model.distinct(field, ensureCollection(conditions), callback);
    };

    /**
     * Issues a mongodb findAndModify update command.
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
     */
    self.findOneAndUpdate = function (conditions, update, options, callback) {
        if (_.isFunction(callback)) {
            callback = getCallbackDocument(callback);
        } else if (_.isFunction(options)) {
            options = getCallbackDocument(options);
        } else if (_.isFunction(update)) {
            options = getCallbackDocument(update);
            update = conditions;
            conditions = {};
        }
        // if there is only one argument, is update
        if (arguments.length === 1) {
            update = conditions;
            conditions = {};
        }
        model.findOneAndUpdate(ensureCollection(conditions), update, options, callback);
    };

    /**
     * Issues a mongodb findAndModify update command
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
     */
    self.findByIdAndUpdate = function (id, update, options, callback) {
        if (arguments.length === 1) {
            model.findByIdAndUpdate(id);
            return;
        }
        self.findOneAndUpdate({ _id: id }, update, options, callback);
    };

    /**
     * Issue a mongodb findAndModify remove command
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove
     */
    self.findOneAndRemove = function (conditions, options, callback) {
        if (arguments.length === 1 && _.isFunction(conditions)) {
            model.findOneAndRemove(conditions);
            return;
        }
        if (_.isFunction(callback)) {
            callback = getCallbackDocument(callback);
        } else if (_.isFunction(options)) {
            options = getCallbackDocument(options);
        }
        model.findOneAndRemove(ensureCollection(conditions), options, callback);
    };

    /**
     * Issue a mongodb findAndModify remove command by a documents id
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove
     */
    self.findByIdAndRemove = function (id, options, callback) {
        if (arguments.length === 1 && _.isFunction(id)) {
            model.findOneAndRemove(id);
            return;
        }
        self.findOneAndRemove({ _id: id }, options, callback);
    };

    /**
     * Shortcut for creating a new Document that is automatically saved to the db if valid
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.create
     */
    self.create = function (doc, callback) {
        model.create(doc, getCallbackDocument(callback));
    };

    /**
     * Updates documents in the database without returning them
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.update
     */
    self.update = function (conditions, doc, options, callback) {
        ensureCollection(conditions);
        if (_.isFunction(options)) {
            callback = options;
            options = {};
        }
        model.update(conditions, doc, options, getCallbackDocument(callback));
    };

    /**
     * Executes a mapReduce command
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.mapReduce
     */
    self.mapReduce = function (o, callback) {
        model.mapReduce(o, getCallbackArrayDocs(callback));
    };

    /**
     * geoNear support for Mongoose
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.geoNear
     */
    self.geoNear = function (near, options, callback) {
        if (arguments.length === 1) {
            model.geoNear(near);
            return;
        }
        if (_.isFunction(near)) {
            near = getCallbackArrayDocs(near);
        } else if (_.isFunction(options)) {
            options = getCallbackArrayDocs(options);
        } else if (_.isFunction(callback)) {
            callback = getCallbackArrayDocs(callback);
        }
        model.geoNear(near, options, callback);
    };

    /**
     * Performs aggregations on the models collection
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.aggregate
     */
    self.aggregate = function () {
        model.aggregate.apply(null, arguments);
    };

    /**
     * Implements $geoSearch functionality for Mongoose
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.geoSearch
     */
    self.geoSearch = function (conditions, options, callback) {
        if (_.isFunction(conditions)) {
            options = conditions;
            conditions = {};
        }
        if (_.isFunction(options)) {
            options = getCallbackArrayDocs(options);
        } else if (_.isFunction(callback)) {
            callback = getCallbackArrayDocs(callback);
        }
        model.geoSearch(ensureCollection(conditions), options, callback);
    };

    /**
     * Populates document references
     *
     * @see http://mongoosejs.com/docs/api.html#model_Model.populate
     */
    self.populate = function (docs, options, callback) {
        if (_.isFunction(options)) {
            v = getCallbackArrayDocs(options);
            options = {};
        } else if (_.isFunction(cb)) {
            callback = getCallbackArrayDocs(callback);
        }
        model.populate(docs, ensureCollection(options), callback);
    };

    return schema;
};

module.exports = Model;<|endoftext|>


<commit_before>// if node
var Stream = require('stream').Stream;
var EventEmitter = require('events').EventEmitter;
var BufferReadStream = require('streamers').BufferReadStream;
var WebSocket = require('streamws');

var util = require('./util');
var BinaryStream = require('./stream').BinaryStream;

// end node

function BinaryClient(socket, options) {
  if (!(this instanceof BinaryClient)) return new BinaryClient(options);

  EventEmitter.call(this);

  var self = this;

  this._options = util.extend({
    chunkSize: 40960
  }, options);

  this.streams = {};

  if(typeof socket === 'string') {
    this._nextId = 0;
    this._socket = new WebSocket(socket);
  } else {
    // Use odd numbered ids for server originated streams
    this._nextId = 1;
    this._socket = socket;
  }

  this._socket.binaryType = 'arraybuffer';

  this._socket.addEventListener('open', function(){
    self.emit('open');
  });
  this._socket.addEventListener('error', function(error){
    self.emit('error', error);
  });
  this._socket.addEventListener('close', function(code, message){
    self.emit('close', code, message);
  });
  this._socket.addEventListener('message', function(data, flags){
    util.setZeroTimeout(function(){

      // Message format
      // [type, payload, bonus ]
      //
      // Reserved
      // [ 0  , X , X ]
      //
      //
      // New stream
      // [ 1  , Meta , new streamId ]
      //
      //
      // Data
      // [ 2  , Data , streamId ]
      //
      //
      // Pause
      // [ 3  , null , streamId ]
      //
      //
      // Resume
      // [ 4  , null , streamId ]
      //
      //
      // End
      // [ 5  , null , streamId ]
      //
      //
      // Close
      // [ 6  , null , streamId ]
      //

      if(util.isNode !== true){
        data = data.data;
      }

      data = util.unpack(data);
      switch(data[0]) {
        case 0:
          // Reserved
          break;
        case 1:
          var meta = data[1];
          var streamId = data[2];
          var binaryStream = self._receiveStream(streamId);
          self.emit('stream', binaryStream, meta);
          break;
        case 2:
          var payload = data[1];
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onData(payload);
          } else {
            self.emit('error', 'Received `data` message for unknown stream: ' + streamId);
          }
          break;
        case 3:
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onPause();
          } else {
            self.emit('error', 'Received `pause` message for unknown stream: ' + streamId);
          }
          break;
        case 4:
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onResume();
          } else {
            self.emit('error', 'Received `resume` message for unknown stream: ' + streamId);
          }
          break;
        case 5:
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onEnd();
          } else {
            self.emit('error', 'Received `end` message for unknown stream: ' + streamId);
          }
          break;
        case 6:
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onClose();
          } else {
            self.emit('error', 'Received `close` message for unknown stream: ' + streamId);
          }
          break;
        default:
          self.emit('error', 'Unrecognized message type received: ' + data[0]);
      }
    });
  });
}

util.inherits(BinaryClient, EventEmitter);

BinaryClient.prototype.send = function(data, meta){
  var stream = this.createStream(meta);
  if(data instanceof Stream) {
    data.pipe(stream);
  } else if (util.isNode === true) {
    if(Buffer.isBuffer(data)) {
      (new BufferReadStream(data, {chunkSize: this._options.chunkSize})).pipe(stream);
    } else {
      stream.write(data);
    }
  } else if (util.isNode !== true) {
    if(data.constructor == Blob || data.constructor == File) {
      (new BlobReadStream(data, {chunkSize: this._options.chunkSize})).pipe(stream);
    } else if (data.constructor == ArrayBuffer) {
      var blob;
      if(binaryFeatures.useArrayBufferView) {
        data = new Uint8Array(data.buffer);
      }
      if(binaryFeatures.useBlobBuilder) {
        var builder = new BlobBuilder();
        builder.append(data);
        blob = builder.getBlob()
      } else {
        blob = new Blob([data]);
      }
      (new BlobReadStream(blob, {chunkSize: this._options.chunkSize})).pipe(stream);
    } else if (typeof data === 'object' && 'BYTES_PER_ELEMENT' in data) {
      var blob;
      if(!binaryFeatures.useArrayBufferView) {
        // Warn
        data = data.buffer;
      }
      if(binaryFeatures.useBlobBuilder) {
        var builder = new BlobBuilder();
        builder.append(data);
        blob = builder.getBlob()
      } else {
        blob = new Blob([data]);
      }
      (new BlobReadStream(blob, {chunkSize: this._options.chunkSize})).pipe(stream);
    } else {
      stream.write(data);
    }
  }
  return stream;
};

BinaryClient.prototype._receiveStream = function(streamId){
  var self = this;
  var binaryStream = new BinaryStream(this._socket, streamId, false);
  binaryStream.on('close', function(){
    delete self.streams[streamId];
  });
  this.streams[streamId] = binaryStream;
  return binaryStream;
};

BinaryClient.prototype.createStream = function(meta){
  if(this._socket.readyState !== WebSocket.OPEN) {
    throw new Error('Client is not yet connected or has closed');
    return;
  }
  var self = this;
  var streamId = this._nextId;
  this._nextId += 2;
  var binaryStream = new BinaryStream(this._socket, streamId, true, meta);
  binaryStream.on('close', function(){
    delete self.streams[streamId];
  });
  this.streams[streamId] = binaryStream;
  return binaryStream;
};

BinaryClient.prototype.close = BinaryClient.prototype.destroy = function(code, message) {
  this._socket.close(code, message);
};

exports.BinaryClient = BinaryClient;
<commit_msg>turns out you dont even need the messagevent check<commit_after>// if node
var Stream = require('stream').Stream;
var EventEmitter = require('events').EventEmitter;
var BufferReadStream = require('streamers').BufferReadStream;
var WebSocket = require('streamws');

var util = require('./util');
var BinaryStream = require('./stream').BinaryStream;

// end node

function BinaryClient(socket, options) {
  if (!(this instanceof BinaryClient)) return new BinaryClient(options);

  EventEmitter.call(this);

  var self = this;

  this._options = util.extend({
    chunkSize: 40960
  }, options);

  this.streams = {};

  if(typeof socket === 'string') {
    this._nextId = 0;
    this._socket = new WebSocket(socket);
  } else {
    // Use odd numbered ids for server originated streams
    this._nextId = 1;
    this._socket = socket;
  }

  this._socket.binaryType = 'arraybuffer';

  this._socket.addEventListener('open', function(){
    self.emit('open');
  });
  this._socket.addEventListener('error', function(error){
    self.emit('error', error);
  });
  this._socket.addEventListener('close', function(code, message){
    self.emit('close', code, message);
  });
  this._socket.addEventListener('message', function(data, flags){
    util.setZeroTimeout(function(){

      // Message format
      // [type, payload, bonus ]
      //
      // Reserved
      // [ 0  , X , X ]
      //
      //
      // New stream
      // [ 1  , Meta , new streamId ]
      //
      //
      // Data
      // [ 2  , Data , streamId ]
      //
      //
      // Pause
      // [ 3  , null , streamId ]
      //
      //
      // Resume
      // [ 4  , null , streamId ]
      //
      //
      // End
      // [ 5  , null , streamId ]
      //
      //
      // Close
      // [ 6  , null , streamId ]
      //

      data = data.data;

      data = util.unpack(data);
      switch(data[0]) {
        case 0:
          // Reserved
          break;
        case 1:
          var meta = data[1];
          var streamId = data[2];
          var binaryStream = self._receiveStream(streamId);
          self.emit('stream', binaryStream, meta);
          break;
        case 2:
          var payload = data[1];
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onData(payload);
          } else {
            self.emit('error', 'Received `data` message for unknown stream: ' + streamId);
          }
          break;
        case 3:
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onPause();
          } else {
            self.emit('error', 'Received `pause` message for unknown stream: ' + streamId);
          }
          break;
        case 4:
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onResume();
          } else {
            self.emit('error', 'Received `resume` message for unknown stream: ' + streamId);
          }
          break;
        case 5:
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onEnd();
          } else {
            self.emit('error', 'Received `end` message for unknown stream: ' + streamId);
          }
          break;
        case 6:
          var streamId = data[2];
          var binaryStream = self.streams[streamId];
          if(binaryStream) {
            binaryStream._onClose();
          } else {
            self.emit('error', 'Received `close` message for unknown stream: ' + streamId);
          }
          break;
        default:
          self.emit('error', 'Unrecognized message type received: ' + data[0]);
      }
    });
  });
}

util.inherits(BinaryClient, EventEmitter);

BinaryClient.prototype.send = function(data, meta){
  var stream = this.createStream(meta);
  if(data instanceof Stream) {
    data.pipe(stream);
  } else if (util.isNode === true) {
    if(Buffer.isBuffer(data)) {
      (new BufferReadStream(data, {chunkSize: this._options.chunkSize})).pipe(stream);
    } else {
      stream.write(data);
    }
  } else if (util.isNode !== true) {
    if(data.constructor == Blob || data.constructor == File) {
      (new BlobReadStream(data, {chunkSize: this._options.chunkSize})).pipe(stream);
    } else if (data.constructor == ArrayBuffer) {
      var blob;
      if(binaryFeatures.useArrayBufferView) {
        data = new Uint8Array(data.buffer);
      }
      if(binaryFeatures.useBlobBuilder) {
        var builder = new BlobBuilder();
        builder.append(data);
        blob = builder.getBlob()
      } else {
        blob = new Blob([data]);
      }
      (new BlobReadStream(blob, {chunkSize: this._options.chunkSize})).pipe(stream);
    } else if (typeof data === 'object' && 'BYTES_PER_ELEMENT' in data) {
      var blob;
      if(!binaryFeatures.useArrayBufferView) {
        // Warn
        data = data.buffer;
      }
      if(binaryFeatures.useBlobBuilder) {
        var builder = new BlobBuilder();
        builder.append(data);
        blob = builder.getBlob()
      } else {
        blob = new Blob([data]);
      }
      (new BlobReadStream(blob, {chunkSize: this._options.chunkSize})).pipe(stream);
    } else {
      stream.write(data);
    }
  }
  return stream;
};

BinaryClient.prototype._receiveStream = function(streamId){
  var self = this;
  var binaryStream = new BinaryStream(this._socket, streamId, false);
  binaryStream.on('close', function(){
    delete self.streams[streamId];
  });
  this.streams[streamId] = binaryStream;
  return binaryStream;
};

BinaryClient.prototype.createStream = function(meta){
  if(this._socket.readyState !== WebSocket.OPEN) {
    throw new Error('Client is not yet connected or has closed');
    return;
  }
  var self = this;
  var streamId = this._nextId;
  this._nextId += 2;
  var binaryStream = new BinaryStream(this._socket, streamId, true, meta);
  binaryStream.on('close', function(){
    delete self.streams[streamId];
  });
  this.streams[streamId] = binaryStream;
  return binaryStream;
};

BinaryClient.prototype.close = BinaryClient.prototype.destroy = function(code, message) {
  this._socket.close(code, message);
};

exports.BinaryClient = BinaryClient;
<|endoftext|>


<commit_before>import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import PodcastChannel from './podcast-channel.js';
import PodcastSettings from './podcast-settings.js';
import PodcastItem from './podcast-item.js';
import podcastParser from './podcast-parser.js';
import podcastRender from './podcast-render.js';
import syncRemote from './sync-remote.js';
import id3Tag from './id3-tag.js';
var fs = require('fs');
var path = require('path');
var moment = require('moment');

export default class Podcast extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      podcast: {
        title: '',
        link: '',
        subtitle: '',
        description: '',
        language: 'en-us',
        image: '',
        author: '',
        copyright: '',
        ownerName: '',
        ownerEmail: '',
        explicit: false,
        categories: [],
        items: []
      },
      settings: {
        showOptionalFields: false,
        deploytype: 'none',
        prefixUrl: '',
        sftphost: '',
        sftpuser: '',
        sftpuseprivatekey: false,
        sftpprivatekeyloc: '',
        sftppass: '',
        sftppath: '',
        s3bucketname: '',
        s3accesskey: '',
        s3secretkey: ''
      },
      activeItem: -1,
      settingsDialogOpen: false
    };
    this.updateSubState = this.updateSubState.bind(this);
    this.handleItemChange = this.handleItemChange.bind(this);
    this.newItem = this.newItem.bind(this);
    this.deleteItem = this.deleteItem.bind(this);
    this.saveChanges = this.saveChanges.bind(this);
    this.setActiveItem = this.setActiveItem.bind(this);
    this.openSettingsDialog = this.openSettingsDialog.bind(this);
    this.closeSettingsDialog = this.closeSettingsDialog.bind(this);
    this.publish = this.publish.bind(this);
    this.merge = this.merge.bind(this);
  }
  merge(stateKey, newStateObject) {
    var newObject = this.state[stateKey];
    var k;
    for (k in newStateObject) {
      if (newStateObject.hasOwnProperty(k) === false) {
        continue;
      }
      newObject[k] = newStateObject[k];
    }
    return newObject;
  }
  componentDidMount() {
    var localPath = path.join(this.props.directory, '.podcast-local.xml');
    var xmlPath = path.join(this.props.directory, 'podcast.xml');
    var outerThis = this;
    try {
      fs.accessSync(localPath, fs.F_OK);
      podcastParser(localPath, function(parsed) {
        outerThis.setState({podcast: outerThis.merge('podcast', parsed)});
      });
    } catch (e) {
      console.log('no .podcast-local.xml file - trying podcast.xml');
      try {
        fs.accessSync(xmlPath, fs.F_OK);
        podcastParser(xmlPath, function(parsed) {
          outerThis.setState({podcast: outerThis.merge('podcast', parsed)});
        });
      } catch (e) {
        console.log('no podcast.xml - starting from scratch');
      }
    }
    var settingsPath = path.join(this.props.directory, '.settings.json');
    try {
      fs.accessSync(settingsPath, fs.F_OK);
      fs.readFile(settingsPath, 'utf8', function(err, data) {
        if (err) throw err;
        outerThis.setState({
          settings: outerThis.merge('settings', JSON.parse(data))
        });
      });
    } catch (e) {
      console.log('no .settings.json file found');
    }
  }
  updateSubState(outerKey, innerKey, value) {
    var newState = {};
    newState[outerKey] = this.state[outerKey];
    newState[outerKey][innerKey] = value;
    this.setState(newState);
  }
  handleItemChange(i, k, v) {
    var tmpPodcast = this.state.podcast;
    tmpPodcast.items[i][k] = v;
    this.setState({podcast: tmpPodcast});
  }
  newItem() {
    var DATE_RFC2822 = "ddd, DD MMM YYYY HH:mm:ss ZZ";
    var newItem = {
      title: '',
      description: '',
      author: '',
      duration: 0.0,
      subtitle: '',
      pubdate: moment().format(DATE_RFC2822),
      filesize: 0,
      filetype: '',
      fileurl: ''
    };
    var tmpPodcast = this.state.podcast;
    tmpPodcast.items = [newItem].concat(tmpPodcast.items);
    this.setState({
      podcast: tmpPodcast,
      activeItem: 0
    });
  }
  deleteItem(i) {
    var tmpPodcast = this.state.podcast;
    tmpPodcast.items.splice(i, 1);
    this.setState({podcast: tmpPodcast});
  }
  setActiveItem(i) {
    this.setState({activeItem: i});
  }
  openSettingsDialog() {
    this.setState({settingsDialogOpen: true});
  }
  closeSettingsDialog() {
    this.setState({settingsDialogOpen: false});
  }
  publish() {
    syncRemote(
      this.props.directory,
      this.state.settings,
      this.props.setError,
      this.props.setSnackbar
    );
  }
  saveChanges() {
    podcastRender(this.props.directory,
      this.state.podcast,
      this.state.settings.prefixUrl);
    for (var i = 0; i < this.state.podcast.items.length; i++) {
      id3Tag(this.state.podcast, i, this.props.setSnackbar);
    }
  }
  render() {
    var outerThis = this;
    return (

            <RaisedButton
              label="Go Home"
              onTouchTap={this.props.goHome}
            />

            <RaisedButton
              label="Settings"
              onTouchTap={this.openSettingsDialog}
            />

            {this.state.settings.deploytype === 'none' ? null : (
              <RaisedButton
                label="Publish"
                onTouchTap={this.publish}
              />
            )}

            <RaisedButton
              label="Save Changes"
              onTouchTap={this.saveChanges}
            />

        <PodcastChannel {...this.props}
          podcast={this.state.podcast}
          settings={this.state.settings}
          handleChange={(k, v) => this.updateSubState('podcast', k, v)}
        />
        <PodcastSettings {...this.props}
          open={this.state.settingsDialogOpen}
          close={this.closeSettingsDialog}
          settings={this.state.settings}
          handleChange={(k, v) => this.updateSubState('settings', k, v)}
        />

Episodes:

              <RaisedButton
                label="New Episode"
                onTouchTap={this.newItem}
              />

          {
            this.state.podcast.items.map(function(item, i) {
              return (
                <PodcastItem key={i} {...outerThis.props}
                  episode={item}
                  settings={outerThis.state.settings}
                  handleChange={(k, v) => outerThis.handleItemChange(i, k, v)}
                  deleteItem={() => outerThis.deleteItem(i)}
                  expanded={outerThis.state.activeItem === i}
                  expand={() => outerThis.setActiveItem(i)}
                  collapse={() => outerThis.setActiveItem(-1)}
                />
              );
            })
          }

    );
  }
}
<commit_msg>hide parent div when publish button is hidden<commit_after>import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import PodcastChannel from './podcast-channel.js';
import PodcastSettings from './podcast-settings.js';
import PodcastItem from './podcast-item.js';
import podcastParser from './podcast-parser.js';
import podcastRender from './podcast-render.js';
import syncRemote from './sync-remote.js';
import id3Tag from './id3-tag.js';
var fs = require('fs');
var path = require('path');
var moment = require('moment');

export default class Podcast extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      podcast: {
        title: '',
        link: '',
        subtitle: '',
        description: '',
        language: 'en-us',
        image: '',
        author: '',
        copyright: '',
        ownerName: '',
        ownerEmail: '',
        explicit: false,
        categories: [],
        items: []
      },
      settings: {
        showOptionalFields: false,
        deploytype: 'none',
        prefixUrl: '',
        sftphost: '',
        sftpuser: '',
        sftpuseprivatekey: false,
        sftpprivatekeyloc: '',
        sftppass: '',
        sftppath: '',
        s3bucketname: '',
        s3accesskey: '',
        s3secretkey: ''
      },
      activeItem: -1,
      settingsDialogOpen: false
    };
    this.updateSubState = this.updateSubState.bind(this);
    this.handleItemChange = this.handleItemChange.bind(this);
    this.newItem = this.newItem.bind(this);
    this.deleteItem = this.deleteItem.bind(this);
    this.saveChanges = this.saveChanges.bind(this);
    this.setActiveItem = this.setActiveItem.bind(this);
    this.openSettingsDialog = this.openSettingsDialog.bind(this);
    this.closeSettingsDialog = this.closeSettingsDialog.bind(this);
    this.publish = this.publish.bind(this);
    this.merge = this.merge.bind(this);
  }
  merge(stateKey, newStateObject) {
    var newObject = this.state[stateKey];
    var k;
    for (k in newStateObject) {
      if (newStateObject.hasOwnProperty(k) === false) {
        continue;
      }
      newObject[k] = newStateObject[k];
    }
    return newObject;
  }
  componentDidMount() {
    var localPath = path.join(this.props.directory, '.podcast-local.xml');
    var xmlPath = path.join(this.props.directory, 'podcast.xml');
    var outerThis = this;
    try {
      fs.accessSync(localPath, fs.F_OK);
      podcastParser(localPath, function(parsed) {
        outerThis.setState({podcast: outerThis.merge('podcast', parsed)});
      });
    } catch (e) {
      console.log('no .podcast-local.xml file - trying podcast.xml');
      try {
        fs.accessSync(xmlPath, fs.F_OK);
        podcastParser(xmlPath, function(parsed) {
          outerThis.setState({podcast: outerThis.merge('podcast', parsed)});
        });
      } catch (e) {
        console.log('no podcast.xml - starting from scratch');
      }
    }
    var settingsPath = path.join(this.props.directory, '.settings.json');
    try {
      fs.accessSync(settingsPath, fs.F_OK);
      fs.readFile(settingsPath, 'utf8', function(err, data) {
        if (err) throw err;
        outerThis.setState({
          settings: outerThis.merge('settings', JSON.parse(data))
        });
      });
    } catch (e) {
      console.log('no .settings.json file found');
    }
  }
  updateSubState(outerKey, innerKey, value) {
    var newState = {};
    newState[outerKey] = this.state[outerKey];
    newState[outerKey][innerKey] = value;
    this.setState(newState);
  }
  handleItemChange(i, k, v) {
    var tmpPodcast = this.state.podcast;
    tmpPodcast.items[i][k] = v;
    this.setState({podcast: tmpPodcast});
  }
  newItem() {
    var DATE_RFC2822 = "ddd, DD MMM YYYY HH:mm:ss ZZ";
    var newItem = {
      title: '',
      description: '',
      author: '',
      duration: 0.0,
      subtitle: '',
      pubdate: moment().format(DATE_RFC2822),
      filesize: 0,
      filetype: '',
      fileurl: ''
    };
    var tmpPodcast = this.state.podcast;
    tmpPodcast.items = [newItem].concat(tmpPodcast.items);
    this.setState({
      podcast: tmpPodcast,
      activeItem: 0
    });
  }
  deleteItem(i) {
    var tmpPodcast = this.state.podcast;
    tmpPodcast.items.splice(i, 1);
    this.setState({podcast: tmpPodcast});
  }
  setActiveItem(i) {
    this.setState({activeItem: i});
  }
  openSettingsDialog() {
    this.setState({settingsDialogOpen: true});
  }
  closeSettingsDialog() {
    this.setState({settingsDialogOpen: false});
  }
  publish() {
    syncRemote(
      this.props.directory,
      this.state.settings,
      this.props.setError,
      this.props.setSnackbar
    );
  }
  saveChanges() {
    podcastRender(this.props.directory,
      this.state.podcast,
      this.state.settings.prefixUrl);
    for (var i = 0; i < this.state.podcast.items.length; i++) {
      id3Tag(this.state.podcast, i, this.props.setSnackbar);
    }
  }
  render() {
    var outerThis = this;
    return (

            <RaisedButton
              label="Go Home"
              onTouchTap={this.props.goHome}
            />

            <RaisedButton
              label="Settings"
              onTouchTap={this.openSettingsDialog}
            />

          {this.state.settings.deploytype === 'none' ? null : (

              <RaisedButton
                label="Publish"
                onTouchTap={this.publish}
              />

          )}

            <RaisedButton
              label="Save Changes"
              onTouchTap={this.saveChanges}
            />

        <PodcastChannel {...this.props}
          podcast={this.state.podcast}
          settings={this.state.settings}
          handleChange={(k, v) => this.updateSubState('podcast', k, v)}
        />
        <PodcastSettings {...this.props}
          open={this.state.settingsDialogOpen}
          close={this.closeSettingsDialog}
          settings={this.state.settings}
          handleChange={(k, v) => this.updateSubState('settings', k, v)}
        />

Episodes:

              <RaisedButton
                label="New Episode"
                onTouchTap={this.newItem}
              />

          {
            this.state.podcast.items.map(function(item, i) {
              return (
                <PodcastItem key={i} {...outerThis.props}
                  episode={item}
                  settings={outerThis.state.settings}
                  handleChange={(k, v) => outerThis.handleItemChange(i, k, v)}
                  deleteItem={() => outerThis.deleteItem(i)}
                  expanded={outerThis.state.activeItem === i}
                  expand={() => outerThis.setActiveItem(i)}
                  collapse={() => outerThis.setActiveItem(-1)}
                />
              );
            })
          }

    );
  }
}
<|endoftext|>