<commit_before>/* jshint node: true */
'use strict';

var Class               = require( 'uberclass' )
  , path                = require( 'path' )
  , util                = require( 'util' )
  , i                   = require( 'i' )()
  , debug               = require( 'debug' )( 'clever-controller' )
  , NoActionException   = require('./exceptions/NoAction')
  , routedControllers   = [];

/**
 * Clever Controller - lightning-fast flexible controller prototype
 *
 * @define {CleverController} Clever Controller Class
 * @type {Class}
 */
var Controller = Class.extend(
/* @Static */
{
    /**
     * Defines any (string) route or (array) routes to be used in conjuction with autoRouting
     *
     * Note:
     *     You do not need to provide a value for this, if autoRouting is enabled,
     *     and you haven't defined a route one will be assigned based on the filename of your Controller.
     *
     * @examples
     *     route: false
     *     route: '[POST] /example/:id'
     *     route: [
     *         '[POST] /example/?',
     *         '/example/:id/?',
     *         '/example/:id/:action/?',
     *         '/examples/?',
     *         '/examples/:action/?'
     *     ]
     *
     * @default false
     * @type {Boolean|String|Array}
     */
    route: false,

    /**
     * Turns autoRouting on when not set to false, and when set to an array provides an
     * easy way to define middleware for the controller
     *
     * @examples
     *     autoRouting: false
     *     autoRouting: [
     *         function( req, res, next ) {
     *             // define middleware here
     *         },
     *         PermissionController.requiresPermission({
     *             all: 'Permission.*'
     *         }),
     *         'controllerFunction' // Where the controller has a function with this name
     *     ]
     *
     * @default  true
     * @type {Boolean|Array}
     */
    autoRouting: true,

    /**
     * Turns action based routing on or off
     *
     * @default true
     * @type {Boolean}
     */
    actionRouting: true,

    /**
     * Turns restful method based routing on or off
     *
     * @default true
     * @type {Boolean}
     */
    restfulRouting: true,

    /**
     * Use this function to attach your controller's to routes (either express or restify are supported)
     * @return {Function} returns constructor function
     */
    attach: function() {
        return this.callback( 'newInstance' );
    },

    /**
     * Class (Static) constructor
     *
     * @constructor
     * @return {undefined}
     */
    setup: function() {
        if ( this.autoRouting !== false && this.route !== false && routedControllers.indexOf( this.route ) === -1 ) {

            routedControllers.push( this.route );

            if ( typeof this.app !== 'undefined' ) {
                this.autoRoute( this.app );
            } else {
                try {
                    var injector = require( 'clever-injector' );
                    injector.inject( this.callback( function( app ) {
                        this.autoRoute( app );
                    }));
                } catch( e ) {
                    debug( 'Unable to autoRoute, Controller.app is not defined and clever-injector attempt failed with: ' + e + ( e.stack || ' Without a StackTrace') );
                }

            }
        }
    },

    /**
     * Attaches controllers routes to the app if autoRouting is enabled and routes have been defined
     *
     * @param  {Object}     app  Either express.app or restify
     * @return {undefined}
     */
    autoRoute: function( app ) {
        var middleware  = []
          , routes      = this.route instanceof Array ? this.route : this.route.split( '|' );

        debug( 'Autorouting for route ' + routes.join( ', ' ) );

        // Check for middleware that we need to put before the actual attach()
        if ( this.autoRouting instanceof Array ) {
            debug( 'Found middleware for ' + routes.join( ', ' ) + ' - ' + util.inspect( this.autoRouting ).replace( /\n/ig, ' ' )  );

            this.autoRouting.forEach( this.callback( function( mw ) {
                middleware.push( typeof mw === 'string' ? this.callback( mw ) : mw );
            }));
        }

        // Add our attach() function to handle requests
        middleware.push( this.attach() );

        // Bind the actual routes
        routes.forEach(function( route ) {
            var methods = [ 'GET', 'POST', 'PUT', 'DELETE' ];

            debug( 'Attaching route ' + route );
            if ( /(^[^\/]+)\ ?(\/.*)/ig.test( route ) ) {
                methods = RegExp.$1;
                route   = RegExp.$2;
                methods = methods.match( /\[([^\[\]]+)\]/ig );

                if ( methods.length ) {
                    methods = methods[ 0 ].replace( /(\[|\])/ig, '' );
                    methods = methods.split( ',' );
                }
            }

            methods.forEach( function( method ) {
                app[ method.toLowerCase() ].apply( app, [ route ].concat( middleware ) );
            });
        });
    },

    /**
     * Use this function to create a new controller that extends from Controller
     * @return {Controller} the newly created controller class
     */
    extend: function() {
        var extendingArgs = [].slice.call( arguments )
          , autoRouting = ( extendingArgs.length === 2 ) ? extendingArgs[ 0 ].autoRouting !== false : this.autoRouting
          , definedRoute = ( extendingArgs.length === 2 ) ? extendingArgs[ 0 ].route !== undefined : this.route;

        // Figure out if we are autoRouting and do not have a defined route already
        if ( autoRouting && !definedRoute ) {
            var stack = new Error().stack.split( '\n' )
              , extendingFilePath = false
              , extendingFileName = false
              , route = null;

            stack = stack.splice( 1, stack.length - 1 );

            // Walk backwards over the stack to find the filename where this is defined
            while( stack.length > 0 && extendingFilePath === false ) {
                var file = stack.shift();
                if ( !/clever-controller/ig.test( file ) && !/uberclass/ig.test( file ) ) {
                    if ( /.*\(([^\)]+)\:.*\:.*\)/ig.test( file ) ) {
                        extendingFilePath = RegExp.$1;
                        extendingFileName = path.basename( extendingFilePath );
                    }
                }
            }

            // Determine the route names if we have found a file
            if ( [ '', 'controller.js' ].indexOf( extendingFileName.toLowerCase() ) === -1 ) {
                var singular = i.singularize( extendingFileName.replace( /(controller)?.js/ig, '' ).toLowerCase() )
                  , plural = i.pluralize( singular );

                route = [];
                route.push( '[POST] /' + singular + '/?' )
                route.push( '/' + singular + '/:id/?' );
                route.push( '/' + singular + '/:id/:action/?' );
                route.push( '/' + plural + '/?' );
                route.push( '/' + plural + '/:action/?' );

                route = route.join( '|' );

                if ( extendingArgs.length === 2 ) {
                    extendingArgs[ 0 ].route = route;
                } else {
                    extendingArgs.unshift({ route: route  });
                }
            }
        }

        // Call extend on the parent
        return this._super.apply( this, extendingArgs );
    }
},
/* @Prototype */
{
    /**
     * The Request Object
     * @type {Request}
     */
    req:        null,

    /**
     * The Response Object
     * @type {Response}
     */
    res:        null,

    /**
     * The next function provided by connect, used to continue past this controller
     * @type {Function}
     */
    next:       null,

    /**
     * Is set to the most recent action function that was called
     * @type {String}
     */
    action:     null,

    /**
     * The name of the default Response handler function
     *
     * @default 'json'
     * @type {String}
     */
    resFunc:    'json',

    /**
     * This will wrap the performanceSafeSetup() function with a try/catch for safety,
     * most of the actual construction is done inside of Controller.performanceSafeSetup()
     *
     * @constructor
     * @param  {Request}    req  the Request Object
     * @param  {Response}   res  the Response Object
     * @param  {Function}   next connects next() function
     * @return {Array}           arguments that will be passed to init() to complete the constructor loop
     */
    setup: function( req, res, next ) {
        this.next = next;
        this.req  = req;
        this.res  = res;

        try {
            return this.performanceSafeSetup( req, res, next );
        } catch( e ) {
            return [ e ];
        }
    },

    /**
     * This is effectively what would be in setup() but instead lives here outside of the try/catch
     * so google v8 can optimise the code within
     *
     * @param  {Request}    req  the Request Object
     * @param  {Response}   res  the Response Object
     * @param  {Function}   next connects next() function
     * @return {Array}           arguments that will be passed to init() to complete the constructor loop
     */
    performanceSafeSetup: function( req, res, next ) {
        var methodAction    = req.method.toLowerCase() + 'Action'
          , actionRouting   = this.Class.actionRouting
          , actionMethod    = /\/([a-zA-z\.]+)(\/?|\?.*|\#.*)?$/ig.test( req.url ) ? RegExp.$1 + 'Action' : ( req.params.action !== undefined ? req.params.action : false )
          , restfulRouting  = this.Class.restfulRouting
          , idRegex         = /(^[0-9]+$|^[0-9a-fA-F]{24}$)/
          , hasIdParam      = req.params && req.params.id !== undefined ? true : false
          , id              = !!hasIdParam && idRegex.test( req.params.id ) ? req.params.id : false
          , hasActionParam  = req.params && req.params.action !== undefined ? true : false
          , action          = !!hasActionParam && !idRegex.test( req.params.action ) ? req.params.action + 'Action' : false;

        debug( 'methodAction:' + methodAction );
        debug( 'actionMethod:' + actionMethod );
        debug( 'actionRouting:' + actionRouting );
        debug( 'actionMethod:' + actionMethod );
        debug( 'restfulRouting:' + restfulRouting );
        debug( 'hasIdParam:' + hasIdParam );
        debug( 'id:' + id );
        debug( 'hasActionParam:' + hasActionParam );
        debug( 'action:' + action );

        if ( !!actionRouting && !!hasActionParam && action !== false && typeof this[ action ] === 'function' ) {
            debug( 'actionRouting: mapped by url to ' + action );
            return [ null, action, next ];
        }

        if ( actionMethod !== false && typeof this[ actionMethod ] === 'function' ) {
            debug( 'actionRouting: mapped by param to ' + actionMethod );
            return [ null, actionMethod, next ];
        }

        if ( !!restfulRouting ) {
            if ( methodAction === 'getAction' && !id && typeof this.listAction === 'function' ) {
                methodAction = 'listAction';
            }

            if ( typeof this[ methodAction ] === 'function' ) {
                debug( 'restfulRouting mapped to ' + methodAction );
                return [ null, methodAction, next ];
            }
        }

        return [ new NoActionException(), null, next ];
    },

    /**
     * The final function in the constructor routine, called eventually after setup() has finished
     *
     * @param  {Error}    error  any errors encountered during the setup() portion of the constructor
     * @param  {String}   method the name of the method to call on this controller
     * @param  {Function} next   connects next() function
     * @return {undefined}
     */
    init: function( error, method, next ) {
        if ( error && error instanceof NoActionException ) {
            debug( 'No route mapping found, calling next()' );
            next();
        } else {
            try {
                if ( error ) {
                    throw error;
                }

                if ( method !== null ) {
                    this.action = method;

                    debug( 'calling ' + this.action );
                    this[ method ]( this.req, this.res );
                } else {
                    this.next();
                }

            } catch( e ) {
                this.handleException( e );
            }
        }
    },

    send: function( content, code, type ) {
        var toCall = type || this.resFunc;
        if ( code ) {
            this.res[ toCall ]( code, content );
        } else {
            this.res[ toCall ]( content );
        }
    },

    render: function( template, data ) {
        this.res.render( template, data );
    },

    handleException: function( exception ) {
        this.send({
            message: 'Unhandled exception: ' + exception,
            stack: exception.stack ? exception.stack.split( '\n' ) : undefined
        },
        exception.statusCode || 500 );
    },

    isGet: function() {
        return this.req.method.toLowerCase() === 'get';
    },

    isPost: function() {
        return this.req.method.toLowerCase() === 'post';
    },

    isPut: function() {
        return this.req.method.toLowerCase() === 'put';
    }
});

module.exports = Controller;
<commit_msg>refactor(status): Updated to use express 4.x style<commit_after>/* jshint node: true */
'use strict';

var Class               = require( 'uberclass' )
  , path                = require( 'path' )
  , util                = require( 'util' )
  , i                   = require( 'i' )()
  , debug               = require( 'debug' )( 'clever-controller' )
  , NoActionException   = require('./exceptions/NoAction')
  , routedControllers   = [];

