<commit_before>var injector    = require( 'injector' )
  , Class       = injector.getInstance( 'Class' )
  , Promise     = require( 'bluebird' )
  , async       = require( 'async' )
  , util        = require( 'util' )
  , debuggr     = require( 'debug' )( 'Models' )
  , moduleLdr   = injector.getInstance( 'moduleLoader' )
  , models      = {};

module.exports = Class.extend(
/* @Static */
{
    // Either 'ORM' or 'ODM'
    type: 'ORM',

    // Get the model cache
    getDefinedModels: function() {
        return models;
    },

    // Behaviours you can use on models you define
    softDeletable: false,
    versionable: false,
    timeStampable: true,

    // The function you call to create a new model
    extend: function() {
        var extendingArgs = [].slice.call( arguments )
          , modelName = ( typeof extendingArgs[ 0 ] === 'string' ) ? extendingArgs.shift() : false
          , Static = ( extendingArgs.length === 2 ) ? extendingArgs.shift() : {}
          , Proto = extendingArgs.shift()
          , modelType = Static.type !== undefined ? Static.type : this.type
          , moduleName = 'clever-' + modelType.toLowerCase()
          , driver = null
          , model = null
          , debug = null;

        extendingArgs = [ Static, Proto ];

        if ( !modelName ) {
            var Reg = new RegExp( '\\)?.*\\(([^\\[\\:]+).*\\)', 'ig' )
              , stack = new Error().stack.split( '\n' )
              , file = stack.splice( 2, 1 );

            if ( Reg.test( file ) ) {
                modelName = RegExp.$1.split( path.sep ).pop().replace( '.js', '' );
            } else {
                throw new Error( 'Unable to determine model name.' );
            }
        }

        debug = function( msg ) {
            debuggr( modelName + 'Model: ' + msg );
        };

        if ( models[ modelName ] !== undefined ) {
            debuggr( modelName + 'Model: Returning model class from the cache...' );
            return models[ modelName ];
        }

        debuggr( [ modelName + 'Model: Defining model using', modelType, 'type...' ].join( ' ' ) );

        Static._name = modelName;
        Static.type = modelType;

        debuggr( modelName + 'Model: Checking to see if the driver is installed and enabled...' );
        if ( moduleLdr.moduleIsEnabled( moduleName ) !== true ) {
            throw new Error( [ 'To use type', modelType, 'on your', modelName, 'model you need to enable the', moduleName, 'module!' ].join( ' ' ) );
        } else {
            Static._driver = driver = injector.getInstance( modelType.toLowerCase() === 'orm' ? 'cleverOrm' : 'cleverOdm' );
        }

        debug( 'Defining models this.debug() helper...' );
        Proto.debug = Static.debug = function( msg ) {
            driver.debug( modelName + 'Model: ' + msg );
        };

        debug( 'Checking for defined getters and setters...' );

        if ( Proto.getters !== undefined ) {
            Static._getters = Proto.getters;
            delete Proto.getters;
        }

        if ( Proto.setters !== undefined ) {
            Static._setters = Proto.setters;
            delete Proto.setters;
        }

        debug( 'Defining schema...' );
        Object.keys( Proto ).forEach( this.callback( 'getSchemaFromProto', Proto, Static ) );

        debug( 'Setting up behaviours...' );
        [ 'softDeletable', 'versionable', 'timeStampable' ].forEach(function( behaviour ) {

            Static[ behaviour ] = Static[ behaviour ] !== undefined ? Static[ behaviour ] : this[ behaviour ];

        }.bind( this ));

        // Add the accessedAt field if we are timeStampable
        if ( Static.timeStampable === true ) {
            debug( 'Defining timeStampable behaviour schema fields...' );

            Static._schema.createdAt = Date;
            Static._schema.updatedAt = Date;
        }

        // Add the deletedAt field if we are softDeletable
        if ( !!Static.softDeletable ) {
            debug( 'Defining softDeletable behaviour schema fields...' );

            if ( modelType.toLowerCase() === 'odm' ) {
                Static._schema.deletedAt = {
                    type: Date,
                    default: null
                };
            }
        }

        debug( 'Generating native model using driver.parseModelSchema()...' );
        Static._model = driver.parseModelSchema( Static, Proto );

        debug( 'Creating model class...' );
        model = this._super.apply( this, extendingArgs );

        models[ modelName ] = model;
        return model;
    },

    // Private function used to build _schema so it can be passed to the _driver for schema creation
    getSchemaFromProto: function( Proto, Static, key ) {
        var prop = Proto[ key ];

        if ( prop === Date || prop === String || prop instanceof String || prop === Number || prop === Boolean || ( typeof prop === 'object' ) ) {
            if ( typeof Static._schema !== 'object' ) {
                Static._schema = {};
            }

            if ( typeof Static._getters !== 'object' ) {
                Static._getters = {};
            }

            if ( typeof Static._setters !== 'object' ) {
                Static._setters = {};
            }

            Static._schema[ key ] = prop;
            Static._getters[ key ] = function() {
                if ( key === 'id' && Static.type.toLowerCase() === 'odm' ) {
                    return this._model._id;
                } else {
                    return this._model[ key ];
                }
            };
            Static._setters[ key ] = function( val ) {
                this._model[ key ] = val;
            };

            delete Proto[ key ];
        }
    },

    find: function( id ) {
        var modelType = this.type.toUpperCase ? this.type.toUpperCase() : this.type
          , isModel = !!this._model && this._model !== null
          , that = this
          , options = !/^[0-9a-fA-F]{24}$/.test( id ) && isNaN( id ) ? id : {};

        return new Promise(function( resolve, reject ) {

            // Configure options
            if ( !!id && ( /^[0-9a-fA-F]{24}$/.test( id ) || !isNaN( id ) ) ) {
                if ( modelType === 'ODM' ) {
                    try {
                        options._id = that._driver.mongoose.Types.ObjectId.fromString( id );
                    } catch( err ) {
                        resolve( null );
                        return;
                    }
                } else {
                    options.id = id;
                }
            }

            // Make sure we have either an id or options to find by models with
            if ( !!isModel && !id && !options ) {
                reject( [ 'You must specify either an id or an object containing fields to find a', that._name ].join( ' ' ) );
                return;
            }

            async.waterfall(
                [
                    function validateModel( callback ) {
                        callback( !!isModel ? null : 'You cannot call Model.find() directly on the model class.' );
                    },

                    function softDeletable( callback ) {
                        if ( !!that.softDeletable ) {
                            options.deletedAt = null;
                        }
                        callback( null );
                    },

                    function findModel( callback ) {
                        that.debug( 'find(' + ( typeof options === 'object' ? JSON.stringify( options ) : options ) + ')' );

                        if ( modelType === 'ORM' ) {

                            that._model
                                .find( { where: options } )
                                .success( that.callback( callback, null ) )
                                .error( callback );

                        } else if ( modelType === 'ODM' ) {

                            that._model.findOne( options, callback );

                        } else {

                            callback( 'Unsupported Model Type(' + modelType + ')' );

                        }
                    }
                ],
                function returnFoundModel( err, _model ) {
                    if ( !err ) {
                        resolve( !!_model && _model !== null ? new that( _model ) : null );
                    } else {
                        reject( [ 'Unable to find the', that._name, 'model because of', err ].join( ' ' ) );
                    }
                }
            );
        });
    },

    findById: function( id ) {
        return this.find( id );
    },

    findOne: function( id ) {
        return this.find( id );
    },

    findAll: function( options ) {
        var modelType = this.type.toUpperCase ? this.type.toUpperCase() : this.type
          , isModel = !!this._model && this._model !== null
          , that = this;

        return new Promise(function( resolve, reject ) {
            async.waterfall(
                [
                    function validateModel( callback ) {
                        callback( !!isModel ? null : 'You cannot call Model.findAll() directly on the model class.' );
                    },

                    function softDeletable( callback ) {
                        if ( !!that.softDeletable ) {
                            options.deletedAt = null;
                        }
                        callback( null );
                    },

                    function findModels( callback ) {
                        that.debug( 'findAll(' + ( typeof options === 'object' ? JSON.stringify( options ) : options ) + ')' );

                        if ( modelType === 'ORM' ) {

                            that._model
                                .findAll( { where: options } )
                                .success( that.callback( callback, null ) )
                                .error( callback );

                        } else if ( modelType === 'ODM' ) {

                            that._model.find( options, callback );

                        } else {

                            callback( 'Unsupported Model Type(' + modelType + ')' );

                        }
                    }
                ],
                function returnFoundModels( err, _models ) {
                    var models = [];

                    _models = _models instanceof Array ? _models : [ _models ];

                    if ( !err ) {
                        _models.forEach(function( model ) {
                            if ( model !== null ) {
                                models.push( new that( model ) );
                            }
                        });

                        resolve( models );
                    } else {
                        reject( [ 'Unable to find any', that._name, 'models because of', err ].join( ' ' ) );
                    }
                }
            );
        });
    },

    create: function( data ) {
        var modelType = this.type.toUpperCase ? this.type.toUpperCase() : this.type
          , isModel = !!this._model && this._model !== null
          , that = this;

        return new Promise(function( resolve, reject ) {
            async.waterfall(
                [
                    function validateModel( callback ) {
                        callback( !!isModel ? null : 'You cannot call Model.create() directly on the model class.' );
                    },

                    function hasValidData( callback ) {
                        if ( !!data && !data.id && Object.keys( data ).length ) {
                            callback( null );
                        } else {
                            reject( 'Invalid data provided to Model.create(' + util.inspect( data ) + ')' );
                        }
                    },

                    function timeStampable( callback ) {
                        if ( modelType === 'ODM' && !!that.timeStampable ) {
                            data.createdAt = Date.now();
                            data.updatedAt = Date.now();
                        }
                        callback( null );
                    },

                    function createModel( callback ) {
                        that.debug( 'create(' + JSON.stringify( data ) + ')' );

                        if ( modelType === 'ORM' ) {

                            that._model
                                .create( data )
                                .success( that.callback( callback, null ) )
                                .error( callback )

                        } else if ( modelType === 'ODM' ) {

                            that._model.create( data, callback );

                        } else {

                            callback( 'Unsupported Model Type(' + modelType + ')' );

                        }
                    }
                ],
                function returnCreatedModel( err, _model ) {
                    if ( !err ) {
                        resolve( new that( _model instanceof Array ? _model[ 0 ] : _model ) );
                    } else {
                        reject( [ 'Unable to create', that._name, 'because of', err ].join( ' ' ) );
                    }
                }
            );
        });
    }
},
/* @Prototype */
{
    _model: null,

    setup: function( model ) {
        this._setModel( model );
        Object.keys( this.Class._getters ).forEach( this.proxy( '_setupProperty' ) );
    },

    _setupProperty: function( propName ) {
        Object.defineProperty( this, propName, {
            get: this.proxy( this.Class._getters[ propName ] ),
            set: this.proxy( this.Class._setters[ propName ] ),
            enumerable: true
        });
    },

    _setModel: function( _model ) {
        this._model = _model;
    },

    map: function() {
        return this._model.map.apply( this, arguments );
    },

    save: function() {
        var that = this;

        this.debug( 'save(' + JSON.stringify( this ) + ')' );

        return new Promise( function( resolve, reject ) {
            if ( that.Class.type.toLowerCase() === 'orm' ) {

                that._model
                    .save()
                    .success( function( _model ) {
                        that._setModel( _model );
                        resolve( that );
                    })
                    .error( reject )

            } else if ( that.Class.type.toLowerCase() === 'odm' ) {

                if ( !!that.Class.timeStampable ) {
                    that._model.updatedAt = Date.now();
                }

                that._model
                    .save( function( err, _model ) {
                        if ( !err ) {
                            that._setModel( _model );
                            resolve( that );
                        } else {
                            reject( err );
                        }
                    });

            } else {
                reject( 'Unsupported Model Type' );
            }
        });
    },

    destroy: function() {
        var that = this;

        this.debug( 'destroy(' + JSON.stringify( this ) + ')' );

        return new Promise( function( resolve, reject ) {
            if ( that.Class.type.toLowerCase() === 'orm' ) {

                that._model
                    .destroy()
                    .success( function() {
                        delete that._model;
                        resolve( {} );
                    })
                    .error( reject )

            } else if ( that.Class.type.toLowerCase() === 'odm' ) {

                if ( !!that.Class.softDeletable ) {
                    // Perform softDelete
                    that._model.deletedAt = Date.now();
                    that._model
                        .save( function( err ) {
                            if ( !err ) {
                                delete that._model;
                                resolve( {} );
                            } else {
                                reject( err );
                            }
                        });

                } else {
                    that._model.remove(function( err ) {
                        if ( !err ) {
                            delete that._model;
                            resolve( {} );
                        } else {
                            reject( err );
                        }
                    });
                }

            } else {
                reject( 'Unsupported Model Type' );
            }
        });
    },

    toJSON: function() {
        var that = this
          , json;

        if ( this.Class.type === 'ORM' ) {
            json = this._model.values;
        } else {
            json = this._model.toObject();

            // Add in the id if we have it defined
            if ( !!json._id ) {
                json.id = json._id;
                delete json._id;
            }
        }

        // Add in getters
        Object.keys( this.Class._getters ).forEach( function( getterName ) {
            if ( json[ getterName ] === undefined ) {
                json[ getterName ] = that[ getterName ];
            }
        });

        return json;
    }
});<commit_msg>feat(models): Code needed to support expanded model data types<commit_after>var injector    = require( 'injector' )
  , Class       = injector.getInstance( 'Class' )
  , Promise     = require( 'bluebird' )
  , async       = require( 'async' )
  , util        = require( 'util' )
  , debuggr     = require( 'debug' )( 'Models' )
  , moduleLdr   = injector.getInstance( 'moduleLoader' )
  , models      = {};

module.exports = Class.extend(
/* @Static */
{
    // Either 'ORM' or 'ODM'
    type: 'ORM',

    // Get the model cache
    getDefinedModels: function() {
        return models;
    },

    // Behaviours you can use on models you define
    softDeletable: false,
    versionable: false,
    timeStampable: true,

    Types: {
        ENUM: function() {
            return {
                type: this.ENUM,
                values: [].slice.call( arguments ),
                toString: function() {
                    return this.type;
                }
            }
        }
    },

    // The function you call to create a new model
    extend: function() {
        var extendingArgs = [].slice.call( arguments )
          , modelName = ( typeof extendingArgs[ 0 ] === 'string' ) ? extendingArgs.shift() : false
          , Static = ( extendingArgs.length === 2 ) ? extendingArgs.shift() : {}
          , Proto = extendingArgs.shift()
          , modelType = Static.type !== undefined ? Static.type : this.type
          , moduleName = 'clever-' + modelType.toLowerCase()
          , driver = null
          , model = null
          , debug = null;

        extendingArgs = [ Static, Proto ];

        if ( !modelName ) {
            var Reg = new RegExp( '\\)?.*\\(([^\\[\\:]+).*\\)', 'ig' )
              , stack = new Error().stack.split( '\n' )
              , file = stack.splice( 2, 1 );

            if ( Reg.test( file ) ) {
                modelName = RegExp.$1.split( path.sep ).pop().replace( '.js', '' );
            } else {
                throw new Error( 'Unable to determine model name.' );
            }
        }

        debug = function( msg ) {
            debuggr( modelName + 'Model: ' + msg );
        };

        if ( models[ modelName ] !== undefined ) {
            debuggr( modelName + 'Model: Returning model class from the cache...' );
            return models[ modelName ];
        }

        debuggr( [ modelName + 'Model: Defining model using', modelType, 'type...' ].join( ' ' ) );

        Static._name = modelName;
        Static.type = modelType;

        debuggr( modelName + 'Model: Checking to see if the driver is installed and enabled...' );
        if ( moduleLdr.moduleIsEnabled( moduleName ) !== true ) {
            throw new Error( [ 'To use type', modelType, 'on your', modelName, 'model you need to enable the', moduleName, 'module!' ].join( ' ' ) );
        } else {
            Static._driver = driver = injector.getInstance( modelType.toLowerCase() === 'orm' ? 'cleverOrm' : 'cleverOdm' );
        }

        debug( 'Defining models this.debug() helper...' );
        Proto.debug = Static.debug = function( msg ) {
            driver.debug( modelName + 'Model: ' + msg );
        };

        debug( 'Checking for defined getters and setters...' );

        if ( Proto.getters !== undefined ) {
            Static._getters = Proto.getters;
            delete Proto.getters;
        }

        if ( Proto.setters !== undefined ) {
            Static._setters = Proto.setters;
            delete Proto.setters;
        }

        debug( 'Defining schema...' );
        Object.keys( Proto ).forEach( this.callback( 'getSchemaFromProto', Proto, Static ) );

        debug( 'Setting up behaviours...' );
        [ 'softDeletable', 'versionable', 'timeStampable' ].forEach(function( behaviour ) {

            Static[ behaviour ] = Static[ behaviour ] !== undefined ? Static[ behaviour ] : this[ behaviour ];

        }.bind( this ));

        // Add the accessedAt field if we are timeStampable
        if ( Static.timeStampable === true ) {
            debug( 'Defining timeStampable behaviour schema fields...' );

            Static._schema.createdAt = Date;
            Static._schema.updatedAt = Date;
        }

        // Add the deletedAt field if we are softDeletable
        if ( !!Static.softDeletable ) {
            debug( 'Defining softDeletable behaviour schema fields...' );

            if ( modelType.toLowerCase() === 'odm' ) {
                Static._schema.deletedAt = {
                    type: Date,
                    default: null
                };
            }
        }

        debug( 'Generating native model using driver.parseModelSchema()...' );
        Static._model = driver.parseModelSchema( Static, Proto );

        debug( 'Creating model class...' );
        model = this._super.apply( this, extendingArgs );

        models[ modelName ] = model;
        return model;
    },

    // Private function used to build _schema so it can be passed to the _driver for schema creation
    getSchemaFromProto: function( Proto, Static, key ) {
        var prop = Proto[ key ];

        if ( prop === Date || prop === String || prop instanceof String || prop === Number || prop === Boolean || ( typeof prop === 'object' ) ) {
            if ( typeof Static._schema !== 'object' ) {
                Static._schema = {};
            }

            if ( typeof Static._getters !== 'object' ) {
                Static._getters = {};
            }

            if ( typeof Static._setters !== 'object' ) {
                Static._setters = {};
            }

            Static._schema[ key ] = prop;
            Static._getters[ key ] = function() {
                if ( key === 'id' && Static.type.toLowerCase() === 'odm' ) {
                    return this._model._id;
                } else {
                    return this._model[ key ];
                }
            };
            Static._setters[ key ] = function( val ) {
                this._model[ key ] = val;
            };

            delete Proto[ key ];
        }
    },

    find: function( id ) {
        var modelType = this.type.toUpperCase ? this.type.toUpperCase() : this.type
          , isModel = !!this._model && this._model !== null
          , that = this
          , options = !/^[0-9a-fA-F]{24}$/.test( id ) && isNaN( id ) ? id : {};

        return new Promise(function( resolve, reject ) {

            // Configure options
            if ( !!id && ( /^[0-9a-fA-F]{24}$/.test( id ) || !isNaN( id ) ) ) {
                if ( modelType === 'ODM' ) {
                    try {
                        options._id = that._driver.mongoose.Types.ObjectId.fromString( id );
                    } catch( err ) {
                        resolve( null );
                        return;
                    }
                } else {
                    options.id = id;
                }
            }

            // Make sure we have either an id or options to find by models with
            if ( !!isModel && !id && !options ) {
                reject( [ 'You must specify either an id or an object containing fields to find a', that._name ].join( ' ' ) );
                return;
            }

            async.waterfall(
                [
                    function validateModel( callback ) {
                        callback( !!isModel ? null : 'You cannot call Model.find() directly on the model class.' );
                    },

                    function softDeletable( callback ) {
                        if ( !!that.softDeletable ) {
                            options.deletedAt = null;
                        }
                        callback( null );
                    },

                    function findModel( callback ) {
                        that.debug( 'find(' + ( typeof options === 'object' ? JSON.stringify( options ) : options ) + ')' );

                        if ( modelType === 'ORM' ) {

                            that._model
                                .find( { where: options } )
                                .success( that.callback( callback, null ) )
                                .error( callback );

                        } else if ( modelType === 'ODM' ) {

                            that._model.findOne( options, callback );

                        } else {

                            callback( 'Unsupported Model Type(' + modelType + ')' );

                        }
                    }
                ],
                function returnFoundModel( err, _model ) {
                    if ( !err ) {
                        resolve( !!_model && _model !== null ? new that( _model ) : null );
                    } else {
                        reject( [ 'Unable to find the', that._name, 'model because of', err ].join( ' ' ) );
                    }
                }
            );
        });
    },

    findById: function( id ) {
        return this.find( id );
    },

    findOne: function( id ) {
        return this.find( id );
    },

    findAll: function( options ) {
        var modelType = this.type.toUpperCase ? this.type.toUpperCase() : this.type
          , isModel = !!this._model && this._model !== null
          , that = this;

        return new Promise(function( resolve, reject ) {
            async.waterfall(
                [
                    function validateModel( callback ) {
                        callback( !!isModel ? null : 'You cannot call Model.findAll() directly on the model class.' );
                    },

                    function softDeletable( callback ) {
                        if ( !!that.softDeletable ) {
                            options.deletedAt = null;
                        }
                        callback( null );
                    },

                    function findModels( callback ) {
                        that.debug( 'findAll(' + ( typeof options === 'object' ? JSON.stringify( options ) : options ) + ')' );

                        if ( modelType === 'ORM' ) {

                            that._model
                                .findAll( { where: options } )
                                .success( that.callback( callback, null ) )
                                .error( callback );

                        } else if ( modelType === 'ODM' ) {

                            that._model.find( options, callback );

                        } else {

                            callback( 'Unsupported Model Type(' + modelType + ')' );

                        }
                    }
                ],
                function returnFoundModels( err, _models ) {
                    var models = [];

                    _models = _models instanceof Array ? _models : [ _models ];

                    if ( !err ) {
                        _models.forEach(function( model ) {
                            if ( model !== null ) {
                                models.push( new that( model ) );
                            }
                        });

                        resolve( models );
                    } else {
                        reject( [ 'Unable to find any', that._name, 'models because of', err ].join( ' ' ) );
                    }
                }
            );
        });
    },

    create: function( data ) {
        var modelType = this.type.toUpperCase ? this.type.toUpperCase() : this.type
          , isModel = !!this._model && this._model !== null
          , that = this;

        return new Promise(function( resolve, reject ) {
            async.waterfall(
                [
                    function validateModel( callback ) {
                        callback( !!isModel ? null : 'You cannot call Model.create() directly on the model class.' );
                    },

                    function hasValidData( callback ) {
                        if ( !!data && !data.id && Object.keys( data ).length ) {
                            callback( null );
                        } else {
                            reject( 'Invalid data provided to Model.create(' + util.inspect( data ) + ')' );
                        }
                    },

                    function timeStampable( callback ) {
                        if ( modelType === 'ODM' && !!that.timeStampable ) {
                            data.createdAt = Date.now();
                            data.updatedAt = Date.now();
                        }
                        callback( null );
                    },

                    function createModel( callback ) {
                        that.debug( 'create(' + JSON.stringify( data ) + ')' );

                        if ( modelType === 'ORM' ) {

                            that._model
                                .create( data )
                                .success( that.callback( callback, null ) )
                                .error( callback )

                        } else if ( modelType === 'ODM' ) {

                            that._model.create( data, callback );

                        } else {

                            callback( 'Unsupported Model Type(' + modelType + ')' );

                        }
                    }
                ],
                function returnCreatedModel( err, _model ) {
                    if ( !err ) {
                        resolve( new that( _model instanceof Array ? _model[ 0 ] : _model ) );
                    } else {
                        reject( [ 'Unable to create', that._name, 'because of', err ].join( ' ' ) );
                    }
                }
            );
        });
    }
},
/* @Prototype */
{
    _model: null,

    setup: function( model ) {
        this._setModel( model );
        Object.keys( this.Class._getters ).forEach( this.proxy( '_setupProperty' ) );
    },

    _setupProperty: function( propName ) {
        Object.defineProperty( this, propName, {
            get: this.proxy( this.Class._getters[ propName ] ),
            set: this.proxy( this.Class._setters[ propName ] ),
            enumerable: true
        });
    },

    _setModel: function( _model ) {
        this._model = _model;
    },

    map: function() {
        return this._model.map.apply( this, arguments );
    },

    save: function() {
        var that = this;

        this.debug( 'save(' + JSON.stringify( this ) + ')' );

        return new Promise( function( resolve, reject ) {
            if ( that.Class.type.toLowerCase() === 'orm' ) {

                that._model
                    .save()
                    .success( function( _model ) {
                        that._setModel( _model );
                        resolve( that );
                    })
                    .error( reject )

            } else if ( that.Class.type.toLowerCase() === 'odm' ) {

                if ( !!that.Class.timeStampable ) {
                    that._model.updatedAt = Date.now();
                }

                that._model
                    .save( function( err, _model ) {
                        if ( !err ) {
                            that._setModel( _model );
                            resolve( that );
                        } else {
                            reject( err );
                        }
                    });

            } else {
                reject( 'Unsupported Model Type' );
            }
        });
    },

    destroy: function() {
        var that = this;

        this.debug( 'destroy(' + JSON.stringify( this ) + ')' );

        return new Promise( function( resolve, reject ) {
            if ( that.Class.type.toLowerCase() === 'orm' ) {

                that._model
                    .destroy()
                    .success( function() {
                        delete that._model;
                        resolve( {} );
                    })
                    .error( reject )

            } else if ( that.Class.type.toLowerCase() === 'odm' ) {

                if ( !!that.Class.softDeletable ) {
                    // Perform softDelete
                    that._model.deletedAt = Date.now();
                    that._model
                        .save( function( err ) {
                            if ( !err ) {
                                delete that._model;
                                resolve( {} );
                            } else {
                                reject( err );
                            }
                        });

                } else {
                    that._model.remove(function( err ) {
                        if ( !err ) {
                            delete that._model;
                            resolve( {} );
                        } else {
                            reject( err );
                        }
                    });
                }

            } else {
                reject( 'Unsupported Model Type' );
            }
        });
    },

    toJSON: function() {
        var that = this
          , json;

        if ( this.Class.type === 'ORM' ) {
            json = this._model.values;
        } else {
            json = this._model.toObject();

            // Add in the id if we have it defined
            if ( !!json._id ) {
                json.id = json._id;
                delete json._id;
            }
        }

        // Add in getters
        Object.keys( this.Class._getters ).forEach( function( getterName ) {
            if ( json[ getterName ] === undefined ) {
                json[ getterName ] = that[ getterName ];
            }
        });

        return json;
    }
});<|endoftext|>