/**
 * Clever Controller - lightning-fast flexible controller prototype
 *
 * @define {CleverController} Clever Controller Class
 * @type {Class}
 */
var Controller = Class.extend(
/* @Static */
{
    /**
     * Defines any (string) route or (array) routes to be used in conjuction with autoRouting
     *
     * Note:
     *     You do not need to provide a value for this, if autoRouting is enabled,
     *     and you haven't defined a route one will be assigned based on the filename of your Controller.
     *
     * @examples
     *     route: false
     *     route: '[POST] /example/:id'
     *     route: [
     *         '[POST] /example/?',
     *         '/example/:id/?',
     *         '/example/:id/:action/?',
     *         '/examples/?',
     *         '/examples/:action/?'
     *     ]
     *
     * @default false
     * @type {Boolean|String|Array}
     */
    route: false,

    /**
     * Turns autoRouting on when not set to false, and when set to an array provides an
     * easy way to define middleware for the controller
     *
     * @examples
     *     autoRouting: false
     *     autoRouting: [
     *         function( req, res, next ) {
     *             // define middleware here
     *         },
     *         PermissionController.requiresPermission({
     *             all: 'Permission.*'
     *         }),
     *         'controllerFunction' // Where the controller has a function with this name
     *     ]
     *
     * @default  true
     * @type {Boolean|Array}
     */
    autoRouting: true,

    /**
     * Turns action based routing on or off
     *
     * @default true
     * @type {Boolean}
     */
    actionRouting: true,

    /**
     * Turns restful method based routing on or off
     *
     * @default true
     * @type {Boolean}
     */
    restfulRouting: true,

    /**
     * Use this function to attach your controller's to routes (either express or restify are supported)
     * @return {Function} returns constructor function
     */
    attach: function() {
        return this.callback( 'newInstance' );
    },

    /**
     * Class (Static) constructor
     *
     * @constructor
     * @return {undefined}
     */
    setup: function() {
        if ( this.autoRouting !== false && this.route !== false && routedControllers.indexOf( this.route ) === -1 ) {

            routedControllers.push( this.route );

            if ( typeof this.app !== 'undefined' ) {
                this.autoRoute( this.app );
            } else {
                try {
                    var injector = require( 'clever-injector' );
                    injector.inject( this.callback( function( app ) {
                        this.autoRoute( app );
                    }));
                } catch( e ) {
                    debug( 'Unable to autoRoute, Controller.app is not defined and clever-injector attempt failed with: ' + e + ( e.stack || ' Without a StackTrace') );
                }

            }
        }
    },

    /**
     * Attaches controllers routes to the app if autoRouting is enabled and routes have been defined
     *
     * @param  {Object}     app  Either express.app or restify
     * @return {undefined}
     */
    autoRoute: function( app ) {
        var middleware  = []
          , routes      = this.route instanceof Array ? this.route : this.route.split( '|' );

        debug( 'Autorouting for route ' + routes.join( ', ' ) );

        // Check for middleware that we need to put before the actual attach()
        if ( this.autoRouting instanceof Array ) {
            debug( 'Found middleware for ' + routes.join( ', ' ) + ' - ' + util.inspect( this.autoRouting ).replace( /\n/ig, ' ' )  );

            this.autoRouting.forEach( this.callback( function( mw ) {
                middleware.push( typeof mw === 'string' ? this.callback( mw ) : mw );
            }));
        }

        // Add our attach() function to handle requests
        middleware.push( this.attach() );

        // Bind the actual routes
        routes.forEach(function( route ) {
            var methods = [ 'GET', 'POST', 'PUT', 'DELETE' ];

            debug( 'Attaching route ' + route );
            if ( /(^[^\/]+)\ ?(\/.*)/ig.test( route ) ) {
                methods = RegExp.$1;
                route   = RegExp.$2;
                methods = methods.match( /\[([^\[\]]+)\]/ig );

                if ( methods.length ) {
                    methods = methods[ 0 ].replace( /(\[|\])/ig, '' );
                    methods = methods.split( ',' );
                }
            }

            methods.forEach( function( method ) {
                app[ method.toLowerCase() ].apply( app, [ route ].concat( middleware ) );
            });
        });
    },

    /**
     * Use this function to create a new controller that extends from Controller
     * @return {Controller} the newly created controller class
     */
    extend: function() {
        var extendingArgs = [].slice.call( arguments )
          , autoRouting = ( extendingArgs.length === 2 ) ? extendingArgs[ 0 ].autoRouting !== false : this.autoRouting
          , definedRoute = ( extendingArgs.length === 2 ) ? extendingArgs[ 0 ].route !== undefined : this.route;

        // Figure out if we are autoRouting and do not have a defined route already
        if ( autoRouting && !definedRoute ) {
            var stack = new Error().stack.split( '\n' )
              , extendingFilePath = false
              , extendingFileName = false
              , route = null;

            stack = stack.splice( 1, stack.length - 1 );

            // Walk backwards over the stack to find the filename where this is defined
            while( stack.length > 0 && extendingFilePath === false ) {
                var file = stack.shift();
                if ( !/clever-controller/ig.test( file ) && !/uberclass/ig.test( file ) ) {
                    if ( /.*\(([^\)]+)\:.*\:.*\)/ig.test( file ) ) {
                        extendingFilePath = RegExp.$1;
                        extendingFileName = path.basename( extendingFilePath );
                    }
                }
            }

            // Determine the route names if we have found a file
            if ( [ '', 'controller.js' ].indexOf( extendingFileName.toLowerCase() ) === -1 ) {
                var singular = i.singularize( extendingFileName.replace( /(controller)?.js/ig, '' ).toLowerCase() )
                  , plural = i.pluralize( singular );

                route = [];
                route.push( '[POST] /' + singular + '/?' )
                route.push( '/' + singular + '/:id/?' );
                route.push( '/' + singular + '/:id/:action/?' );
                route.push( '/' + plural + '/?' );
                route.push( '/' + plural + '/:action/?' );

                route = route.join( '|' );

                if ( extendingArgs.length === 2 ) {
                    extendingArgs[ 0 ].route = route;
                } else {
                    extendingArgs.unshift({ route: route  });
                }
            }
        }

        // Call extend on the parent
        return this._super.apply( this, extendingArgs );
    }
},
/* @Prototype */
{
    /**
     * The Request Object
     * @type {Request}
     */
    req:        null,

    /**
     * The Response Object
     * @type {Response}
     */
    res:        null,

    /**
     * The next function provided by connect, used to continue past this controller
     * @type {Function}
     */
    next:       null,

    /**
     * Is set to the most recent action function that was called
     * @type {String}
     */
    action:     null,

    /**
     * The name of the default Response handler function
     *
     * @default 'json'
     * @type {String}
     */
    resFunc:    'json',

    /**
     * This will wrap the performanceSafeSetup() function with a try/catch for safety,
     * most of the actual construction is done inside of Controller.performanceSafeSetup()
     *
     * @constructor
     * @param  {Request}    req  the Request Object
     * @param  {Response}   res  the Response Object
     * @param  {Function}   next connects next() function
     * @return {Array}           arguments that will be passed to init() to complete the constructor loop
     */
    setup: function( req, res, next ) {
        this.next = next;
        this.req  = req;
        this.res  = res;

        try {
            return this.performanceSafeSetup( req, res, next );
        } catch( e ) {
            return [ e ];
        }
    },

    /**
     * This is effectively what would be in setup() but instead lives here outside of the try/catch
     * so google v8 can optimise the code within
     *
     * @param  {Request}    req  the Request Object
     * @param  {Response}   res  the Response Object
     * @param  {Function}   next connects next() function
     * @return {Array}           arguments that will be passed to init() to complete the constructor loop
     */
    performanceSafeSetup: function( req, res, next ) {
        var methodAction    = req.method.toLowerCase() + 'Action'
          , actionRouting   = this.Class.actionRouting
          , actionMethod    = /\/([a-zA-z\.]+)(\/?|\?.*|\#.*)?$/ig.test( req.url ) ? RegExp.$1 + 'Action' : ( req.params.action !== undefined ? req.params.action : false )
          , restfulRouting  = this.Class.restfulRouting
          , idRegex         = /(^[0-9]+$|^[0-9a-fA-F]{24}$)/
          , hasIdParam      = req.params && req.params.id !== undefined ? true : false
          , id              = !!hasIdParam && idRegex.test( req.params.id ) ? req.params.id : false
          , hasActionParam  = req.params && req.params.action !== undefined ? true : false
          , action          = !!hasActionParam && !idRegex.test( req.params.action ) ? req.params.action + 'Action' : false;

        debug( 'methodAction:' + methodAction );
        debug( 'actionMethod:' + actionMethod );
        debug( 'actionRouting:' + actionRouting );
        debug( 'actionMethod:' + actionMethod );
        debug( 'restfulRouting:' + restfulRouting );
        debug( 'hasIdParam:' + hasIdParam );
        debug( 'id:' + id );
        debug( 'hasActionParam:' + hasActionParam );
        debug( 'action:' + action );

        if ( !!actionRouting && !!hasActionParam && action !== false && typeof this[ action ] === 'function' ) {
            debug( 'actionRouting: mapped by url to ' + action );
            return [ null, action, next ];
        }

        if ( actionMethod !== false && typeof this[ actionMethod ] === 'function' ) {
            debug( 'actionRouting: mapped by param to ' + actionMethod );
            return [ null, actionMethod, next ];
        }

        if ( !!restfulRouting ) {
            if ( methodAction === 'getAction' && !id && typeof this.listAction === 'function' ) {
                methodAction = 'listAction';
            }

            if ( typeof this[ methodAction ] === 'function' ) {
                debug( 'restfulRouting mapped to ' + methodAction );
                return [ null, methodAction, next ];
            }
        }

        return [ new NoActionException(), null, next ];
    },

    /**
     * The final function in the constructor routine, called eventually after setup() has finished
     *
     * @param  {Error}    error  any errors encountered during the setup() portion of the constructor
     * @param  {String}   method the name of the method to call on this controller
     * @param  {Function} next   connects next() function
     * @return {undefined}
     */
    init: function( error, method, next ) {
        if ( error && error instanceof NoActionException ) {
            debug( 'No route mapping found, calling next()' );
            next();
        } else {
            try {
                if ( error ) {
                    throw error;
                }

                if ( method !== null ) {
                    this.action = method;

                    debug( 'calling ' + this.action );
                    this[ method ]( this.req, this.res );
                } else {
                    this.next();
                }

            } catch( e ) {
                this.handleException( e );
            }
        }
    },

    send: function( content, code, type ) {
        var toCall = type || this.resFunc;
        if ( code ) {
            this.res.status( code )[ toCall ]( content );
        } else {
            this.res[ toCall ]( content );
        }
    },

    render: function( template, data ) {
        this.res.render( template, data );
    },

    handleException: function( exception ) {
        this.send({
            message: 'Unhandled exception: ' + exception,
            stack: exception.stack ? exception.stack.split( '\n' ) : undefined
        },
        exception.statusCode || 500 );
    },

    isGet: function() {
        return this.req.method.toLowerCase() === 'get';
    },

    isPost: function() {
        return this.req.method.toLowerCase() === 'post';
    },

    isPut: function() {
        return this.req.method.toLowerCase() === 'put';
    }
});

module.exports = Controller;
<|endoftext|>


<commit_before>/**
 * This package provides general utility functionality.
 *
 * @author Joschi <josua.krause@gmail.com>
 */

jkjs = window.jkjs || {}; // init namespace