<commit_before>window.addEventListener("load", function(){
  var ansi = {
    green:  '\033[32m',
    red:    '\033[31m',
    yellow: '\033[33m',
    none:   '\033[0m'
  };

  var puts = function(str){
    Ruby.puts(str);
  };

  var print = function(str){
    Ruby.print(str);
  };

  QUnit.testDone = function(result){
    puts("Results for: " + result.name);
    print(ansi.red   + result.failed + " failed, " + ansi.none);
    print(ansi.green + result.passed + " passed, " + ansi.none);
    print("Total " + result.total);
    puts("");

    Ichabod.exit();
  };
});<commit_msg>fixed stopping qunit test runner after first test<commit_after>window.addEventListener("load", function(){
  var ansi = {
    green:  '\033[32m',
    red:    '\033[31m',
    yellow: '\033[33m',
    none:   '\033[0m'
  };

  var puts = function(str){
    Ruby.puts(str);
  };

  var print = function(str){
    Ruby.print(str);
  };

  QUnit.testDone = function(result){
    puts("Results for: " + result.name);
    print(ansi.red   + result.failed + " failed, " + ansi.none);
    print(ansi.green + result.passed + " passed, " + ansi.none);
    print("Total " + result.total);
    puts("");
  };

  QUnit.done = function() {
      Ichabod.exit();
  };
});<|endoftext|>


<commit_before>// client.js - main client file that does most of the processing
'use strict';

var fs           = require('fs'),
    constants    = require('constants'),
    request      = require('request'),
    util         = require('util'),
    EventEmitter = require('events').EventEmitter,
    Client       = require('./client').Client,
    qs           = require('querystring'),
    async        = require('async'),
    path         = require('path'),
    flatten      = require('flatten'),
    pjson        = require('./../../package.json'),
    failCodes = {
      400: 'Bad Request',
      401: 'Not Authorized',
      403: 'Forbidden',
      404: 'Item not found',
      405: 'Method not Allowed',
      409: 'Conflict',
      422: 'Unprocessable Entity',   // zendesk sends this one back when you re-use an organization name
      429: 'Too Many Requests',
      500: 'Internal Server Error',
      503: 'Service Unavailable'
    },

Client = exports.Client = function (options) {
  this.options = options;
  this.userAgent = 'node-zendesk/' + pjson.version + ' (node/' + process.versions.node + ')';
  // Each client has its own cookies to isolate authentication between clients
  this._request = request.defaults({
    jar:      this.options.get('no-cookies') ? false : request.jar(),
    encoding: this.options.get('encoding') || null,
    timeout:  this.options.get('timeout')  || 240000,
    proxy:    this.options.get('proxy')    || null,
    secureOptions: constants.SSL_OP_NO_TLSv1_2
  });

  if (!this.jsonAPIName) {
    this.jsonAPIName = null;
  }
  if (!this.jsonAPIName2) {
    this.jsonAPIName2 = null;
  }
  if (!this.jsonAPIName3) {
    this.jsonAPIName3 = null;
  }

  if ('function' !== typeof this.options.get) {
    this.options.get = function (key) {
      return this[key];
    };
  }

};

util.inherits(Client, EventEmitter);

Client.prototype.request = function (method, uri) {
  var options, url, self = this, res, args = Array.prototype.slice.call(arguments),
      callback = args.pop(),
      body     = 'object' === typeof args[args.length - 1] && !Array.isArray(args[args.length - 1]) && args.pop(),
      auth     = this.options.get('password') ? ':' + this.options.get('password') : '/token:' + this.options.get('token'),
      encoded  = new Buffer(this.options.get('username') + auth).toString('base64'),
      proxy    = this.options.get('proxy'),
      useOAuth = this.options.get('oauth'),
      token    = this.options.get('token');

  url = assembleUrl(self, uri);

  if (options) { // is this ever used?
    self.options.headers = options;
  } else {
    self.options.headers = {
      'Content-Type': 'application/json',
      'Accept':       'application/json',
      'User-Agent':   self.userAgent
    };
  }

  if (useOAuth) {// token is an oauth token obtained from OAuth2
    self.options.headers.Authorization = 'Bearer ' + token;
  } else {// token is an API token obtained from the Zendesk Settings UI
    self.options.headers.Authorization = 'Basic ' + encoded;
  }

  self.options.uri = url;
  self.options.method = method || 'GET';

  if (body) {
    self.options.body = JSON.stringify(body);
  } else if ('GET' !== method && 'application/json' === self.options.headers['Content-Type']) {
    self.options.body = '{}';
  }

  if (proxy) {
    self.options.proxy = proxy;
  }

  self.emit('debug::request', self.options);

  return this._request(self.options, function (err, response, result) {
    requestCallback(self, err, response, result, callback);
  });
};

Client.prototype.requestAll = function (method, uri) {
  var args         = Array.prototype.slice.call(arguments),
      callback     = args.pop(),
      nextPage     = 'Not Null!',
      bodyList     = [],
      statusList   = [],
      responseList = [],
      resultList   = [],
      self         = this;

  return Client.prototype.request.apply(this, args.concat(function (error, status, body, response, result) {
    if (error) {
      return callback(error);
    }

    statusList.push(status);
    bodyList.push(body);
    responseList.push(response);
    resultList.push(result);
    nextPage = result.next_page;

    async.whilst(
      function () {return null !== nextPage && 'undefined' !==  typeof nextPage; },
      function (cb) {
        Client.prototype.request.apply(self, ['GET', nextPage, function (error, status, body, response, result) {
          if (error) {
            return cb(error);
          }

          statusList.push(status);
          bodyList.push(body);
          responseList.push(response);
          resultList.push(result);
          nextPage = result.next_page;
          cb(null);
        }]);
      },
      function (err) {
        if (err) {
          callback(err);
        } else {
          return callback(null, statusList, flatten(bodyList), responseList, resultList);
        }
      }
      );
  }));
};

Client.prototype.requestUpload = function (uri, file, callback) {
  var self     = this,
      out,
      auth     = this.options.get('password') ? ':' + this.options.get('password') : '/token:' + this.options.get('token'),
      encoded  = new Buffer(this.options.get('username') + auth).toString('base64'),
      useOAuth = this.options.get('oauth'),
      token    = this.options.get('token'),
      uploadOptions = self.options;

  self.options.uri = assembleUrl(self, uri);
  self.options.method = 'POST';

  self.options.headers = {};

  if (useOAuth) {
    self.options.headers.Authorization = 'Bearer ' + token;
  } else {
    self.options.headers.Authorization = 'Basic ' + encoded;
  }

  out = this._request(self.options, function (err, response, result) {
    requestCallback(self, err, response, result, callback);
  });

  fs.createReadStream(file).pipe(out); // pipe the file!

};

function checkRequestResponse(self, response, result, callback) {
  var statusCode, error, retryAfter, res;

  if (!result) { // should this really be an error?
    error = new Error('Zendesk returned an empty result');
    error.statusCode = 204;
    return callback(error);
  }
  try {
    statusCode = response.statusCode;
    res = JSON.parse(result);
  }
  catch (ex) {
     // Emit Errors
    self.emit('debug::error', {exception: ex, code: statusCode, request: self.options, result: result});
  }

  self.emit('debug::response', {statusCode: statusCode, result: result});

  retryAfter = response.headers['retry-after'];
  if (retryAfter) {
    error = new Error('Zendesk rate limits 200 requests per minute');
    error.statusCode = 429;
    error.result = retryAfter;
    return callback(error);
  }

  if (failCodes[statusCode]) {
    error = new Error('Zendesk Error (' + statusCode + '): ' + failCodes[statusCode]);
    error.statusCode = statusCode;
    error.result = result;
    return callback(error);
  }

  return callback(null, res);
}

function requestCallback(self, err, response, result, callback) {
  if (err) {
    return callback(err);
  }

  checkRequestResponse(self, response, result, function(err, res) {
    if (err) {
      return callback(err);
    }
    var body = null;
    if (res) {
      if (!body && null !== self.jsonAPIName) {
        body = res[(self.jsonAPIName.toString())];
      }
      if (!body && null !== self.jsonAPIName2) {
        body = res[(self.jsonAPIName2.toString())];
      }
      if (!body && null !== self.jsonAPIName3) {
        body = res[(self.jsonAPIName3.toString())];
      }

      if (!body) {
        body = res;
      }
    } else {
      body = '';
    }

    return callback(null, response.statusCode, body, response, res);
  });

}

function assembleUrl(self, uri) {
  var lastElement,
      params = '';

  if ('object' === typeof uri && Array.isArray(uri)) {
    lastElement = uri.pop();
    if (lastElement) {
      // we have received an object ex. {"sort_by":"id","sort_order":"desc"}
      if ('object' === typeof lastElement) {
        params = '?' + qs.stringify(lastElement);
      }
      // we have received a query string ex. '?sort_by=id&sort_order=desc'
      else if (0 === lastElement.toString().indexOf('?')) {
        params = lastElement;
      }
      else {
        uri.push(lastElement);
      }
    }
    return self.options.get('remoteUri') + '/' + uri.join('/') + '.json' + params;
  }
  else if ('string' === typeof uri && uri.indexOf(self.options.get('remoteUri')) === -1) {
    return self.options.get('remoteUri') + uri;
  }
  else {
    return uri;
  }
}
<commit_msg>Changed the data layout a little, as we were losing a little context<commit_after>// client.js - main client file that does most of the processing
'use strict';

var fs           = require('fs'),
    constants    = require('constants'),
    request      = require('request'),
    util         = require('util'),
    EventEmitter = require('events').EventEmitter,
    Client       = require('./client').Client,
    qs           = require('querystring'),
    async        = require('async'),
    path         = require('path'),
    flatten      = require('flatten'),
    pjson        = require('./../../package.json'),
    failCodes = {
      400: 'Bad Request',
      401: 'Not Authorized',
      403: 'Forbidden',
      404: 'Item not found',
      405: 'Method not Allowed',
      409: 'Conflict',
      422: 'Unprocessable Entity',   // zendesk sends this one back when you re-use an organization name
      429: 'Too Many Requests',
      500: 'Internal Server Error',
      503: 'Service Unavailable'
    },

Client = exports.Client = function (options) {
  this.options = options;
  this.userAgent = 'node-zendesk/' + pjson.version + ' (node/' + process.versions.node + ')';
  // Each client has its own cookies to isolate authentication between clients
  this._request = request.defaults({
    jar:      this.options.get('no-cookies') ? false : request.jar(),
    encoding: this.options.get('encoding') || null,
    timeout:  this.options.get('timeout')  || 240000,
    proxy:    this.options.get('proxy')    || null,
    secureOptions: constants.SSL_OP_NO_TLSv1_2
  });

  if (!this.jsonAPIName) {
    this.jsonAPIName = null;
  }
  if (!this.jsonAPIName2) {
    this.jsonAPIName2 = null;
  }
  if (!this.jsonAPIName3) {
    this.jsonAPIName3 = null;
  }

  if ('function' !== typeof this.options.get) {
    this.options.get = function (key) {
      return this[key];
    };
  }

};

util.inherits(Client, EventEmitter);

Client.prototype.request = function (method, uri) {
  var options, url, self = this, res, args = Array.prototype.slice.call(arguments),
      callback = args.pop(),
      body     = 'object' === typeof args[args.length - 1] && !Array.isArray(args[args.length - 1]) && args.pop(),
      auth     = this.options.get('password') ? ':' + this.options.get('password') : '/token:' + this.options.get('token'),
      encoded  = new Buffer(this.options.get('username') + auth).toString('base64'),
      proxy    = this.options.get('proxy'),
      useOAuth = this.options.get('oauth'),
      token    = this.options.get('token');

  url = assembleUrl(self, uri);

  if (options) { // is this ever used?
    self.options.headers = options;
  } else {
    self.options.headers = {
      'Content-Type': 'application/json',
      'Accept':       'application/json',
      'User-Agent':   self.userAgent
    };
  }

  if (useOAuth) {// token is an oauth token obtained from OAuth2
    self.options.headers.Authorization = 'Bearer ' + token;
  } else {// token is an API token obtained from the Zendesk Settings UI
    self.options.headers.Authorization = 'Basic ' + encoded;
  }

  self.options.uri = url;
  self.options.method = method || 'GET';

  if (body) {
    self.options.body = JSON.stringify(body);
  } else if ('GET' !== method && 'application/json' === self.options.headers['Content-Type']) {
    self.options.body = '{}';
  }

  if (proxy) {
    self.options.proxy = proxy;
  }

  self.emit('debug::request', self.options);

  return this._request(self.options, function (err, response, result) {
    requestCallback(self, err, response, result, callback);
  });
};

Client.prototype.requestAll = function (method, uri) {
  var args         = Array.prototype.slice.call(arguments),
      callback     = args.pop(),
      nextPage     = 'Not Null!',
      bodyList     = [],
      statusList   = [],
      responseList = [],
      resultList   = [],
      self         = this;

  return Client.prototype.request.apply(this, args.concat(function (error, status, body, response, result) {
    if (error) {
      return callback(error);
    }

    statusList.push(status);
    bodyList.push(body);
    responseList.push(response);
    resultList.push(result);
    nextPage = result.next_page;

    async.whilst(
      function () {return null !== nextPage && 'undefined' !==  typeof nextPage; },
      function (cb) {
        Client.prototype.request.apply(self, ['GET', nextPage, function (error, status, body, response, result) {
          if (error) {
            return cb(error);
          }

          statusList.push(status);
          bodyList.push(body);
          responseList.push(response);
          resultList.push(result);
          nextPage = result.next_page;
          cb(null);
        }]);
      },
      function (err) {
        if (err) {
          callback(err);
        } else {
          return callback(null, statusList, flatten(bodyList), responseList, resultList);
        }
      }
      );
  }));
};