jkjs.util = function() {
  var that = this;

  // suitable for SVGs
  this.BIG_NUMBER = 1e8;

  /**
   * Converts a list of classes into an object that can be used by the d3 classed function to activate all those classes.
   *
   * @param classes
   *          The classes as array. null is equivalent to an empty list.
   * @returns {Object} The object activating all classes when given to the d3 classed function.
   */
  this.convertClasses = function(classes) {
    if (!classes)
      return {};
    return classes.reduce(function(obj, c) {
      obj[c] = true;
      return obj;
    }, {});
  }

  /**
   * An implementation to set attributes only when they have changed.
   * This might increase performance for attributes which are expensive to update.
   * This does not work with attribute functions or qualified names.
   */
  this.attr = function(sel, attr) {
    // even without the value checks
    // it is still faster because of optimization
    // (at this point value checks slow everything down)
    Object.keys(attr).forEach(function(key) {
      var value = attr[key];
      if(value === null) {
        sel.each(function() {
          // if(!this.hasAttribute(key)) {
            this.removeAttribute(key);
          // }
        });
      } else {
        sel.each(function() {
          // if(this.getAttribute(key) !== value) {
            this.setAttribute(key, value);
          // }
        });
      }
    });
    return sel;
  };

  /**
   * Returns all GET arguments of the current URL location as key value pairs.
   *
   * @returns {Object} The arguments as keys and values.
   */
  this.getQueryStrings = function() {
    var assoc = {};
    var decode = function(s) {
      return decodeURIComponent(s.replace(/\+/g, " "));
    };
    var queryString = location.search.substring(1);
    var keyValues = queryString.split('&');
    keyValues.forEach(function(e) {
      var key = e.split('=');
      if (key.length > 1) {
        assoc[decode(key[0])] = decode(key[1]);
      }
    });
    return assoc;
  }

  /**
   * Getter.
   *
   * @returns {String} The current URL.
   */
  this.getOwnURL = function() {
    return location.origin + location.pathname;
  }

  /**
   * Whether the given object has no keys.
   *
   * @param obj
   *          The object.
   * @returns {Boolean} Whether the given object has no keys.
   */
  this.isEmpty = function(obj) {
    return Object.keys(obj).length == 0;
  }

  /**
   * Maps elements of an array to an array of results.
   *
   *

   * [a] -> (a -> [b]) -> [b]
   *

   *
   * @param arr
   *          The array to map.
   * @param fun
   *          The mapping function. It gets the current element as argument and is expected to return a list of results.
   * @returns The concatenated list of all result lists.
   */
  this.flatMap = function(arr, fun) {
    return [].concat.apply([], arr.map(fun));
  }

  /**
   * Moves all elements of the given selection to either the front or the back of the parent's children list resulting in
   * different visibility.
   *
   * @param sel
   *          The d3 selection to move.
   * @param toFront
   *          Whether to move to the front or to the back.
   */
  this.toFront = function(sel, toFront) {
    sel.each(function() {
      var obj = this;
      var parent = obj.parentElement;
      if (toFront) {
        parent.appendChild(obj);
      } else {
        parent.insertBefore(obj, parent.firstChild);
      }
    });
  }

  this.rectIntersect = function(rectA, rectB) {
    if(rectA.width <= 0 || rectA.height <= 0 || rectB.width <= 0 || rectB.height <= 0) {
      return false;
    }
    return rectB.x + rectB.width  > rectA.x &&
           rectB.y + rectB.height > rectA.y &&
           rectB.x < rectA.x + rectA.width  &&
           rectB.y < rectA.y + rectA.height;
  };

  this.getGrayValue = function(color) {
    return 0.2126 * color.r / 255 + 0.7152 * color.g / 255 + 0.0722 * color.b / 255;
  };

  this.getFontColor = function(color) {
    var grayValue = this.getGrayValue(d3.rgb(color));
    return grayValue > 0.5 ? d3.rgb("black") : d3.rgb("white");
  };

  this.ensureSorted = function(arr) {
    if(!arr.length) return;
    var prev = Number.NEGATIVE_INFINITY;
    for(var i = 0;i < arr.length;i += 1) {
      var v = arr[i];
      if(v < prev) {
        requireSorted(arr);
        return;
      }
      prev = v;
    }
  };

  function requireSorted(arr) {
    console.warn("slow sort test");
    var prev = Number.NEGATIVE_INFINITY;
    var run = false;
    var swapped = [];
    arr.forEach(function(v) {
      if(v < prev) {
        if(!run) {
          swapped.push(prev);
        }
        swapped.push(v);
        run = true;
      } else {
        run = false;
      }
      prev = v;
    });
    if(swapped.length) {
      console.warn("array not sorted", swapped, arr, new Error().stack);
      arr.sort(d3.ascending);
    }
  };

  this.join = function(ixs, add) {
    if(!ixs.length) {
      that.ensureSorted(add);
      return add;
    }
    if(!add.length) {
      that.ensureSorted(ixs);
      return ixs;
    }
    // TODO hacky way -- maybe optimize later
    var tmp = ixs.concat(add);
    tmp.sort(d3.ascending);
    return that.unique(tmp);
  };

  this.unique = function(ixs) {
    if(!ixs.length) return ixs;
    // sorted ixs
    that.ensureSorted(ixs);
    var res = [];
    res.length = ixs.length;
    var prev = ixs[0];
    res[0] = prev;
    var q = 1;
    for(var p = 1;p < ixs.length;p += 1) {
      var cur = ixs[p];
      if(cur !== prev) {
        res[q] = cur;
        q += 1;
        prev = cur;
      }
    }
    if(q < res.length) {
      res.length = q;
    }
    return res;
  };

  this.intersect = function(xs, ys) {
    if(!xs.length || !ys.length) return [];
    // sorted xs and ys
    that.ensureSorted(xs);
    that.ensureSorted(ys);
    var p = 0;
    var q = 0;
    var res = [];
    res.length = Math.min(xs.length, ys.length);
    var i = 0;
    while(p < xs.length && q < ys.length) {
      var a = xs[p];
      var b = ys[q];
      if(a < b) {
        p += 1;
      } else if(b < a) {
        q += 1;
      } else { // a === b
        res[i] = a;
        i += 1;
        p += 1;
        q += 1;
      }
    }
    if(i < res.length) {
      res.length = i;
    }
    return res;
  };

  this.getRemaining = function(ixs, minus) {
    if(!ixs.length || !minus.length) return ixs;
    // sorted ixs and minus
    that.ensureSorted(ixs);
    that.ensureSorted(minus);
    var p = 0;
    var q = 0;
    var res = [];
    res.length = ixs.length;
    var i = 0;
    while(p < ixs.length && q < minus.length) {
      var cur = ixs[p];
      var m = minus[q];
      if(cur < m) {
        res[i] = cur;
        i += 1;
        p += 1;
      } else if(cur > m) {
        q += 1;
      } else { // cur == m
        p += 1;
      }
    }
    var realLength = i + ixs.length - p;
    if(realLength !== res.length) {
      res.length = realLength;
    }
    for(;p < ixs.length;p += 1) {
      res[i] = ixs[p];
      i += 1;
    }
    return res;
  };

  this.getRemainingIter = function(ixs, minus, cb) {
    if(!ixs.length || !minus.length) {
      ixs.forEach(cb);
      return;
    }
    // sorted ixs and minus
    that.ensureSorted(ixs);
    that.ensureSorted(minus);
    var p = 0;
    var q = 0;
    while(p < ixs.length && q < minus.length) {
      var cur = ixs[p];
      var m = minus[q];
      if(cur < m) {
        cb(cur);
        p += 1;
      } else if(cur > m) {
        q += 1;
      } else { // cur == m
        p += 1;
      }
    }
    for(;p < ixs.length;p += 1) {
      cb(ixs[p]);
    }
  };

  this.applyPerm = function(arr, perm) {
    var tmp = arr.slice();
    if(tmp.length !== perm.length) console.warn(tmp.length + " != " + perm.length, new Error().stack);
    for(var i = 0;i < perm.length;i += 1) {
      arr[i] = tmp[perm[i]];
    }
  };

  this.randomNorm = function(maxRad, norm) {
    // maxRad should be >= 1e-3
    for(;;) {
      var rnd = ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3; // stddev: ~1, mean: ~0
      if(arguments.length < 1 || maxRad < 1e-3 || Math.abs(rnd) <= maxRad) {
        if(arguments.length >= 2 && norm) {
          return rnd / maxRad;
        }
        return rnd;
      }
    }
  };

  this.timing = function(name, cb, obj, args) {
    var from = new Date().getTime();
    var res = cb.apply(obj, args);
    var to = new Date().getTime();
    if(obj || args) {
      console.log("TIMING " + name, (to - from) + "ms", obj, args);
    } else {
      console.log("TIMING " + name, (to - from) + "ms");
    }
    return res;
  }

  this.shuffle = function(arr, from, to) {
    var f = arguments.length < 2 ? 0 : from;
    var t = arguments.length < 3 ? arr.length : to;
    var i = t;
    while(i > f) {
      var j = Math.floor(Math.random() * (i - f)) + f;
      i -= 1;
      var tmp = arr[i];
      arr[i] = arr[j];
      arr[j] = tmp;
    }
  };

  this.Randomizer = function(estSize) {
    var arr = [];
    arr.length = estSize;
    var ix = 0;

    this.push = function(el) {
      arr[ix] = el;
      ix += 1;
    };
    this.each = function(cb) {
      that.shuffle(arr, 0, ix);
      for(var i = 0;i < ix;i += 1) {
        cb(arr[i]);
      }
    };
  } // Randomizer

}; // jkjs.util

jkjs.util = new jkjs.util(); // create instance

if(!String.prototype.startsWith) {
  Object.defineProperty(String.prototype, 'startsWith', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function(searchString, position) {
      position = position || 0;
      return this.lastIndexOf(searchString, position) === position;
    }
  });
}

Number.isNaN = Number.isNaN || function(value) {
  return typeof value === "number" && value !== value;
}

if(typeof Number.isFinite !== 'function') {
  Number.isFinite = function isFinite(value) {
    if(typeof value !== 'number') return false;
    return !(value !== value || value === Number.NEGATIVE_INFINITY || value === Number.POSITIVE_INFINITY);
  };
}
<commit_msg>reservoir sampling<commit_after>/**
 * This package provides general utility functionality.
 *
 * @author Joschi <josua.krause@gmail.com>
 */

jkjs = window.jkjs || {}; // init namespace

jkjs.util = function() {
  var that = this;

  // suitable for SVGs
  this.BIG_NUMBER = 1e8;

  /**
   * Converts a list of classes into an object that can be used by the d3 classed function to activate all those classes.
   *
   * @param classes
   *          The classes as array. null is equivalent to an empty list.
   * @returns {Object} The object activating all classes when given to the d3 classed function.
   */
  this.convertClasses = function(classes) {
    if (!classes)
      return {};
    return classes.reduce(function(obj, c) {
      obj[c] = true;
      return obj;
    }, {});
  }

  /**
   * An implementation to set attributes only when they have changed.
   * This might increase performance for attributes which are expensive to update.
   * This does not work with attribute functions or qualified names.
   */
  this.attr = function(sel, attr) {
    // even without the value checks
    // it is still faster because of optimization
    // (at this point value checks slow everything down)
    Object.keys(attr).forEach(function(key) {
      var value = attr[key];
      if(value === null) {
        sel.each(function() {
          // if(!this.hasAttribute(key)) {
            this.removeAttribute(key);
          // }
        });
      } else {
        sel.each(function() {
          // if(this.getAttribute(key) !== value) {
            this.setAttribute(key, value);
          // }
        });
      }
    });
    return sel;
  };

  /**
   * Returns all GET arguments of the current URL location as key value pairs.
   *
   * @returns {Object} The arguments as keys and values.
   */
  this.getQueryStrings = function() {
    var assoc = {};
    var decode = function(s) {
      return decodeURIComponent(s.replace(/\+/g, " "));
    };
    var queryString = location.search.substring(1);
    var keyValues = queryString.split('&');
    keyValues.forEach(function(e) {
      var key = e.split('=');
      if (key.length > 1) {
        assoc[decode(key[0])] = decode(key[1]);
      }
    });
    return assoc;
  }

  /**
   * Getter.
   *
   * @returns {String} The current URL.
   */
  this.getOwnURL = function() {
    return location.origin + location.pathname;
  }

  /**
   * Whether the given object has no keys.
   *
   * @param obj
   *          The object.
   * @returns {Boolean} Whether the given object has no keys.
   */
  this.isEmpty = function(obj) {
    return Object.keys(obj).length == 0;
  }

  /**
   * Maps elements of an array to an array of results.
   *
   *

   * [a] -> (a -> [b]) -> [b]
   *

   *
   * @param arr
   *          The array to map.
   * @param fun
   *          The mapping function. It gets the current element as argument and is expected to return a list of results.
   * @returns The concatenated list of all result lists.
   */
  this.flatMap = function(arr, fun) {
    return [].concat.apply([], arr.map(fun));
  }

  /**
   * Moves all elements of the given selection to either the front or the back of the parent's children list resulting in
   * different visibility.
   *
   * @param sel
   *          The d3 selection to move.
   * @param toFront
   *          Whether to move to the front or to the back.
   */
  this.toFront = function(sel, toFront) {
    sel.each(function() {
      var obj = this;
      var parent = obj.parentElement;
      if (toFront) {
        parent.appendChild(obj);
      } else {
        parent.insertBefore(obj, parent.firstChild);
      }
    });
  }

  this.rectIntersect = function(rectA, rectB) {
    if(rectA.width <= 0 || rectA.height <= 0 || rectB.width <= 0 || rectB.height <= 0) {
      return false;
    }
    return rectB.x + rectB.width  > rectA.x &&
           rectB.y + rectB.height > rectA.y &&
           rectB.x < rectA.x + rectA.width  &&
           rectB.y < rectA.y + rectA.height;
  };

  this.getGrayValue = function(color) {
    return 0.2126 * color.r / 255 + 0.7152 * color.g / 255 + 0.0722 * color.b / 255;
  };

  this.getFontColor = function(color) {
    var grayValue = this.getGrayValue(d3.rgb(color));
    return grayValue > 0.5 ? d3.rgb("black") : d3.rgb("white");
  };

  this.ensureSorted = function(arr) {
    if(!arr.length) return;
    var prev = Number.NEGATIVE_INFINITY;
    for(var i = 0;i < arr.length;i += 1) {
      var v = arr[i];
      if(v < prev) {
        requireSorted(arr);
        return;
      }
      prev = v;
    }
  };

  function requireSorted(arr) {
    console.warn("slow sort test");
    var prev = Number.NEGATIVE_INFINITY;
    var run = false;
    var swapped = [];
    arr.forEach(function(v) {
      if(v < prev) {
        if(!run) {
          swapped.push(prev);
        }
        swapped.push(v);
        run = true;
      } else {
        run = false;
      }
      prev = v;
    });
    if(swapped.length) {
      console.warn("array not sorted", swapped, arr, new Error().stack);
      arr.sort(d3.ascending);
    }
  };

  this.join = function(ixs, add) {
    if(!ixs.length) {
      that.ensureSorted(add);
      return add;
    }
    if(!add.length) {
      that.ensureSorted(ixs);
      return ixs;
    }
    // TODO hacky way -- maybe optimize later
    var tmp = ixs.concat(add);
    tmp.sort(d3.ascending);
    return that.unique(tmp);
  };

  this.unique = function(ixs) {
    if(!ixs.length) return ixs;
    // sorted ixs
    that.ensureSorted(ixs);
    var res = [];
    res.length = ixs.length;
    var prev = ixs[0];
    res[0] = prev;
    var q = 1;
    for(var p = 1;p < ixs.length;p += 1) {
      var cur = ixs[p];
      if(cur !== prev) {
        res[q] = cur;
        q += 1;
        prev = cur;
      }
    }
    if(q < res.length) {
      res.length = q;
    }
    return res;
  };

  this.intersect = function(xs, ys) {
    if(!xs.length || !ys.length) return [];
    // sorted xs and ys
    that.ensureSorted(xs);
    that.ensureSorted(ys);
    var p = 0;
    var q = 0;
    var res = [];
    res.length = Math.min(xs.length, ys.length);
    var i = 0;
    while(p < xs.length && q < ys.length) {
      var a = xs[p];
      var b = ys[q];
      if(a < b) {
        p += 1;
      } else if(b < a) {
        q += 1;
      } else { // a === b
        res[i] = a;
        i += 1;
        p += 1;
        q += 1;
      }
    }
    if(i < res.length) {
      res.length = i;
    }
    return res;
  };

  this.getRemaining = function(ixs, minus) {
    if(!ixs.length || !minus.length) return ixs;
    // sorted ixs and minus
    that.ensureSorted(ixs);
    that.ensureSorted(minus);
    var p = 0;
    var q = 0;
    var res = [];
    res.length = ixs.length;
    var i = 0;
    while(p < ixs.length && q < minus.length) {
      var cur = ixs[p];
      var m = minus[q];
      if(cur < m) {
        res[i] = cur;
        i += 1;
        p += 1;
      } else if(cur > m) {
        q += 1;
      } else { // cur == m
        p += 1;
      }
    }
    var realLength = i + ixs.length - p;
    if(realLength !== res.length) {
      res.length = realLength;
    }
    for(;p < ixs.length;p += 1) {
      res[i] = ixs[p];
      i += 1;
    }
    return res;
  };

  this.getRemainingIter = function(ixs, minus, cb) {
    if(!ixs.length || !minus.length) {
      ixs.forEach(cb);
      return;
    }
    // sorted ixs and minus
    that.ensureSorted(ixs);
    that.ensureSorted(minus);
    var p = 0;
    var q = 0;
    while(p < ixs.length && q < minus.length) {
      var cur = ixs[p];
      var m = minus[q];
      if(cur < m) {
        cb(cur);
        p += 1;
      } else if(cur > m) {
        q += 1;
      } else { // cur == m
        p += 1;
      }
    }
    for(;p < ixs.length;p += 1) {
      cb(ixs[p]);
    }
  };

  this.applyPerm = function(arr, perm) {
    var tmp = arr.slice();
    if(tmp.length !== perm.length) console.warn(tmp.length + " != " + perm.length, new Error().stack);
    for(var i = 0;i < perm.length;i += 1) {
      arr[i] = tmp[perm[i]];
    }
  };

  this.randomNorm = function(maxRad, norm) {
    // maxRad should be >= 1e-3
    for(;;) {
      var rnd = ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3; // stddev: ~1, mean: ~0
      if(arguments.length < 1 || maxRad < 1e-3 || Math.abs(rnd) <= maxRad) {
        if(arguments.length >= 2 && norm) {
          return rnd / maxRad;
        }
        return rnd;
      }
    }
  };

  this.timing = function(name, cb, obj, args) {
    var from = new Date().getTime();
    var res = cb.apply(obj, args);
    var to = new Date().getTime();
    if(obj || args) {
      console.log("TIMING " + name, (to - from) + "ms", obj, args);
    } else {
      console.log("TIMING " + name, (to - from) + "ms");
    }
    return res;
  }

  this.sample = function(arr, count) {
    var reservoir = [];
    arr.forEach(function(v, ix) {
      if(reservoir.length < count) {
        reservoir.push(v);
        return;
      }
      var pos = Math.floor(Math.random() * (ix + 1));
      if(pos < reservoir.length) {
        reservoir[pos] = v;
      }
    });
    return reservoir;
  };

  this.shuffle = function(arr, from, to) {
    var f = arguments.length < 2 ? 0 : from;
    var t = arguments.length < 3 ? arr.length : to;
    var i = t;
    while(i > f) {
      var j = Math.floor(Math.random() * (i - f)) + f;
      i -= 1;
      var tmp = arr[i];
      arr[i] = arr[j];
      arr[j] = tmp;
    }
  };

  this.Randomizer = function(estSize) {
    var arr = [];
    arr.length = estSize;
    var ix = 0;

    this.push = function(el) {
      arr[ix] = el;
      ix += 1;
    };
    this.each = function(cb) {
      that.shuffle(arr, 0, ix);
      for(var i = 0;i < ix;i += 1) {
        cb(arr[i]);
      }
    };
  } // Randomizer

}; // jkjs.util

jkjs.util = new jkjs.util(); // create instance

if(!String.prototype.startsWith) {
  Object.defineProperty(String.prototype, 'startsWith', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function(searchString, position) {
      position = position || 0;
      return this.lastIndexOf(searchString, position) === position;
    }
  });
}

Number.isNaN = Number.isNaN || function(value) {
  return typeof value === "number" && value !== value;
}

if(typeof Number.isFinite !== 'function') {
  Number.isFinite = function isFinite(value) {
    if(typeof value !== 'number') return false;
    return !(value !== value || value === Number.NEGATIVE_INFINITY || value === Number.POSITIVE_INFINITY);
  };
}
<|endoftext|>


<commit_before>define([

  'Backbone',

  //Collection
  'app/collections/tasks',

  //Template
  'text!templates/project-page/taskItemTemplate.html',
], function(

  Backbone,

  //Collection
  Tasks,

  //Template
  itemTemplate
){

  var TaskController = Backbone.View.extend({

    template: _.template(itemTemplate),

    initialize: function(){
      this.collection =  new Tasks(this.options.projectId);
      this.collection.bind('reset', this.render, this);
    },

    render: function(){
      this.$el.html('');

      var template = this.template;
      var $list    = this.$el;

      _.each(this.collection.models, function(model){
        $list.append(template({title: model.get('title')}));
      });
    }
  });

  return TaskController;

});
<commit_msg>Extend task controller to render search results<commit_after>define([

  'Backbone',

  //Collection
  'app/collections/tasks',

  //Template
  'text!templates/project-page/taskItemTemplate.html',
], function(

  Backbone,

  //Collection
  Tasks,

  //Template
  itemTemplate
){

  var TaskController = Backbone.View.extend({

    template: _.template(itemTemplate),

    initialize: function(){
      this.collection =  new Tasks(this.options.projectId);
      this.collection.bind('reset', this.render, this);
      this.collection.bind('search', this.searchResult, this);
    },

    searchResult: function(result){
      this.$el.html('');

      var template = this.template;
      var $list    = this.$el;
      var title;

      _.each(result, function(model){
        title = model.get('title');

        if(title){
          $list.append(template({title: model.get('title')}));
        }
      });
    },

    render: function(){
      this.$el.html('');

      var template = this.template;
      var $list    = this.$el;

      _.each(this.collection.models, function(model){
        $list.append(template({title: model.get('title')}));
      });
    }
  });

  return TaskController;

});
<|endoftext|>


<commit_before>export default class Message {
  constructor(data) {
    this.data = data;
  }

  getSubject() {
    return this.constructor;
  }

  toJSON() {
    return {
      type: this.constructor.name,
      data: this.data
    };
  }
}
<commit_msg>make Message working cross-env<commit_after>export default class Message {
  constructor(data) {
    this.data = data;
    this.type = this.constructor.name;
  }
}
<|endoftext|>


<commit_before>// Copyright 2015 Endless Mobile, Inc.

/* exported SideMenu */

const Gdk = imports.gi.Gdk;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;

const Actions = imports.app.actions;
const Dispatcher = imports.app.dispatcher;
const Module = imports.app.interfaces.module;
const SlidingPanelOverlay = imports.app.widgets.slidingPanelOverlay;
const Utils = imports.app.utils;

const _MENU_HOT_ZONE_WIDTH_PX = 3;