Client.prototype.requestUpload = function (uri, file, callback) {
  var self     = this,
      out,
      auth     = this.options.get('password') ? ':' + this.options.get('password') : '/token:' + this.options.get('token'),
      encoded  = new Buffer(this.options.get('username') + auth).toString('base64'),
      useOAuth = this.options.get('oauth'),
      token    = this.options.get('token'),
      uploadOptions = self.options;

  self.options.uri = assembleUrl(self, uri);
  self.options.method = 'POST';

  self.options.headers = {};

  if (useOAuth) {
    self.options.headers.Authorization = 'Bearer ' + token;
  } else {
    self.options.headers.Authorization = 'Basic ' + encoded;
  }

  out = this._request(self.options, function (err, response, result) {
    requestCallback(self, err, response, result, callback);
  });

  fs.createReadStream(file).pipe(out); // pipe the file!

};

function checkRequestResponse(self, response, result, callback) {
  var statusCode, error, retryAfter, res;

  if (!result) { // should this really be an error?
    error = new Error('Zendesk returned an empty result');
    error.statusCode = 204;
    return callback(error);
  }
  try {
    statusCode = response.statusCode;
    res = JSON.parse(result);
  }
  catch (ex) {
     // Emit Errors
    self.emit('debug::error', {exception: ex, code: statusCode, request: self.options, result: result});
  }

  self.emit('debug::response', {statusCode: statusCode, result: result});

  retryAfter = response.headers['retry-after'];
  if (retryAfter) {
    error = new Error('Zendesk rate limits 200 requests per minute');
    error.statusCode = 429;
    error.result = result;
    error.retryAfter = retryAfter;
    return callback(error);
  }

  if (failCodes[statusCode]) {
    error = new Error('Zendesk Error (' + statusCode + '): ' + failCodes[statusCode]);
    error.statusCode = statusCode;
    error.result = result;
    error.retryAfter = null;
    return callback(error);
  }

  return callback(null, res);
}

function requestCallback(self, err, response, result, callback) {
  if (err) {
    return callback(err);
  }

  checkRequestResponse(self, response, result, function(err, res) {
    if (err) {
      return callback(err);
    }
    var body = null;
    if (res) {
      if (!body && null !== self.jsonAPIName) {
        body = res[(self.jsonAPIName.toString())];
      }
      if (!body && null !== self.jsonAPIName2) {
        body = res[(self.jsonAPIName2.toString())];
      }
      if (!body && null !== self.jsonAPIName3) {
        body = res[(self.jsonAPIName3.toString())];
      }

      if (!body) {
        body = res;
      }
    } else {
      body = '';
    }

    return callback(null, response.statusCode, body, response, res);
  });

}

function assembleUrl(self, uri) {
  var lastElement,
      params = '';

  if ('object' === typeof uri && Array.isArray(uri)) {
    lastElement = uri.pop();
    if (lastElement) {
      // we have received an object ex. {"sort_by":"id","sort_order":"desc"}
      if ('object' === typeof lastElement) {
        params = '?' + qs.stringify(lastElement);
      }
      // we have received a query string ex. '?sort_by=id&sort_order=desc'
      else if (0 === lastElement.toString().indexOf('?')) {
        params = lastElement;
      }
      else {
        uri.push(lastElement);
      }
    }
    return self.options.get('remoteUri') + '/' + uri.join('/') + '.json' + params;
  }
  else if ('string' === typeof uri && uri.indexOf(self.options.get('remoteUri')) === -1) {
    return self.options.get('remoteUri') + uri;
  }
  else {
    return uri;
  }
}
<|endoftext|>


<commit_before><commit_msg>Implement CLI plugin<commit_after>import append from './index';

export default function (...strings) {
  return append(strings.join(' '));
}
<|endoftext|>


<commit_before>var CloudCmd, Util, DOM;

(function(CloudCmd, Util, DOM) {
    'use strict';

    CloudCmd.Config = ConfigProto;

    function ConfigProto() {
        var Loading     = true,
            Key         = CloudCmd.Key,
            Images      = DOM.Images,
            Events      = DOM.Events,
            showLoad    = Images.showLoad.bind(DOM, {
                top: true
            }),
            INPUT       = 'INPUT',
            CONFIG,
            TEMPLATE,
            Notify      = DOM.Notify,
            Config      = this;

        function init(pCallBack) {
            Loading     = true;

            showLoad();
            Util.loadOnLoad([
                CloudCmd.View,
                function(callback) {
                    Loading = false;
                    Util.exec(callback);
                },
                Config.show
            ]);
        }

        this.show               = function() {
            var funcs = [
                getTemplate,
                cssLoad
            ];

            if (!Loading) {
                showLoad();
                Util.asyncCall(funcs, fillTemplate);
            }
        };

        function cssLoad(callback) {
            DOM.cssLoad({
                src : '/css/config.css',
                func: Util.retExec(callback)
            });
        }

        function getTemplate(callback) {
            Util.ifExec(TEMPLATE, callback, function (execCall) {
                    DOM.ajax({
                    url     : '/html/config.html',
                    success :  function(data) {
                        TEMPLATE = data;
                        execCall();
                    },
                    error   : Images.showError
                });

            });
        }

        function fillTemplate() {
            CloudCmd.getConfig(function(config) {
                var i, n, div, data, li, param, obj;

                CONFIG  = config;
                obj     = Util.copyObj(CONFIG);
                changeConfig(obj);
                data    = Util.render(TEMPLATE, obj);
                div     = DOM.anyload({
                    name        : 'div',
                    className   : 'config',
                    inner       : data.toString()
                });

                li      = DOM.getByTag(INPUT, div);
                n       = li.length;

                for (i = 0; i < n; i++) {
                    param = li[i];
                    Events.add('change', onChange, param);
                    Events.addKey(key, param);
                }

                Images.hideLoad();
                CloudCmd.View.show(div, null, {
                    autoSize: true
                });
            });
        }

        this.hide               = function() {
            CloudCmd.View.hide();
        };

        function changeConfig(config) {
            var name;

            for (name in config)
                if (Util.isBoolean(config[name]))
                    config[name] = setState(config[name]);
        }

        function setState(state) {
            var ret = "";

            if (state)
                ret = " checked";

            return ret;
        }

        function onChange(event) {
            var data,
                obj     = {},
                el      = event.target,
                name    = el.id,
                type    = el.type;

            switch(type) {
            case 'checkbox':
                data = el.checked;
                break;
            case 'number':
                data = el.value - 0;
                break;
            default:
                data = el.value;
                break;
            }

            obj[name] = data;

            if (name === 'notifications') {
                if (data && !Notify.check())
                    Notify.request();
            }

            CONFIG[name] = data;
            CloudCmd.setConfig(CONFIG);

            DOM.RESTful.config(obj);
        }

        function key(event) {
            var keyCode = event.keyCode,
                ESC     = Key.ESC;

            if (keyCode === ESC)
                Config.hide();
        }

        init();
    }

})(CloudCmd, Util, DOM);
<commit_msg>feature(config) fillTemplate: li -> input, add focus<commit_after>var CloudCmd, Util, DOM;

(function(CloudCmd, Util, DOM) {
    'use strict';

    CloudCmd.Config = ConfigProto;

    function ConfigProto() {
        var Loading     = true,
            Key         = CloudCmd.Key,
            Images      = DOM.Images,
            Events      = DOM.Events,
            showLoad    = Images.showLoad.bind(DOM, {
                top: true
            }),
            INPUT       = 'INPUT',
            CONFIG,
            TEMPLATE,
            Notify      = DOM.Notify,
            Config      = this;

        function init(pCallBack) {
            Loading     = true;

            showLoad();
            Util.loadOnLoad([
                CloudCmd.View,
                function(callback) {
                    Loading = false;
                    Util.exec(callback);
                },
                Config.show
            ]);
        }

        this.show               = function() {
            var funcs = [
                getTemplate,
                cssLoad
            ];

            if (!Loading) {
                showLoad();
                Util.asyncCall(funcs, fillTemplate);
            }
        };

        function cssLoad(callback) {
            DOM.cssLoad({
                src : '/css/config.css',
                func: Util.retExec(callback)
            });
        }

        function getTemplate(callback) {
            Util.ifExec(TEMPLATE, callback, function (execCall) {
                    DOM.ajax({
                    url     : '/html/config.html',
                    success :  function(data) {
                        TEMPLATE = data;
                        execCall();
                    },
                    error   : Images.showError
                });

            });
        }

        function fillTemplate() {
            CloudCmd.getConfig(function(config) {
                var i, n, div, data, inputs, input, inputFirst,
                    focus, obj;

                CONFIG  = config;
                obj     = Util.copyObj(CONFIG);
                changeConfig(obj);
                data    = Util.render(TEMPLATE, obj);
                div     = DOM.anyload({
                    name        : 'div',
                    className   : 'config',
                    inner       : data.toString()
                });

                inputs      = DOM.getByTag(INPUT, div);
                inputFirst  = inputs[0];

                if (inputFirst)
                    focus   = inputFirst.focus.bind(inputFirst);

                n           = inputs.length;
                for (i = 0; i < n; i++) {
                    input = inputs[i];
                    Events.add('change', onChange, input);
                    Events.addKey(key, input);
                }

                Images.hideLoad();
                CloudCmd.View.show(div, focus, {
                    autoSize: true
                });
            });
        }

        this.hide               = function() {
            CloudCmd.View.hide();
        };

        function changeConfig(config) {
            var name;

            for (name in config)
                if (Util.isBoolean(config[name]))
                    config[name] = setState(config[name]);
        }

        function setState(state) {
            var ret = "";

            if (state)
                ret = " checked";

            return ret;
        }

        function onChange(event) {
            var data,
                obj     = {},
                el      = event.target,
                name    = el.id,
                type    = el.type;

            switch(type) {
            case 'checkbox':
                data = el.checked;
                break;
            case 'number':
                data = el.value - 0;
                break;
            default:
                data = el.value;
                break;
            }

            obj[name] = data;

            if (name === 'notifications') {
                if (data && !Notify.check())
                    Notify.request();
            }

            CONFIG[name] = data;
            CloudCmd.setConfig(CONFIG);

            DOM.RESTful.config(obj);
        }

        function key(event) {
            var keyCode = event.keyCode,
                ESC     = Key.ESC;

            if (keyCode === ESC)
                Config.hide();
        }

        init();
    }

})(CloudCmd, Util, DOM);
<|endoftext|>