/**
 * Class: SideMenu
 * A layout template that provides a context bar on top and a sliding side menu
 *
 * This layout template has one slot for its main content.
 * It shows a "context bar" above that content, which has another slot for a
 * module that shows context about the page, such as a <SetBanner>.
 *
 * The context bar has a button that opens a side menu, which is also a slot
 * that can be filled.
 * The side menu has a "Home" button on the top.
 * You can also open the menu by hovering the mouse near the side of the
 * template and close it by moving the mouse out of the menu.
 *
 * Implements:
 *   <Module>
 *
 * Slots:
 *   sidebar - The content in the menu that slides out (optional)
 *   context - Slot for content in the center of the context bar (optional)
 *   content - Main content of the template
 *
 * CSS classes:
 *   close-button - on the button that closes the menu
 *   context-bar - on the context bar
 *   context-bar-shadow-frame - hack for padding the context bar so the shadow
 *                              shows up
 *   home-button - on the button that goes to the home page
 *   menu - on the whole menu sidebar
 *   menu-button - on the button that opens the menu
 */
const SideMenu = new Module.Class({
    Name: 'Layout.SideMenu',
    Extends: SlidingPanelOverlay.SlidingPanelOverlay,

    Properties: {
        'menu-open': GObject.ParamSpec.boolean('menu-open', 'Menu open',
            'Whether the menu is showing', GObject.ParamFlags.READABLE, false),
    },

    Slots: {
        'content': {},
        'context': {},
        'sidebar': {},
    },

    Template: 'resource:///com/endlessm/knowledge/data/widgets/layout/sideMenu.ui',
    Children: [ 'home-button', 'menu-button', 'menu-close-button' ],
    InternalChildren: [ 'context-bar', 'grid', 'menu-grid', 'separator' ],

    _init: function (props={}) {
        this._menu_open = false;
        this.parent(props);

        let context = this.create_submodule('context', {
            hexpand: true,
            halign: Gtk.Align.CENTER,
        });
        if (context)
            this._context_bar.set_center_widget(context);

        this._menu_panel = this.add_panel_widget(this._menu_grid, Gtk.PositionType.LEFT);
        let klass = Utils.get_element_style_class(SideMenu, 'panel');
        this._menu_panel.get_style_context().add_class(klass);

        let sidebar = this.create_submodule('sidebar', {
            vexpand: true,
        });
        if (sidebar) {
            this._menu_grid.attach(sidebar, 0, 2, 2, 1);
        }

        let content = this.create_submodule('content');
        this._grid.add(content);

        this.menu_button.connect('clicked', this._open_menu.bind(this));
        this.menu_close_button.connect('clicked', this._close_menu.bind(this));
        this.connect('motion-notify-event', this._on_motion.bind(this));
        this.home_button.connect('clicked', this._on_home_clicked.bind(this));
    },

    get menu_open() {
        return this._menu_panel.reveal_panel;
    },

    _open_menu: function () {
        this._menu_panel.reveal_panel = true;
    },

    _close_menu: function () {
        this._menu_panel.reveal_panel = false;
    },

    _on_motion: function (widget, event) {
        let [got_coords, event_x] = event.get_root_coords();
        if (!got_coords)
            return Gdk.EVENT_PROPAGATE;

        let [ret, win_x, win_y] = this.get_window().get_origin();
        let x = event_x - win_x;
        if (!this.menu_open && x <= _MENU_HOT_ZONE_WIDTH_PX)
            this._open_menu();
        else if (this.menu_open && x > this._menu_grid.get_allocation().width)
            this._close_menu();
        return Gdk.EVENT_PROPAGATE;
    },

    _on_home_clicked: function () {
        this._close_menu();
        Dispatcher.get_default().dispatch({
            action_type: Actions.HOME_CLICKED,
        });
    },

    vfunc_draw: Utils.vfunc_draw_background_default,
});
<commit_msg>sideMenu: hide when page is unmapped<commit_after>// Copyright 2015 Endless Mobile, Inc.

/* exported SideMenu */

const Gdk = imports.gi.Gdk;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;

const Actions = imports.app.actions;
const Dispatcher = imports.app.dispatcher;
const Module = imports.app.interfaces.module;
const SlidingPanelOverlay = imports.app.widgets.slidingPanelOverlay;
const Utils = imports.app.utils;

const _MENU_HOT_ZONE_WIDTH_PX = 3;

/**
 * Class: SideMenu
 * A layout template that provides a context bar on top and a sliding side menu
 *
 * This layout template has one slot for its main content.
 * It shows a "context bar" above that content, which has another slot for a
 * module that shows context about the page, such as a <SetBanner>.
 *
 * The context bar has a button that opens a side menu, which is also a slot
 * that can be filled.
 * The side menu has a "Home" button on the top.
 * You can also open the menu by hovering the mouse near the side of the
 * template and close it by moving the mouse out of the menu.
 *
 * Implements:
 *   <Module>
 *
 * Slots:
 *   sidebar - The content in the menu that slides out (optional)
 *   context - Slot for content in the center of the context bar (optional)
 *   content - Main content of the template
 *
 * CSS classes:
 *   close-button - on the button that closes the menu
 *   context-bar - on the context bar
 *   context-bar-shadow-frame - hack for padding the context bar so the shadow
 *                              shows up
 *   home-button - on the button that goes to the home page
 *   menu - on the whole menu sidebar
 *   menu-button - on the button that opens the menu
 */
const SideMenu = new Module.Class({
    Name: 'Layout.SideMenu',
    Extends: SlidingPanelOverlay.SlidingPanelOverlay,

    Properties: {
        'menu-open': GObject.ParamSpec.boolean('menu-open', 'Menu open',
            'Whether the menu is showing', GObject.ParamFlags.READABLE, false),
    },

    Slots: {
        'content': {},
        'context': {},
        'sidebar': {},
    },

    Template: 'resource:///com/endlessm/knowledge/data/widgets/layout/sideMenu.ui',
    Children: [ 'home-button', 'menu-button', 'menu-close-button' ],
    InternalChildren: [ 'context-bar', 'grid', 'menu-grid', 'separator' ],

    _init: function (props={}) {
        this._menu_open = false;
        this.parent(props);

        let context = this.create_submodule('context', {
            hexpand: true,
            halign: Gtk.Align.CENTER,
        });
        if (context)
            this._context_bar.set_center_widget(context);

        this._menu_panel = this.add_panel_widget(this._menu_grid, Gtk.PositionType.LEFT);
        let klass = Utils.get_element_style_class(SideMenu, 'panel');
        this._menu_panel.get_style_context().add_class(klass);

        let sidebar = this.create_submodule('sidebar', {
            vexpand: true,
        });
        if (sidebar) {
            this._menu_grid.attach(sidebar, 0, 2, 2, 1);
        }

        let content = this.create_submodule('content');
        this._grid.add(content);

        this.menu_button.connect('clicked', this._open_menu.bind(this));
        this.menu_close_button.connect('clicked', this._close_menu.bind(this));
        this.connect('motion-notify-event', this._on_motion.bind(this));
        this.home_button.connect('clicked', this._on_home_clicked.bind(this));
    },

    get menu_open() {
        return this._menu_panel.reveal_panel;
    },

    _open_menu: function () {
        this._menu_panel.reveal_panel = true;
    },

    _close_menu: function () {
        this._menu_panel.reveal_panel = false;
    },

    _on_motion: function (widget, event) {
        let [got_coords, event_x] = event.get_root_coords();
        if (!got_coords)
            return Gdk.EVENT_PROPAGATE;

        let [ret, win_x, win_y] = this.get_window().get_origin();
        let x = event_x - win_x;
        if (!this.menu_open && x <= _MENU_HOT_ZONE_WIDTH_PX)
            this._open_menu();
        else if (this.menu_open && x > this._menu_grid.get_allocation().width)
            this._close_menu();
        return Gdk.EVENT_PROPAGATE;
    },

    vfunc_unmap: function () {
        this._close_menu();
        return this.parent();
    },

    _on_home_clicked: function () {
        this._close_menu();
        Dispatcher.get_default().dispatch({
            action_type: Actions.HOME_CLICKED,
        });
    },

    vfunc_draw: Utils.vfunc_draw_background_default,
});
<|endoftext|>


<commit_before>import React from 'react';

import ITEMS from 'common/ITEMS/OTHERS';
import SPELLS from 'common/SPELLS/OTHERS';
import Analyzer from 'Parser/Core/Analyzer';
import Combatants from 'Parser/Core/Modules/Combatants';
import ItemDamageDone from 'Main/ItemDamageDone';
import { formatPercentage } from 'common/format';

/*
 * Golganneth's Vitality
 * Equip: Your damaging abilities have a chance to create a Ravaging Storm at your target's location, inflicting (87377 * 6) Nature damage split among all enemies within 6 yds over 6 sec.
 *
 * Golganneth's Thunderous Wrath
 * When empowered by the Pantheon, your autoattacks cause an explosion of lightning dealing [1 * (Mainhand weapon base speed) * 47857] Nature damage to all enemies within 8 yds of the target. Lasts 15 sec.
 *
 */
class GolgannethsVitality extends Analyzer {
  static dependencies = {
    combatants: Combatants,
  };

  damage = 0;

  on_initialized() {
    this.active = this.combatants.selected.hasTrinket(ITEMS.GOLGANNETHS_VITALITY.id);
  }

  on_byPlayer_damage(event) {
    const spellID = event.ability.guid;
    if (spellID === SPELLS.GOLGANNETHS_VITALITY_RAVAGING_STORM.id || spellID === SPELLS.GOLGANNETHS_VITALITY_THUNDEROUS_WRATH.id) {
      this.damage += event.amount + (event.absorbed || 0);
    }
  }

  get empoweredProcUptime() {
    return this.combatants.selected.getBuffUptime(SPELLS.GOLGANNETHS_VITALITY_THUNDEROUS_WRATH_BUFF.id) / this.owner.fightDuration;
  }

  get normalProcUptime() {
    return this.combatants.selected.getBuffUptime(SPELLS.GOLGANNETHS_VITALITY_MARK_OF_GOLGANNETH.id) / this.owner.fightDuration;

  }

  item() {
    return {
      item: ITEMS.GOLGANNETHS_VITALITY,
      result: (
        <dfn data-tip={`You had the following uptime on the two procs of this trinket: <ul><li>Normal: ${formatPercentage(this.normalProcUptime)}%</li><li>Empowered: ${formatPercentage(this.empoweredProcUptime)}%</li></ul>`}>
          <ItemDamageDone amount={this.damage} />
        </dfn>
      ),
    };
  }
}

export default GolgannethsVitality;
<commit_msg>golg update<commit_after>import React from 'react';

import ITEMS from 'common/ITEMS/OTHERS';
import SPELLS from 'common/SPELLS/OTHERS';
import Analyzer from 'Parser/Core/Analyzer';
import Combatants from 'Parser/Core/Modules/Combatants';
import ItemDamageDone from 'Main/ItemDamageDone';
import { formatNumber, formatPercentage } from 'common/format';

/*
 * Golganneth's Vitality
 * Equip: Your damaging abilities have a chance to create a Ravaging Storm at your target's location, inflicting (87377 * 6) Nature damage split among all enemies within 6 yds over 6 sec.
 *
 * Golganneth's Thunderous Wrath
 * When empowered by the Pantheon, your autoattacks cause an explosion of lightning dealing [1 * (Mainhand weapon base speed) * 47857] Nature damage to all enemies within 8 yds of the target. Lasts 15 sec.
 *
 */
class GolgannethsVitality extends Analyzer {
  static dependencies = {
    combatants: Combatants,
  };

  damage = 0;
  normalDamage = 0;
  empoweredDamage = 0;

  on_initialized() {
    this.active = this.combatants.selected.hasTrinket(ITEMS.GOLGANNETHS_VITALITY.id);
  }

  on_byPlayer_damage(event) {
    const spellID = event.ability.guid;
    if (spellID === SPELLS.GOLGANNETHS_VITALITY_RAVAGING_STORM.id) {
      this.damage += event.amount + (event.absorbed || 0);
      this.normalDamage += event.amount + (event.absorbed || 0);
    }
    if (spellID === SPELLS.GOLGANNETHS_VITALITY_THUNDEROUS_WRATH.id) {
      this.damage += event.amount + (event.absorbed || 0);
      this.empoweredDamage += event.amount + (event.absorbed || 0);
    }
  }

  get empoweredProcUptime() {
    return this.combatants.selected.getBuffUptime(SPELLS.GOLGANNETHS_VITALITY_THUNDEROUS_WRATH_BUFF.id) / this.owner.fightDuration;
  }

  get normalProcUptime() {
    return this.combatants.selected.getBuffUptime(SPELLS.GOLGANNETHS_VITALITY_MARK_OF_GOLGANNETH.id) / this.owner.fightDuration;
  }

  item() {
    return {
      item: ITEMS.GOLGANNETHS_VITALITY,
      result: (
        <dfn data-tip={`This shows a breakdown of the damage contribution of this trinket:

Normal:

 ${formatPercentage(this.normalProcUptime)}% uptime

 ${formatNumber(this.normalDamage)} damage

Empowered:

 ${formatPercentage(this.empoweredProcUptime)}%

 ${formatNumber(this.empoweredDamage)} damage

`}>
          <ItemDamageDone amount={this.damage} />
        </dfn>
      ),
    };
  }
}

export default GolgannethsVitality;
<|endoftext|>


<commit_before>const _ = require('lodash');
const pluralize = require('pluralize');

module.exports = async cb => {
  const pickData = (model) => _.pick(model, [
    'info',
    'connection',
    'collectionName',
    'attributes',
    'identity',
    'globalId',
    'globalName',
    'orm',
    'loadedModel',
    'primaryKey',
    'associations'
  ]);

  const models = _.mapValues(strapi.models, pickData);
  delete models['core_store'];
  const pluginsModel = Object.keys(strapi.plugins).reduce((acc, current) => {
    acc[current] = {
      models: _.mapValues(strapi.plugins[current].models, pickData),
    };

    return acc;
  }, {});

  // Init schema
  const schema = {
    generalSettings: {
      search: true,
      filters: true,
      bulkActions: true,
      pageEntries: 10,
    },
    models: {
      plugins: {},
    },
  };

  const buildSchema = (model, name, plugin = false) => {
    // Model data
    const schemaModel = Object.assign({
      label: _.upperFirst(name),
      labelPlural: _.upperFirst(pluralize(name)),
      orm: model.orm || 'mongoose',
      search: true,
      filters: true,
      bulkActions: true,
      pageEntries: 10,
      defaultSort: model.primaryKey,
      sort: 'ASC',
      editDisplay: {
        fields: [],
        relations: [],
      },
    }, model);

    // Fields (non relation)
    const fields = _.mapValues(_.pickBy(model.attributes, attribute =>
      !attribute.model && !attribute.collection
    ), (value, attribute) => ({
      label: _.upperFirst(attribute),
      description: '',
      type: value.type || 'string',
    }));

    schemaModel.fields = fields;
    schemaModel.editDisplay.fields = Object.keys(fields);

    // Select fields displayed in list view
    schemaModel.listDisplay = Object.keys(schemaModel.fields)
      // Construct Array of attr ex { type: 'string', label: 'Foo', name: 'Foo', description: '' }
      // NOTE: Do we allow sort on boolean?
      .map(attr => {
        const attrType = schemaModel.fields[attr].type;
        const sortable = attrType !== 'json' && attrType !== 'array';

        return Object.assign(schemaModel.fields[attr], { name: attr, sortable, searchable: sortable });
      })
      // Retrieve only the fourth first items
      .slice(0, 4);

    schemaModel.listDisplay.splice(0, 0, {
      name: model.primaryKey || 'id',
      label: 'Id',
      type: 'string',
      sortable: true,
      searchable: true,
    });

    if (model.associations) {
      // Model relations
      schemaModel.relations = model.associations.reduce((acc, current) => {
        const displayedAttribute = current.plugin ?
          _.get(pluginsModel, [current.plugin, 'models', current.model || current.collection, 'info', 'mainField']) ||
          _.findKey(_.get(pluginsModel, [current.plugin, 'models', current.model || current.collection, 'attributes']), { type : 'string'}) ||
          'id' :
          _.get(models, [current.model || current.collection, 'info', 'mainField']) ||
          _.findKey(_.get(models, [current.model || current.collection, 'attributes']), { type : 'string'}) ||
          'id';

        acc[current.alias] = {
          ...current,
          description: '',
          displayedAttribute,
        };

        return acc;
      }, {});
      const relationsArray = Object.keys(schemaModel.relations).filter(relation => {
        const isUploadRelation = _.get(schemaModel, ['relations', relation, 'plugin'], '') === 'upload';
        const isMorphSide = _.get(schemaModel, ['relations', relation, 'nature'], '').toLowerCase().includes('morp') &&  _.get(schemaModel, ['relations', relation, relation]) !== undefined;

        return !isUploadRelation && !isMorphSide;
      });

      schemaModel.editDisplay.relations = relationsArray;

    }

    if (plugin) {
      return _.set(schema.models.plugins, `${plugin}.${name}`, schemaModel);
    }

    // Set the formatted model to the schema
    schema.models[name] = schemaModel;
  };

  _.forEach(pluginsModel, (plugin, pluginName) => {
    _.forEach(plugin.models, (model, name) => {
      buildSchema(model, name, pluginName);
    });
  });

  // Generate schema for models.
  _.forEach(models, (model, name) => {
    buildSchema(model, name);
  });

  const pluginStore = strapi.store({
    environment: '',
    type: 'plugin',
    name: 'content-manager'
  });

  const getApis = (data) => Object.keys(data).reduce((acc, curr) => {
    if (data[curr].fields) {
      return acc.concat([curr]);
    }

    if (curr === 'plugins') {
      Object.keys(data[curr]).map(plugin => {
        Object.keys(data[curr][plugin]).map(api => {
          acc = acc.concat([`${curr}.${plugin}.${api}`]);
        });
      });
    }

    return acc;
  }, []);

  const getApisKeys = (data, sameArray) => sameArray.map(apiPath => {
    const fields = Object.keys(_.get(data.models, apiPath.concat(['fields'])));

    return fields.map(field => `${apiPath.join('.')}.fields.${field}`);
  });

  try {
    const prevSchema = await pluginStore.get({ key: 'schema' });

    if (!prevSchema) {
      pluginStore.set({ key: 'schema', value: schema });
      return cb();
    }

    const splitted = str => str.split('.');
    const prevSchemaApis = getApis(prevSchema.models);
    const schemaApis = getApis(schema.models);
    const apisToAdd = schemaApis.filter(api => prevSchemaApis.indexOf(api) === -1).map(splitted);
    const apisToRemove = prevSchemaApis.filter(api => schemaApis.indexOf(api) === -1).map(splitted);
    const sameApis = schemaApis.filter(api => prevSchemaApis.indexOf(api) !== -1).map(splitted);
    const schemaSameApisKeys = _.flattenDeep(getApisKeys(schema, sameApis));
    const prevSchemaSameApisKeys = _.flattenDeep(getApisKeys(prevSchema, sameApis));
    const sameApisAttrToAdd = schemaSameApisKeys.filter(attr => prevSchemaSameApisKeys.indexOf(attr) === -1).map(splitted);
    const sameApisAttrToRemove = prevSchemaSameApisKeys.filter(attr => schemaSameApisKeys.indexOf(attr) === -1).map(splitted);

    // Remove api
    apisToRemove.map(apiPath => {
      _.unset(prevSchema.models, apiPath);
    });

    // Remove API attribute
    sameApisAttrToRemove.map(attrPath => {
      // Check default sort and change it if needed
      _.unset(prevSchema.models, attrPath);
      const apiPath = attrPath.length > 3 ? _.take(attrPath, 3) : _.take(attrPath, 1);
      const listDisplayPath = apiPath.concat('listDisplay');
      const prevListDisplay = _.get(prevSchema.models, listDisplayPath);
      const defaultSortPath = apiPath.concat('defaultSort');
      const currentAttr = attrPath.slice(-1);

      const defaultSort = _.get(prevSchema.models, defaultSortPath);

      if (_.includes(currentAttr, defaultSort)) {
        _.set(prevSchema.models, defaultSortPath, _.get(schema.models, defaultSortPath));
      }

      // Update the displayed fields
      const updatedListDisplay = prevListDisplay.filter(obj => obj.name !== currentAttr.join());

      if (updatedListDisplay.length === 0) {
        // Update it with the one from the generaeted schema
        _.set(prevSchema.models, listDisplayPath, _.get(schema.models, listDisplayPath, []));
      } else {
        _.set(prevSchema.models, listDisplayPath, updatedListDisplay);
      }
    });

    // Add API
    apisToAdd.map(apiPath => {
      const api = _.get(schema.models, apiPath);
      const { search, filters, bulkActions, pageEntries } = _.get(prevSchema, 'generalSettings');

      _.set(api, 'filters', filters);
      _.set(api, 'search', search);
      _.set(api, 'bulkActions', bulkActions);
      _.set(api, 'pageEntries', pageEntries);
      _.set(prevSchema.models, apiPath, api);
    });

    // Add attribute to existing API
    sameApisAttrToAdd.map(attrPath => {
      const attr = _.get(schema.models, attrPath);
      _.set(prevSchema.models, attrPath, attr);
    });

    // Update other keys
    sameApis.map(apiPath => {
      const keysToUpdate = ['relations', 'loadedModel', 'associations', 'attributes'].map(key => apiPath.concat(key));

      keysToUpdate.map(keyPath => {
        const newValue = _.get(schema.models, keyPath);

        _.set(prevSchema.models, keyPath, newValue);
      });
    });

    await pluginStore.set({ key: 'schema', value: prevSchema });

  } catch(err) {
    console.log('error', err);
  }

  cb();
};<commit_msg>Add data in editDisplay object in order to override the label and placeholder of an input<commit_after>const _ = require('lodash');
const pluralize = require('pluralize');