<commit_before>(function (webgl) {

    /**
     *
     * @param {GLContext} context
     * @param shaderInfo
     * @param {HTMLScriptElement} node
     * @extends AbstractShaderComposer
     * @constructor
     */
    var JSShaderComposer = function(context, shaderInfo, node) {
        webgl.AbstractShaderComposer.call(this, context, shaderInfo);

        if (!window.Shade)
            throw new Error("Shade.js not found");

        this.context = context;

        this.shaderURL = null;
        /** @type string*/
        this.sourceTemplate = null;

        /**
         * @private
         * @type {Array.<string>}
         */
        this.extractedParams = [];

        /**
         * @private
         * @type {Xflow.ComputeRequest|null}
         */
        this.request = null;

        this.setShaderInfo(shaderInfo);
    };

    JSShaderComposer.convertEnvName = function(name){
        return ("_env_" + name).replace(/_+/g, "_");
    }

    JSShaderComposer.convertSysName = function(name){
        return name;
    }

    XML3D.createClass(JSShaderComposer, webgl.AbstractShaderComposer, {
        setShaderInfo: function(shaderInfo) {
            this.sourceTemplate = shaderInfo.getScriptCode();
            try{
                this.extractedParams = Shade.extractParameters(this.sourceTemplate,
                    {implementation: "xml3d-glsl-forward"}).shaderParameters;
                // FIXME: Shader.js should always request position (in case
            }
            catch(e){
                // We ignore errors here. They will reoccur when updating connected mesh closures
                this.extractedParams = [];
            }
            if(this.extractedParams.indexOf("position") == -1) this.extractedParams.push("position");

            // The composer is interested in changes of all possible shader parameters (extracted)
            // the instances (closures) will only set those, that occur in the instance
            if (this.extractedParams.length) {
                this.updateRequest(shaderInfo.getData());
            }
        },
        getRequestFields: function() {
            return this.extractedParams;
        },
        getShaderAttributes: function() {
            return { color: null, normal: null, texcoord: null };
        },
        createShaderClosure: function () {
            return new webgl.JSShaderClosure(this.context, this.sourceTemplate, this.extractedParams);
        },
        createObjectDataRequest: function(objectDataNode, callback){

            var vsConfig = new Xflow.VSConfig();
            var names = this.extractedParams.slice();
            //if(names.indexOf("position") == -1) names.push("position");

            for(var i = 0; i < names.length; ++i){
                var name = names[i];
                var xflowInfo = objectDataNode.getOutputChannelInfo(name);
                if(xflowInfo){
                    vsConfig.addAttribute(xflowInfo.type, name, true);
                }
            }
            var request = new Xflow.VertexShaderRequest(objectDataNode, vsConfig, callback);
            return request;
        },

        distributeObjectShaderData: function(objectRequest, attributeCallback, uniformCallback){
            var vertexShader = objectRequest.getVertexShader();
            var inputNames = vertexShader.inputNames;
            for(var i = 0; i < inputNames.length; ++i){
                var name = inputNames[i], entry = vertexShader.getInputData(name);
                if(vertexShader.isInputUniform(name))
                    uniformCallback(name, entry);
                else
                    attributeCallback(name, entry);
            }
            var outputNames = vertexShader.outputNames;
            for(var i = 0; i < outputNames.length; ++i){
                var name = outputNames[i];
                if(vertexShader.isOutputFragmentUniform(name)){
                    uniformCallback(vertexShader.getOutputSourceName(name), vertexShader.getUniformOutputData(name));
                }
            }
        }

    });

    webgl.JSShaderComposer = JSShaderComposer;

}(XML3D.webgl));
<commit_msg>Small hack to force position attributes to come first in vertex shader.<commit_after>(function (webgl) {

    /**
     *
     * @param {GLContext} context
     * @param shaderInfo
     * @param {HTMLScriptElement} node
     * @extends AbstractShaderComposer
     * @constructor
     */
    var JSShaderComposer = function(context, shaderInfo, node) {
        webgl.AbstractShaderComposer.call(this, context, shaderInfo);

        if (!window.Shade)
            throw new Error("Shade.js not found");

        this.context = context;

        this.shaderURL = null;
        /** @type string*/
        this.sourceTemplate = null;

        /**
         * @private
         * @type {Array.<string>}
         */
        this.extractedParams = [];

        /**
         * @private
         * @type {Xflow.ComputeRequest|null}
         */
        this.request = null;

        this.setShaderInfo(shaderInfo);
    };

    JSShaderComposer.convertEnvName = function(name){
        return ("_env_" + name).replace(/_+/g, "_");
    }

    JSShaderComposer.convertSysName = function(name){
        return name;
    }

    XML3D.createClass(JSShaderComposer, webgl.AbstractShaderComposer, {
        setShaderInfo: function(shaderInfo) {
            this.sourceTemplate = shaderInfo.getScriptCode();
            try{
                this.extractedParams = Shade.extractParameters(this.sourceTemplate,
                    {implementation: "xml3d-glsl-forward"}).shaderParameters;
                // FIXME: Shader.js should always request position (in case
            }
            catch(e){
                // We ignore errors here. They will reoccur when updating connected mesh closures
                this.extractedParams = [];
            }
            if(this.extractedParams.indexOf("position") == -1) this.extractedParams.push("position");

            // The composer is interested in changes of all possible shader parameters (extracted)
            // the instances (closures) will only set those, that occur in the instance
            if (this.extractedParams.length) {
                this.updateRequest(shaderInfo.getData());
            }
        },
        getRequestFields: function() {
            return this.extractedParams;
        },
        getShaderAttributes: function() {
            return { color: null, normal: null, texcoord: null };
        },
        createShaderClosure: function () {
            return new webgl.JSShaderClosure(this.context, this.sourceTemplate, this.extractedParams);
        },
        createObjectDataRequest: function(objectDataNode, callback){

            var vsConfig = new Xflow.VSConfig();
            var names = this.extractedParams.slice();
            //if(names.indexOf("position") == -1) names.push("position");
            vsConfig.addAttribute(Xflow.DATA_TYPE.FLOAT3, "position", true);
            for(var i = 0; i < names.length; ++i){
                var name = names[i];
                if(name == "position") continue;
                var xflowInfo = objectDataNode.getOutputChannelInfo(name);
                if(xflowInfo){
                    vsConfig.addAttribute(xflowInfo.type, name, true);
                }
            }
            var request = new Xflow.VertexShaderRequest(objectDataNode, vsConfig, callback);
            return request;
        },

        distributeObjectShaderData: function(objectRequest, attributeCallback, uniformCallback){
            var vertexShader = objectRequest.getVertexShader();
            var inputNames = vertexShader.inputNames;
            for(var i = 0; i < inputNames.length; ++i){
                var name = inputNames[i], entry = vertexShader.getInputData(name);
                if(vertexShader.isInputUniform(name))
                    uniformCallback(name, entry);
                else
                    attributeCallback(name, entry);
            }
            var outputNames = vertexShader.outputNames;
            for(var i = 0; i < outputNames.length; ++i){
                var name = outputNames[i];
                if(vertexShader.isOutputFragmentUniform(name)){
                    uniformCallback(vertexShader.getOutputSourceName(name), vertexShader.getUniformOutputData(name));
                }
            }
        }

    });

    webgl.JSShaderComposer = JSShaderComposer;

}(XML3D.webgl));
<|endoftext|>


<commit_before>var _    = require('lodash');
var util = require('util');

module.exports = commandChart;

function commandChart (token, options) {
  this.token = token;
  this.options = options;
}

commandChart.prototype.handle = function (req, res, cb) {
  /*
  {
    "text": "<http://chart.finance.yahoo.com...>",
    "attachments": [
      { "image_url": "<http://chart.finance.yahoo.com...>" }
    ]
  }
  */
  res.setHeader('content-type', 'application/json');
  //var chartUrl = "http://chart.finance.yahoo.com/t?lang=en-US&region=US&width=300&height=180&s=";
  var chartUrl = "http://www.marketwatch.com/kaavio.Webhost/charts/big.chart?&symb="
  var symbol = req.body.text;
  chartUrl += symbol;
  console.log(chartUrl);
  //var output = '<'+chartUrl+'>';
  //var output = '{"text":' + ' "<' + chartUrl + '>"' + ', "unfurl_link": true}';
  var output = '{';
  output += '"text":' + ' "' + 'Daily Chart for : ' + symbol + '" ,';
  output += '"attachments":' + '[';
  output += '{';
  output += '"image_url":' + ' "' + chartUrl + '" ,';
  output += '"unfurl_media": true';
  output += '}';
  output += ']';
  output += '}';
  cb(null, output);
};
<commit_msg>Trying a png image<commit_after>var _    = require('lodash');
var util = require('util');

module.exports = commandChart;

function commandChart (token, options) {
  this.token = token;
  this.options = options;
}

commandChart.prototype.handle = function (req, res, cb) {
  /*
  {
    "text": "<http://chart.finance.yahoo.com...>",
    "attachments": [
      { "image_url": "<http://chart.finance.yahoo.com...>" }
    ]
  }
  */
  res.setHeader('content-type', 'application/json');
  //var chartUrl = "http://chart.finance.yahoo.com/t?lang=en-US&region=US&width=300&height=180&s=";
  //var chartUrl = "http://www.marketwatch.com/kaavio.Webhost/charts/big.chart?&symb="
  var chartUrl = "http://www.pnglogo.com/wp-content/uploads/2015/10/Monsters-Inc.-James-P.-Sullivan-PNG-04025.png";
  var symbol = req.body.text;
  //chartUrl += symbol;
  console.log(chartUrl);
  //var output = '<'+chartUrl+'>';
  //var output = '{"text":' + ' "<' + chartUrl + '>"' + ', "unfurl_link": true}';
  var output = '{';
  output += '"text":' + ' "' + 'Daily Chart for : ' + symbol + '" ,';
  output += '"attachments":' + '[';
  output += '{';
  output += '"image_url":' + ' "' + chartUrl + '" ,';
  output += '"unfurl_media": true';
  output += '}';
  output += ']';
  output += '}';
  cb(null, output);
};
<|endoftext|>


<commit_before>var eppGenerator = require('./epp.js');
var eppConnection = require('./connection.js');
var parser = require('xml2json');

module.exports = function(registry, config) {
    var epp = eppGenerator(registry, config);
    var connection = eppConnection(config);
    return {
        "connection": connection,
        "state": "offline",
        "idle": function() {
            var self = this;
            this.state = 'idle';
            this.interval = setInterval(function() {
                self.command('hello', null, function() {
                    return {
                        "status": "OK"
                    };
                });
            },
            60000);
        },

        "resultOk": function(result) {
            if (result.status === 'OK') {
                return true;
            }
            return false;
        },

        // actions
        "login": function(data, callback) {
            var self = this;
            var xml = epp.login(data, 'test-1234');
            //console.log(xml);
            // send login
            this.connection.send(xml, callback);
            return xml;
        },

        "logout": function(callback) {
            var self = this;
            var xml = epp.logout('test-1234');
            console.log(xml);
            // send login
            this.connection.send(xml, callback);
            return xml;
        },

        "hello": function() {
            var self = this;
            var xml = epp.hello();
            console.log(xml);
            this.connection.send(xml, function(returnedXML) {
                var response = parser.toJson(returnedXML);
                console.log("Hello response: ", response);
                self.idle();
            });
        },

        "createContact": function(data, callback) {
            var self = this;
            var xml = epp.createContact(data, 'test-check-1234');
            this.connection.send(xml, callback);
        },
        "checkContact": function(data, callback) {
            var self = this;
            var xml = epp.checkContact(data, 'test-check-1234');
            this.connection.send(xml, callback);
        },
        "infoContact": function(data, callback) {
            var self = this;
            var xml = epp.infoContact(data, 'test-info-1234');
            this.connection.send(xml, callback);
        },
        "checkDomain": function(data, callback) {
            var self = this;
            var xml = epp.checkDomain(data, 'test-check-1234');
            this.connection.send(xml, callback);
        },
        "createDomain": function(data, callback) {
            var self = this;
            var xml = epp.createDomain(data, 'test-create-1234');
            this.connection.send(xml, callback);
        },
        "infoDomain": function(data, callback) {
            var self = this;
            var xml = epp.infoDomain(data, 'test-info-1234');
            this.connection.send(xml, callback);
        },

        "domain_command": function(command, data, callback) {
            console.log("Executing a " + command + " with: ", data);
            var result = callback();
            this.idle();
            return result;
        },
        "contact_command": function(command, data, callback) {
            console.log("Executing a " + command + " with: ", data);
            var result = callback();
            this.idle();
            return result;
        },
        "command": function(command, data, callback) {
            var self = this;
            var result;
            // TODO May have to check that we're not actually executing a
            // 'hello' command when this happens otherwise we could cause
            // a race condition.
            // stop hello loop
            if (this.interval) {
                clearInterval(this.interval);
            }

            // standard callbacks for all responses
            var returnCallback = function(returnedXML) {
                var response = parser.toJson(returnedXML);
                callback(response);
                self.idle();
            };

            this.state = 'command';
            var xml;
            try {
                switch (command) {
                case 'login':
                    this.login(data, returnCallback);
                    break;
                case 'logout':
                    this.logout(returnCallback);
                    break;
                case 'hello':
                    this.hello();
                    break;
                case 'checkDomain':
                    this.checkDomain(data, returnCallback);
                    break;
                case 'createDomain':
                    this.createDomain(data, returnCallback);
                    break;
                case 'infoDomain':
                case 'deleteDomain':
                    result = this.domain_command(command, data, returnCallback);
                    break;

                case 'checkContact':
                case 'infoContact':
                case 'createContact':
                    this.createContact(data, returnCallback);
                    break;
                case 'deleteContact':
                    result = this.contact_command(command, data, returnCallback);
                    break;
                default:
                    console.log("Unknown command: " + command);
                }

            } catch(e) {
                console.log("Encountered an error: ", e);
            }
            return result;
        },

    };
};

<commit_msg>strip out some of the epp cruft<commit_after>var eppGenerator = require('./epp.js');
var eppConnection = require('./connection.js');
var parser = require('xml2json');

module.exports = function(registry, config) {
    var epp = eppGenerator(registry, config);
    var connection = eppConnection(config);
    return {
        "connection": connection,
        "state": "offline",
        "idle": function() {
            var self = this;
            this.state = 'idle';
            this.interval = setInterval(function() {
                self.command('hello', null, function() {
                    return {
                        "status": "OK"
                    };
                });
            },
            60000);
        },

        "resultOk": function(result) {
            if (result.status === 'OK') {
                return true;
            }
            return false;
        },

        // actions
        "login": function(data, callback) {
            var self = this;
            var xml = epp.login(data, 'test-1234');
            //console.log(xml);
            // send login
            this.connection.send(xml, callback);
            return xml;
        },

        "logout": function(callback) {
            var self = this;
            var xml = epp.logout('test-1234');
            console.log(xml);
            // send login
            this.connection.send(xml, callback);
            return xml;
        },

        "hello": function() {
            var self = this;
            var xml = epp.hello();
            console.log(xml);
            this.connection.send(xml, function(returnedXML) {
                var response = parser.toJson(returnedXML);
                console.log("Hello response: ", response);
                self.idle();
            });
        },

        "createContact": function(data, callback) {
            var self = this;
            var xml = epp.createContact(data, 'test-check-1234');
            this.connection.send(xml, callback);
        },
        "checkContact": function(data, callback) {
            var self = this;
            var xml = epp.checkContact(data, 'test-check-1234');
            this.connection.send(xml, callback);
        },
        "infoContact": function(data, callback) {
            var self = this;
            var xml = epp.infoContact(data, 'test-info-1234');
            this.connection.send(xml, callback);
        },
        "checkDomain": function(data, callback) {
            var self = this;
            var xml = epp.checkDomain(data, 'test-check-1234');
            this.connection.send(xml, callback);
        },
        "createDomain": function(data, callback) {
            var self = this;
            var xml = epp.createDomain(data, 'test-create-1234');
            this.connection.send(xml, callback);
        },
        "infoDomain": function(data, callback) {
            var self = this;
            var xml = epp.infoDomain(data, 'test-info-1234');
            this.connection.send(xml, callback);
        },

        "domain_command": function(command, data, callback) {
            console.log("Executing a " + command + " with: ", data);
            var result = callback();
            this.idle();
            return result;
        },
        "contact_command": function(command, data, callback) {
            console.log("Executing a " + command + " with: ", data);
            var result = callback();
            this.idle();
            return result;
        },
        "processResponse": function (eppJson) {
            var epp = eppJson.epp;
            var eppResponse = epp.response;
            var returnData = {
                "result": eppResponse.result,
                "data": eppResponse.resData,
                "transactionId": eppResponse.trID
            };
            return returnData;
        },
        "command": function(command, data, callback) {
            var self = this;
            var result;
            // TODO May have to check that we're not actually executing a
            // 'hello' command when this happens otherwise we could cause
            // a race condition.
            // stop hello loop
            if (this.interval) {
                clearInterval(this.interval);
            }

            // standard callbacks for all responses
            var returnCallback = function(returnedXML) {
                var response = parser.toJson(returnedXML);
                var processedResponse = self.processResponse(response);
                callback(processedResponse);
                self.idle();
            };

            this.state = 'command';
            var xml;
            try {
                switch (command) {
                case 'login':
                    this.login(data, returnCallback);
                    break;
                case 'logout':
                    this.logout(returnCallback);
                    break;
                case 'hello':
                    this.hello();
                    break;
                case 'checkDomain':
                    this.checkDomain(data, returnCallback);
                    break;
                case 'createDomain':
                    this.createDomain(data, returnCallback);
                    break;
                case 'infoDomain':
                case 'deleteDomain':
                    result = this.domain_command(command, data, returnCallback);
                    break;

                case 'checkContact':
                case 'infoContact':
                case 'createContact':
                    this.createContact(data, returnCallback);
                    break;
                case 'deleteContact':
                    result = this.contact_command(command, data, returnCallback);
                    break;
                default:
                    console.log("Unknown command: " + command);
                }

            } catch(e) {
                console.log("Encountered an error: ", e);
            }
            return result;
        },

    };
};

<|endoftext|>


<commit_before>'use strict';

var watch       = module.exports = {};

// var node_fs = require('fs');
var node_path   = require('path');
var stares      = require('stares');
var cortex_json = require('read-cortex-json');
var pf          = require('cortex-package-files');
var handler   = require('cortex-command-errors');
var expand      = require('fs-expand');
var async       = require('async');
var ignore      = require('ignore');
var glob        = require('glob');

// @param {Object} options
// - cwd: {Array.<path>}
watch.run = function(options, callback) {
  this._createWatcher(options);
  options.stop ?
    this.unwatch(options, callback) :
    this.watch(options, callback);
};