module.exports = async cb => {
  const pickData = (model) => _.pick(model, [
    'info',
    'connection',
    'collectionName',
    'attributes',
    'identity',
    'globalId',
    'globalName',
    'orm',
    'loadedModel',
    'primaryKey',
    'associations'
  ]);

  const models = _.mapValues(strapi.models, pickData);
  delete models['core_store'];
  const pluginsModel = Object.keys(strapi.plugins).reduce((acc, current) => {
    acc[current] = {
      models: _.mapValues(strapi.plugins[current].models, pickData),
    };

    return acc;
  }, {});

  // Init schema
  const schema = {
    generalSettings: {
      search: true,
      filters: true,
      bulkActions: true,
      pageEntries: 10,
    },
    models: {
      plugins: {},
    },
  };

  const buildSchema = (model, name, plugin = false) => {
    // Model data
    const schemaModel = Object.assign({
      label: _.upperFirst(name),
      labelPlural: _.upperFirst(pluralize(name)),
      orm: model.orm || 'mongoose',
      search: true,
      filters: true,
      bulkActions: true,
      pageEntries: 10,
      defaultSort: model.primaryKey,
      sort: 'ASC',
      editDisplay: {
        availableFields: {},
        fields: [],
        relations: [],
      },
    }, model);

    // Fields (non relation)
    const fields = _.mapValues(_.pickBy(model.attributes, attribute =>
      !attribute.model && !attribute.collection
    ), (value, attribute) => ({
      label: _.upperFirst(attribute),
      description: '',
      type: value.type || 'string',
    }));

    schemaModel.fields = fields;
    schemaModel.editDisplay.availableFields = fields;

    // Select fields displayed in list view
    schemaModel.listDisplay = Object.keys(schemaModel.fields)
      // Construct Array of attr ex { type: 'string', label: 'Foo', name: 'Foo', description: '' }
      // NOTE: Do we allow sort on boolean?
      .map(attr => {
        const attrType = schemaModel.fields[attr].type;
        const sortable = attrType !== 'json' && attrType !== 'array';

        return Object.assign(schemaModel.fields[attr], { name: attr, sortable, searchable: sortable });
      })
      // Retrieve only the fourth first items
      .slice(0, 4);

    schemaModel.listDisplay.splice(0, 0, {
      name: model.primaryKey || 'id',
      label: 'Id',
      type: 'string',
      sortable: true,
      searchable: true,
    });

    if (model.associations) {
      // Model relations
      schemaModel.relations = model.associations.reduce((acc, current) => {
        const displayedAttribute = current.plugin ?
          _.get(pluginsModel, [current.plugin, 'models', current.model || current.collection, 'info', 'mainField']) ||
          _.findKey(_.get(pluginsModel, [current.plugin, 'models', current.model || current.collection, 'attributes']), { type : 'string'}) ||
          'id' :
          _.get(models, [current.model || current.collection, 'info', 'mainField']) ||
          _.findKey(_.get(models, [current.model || current.collection, 'attributes']), { type : 'string'}) ||
          'id';

        acc[current.alias] = {
          ...current,
          description: '',
          displayedAttribute,
        };

        return acc;
      }, {});
      const relationsArray = Object.keys(schemaModel.relations).filter(relation => {
        const isUploadRelation = _.get(schemaModel, ['relations', relation, 'plugin'], '') === 'upload';
        const isMorphSide = _.get(schemaModel, ['relations', relation, 'nature'], '').toLowerCase().includes('morp') &&  _.get(schemaModel, ['relations', relation, relation]) !== undefined;

        return !isUploadRelation && !isMorphSide;
      });

      const uploadRelations = Object.keys(schemaModel.relations).reduce((acc, current) => {
        if (_.get(schemaModel, ['relations', current, 'plugin']) === 'upload') {
          const model = _.get(schemaModel, ['relations', current]);

          acc[current] = {
            description: '',
            editable: true,
            label: _.upperFirst(current),
            multiple: _.has(model, 'collection'),
            name: current,
            placeholder: '',
            type: 'file',
          };
        }

        return acc;
      }, {});

      const availableFields = Object.keys(schemaModel.fields).reduce((acc, current) => {
        // TODO: Add appearance in this object in order to get rid of the layout.json
        acc[current] = Object.assign(
          _.pick(_.get(schemaModel, ['fields', current], {}), ['label', 'type', 'description', 'name']),
          {
            editable: true,
            placeholder: '',
          });

        return acc;
      }, uploadRelations);

      schemaModel.editDisplay.availableFields = availableFields;

      schemaModel.editDisplay.relations = relationsArray;

    }

    schemaModel.editDisplay.fields = Object.keys(schemaModel.editDisplay.availableFields);

    if (plugin) {
      return _.set(schema.models.plugins, `${plugin}.${name}`, schemaModel);
    }

    // Set the formatted model to the schema
    schema.models[name] = schemaModel;
  };

  _.forEach(pluginsModel, (plugin, pluginName) => {
    _.forEach(plugin.models, (model, name) => {
      buildSchema(model, name, pluginName);
    });
  });

  // Generate schema for models.
  _.forEach(models, (model, name) => {
    buildSchema(model, name);
  });

  const pluginStore = strapi.store({
    environment: '',
    type: 'plugin',
    name: 'content-manager'
  });

  const getApis = (data) => Object.keys(data).reduce((acc, curr) => {
    if (data[curr].fields) {
      return acc.concat([curr]);
    }

    if (curr === 'plugins') {
      Object.keys(data[curr]).map(plugin => {
        Object.keys(data[curr][plugin]).map(api => {
          acc = acc.concat([`${curr}.${plugin}.${api}`]);
        });
      });
    }

    return acc;
  }, []);

  const getApisKeys = (data, sameArray) => sameArray.map(apiPath => {
    const fields = Object.keys(_.get(data.models, apiPath.concat(['fields'])));

    return fields.map(field => `${apiPath.join('.')}.fields.${field}`);
  });

  try {
    const prevSchema = await pluginStore.get({ key: 'schema' });

    if (!prevSchema) {
      pluginStore.set({ key: 'schema', value: schema });
      return cb();
    }

    const splitted = str => str.split('.');
    const prevSchemaApis = getApis(prevSchema.models);
    const schemaApis = getApis(schema.models);
    const apisToAdd = schemaApis.filter(api => prevSchemaApis.indexOf(api) === -1).map(splitted);
    const apisToRemove = prevSchemaApis.filter(api => schemaApis.indexOf(api) === -1).map(splitted);
    const sameApis = schemaApis.filter(api => prevSchemaApis.indexOf(api) !== -1).map(splitted);
    const schemaSameApisKeys = _.flattenDeep(getApisKeys(schema, sameApis));
    const prevSchemaSameApisKeys = _.flattenDeep(getApisKeys(prevSchema, sameApis));
    const sameApisAttrToAdd = schemaSameApisKeys.filter(attr => prevSchemaSameApisKeys.indexOf(attr) === -1).map(splitted);
    const sameApisAttrToRemove = prevSchemaSameApisKeys.filter(attr => schemaSameApisKeys.indexOf(attr) === -1).map(splitted);

    // Remove api
    apisToRemove.map(apiPath => {
      _.unset(prevSchema.models, apiPath);
    });

    // Remove API attribute
    sameApisAttrToRemove.map(attrPath => {
      // Check default sort and change it if needed
      _.unset(prevSchema.models, attrPath);
      const apiPath = attrPath.length > 3 ? _.take(attrPath, 3) : _.take(attrPath, 1);
      const listDisplayPath = apiPath.concat('listDisplay');
      const prevListDisplay = _.get(prevSchema.models, listDisplayPath);
      const defaultSortPath = apiPath.concat('defaultSort');
      const currentAttr = attrPath.slice(-1);

      const defaultSort = _.get(prevSchema.models, defaultSortPath);

      if (_.includes(currentAttr, defaultSort)) {
        _.set(prevSchema.models, defaultSortPath, _.get(schema.models, defaultSortPath));
      }

      // Update the displayed fields
      const updatedListDisplay = prevListDisplay.filter(obj => obj.name !== currentAttr.join());

      if (updatedListDisplay.length === 0) {
        // Update it with the one from the generaeted schema
        _.set(prevSchema.models, listDisplayPath, _.get(schema.models, listDisplayPath, []));
      } else {
        _.set(prevSchema.models, listDisplayPath, updatedListDisplay);
      }
    });

    // Add API
    apisToAdd.map(apiPath => {
      const api = _.get(schema.models, apiPath);
      const { search, filters, bulkActions, pageEntries } = _.get(prevSchema, 'generalSettings');

      _.set(api, 'filters', filters);
      _.set(api, 'search', search);
      _.set(api, 'bulkActions', bulkActions);
      _.set(api, 'pageEntries', pageEntries);
      _.set(prevSchema.models, apiPath, api);
    });

    // Add attribute to existing API
    sameApisAttrToAdd.map(attrPath => {
      const attr = _.get(schema.models, attrPath);
      _.set(prevSchema.models, attrPath, attr);
    });

    // Update other keys
    sameApis.map(apiPath => {
      const keysToUpdate = ['relations', 'loadedModel', 'associations', 'attributes'].map(key => apiPath.concat(key));

      keysToUpdate.map(keyPath => {
        const newValue = _.get(schema.models, keyPath);

        _.set(prevSchema.models, keyPath, newValue);
      });
    });

    await pluginStore.set({ key: 'schema', value: prevSchema });

  } catch(err) {
    console.log('error', err);
  }

  cb();
};<|endoftext|>


<commit_before>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 */
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}

define(["require","../deep", "../lib/stores/collection"], function (require, deep) {

	//_______________________________________________________________ GENERIC STORE TEST CASES

	var unit = {
		title:"deepjs/units/restrictions",
		stopOnError:false,
		tests : {
			restriction_up:function(){
				var a = {
					get:function(){
						return "hello";
					}
				};
				deep.utils.up(deep.store.Restrictions('get'), a);
				return deep(a.get())
				.fail(function(e){
					console.log("e")
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			restriction_up_collection:function(){
				var a = deep.utils.up(deep.store.Restrictions('get'), deep.store.Collection.create());
				return deep(a.get())
				.fail(function(e){
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			restriction_backgrounds:function(){
				var a = {
					backgrounds:[{
						get:function(){
							return "hello";
						}
					}, deep.store.Restrictions('get')]
				};
				return deep.flatten(a)
				.done(function(a){
					return a.get();
				})
				.fail(function(e){
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			restriction_backgrounds_collections:function(){
				var a = {
					backgrounds:[deep.store.Collection.create(), deep.store.Restrictions('get')]
				};
				return deep.flatten(a)
				.done(function(a){
					return a.get();
				})
				.fail(function(e){
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			restriction_ocm_collections:function(){
				var a = deep.ocm({
					role1:deep.store.Collection.create(),
					role2:{
						backgrounds:["this::../role1", deep.store.Restrictions('get')]
					}
				});
				return deep.flatten(a)
				.done(function(a){
					return a("role2").get();
				})
				.fail(function(e){
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			allow_only_up:function(){
				var obj = {
					a:true,
					b:"hello",
					c:function(){
						return "hello";
					}
				}
				obj = deep.utils.up(deep.store.AllowOnly("b"), obj);
				var a,b,c;
				try{
					a = obj.a().status;
					b = obj.b;
					c = obj.c().status;
				}
				catch(e)
				{

				}
				var r = [a, b, c];
				return deep(r)
				.equal([403, "hello", 403])
			},
			allow_only_backgrounds:function(){
				var obj = {
					a:true,
					b:"hello",
					c:function(){
						return "hello";
					}
				}
				var obj2 = {
					backgrounds:[obj, deep.store.AllowOnly("b")]
				}
				deep.flatten(obj2);
				var a,b,c;
				try{
					a = obj2.a().status;
					b = obj2.b;
					c = obj2.c().status;
				}
				catch(e)
				{

				}
				var r = [a, b, c];
				return deep(r)
				.equal([403, "hello", 403])
			},
			allow_only_backgrounds2:function(){
				var obj = {
					backgrounds:[deep.store.AllowOnly("b")],
					a:true,
					b:"hello",
					c:function(){
						return "world";
					}
				}

				deep.flatten(obj);
				var a,b,c, d;
				try{
					a = obj.a;
					b = obj.b;
					c = obj.c();
				}
				catch(e)
				{

				}
				var r = [a, b, c];
				return deep(r)
				.equal([true, "hello", "world"])
			}
		}
	};

	return unit;
});

<commit_msg>correct both testcases allow_only : only function is hidded now<commit_after>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 */
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}

define(["require","../deep", "../lib/stores/collection"], function (require, deep) {

	//_______________________________________________________________ GENERIC STORE TEST CASES

	var unit = {
		title:"deepjs/units/restrictions",
		stopOnError:false,
		tests : {
			restriction_up:function(){
				var a = {
					get:function(){
						return "hello";
					}
				};
				deep.utils.up(deep.store.Restrictions('get'), a);
				return deep(a.get())
				.fail(function(e){
					console.log("e")
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			restriction_up_collection:function(){
				var a = deep.utils.up(deep.store.Restrictions('get'), deep.store.Collection.create());
				return deep(a.get())
				.fail(function(e){
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			restriction_backgrounds:function(){
				var a = {
					backgrounds:[{
						get:function(){
							return "hello";
						}
					}, deep.store.Restrictions('get')]
				};
				return deep.flatten(a)
				.done(function(a){
					return a.get();
				})
				.fail(function(e){
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			restriction_backgrounds_collections:function(){
				var a = {
					backgrounds:[deep.store.Collection.create(), deep.store.Restrictions('get')]
				};
				return deep.flatten(a)
				.done(function(a){
					return a.get();
				})
				.fail(function(e){
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			restriction_ocm_collections:function(){
				var a = deep.ocm({
					role1:deep.store.Collection.create(),
					role2:{
						backgrounds:["this::../role1", deep.store.Restrictions('get')]
					}
				});
				return deep.flatten(a)
				.done(function(a){
					return a("role2").get();
				})
				.fail(function(e){
					if(e.status === 403)
						return "lolipop";
				})
				.equal("lolipop");
			},
			allow_only_up:function(){
				var obj = {
					a:true,
					b:"hello",
					c:function(){
						return "hello";
					}
				}
				obj = deep.utils.up(deep.store.AllowOnly("b"), obj);
				var a,b,c;
				try{
					a = obj.a;
					b = obj.b;
					c = obj.c().status;
				}
				catch(e)
				{

				}
				var r = [a, b, c];
				return deep(r)
				.equal([true, "hello", 403])
			},
			allow_only_backgrounds:function(){
				var obj = {
					a:true,
					b:"hello",
					c:function(){
						return "hello";
					}
				}
				var obj2 = {
					backgrounds:[obj, deep.store.AllowOnly("b")]
				}
				deep.flatten(obj2);
				var a,b,c;
				try{
					a = obj2.a;
					b = obj2.b;
					c = obj2.c().status;
				}
				catch(e)
				{

				}
				var r = [a, b, c];
				return deep(r)
				.equal([true, "hello", 403])
			},
			allow_only_backgrounds2:function(){
				var obj = {
					backgrounds:[deep.store.AllowOnly("b")],
					a:true,
					b:"hello",
					c:function(){
						return "world";
					}
				}

				deep.flatten(obj);
				var a,b,c, d;
				try{
					a = obj.a;
					b = obj.b;
					c = obj.c();
				}
				catch(e)
				{

				}
				var r = [a, b, c];
				return deep(r)
				.equal([true, "hello", "world"])
			}
		}
	};

	return unit;
});

<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import {Field, reduxForm} from 'redux-form';
import RaisedButton from 'material-ui/RaisedButton';
import MenuItem from 'material-ui/MenuItem';

import injectMuiReduxFormHelper from './../../helpers/injectMuiReduxFormHelper';
import injectF from './../../helpers/injectF';
import asyncValidate from './editEntryFormAsyncValidate';
import CATEGORY_VALUES from './../../constants/categoryValues';
import SECT_VALUES from './../../constants/sectValues';

const styles = require('./EditEntryForm.scss');

@reduxForm({
  form: 'editEntryForm',
  asyncValidate
})
@injectF
@injectMuiReduxFormHelper
export default class EditEntryForm extends Component {

  static propTypes = {
    folder: PropTypes.object.isRequired,
    initialValues: PropTypes.object.isRequired,
    renderTextField: PropTypes.func.isRequired,
    renderSelectField: PropTypes.func.isRequired,
    handleSubmit: PropTypes.func.isRequired,
    invalid: PropTypes.bool.isRequired,
    f: PropTypes.func.isRequired
  };

  renderCategoryMenuItems() {
    const {f} = this.props;
    return CATEGORY_VALUES.map(({id, value}) => {
      return <MenuItem key={`category-${id}`} value={value} primaryText={f(id)} />;
    });
  }

  renderSectMenuItems() {
    const {f} = this.props;
    return SECT_VALUES.map(({id, value}) => {
      return <MenuItem key={`sect-${id}`} value={value} primaryText={f(id)} />;
    });
  }

  renderContentFields() {
    const {folder, f, renderTextField, renderSelectField} = this.props;
    const {contentFields} = folder.data;

    return contentFields.map((field) => {

      const matchTargetLanguage = field.match(/^target-entry-lang-(.+)$/);

      if (matchTargetLanguage) {
        const lang = matchTargetLanguage[1];

        return (
          <div key={`target-entry-${lang}`}>
            <Field name={`target-entry-${lang}`} type="text" fullWidth
              component={renderTextField} label={f('target-entry-lang', {lang: f(lang)})} />

        );
      }

      if ('category' === field) {
        return (

            <Field name="category" component={renderSelectField} label={f('category')} fullWidth>
              {this.renderCategoryMenuItems()}
            </Field>

        );
      }

      if ('sect' === field) {
        return (

            <Field name="sect" component={renderSelectField} label={f('sect')} fullWidth>
              {this.renderSectMenuItems()}
            </Field>

        );
      }

      const matchExplaination = field.match(/^explaination-lang-(.+)$/);

      if (matchExplaination) {
        const lang = matchExplaination[1];

        return (
          <div key={`explaination-${lang}`}>
            <Field name={`explaination-${lang}`} type="text" fullWidth
              component={renderTextField} label={f('explaination-lang', {lang: f(lang)})} multiLine />

        );
      }

      const matchOriginal = field.match(/^original-lang-(.+)$/);

      if (matchOriginal) {
        const lang = matchOriginal[1];

        return (
          <div key={`original-${lang}`}>
            <Field name={`original-${lang}`} type="text" fullWidth
              component={renderTextField} label={f('original-lang', {lang: f(lang)})} multiLine />

        );
      }

      const matchSource = field.match(/^source-lang-(.+)$/);

      if (matchSource) {
        const lang = matchSource[1];

        return (
          <div key={`source-${lang}`}>
            <Field name={`source-${lang}`} type="text" fullWidth
              component={renderTextField} label={f('source-lang', {lang: f(lang)})} multiLine />

        );
      }
    });
  }

  render() {

    const {handleSubmit, f, invalid, renderTextField, folder} = this.props;
    const {sourceLanguage} = folder.data;

    return (
      <form className={styles.editEntryForm} onSubmit={handleSubmit}>

            <Field name="sourceEntry" type="text" component={renderTextField}
            label={f('source-entry-lang', {lang: f(sourceLanguage)})} autoFocus fullWidth />

{this.renderContentFields()}

        <RaisedButton className={styles.submitButton} type="submit" label={f('submit')} primary disabled={invalid} />
      </form>
    );
  }
}
<commit_msg>update component EditEntryForm<commit_after>import React, {Component, PropTypes} from 'react';
import {Field, reduxForm} from 'redux-form';
import RaisedButton from 'material-ui/RaisedButton';
import MenuItem from 'material-ui/MenuItem';
import {range} from 'ramda';

import injectMuiReduxFormHelper from './../../helpers/injectMuiReduxFormHelper';
import injectF from './../../helpers/injectF';
import asyncValidate from './editEntryFormAsyncValidate';
import CATEGORY_VALUES from './../../constants/categoryValues';
import SECT_VALUES from './../../constants/sectValues';

const styles = require('./EditEntryForm.scss');

@reduxForm({
  form: 'editEntryForm',
  asyncValidate
})
@injectF
@injectMuiReduxFormHelper
export default class EditEntryForm extends Component {

  static propTypes = {
    folder: PropTypes.object.isRequired,
    initialValues: PropTypes.object.isRequired,
    renderTextField: PropTypes.func.isRequired,
    renderSelectField: PropTypes.func.isRequired,
    handleSubmit: PropTypes.func.isRequired,
    invalid: PropTypes.bool.isRequired,
    f: PropTypes.func.isRequired
  };

  constructor(props) {
    super(props);
    this.state = this.genDefaultState(props.folder.data.contentFields);
    this.state = this.overrideStateByValues(props.initialValues, this.state);
  }

  getExplainationIndex = (lang) => `explaination-${lang}-index`;

  genDefaultState(contentFields) {

    return contentFields.reduce((state, field) => {

      const matchExplaination = field.match(/^explaination-lang-(.+)$/);

      if (matchExplaination) {
        const lang = matchExplaination[1];
        state[this.getExplainationIndex(lang)] = 1;
      }
      return state;
    }, {});
  }

  overrideStateByValues(contentFields, state) {

    return Object.keys(contentFields)
      .reduce((state, field) => {

        const matchExplaination = field.match(/^explaination-(.+)$/);

        if (matchExplaination) {
          const arr = contentFields[field];
          const lang = matchExplaination[1];
          state[this.getExplainationIndex(lang)] = arr.length;
        }
        return state;
      }, state);
  }

  renderCategoryMenuItems() {
    const {f} = this.props;
    return CATEGORY_VALUES.map(({id, value}) => {
      return <MenuItem key={`category-${id}`} value={value} primaryText={f(id)} />;
    });
  }

  renderSectMenuItems() {
    const {f} = this.props;
    return SECT_VALUES.map(({id, value}) => {
      return <MenuItem key={`sect-${id}`} value={value} primaryText={f(id)} />;
    });
  }

  handleExplainationChange = (lang, index) => {
    return (event) => {

      const key = this.getExplainationIndex(lang);

      if (!! event.target.value) {
        this.setState({[key]: index + 2});
      }
    };
  };

  renderContentFields() {
    const {folder, f, renderTextField, renderSelectField} = this.props;
    const {contentFields} = folder.data;

    return contentFields.map((field) => {

      const matchTargetLanguage = field.match(/^target-entry-lang-(.+)$/);

      if (matchTargetLanguage) {
        const lang = matchTargetLanguage[1];

        return (
          <div key={`target-entry-${lang}`}>
            <Field name={`target-entry-${lang}`} type="text" fullWidth
              component={renderTextField} label={f('target-entry-lang', {lang: f(lang)})} />

        );
      }

      if ('category' === field) {
        return (

            <Field name="category" component={renderSelectField} label={f('category')} fullWidth>
              {this.renderCategoryMenuItems()}
            </Field>

        );
      }

      if ('sect' === field) {
        return (

            <Field name="sect" component={renderSelectField} label={f('sect')} fullWidth>
              {this.renderSectMenuItems()}
            </Field>

        );
      }

      const matchExplaination = field.match(/^explaination-lang-(.+)$/);

      if (matchExplaination) {

        const lang = matchExplaination[1];
        const explainationIndex = this.state[this.getExplainationIndex(lang)] || 0;

        const rows = range(0, explainationIndex)
          .reduce((rows, elem, index) => {
            rows.push((
              <div key={`explaination-${lang}-${index}`}>
                <Field name={`explaination-${lang}[${index}]`} type="text" fullWidth onChange={this.handleExplainationChange(lang, index)}
                  component={renderTextField} label={f('explaination-lang', {lang: f(lang), num: (index + 1)})} multiLine />

            ));
            rows.push((
              <div key={`source-${lang}-${index}`}>
                <Field name={`source-${lang}[${index}]`} type="text" fullWidth
                  component={renderTextField} label={f('explaination-source-lang', {lang: f(lang), num: (index + 1)})} multiLine />

            ));
            rows.push((
              <div key={`note-${lang}-${index}`}>
                <Field name={`note-${lang}[${index}]`} type="text" fullWidth
                  component={renderTextField} label={f('explaination-note-lang', {lang: f(lang), num: (index + 1)})} multiLine />

            ));
            return rows;
          }, []);

        return rows;
      }

      const matchOriginal = field.match(/^original-lang-(.+)$/);

      if (matchOriginal) {
        const lang = matchOriginal[1];

        return (
          <div key={`original-${lang}`}>
            <Field name={`original-${lang}`} type="text" fullWidth
              component={renderTextField} label={f('original-lang', {lang: f(lang)})} multiLine />

        );
      }

      const matchSource = field.match(/^source-lang-(.+)$/);

      if (matchSource) {
        const lang = matchSource[1];

        return (
          <div key={`source-${lang}`}>
            <Field name={`source-${lang}`} type="text" fullWidth
              component={renderTextField} label={f('source-lang', {lang: f(lang)})} multiLine />

        );
      }
    });
  }

  render() {

    const {handleSubmit, f, invalid, renderTextField, folder} = this.props;
    const {sourceLanguage} = folder.data;

    return (
      <form className={styles.editEntryForm} onSubmit={handleSubmit}>

            <Field name="sourceEntry" type="text" component={renderTextField}
            label={f('source-entry-lang', {lang: f(sourceLanguage)})} autoFocus fullWidth />

{this.renderContentFields()}

        <RaisedButton className={styles.submitButton} type="submit" label={f('submit')} primary disabled={invalid} />
      </form>
    );
  }
}
<|endoftext|>