// Creates a instance of `stares`
watch._createWatcher = function (options) {
  if (this.watcher) {
    return;
  }

  var self = this;
  this.watcher = stares({
    port: self.profile.get('watcher_rpc_port')

    // only emitted on master watcher process
  })
  .on('all', function(event, filepath) {
    var dir = node_path.dirname(filepath);
    // cortex commander will santitize filepath to the right root directory of the repo
    self._rebuild(options, dir);
  })
  // only emitted on master watcher process
  .on('message', function(msg) {
    if (~['watch', 'unwatch'].indexOf(msg.task)) {
      if (process.pid !== msg.pid) {
        self.logger.info('incomming {{cyan ' + msg.task + '}} request from process <' + msg.pid + '>.');
      }
    }
  })
  .on('listening', function () {
    self.master = true;
  })
  .on('connect', function () {
    if (!self.master) {
      self.logger.info('\nBuilding processes will be delegated to the >>{{cyan MASTER}}<< process.\n');
    }
  });
};

watch.watchFile = function (files, callback) {
  var self = this;
  this.watcher.watch(files, function(err) {
    if (err) {
      return callback(err);
    }

    self.logger.debug('watched', arguments);
    callback(null);
  });
};

watch.watch = function(options, callback) {
  var self = this;
  var init_build = options['init-build'];

  var profile = self.profile;
  var watched = profile.get('watched');

  async.each(options.cwd, function(cwd, done) {
    if (~watched.indexOf(cwd)) {
      self.logger.warn('The current directory has already been watched.');
      return done(null);
    }

    self.logger.info('{{cyan watching}}', cwd, '...');

    if (init_build) {
      self._rebuild(options, cwd, true);
    }

    self._get_files(cwd, function(err, files) {
      if (err) {
        return done(err);
      }
      self.watchFile(files, done);
    });

  }, callback);
};

watch._get_files = function(cwd, callback) {
  glob('**', {
    cwd: cwd,
    // include .dot files
    dot: true,
    // Adds a `/` character to directory matches
    mark: true

  }, function(err, files) {
    if (err) {
      return callback(err);
    }

    var filter = ignore()
    // #420
    // We only filter a few directories even if user ignores them in .gitignore,
    // because most of files will affect the final result of builder.
    .addPattern([
      '/node_modules',
      '/neurons'
    ])
    .createFilter();

    var REGEX_ENDS_BACKSLASH = /\/$/;
    files = files
      // Filter dirs
      .filter(function (file) {
        return !REGEX_ENDS_BACKSLASH.test(file);
      })
      .filter(filter);
    files = files.map(function (f) {
      return node_path.join(cwd, f);
    });

    callback(null, files);
  });
};

watch.unwatch = function(options, callback) {
  var self = this;
  var profile = self.profile;

  async.each(options.cwd, function(cwd, done) {
    self._get_files(cwd, function(err, files) {
      if (err) {
        return done(err);
      }

      self.watcher.unwatch(files, function(err, msg) {
        if (err) {
          (err);
        }

        self.logger.info(cwd, '{{cyan unwatched}}');
        self.logger.debug('unwatched', arguments);
        done(null);
      });
    });
  }, callback);
};

// There will be only one master process of `cortex watch`,
// so, it is ok to use a global variable of flags.
// Use this trick to prevent endless rebuilding
var locked = {};

watch._lock = function(id) {
  locked[id] = true;
};

watch._release = function(id) {
  locked[id] = false;
};

watch._is_locked = function(id) {
  return locked[id];
};

watch._rebuild = function(options, cwd, init) {
  var self = this;

  // If the current directory is already under building,
  // just ignore new tasks
  if (self._is_locked(cwd)) {
    return;
  }

  // lock it
  self._lock(cwd);

  // mock process.argv
  var argv = [
    '', '',
    'build',
    // Use --force to prevent grunt task interrupt the current process
    '--force',
    '--cwd', cwd
  ];

  var commander = self.commander;
  var parsed = commander.parse(argv, function(err, result, details) {
    if (err) {
      // #421
      setImmediate(function() {
        self._release(cwd);
      });
      return self.logger.info('{{red|bold ERR!}}', err);
    }

    var real_cwd = result.options.cwd;

    // if `cwd` is the same as `real_cwd`,
    // skip checking because we already have checked that
    if (real_cwd !== cwd) {
      if (self._is_locked(real_cwd)) {
        return;
      }

      // also lock the root directory of a repo
      self._lock(real_cwd);
    }

    if (init) {
      self.logger.info('{{cyan build}} the project when begins to watch...');
    } else {
      self.logger.info('file "' + cwd + '" changed,', '{{cyan rebuild project...}}');
    }

    // exec cortex.commands.build method
    commander.command('build', result.options, function(err) {
      setImmediate(function() {
        self._release(cwd);
        self._release(real_cwd);
      });

      if (err) {
        handler({
          logger: self.logger,
          harmony: true
        })(err);
      }
    });
  });
};
<commit_msg>command/watch: pass prerelease option to inner build command<commit_after>'use strict';

var watch       = module.exports = {};

// var node_fs = require('fs');
var node_path   = require('path');
var stares      = require('stares');
var cortex_json = require('read-cortex-json');
var pf          = require('cortex-package-files');
var handler   = require('cortex-command-errors');
var expand      = require('fs-expand');
var async       = require('async');
var ignore      = require('ignore');
var glob        = require('glob');

// @param {Object} options
// - cwd: {Array.<path>}
watch.run = function(options, callback) {
  this._createWatcher(options);
  options.stop ?
    this.unwatch(options, callback) :
    this.watch(options, callback);
};

// Creates a instance of `stares`
watch._createWatcher = function (options) {
  if (this.watcher) {
    return;
  }

  var self = this;
  this.watcher = stares({
    port: self.profile.get('watcher_rpc_port')

    // only emitted on master watcher process
  })
  .on('all', function(event, filepath) {
    var dir = node_path.dirname(filepath);
    // cortex commander will santitize filepath to the right root directory of the repo
    self._rebuild(options, dir);
  })
  // only emitted on master watcher process
  .on('message', function(msg) {
    if (~['watch', 'unwatch'].indexOf(msg.task)) {
      if (process.pid !== msg.pid) {
        self.logger.info('incomming {{cyan ' + msg.task + '}} request from process <' + msg.pid + '>.');
      }
    }
  })
  .on('listening', function () {
    self.master = true;
  })
  .on('connect', function () {
    if (!self.master) {
      self.logger.info('\nBuilding processes will be delegated to the >>{{cyan MASTER}}<< process.\n');
    }
  });
};

watch.watchFile = function (files, callback) {
  var self = this;
  this.watcher.watch(files, function(err) {
    if (err) {
      return callback(err);
    }

    self.logger.debug('watched', arguments);
    callback(null);
  });
};

watch.watch = function(options, callback) {
  var self = this;
  var init_build = options['init-build'];

  var profile = self.profile;
  var watched = profile.get('watched');

  async.each(options.cwd, function(cwd, done) {
    if (~watched.indexOf(cwd)) {
      self.logger.warn('The current directory has already been watched.');
      return done(null);
    }

    self.logger.info('{{cyan watching}}', cwd, '...');

    if (init_build) {
      self._rebuild(options, cwd, true);
    }

    self._get_files(cwd, function(err, files) {
      if (err) {
        return done(err);
      }
      self.watchFile(files, done);
    });

  }, callback);
};

watch._get_files = function(cwd, callback) {
  glob('**', {
    cwd: cwd,
    // include .dot files
    dot: true,
    // Adds a `/` character to directory matches
    mark: true

  }, function(err, files) {
    if (err) {
      return callback(err);
    }

    var filter = ignore()
    // #420
    // We only filter a few directories even if user ignores them in .gitignore,
    // because most of files will affect the final result of builder.
    .addPattern([
      '/node_modules',
      '/neurons'
    ])
    .createFilter();

    var REGEX_ENDS_BACKSLASH = /\/$/;
    files = files
      // Filter dirs
      .filter(function (file) {
        return !REGEX_ENDS_BACKSLASH.test(file);
      })
      .filter(filter);
    files = files.map(function (f) {
      return node_path.join(cwd, f);
    });

    callback(null, files);
  });
};

watch.unwatch = function(options, callback) {
  var self = this;
  var profile = self.profile;

  async.each(options.cwd, function(cwd, done) {
    self._get_files(cwd, function(err, files) {
      if (err) {
        return done(err);
      }

      self.watcher.unwatch(files, function(err, msg) {
        if (err) {
          (err);
        }

        self.logger.info(cwd, '{{cyan unwatched}}');
        self.logger.debug('unwatched', arguments);
        done(null);
      });
    });
  }, callback);
};

// There will be only one master process of `cortex watch`,
// so, it is ok to use a global variable of flags.
// Use this trick to prevent endless rebuilding
var locked = {};

watch._lock = function(id) {
  locked[id] = true;
};

watch._release = function(id) {
  locked[id] = false;
};

watch._is_locked = function(id) {
  return locked[id];
};

watch._rebuild = function(options, cwd, init) {
  var self = this;

  // If the current directory is already under building,
  // just ignore new tasks
  if (self._is_locked(cwd)) {
    return;
  }

  // lock it
  self._lock(cwd);

  // mock process.argv
  var argv = [
    '', '',
    'build',
    // Use --force to prevent grunt task interrupt the current process
    '--force',
    '--cwd', cwd
  ];

  var prerelease = options.prerelease;
  if (prerelease) {
    argv.push("--prerelease", prerelease);
  }

  var commander = self.commander;
  var parsed = commander.parse(argv, function(err, result, details) {
    if (err) {
      // #421
      setImmediate(function() {
        self._release(cwd);
      });
      return self.logger.info('{{red|bold ERR!}}', err);
    }

    var real_cwd = result.options.cwd;

    // if `cwd` is the same as `real_cwd`,
    // skip checking because we already have checked that
    if (real_cwd !== cwd) {
      if (self._is_locked(real_cwd)) {
        return;
      }

      // also lock the root directory of a repo
      self._lock(real_cwd);
    }

    if (init) {
      self.logger.info('{{cyan build}} the project when begins to watch...');
    } else {
      self.logger.info('file "' + cwd + '" changed,', '{{cyan rebuild project...}}');
    }

    // exec cortex.commands.build method
    commander.command('build', result.options, function(err) {
      setImmediate(function() {
        self._release(cwd);
        self._release(real_cwd);
      });

      if (err) {
        handler({
          logger: self.logger,
          harmony: true
        })(err);
      }
    });
  });
};
<|endoftext|>