<commit_before>//----------------------------------------------------------------------------
//  Copyright (C) 2013 The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// Base Widget Model and View classes
//============================================================================

/**
 * @module IPython
 * @namespace IPython
 **/

define(["notebook/js/widgetmanager",
        "underscore",
        "backbone"],
function(widget_manager, underscore, backbone){

    //--------------------------------------------------------------------
    // WidgetModel class
    //--------------------------------------------------------------------
    var WidgetModel = Backbone.Model.extend({
        constructor: function (widget_manager, widget_id, comm) {
            this.widget_manager = widget_manager;
            this.pending_msgs = 0;
            this.msg_throttle = 3;
            this.msg_buffer = null;
            this.id = widget_id;
            this.views = [];

            if (comm !== undefined) {
                // Remember comm associated with the model.
                this.comm = comm;
                comm.model = this;

                // Hook comm messages up to model.
                comm.on_close($.proxy(this._handle_comm_closed, this));
                comm.on_msg($.proxy(this._handle_comm_msg, this));
            }
            return Backbone.Model.apply(this);
        },

        send: function (content, callbacks) {
            if (this.comm !== undefined) {
                var data = {method: 'custom', custom_content: content};
                this.comm.send(data, callbacks);
            }
        },

        // Handle when a widget is closed.
        _handle_comm_closed: function (msg) {
            this.trigger('comm:close');
            delete this.comm.model; // Delete ref so GC will collect widget model.
            delete this.comm;
            delete this.widget_id; // Delete id from model so widget manager cleans up.
            // TODO: Handle deletion, like this.destroy(), and delete views, etc.
        },

        // Handle incoming comm msg.
        _handle_comm_msg: function (msg) {
            var method = msg.content.data.method;
            switch (method) {
                case 'update':
                    this.apply_update(msg.content.data.state);
                    break;
                case 'custom':
                    this.trigger('msg:custom', msg.content.data.custom_content);
                    break;
                default:
                    // pass on to widget manager
                    this.widget_manager.handle_msg(msg, this);
            }
        },

        // Handle when a widget is updated via the python side.
        apply_update: function (state) {
            this.updating = true;
            try {
                for (var key in state) {
                    if (state.hasOwnProperty(key)) {
                            this.set(key, state[key]);
                    }
                }
                //TODO: are there callbacks that make sense in this case?  If so, attach them here as an option
                this.save();
            } finally {
                this.updating = false;
            }
        },

        _handle_status: function (msg, callbacks) {
            //execution_state : ('busy', 'idle', 'starting')
            if (this.comm !== undefined && msg.content.execution_state ==='idle') {
                // Send buffer if this message caused another message to be
                // throttled.
                if (this.msg_buffer !== null &&
                    this.msg_throttle === this.pending_msgs) {
                    var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
                    this.comm.send(data, callbacks);
                    this.msg_buffer = null;
                } else {
                    // Only decrease the pending message count if the buffer
                    // doesn't get flushed (sent).
                    --this.pending_msgs;
                }
            }
        },

        // Custom syncronization logic.
        _handle_sync: function (method, options) {
            var model_json = this.toJSON();
            var attr;

            // Only send updated state if the state hasn't been changed
            // during an update.
            if (this.comm !== undefined) {
                if (!this.updating) {
                    if (this.pending_msgs >= this.msg_throttle) {
                        // The throttle has been exceeded, buffer the current msg so
                        // it can be sent once the kernel has finished processing
                        // some of the existing messages.
                        if (method=='patch') {
                            if (this.msg_buffer === null) {
                                this.msg_buffer = $.extend({}, model_json); // Copy
                            }
                            for (attr in options.attrs) {
                                this.msg_buffer[attr] = options.attrs[attr];
                            }
                        } else {
                            this.msg_buffer = $.extend({}, model_json); // Copy
                        }

                    } else {
                        // We haven't exceeded the throttle, send the message like
                        // normal.  If this is a patch operation, just send the
                        // changes.
                        var send_json = model_json;
                        if (method =='patch') {
                            send_json = {};
                            for (attr in options.attrs) {
                                send_json[attr] = options.attrs[attr];
                            }
                        }

                        var data = {method: 'backbone', sync_method: method, sync_data: send_json};
                        this.comm.send(data, options.callbacks);
                        this.pending_msgs++;
                    }
                }
            }

            // Since the comm is a one-way communication, assume the message
            // arrived.
            return model_json;
        },

    });

    //--------------------------------------------------------------------
    // WidgetView class
    //--------------------------------------------------------------------
    var BaseWidgetView = Backbone.View.extend({
        initialize: function(options) {
            this.model.on('change',this.update,this);
            this.widget_manager = options.widget_manager;
            this.comm_manager = options.widget_manager.comm_manager;
            this.cell = options.cell;
            this.child_views = [];
        },

        update: function(){
            // update view to be consistent with this.model
            // triggered on model change
        },

        child_view: function(comm_id, view_name) {
            // create and return a child view, given a comm id for a model and (optionally) a view name
            // if the view name is not given, it defaults to the model's default view attribute
            var child_model = this.comm_manager.comms[comm_id].model;
            var child_view = this.widget_manager.create_view(child_model, view_name, this.cell);
            this.child_views.push(child_view);
            return child_view;
        },

        render: function(){
            // render the view.  By default, this is only called the first time the view is created
        },
        send: function (content) {
            this.model.send(content, this._callbacks());
        },

        touch: function () {
            this.model.save(this.model.changedAttributes(), {patch: true, callbacks: this._callbacks()});
        },

        _callbacks: function () {
            // callback handlers specific to this view's cell
            var callbacks = {};
            var cell = this.cell;
            if (cell !== null) {
                // Try to get output handlers
                var handle_output = null;
                var handle_clear_output = null;
                if (cell.output_area !== undefined && cell.output_area !== null) {
                    handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
                    handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
                }

                // Create callback dict using what is known
                var that = this;
                callbacks = {
                    iopub : {
                        output : handle_output,
                        clear_output : handle_clear_output,

                        status : function (msg) {
                            that.model._handle_status(msg, that._callbacks());
                        },

                        // Special function only registered by widget messages.
                        // Allows us to get the cell for a message so we know
                        // where to add widgets if the code requires it.
                        get_cell : function () {
                            return cell;
                        },
                    },
                };
            }
            return callbacks;
        },

    });

    var WidgetView = BaseWidgetView.extend({
        initialize: function (options) {
            // TODO: make changes more granular (e.g., trigger on visible:change)
            this.model.on('change', this.update, this);
            BaseWidgetView.prototype.initialize.apply(this, arguments);
        },

        add_class: function (selector, class_list) {
            var elements = this._get_selector_element(selector);
            if (elements.length > 0) {
                elements.addClass(class_list);
            }
        },

        remove_class: function (selector, class_list) {
            var elements = this._get_selector_element(selector);
            if (elements.length > 0) {
                elements.removeClass(class_list);
            }
        },

        update: function () {
            // the very first update seems to happen before the element is finished rendering
            // so we use setTimeout to give the element time to render
            var e = this.$el;
            var visible = this.model.get('visible');
            setTimeout(function() {e.toggle(visible)},0);

            var css = this.model.css;
            if (css === undefined) {return;}
            for (var selector in css) {
                if (css.hasOwnProperty(selector)) {
                    // Apply the css traits to all elements that match the selector.
                    var elements = this._get_selector_element(selector);
                    if (elements.length > 0) {
                        var css_traits = css[selector];
                        for (var css_key in css_traits) {
                            if (css_traits.hasOwnProperty(css_key)) {
                                elements.css(css_key, css_traits[css_key]);
                            }
                        }
                    }
                }
            }
        },

        _get_selector_element: function (selector) {
            // Get the elements via the css selector.  If the selector is
            // blank, apply the style to the $el_to_style element.  If
            // the $el_to_style element is not defined, use apply the
            // style to the view's element.
            var elements;
            if (selector === undefined || selector === null || selector === '') {
                if (this.$el_to_style === undefined) {
                    elements = this.$el;
                } else {
                    elements = this.$el_to_style;
                }
            } else {
                elements = this.$el.find(selector);
            }
            return elements;
        },
    });

    IPython.WidgetModel = WidgetModel;
    IPython.WidgetView = WidgetView;
    IPython.BaseWidgetView = BaseWidgetView;

    return widget_manager;
});
<commit_msg>Get the add/remove css classes working<commit_after>//----------------------------------------------------------------------------
//  Copyright (C) 2013 The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// Base Widget Model and View classes
//============================================================================

/**
 * @module IPython
 * @namespace IPython
 **/

define(["notebook/js/widgetmanager",
        "underscore",
        "backbone"],
function(widget_manager, underscore, backbone){

    //--------------------------------------------------------------------
    // WidgetModel class
    //--------------------------------------------------------------------
    var WidgetModel = Backbone.Model.extend({
        constructor: function (widget_manager, widget_id, comm) {
            this.widget_manager = widget_manager;
            this.pending_msgs = 0;
            this.msg_throttle = 3;
            this.msg_buffer = null;
            this.id = widget_id;
            this.views = [];

            if (comm !== undefined) {
                // Remember comm associated with the model.
                this.comm = comm;
                comm.model = this;

                // Hook comm messages up to model.
                comm.on_close($.proxy(this._handle_comm_closed, this));
                comm.on_msg($.proxy(this._handle_comm_msg, this));
            }
            return Backbone.Model.apply(this);
        },

        send: function (content, callbacks) {
            if (this.comm !== undefined) {
                var data = {method: 'custom', custom_content: content};
                this.comm.send(data, callbacks);
            }
        },

        // Handle when a widget is closed.
        _handle_comm_closed: function (msg) {
            this.trigger('comm:close');
            delete this.comm.model; // Delete ref so GC will collect widget model.
            delete this.comm;
            delete this.widget_id; // Delete id from model so widget manager cleans up.
            // TODO: Handle deletion, like this.destroy(), and delete views, etc.
        },

        // Handle incoming comm msg.
        _handle_comm_msg: function (msg) {
            var method = msg.content.data.method;
            switch (method) {
                case 'update':
                    this.apply_update(msg.content.data.state);
                    break;
                case 'custom':
                    this.trigger('msg:custom', msg.content.data.custom_content);
                    break;
                default:
                    // pass on to widget manager
                    this.widget_manager.handle_msg(msg, this);
            }
        },

        // Handle when a widget is updated via the python side.
        apply_update: function (state) {
            this.updating = true;
            try {
                for (var key in state) {
                    if (state.hasOwnProperty(key)) {
                            this.set(key, state[key]);
                    }
                }
                //TODO: are there callbacks that make sense in this case?  If so, attach them here as an option
                this.save();
            } finally {
                this.updating = false;
            }
        },

        _handle_status: function (msg, callbacks) {
            //execution_state : ('busy', 'idle', 'starting')
            if (this.comm !== undefined && msg.content.execution_state ==='idle') {
                // Send buffer if this message caused another message to be
                // throttled.
                if (this.msg_buffer !== null &&
                    this.msg_throttle === this.pending_msgs) {
                    var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
                    this.comm.send(data, callbacks);
                    this.msg_buffer = null;
                } else {
                    // Only decrease the pending message count if the buffer
                    // doesn't get flushed (sent).
                    --this.pending_msgs;
                }
            }
        },

        // Custom syncronization logic.
        _handle_sync: function (method, options) {
            var model_json = this.toJSON();
            var attr;

            // Only send updated state if the state hasn't been changed
            // during an update.
            if (this.comm !== undefined) {
                if (!this.updating) {
                    if (this.pending_msgs >= this.msg_throttle) {
                        // The throttle has been exceeded, buffer the current msg so
                        // it can be sent once the kernel has finished processing
                        // some of the existing messages.
                        if (method=='patch') {
                            if (this.msg_buffer === null) {
                                this.msg_buffer = $.extend({}, model_json); // Copy
                            }
                            for (attr in options.attrs) {
                                this.msg_buffer[attr] = options.attrs[attr];
                            }
                        } else {
                            this.msg_buffer = $.extend({}, model_json); // Copy
                        }

                    } else {
                        // We haven't exceeded the throttle, send the message like
                        // normal.  If this is a patch operation, just send the
                        // changes.
                        var send_json = model_json;
                        if (method =='patch') {
                            send_json = {};
                            for (attr in options.attrs) {
                                send_json[attr] = options.attrs[attr];
                            }
                        }

                        var data = {method: 'backbone', sync_method: method, sync_data: send_json};
                        this.comm.send(data, options.callbacks);
                        this.pending_msgs++;
                    }
                }
            }

            // Since the comm is a one-way communication, assume the message
            // arrived.
            return model_json;
        },

    });

    //--------------------------------------------------------------------
    // WidgetView class
    //--------------------------------------------------------------------
    var BaseWidgetView = Backbone.View.extend({
        initialize: function(options) {
            this.model.on('change',this.update,this);
            this.widget_manager = options.widget_manager;
            this.comm_manager = options.widget_manager.comm_manager;
            this.cell = options.cell;
            this.child_views = [];
        },

        update: function(){
            // update view to be consistent with this.model
            // triggered on model change
        },

        child_view: function(comm_id, view_name) {
            // create and return a child view, given a comm id for a model and (optionally) a view name
            // if the view name is not given, it defaults to the model's default view attribute
            var child_model = this.comm_manager.comms[comm_id].model;
            var child_view = this.widget_manager.create_view(child_model, view_name, this.cell);
            this.child_views.push(child_view);
            return child_view;
        },

        render: function(){
            // render the view.  By default, this is only called the first time the view is created
        },
        send: function (content) {
            this.model.send(content, this._callbacks());
        },

        touch: function () {
            this.model.save(this.model.changedAttributes(), {patch: true, callbacks: this._callbacks()});
        },

        _callbacks: function () {
            // callback handlers specific to this view's cell
            var callbacks = {};
            var cell = this.cell;
            if (cell !== null) {
                // Try to get output handlers
                var handle_output = null;
                var handle_clear_output = null;
                if (cell.output_area !== undefined && cell.output_area !== null) {
                    handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
                    handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
                }

                // Create callback dict using what is known
                var that = this;
                callbacks = {
                    iopub : {
                        output : handle_output,
                        clear_output : handle_clear_output,

                        status : function (msg) {
                            that.model._handle_status(msg, that._callbacks());
                        },

                        // Special function only registered by widget messages.
                        // Allows us to get the cell for a message so we know
                        // where to add widgets if the code requires it.
                        get_cell : function () {
                            return cell;
                        },
                    },
                };
            }
            return callbacks;
        },

    });

    var WidgetView = BaseWidgetView.extend({
        initialize: function (options) {
            // TODO: make changes more granular (e.g., trigger on visible:change)
            this.model.on('change', this.update, this);
            this.model.on('msg:custom', this.on_msg, this);
            BaseWidgetView.prototype.initialize.apply(this, arguments);
        },

        on_msg: function(msg) {
            switch(msg.msg_type) {
                case 'add_class':
                    this.add_class(msg.selector, msg.class_list);
                    break;
                case 'remove_class':
                    this.remove_class(msg.selector, msg.class_list);
                    break;
            }
        },

        add_class: function (selector, class_list) {
            var elements = this._get_selector_element(selector);
            if (elements.length > 0) {
                elements.addClass(class_list);
            }
        },

        remove_class: function (selector, class_list) {
            var elements = this._get_selector_element(selector);
            if (elements.length > 0) {
                elements.removeClass(class_list);
            }
        },

        update: function () {
            // the very first update seems to happen before the element is finished rendering
            // so we use setTimeout to give the element time to render
            var e = this.$el;
            var visible = this.model.get('visible');
            setTimeout(function() {e.toggle(visible)},0);

            var css = this.model.get('_css');
            if (css === undefined) {return;}
            for (var selector in css) {
                if (css.hasOwnProperty(selector)) {
                    // Apply the css traits to all elements that match the selector.
                    var elements = this._get_selector_element(selector);
                    if (elements.length > 0) {
                        var css_traits = css[selector];
                        for (var css_key in css_traits) {
                            if (css_traits.hasOwnProperty(css_key)) {
                                elements.css(css_key, css_traits[css_key]);
                            }
                        }
                    }
                }
            }
        },

        _get_selector_element: function (selector) {
            // Get the elements via the css selector.  If the selector is
            // blank, apply the style to the $el_to_style element.  If
            // the $el_to_style element is not defined, use apply the
            // style to the view's element.
            var elements;
            if (selector === undefined || selector === null || selector === '') {
                if (this.$el_to_style === undefined) {
                    elements = this.$el;
                } else {
                    elements = this.$el_to_style;
                }
            } else {
                elements = this.$el.find(selector);
            }
            return elements;
        },
    });

    IPython.WidgetModel = WidgetModel;
    IPython.WidgetView = WidgetView;
    IPython.BaseWidgetView = BaseWidgetView;

    return widget_manager;
});
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */
const _ = require('lodash');
const util = require('util');
// Parameter used for jsonp callback is constant, as far as
// blueprints are concerned (for now.)
const JSONP_CALLBACK_PARAM = 'callback';

// TODO:
//
// Replace the following helper with the version in sails.util:

// Attempt to parse JSON
// If the parse fails, return the error object
// If JSON is falsey, return null
// (this is so that it will be ignored if not specified)
function tryToParseJSON(json) {
    if (!_.isString(json)) return null;
    try {
        return JSON.parse(json);
    } catch (e) {
        return e;
    }
}

/**
 * Utility methods used in built-in blueprint actions.
 *
 * @type {Object}
 */
module.exports = {
    /**
     * Prepare records for create / update using Sails 1.0 relationship methods
     *
     * This method will alter the DATA object! (it must prevent many relations from directly saving)
     *
     * @param {Associations} associations Definition of the associations, from `req.option.associations`
     * @param {Object} data The individual record that is about to be persisted
     * @return {Array} The returned structure can be utilized by controller actions create / update
     */
    prepareManyRelations(associations, data) {
        const targets = [];
        associations.forEach(assoc => {
            if (assoc.type === 'collection' && Array.isArray(data[assoc.alias])) {
                targets.push({
                    collection: assoc.alias,
                    values: data[assoc.alias]
                });
                delete data[assoc.alias];
            }
        });
        return targets;
    },

    /**
     * Extend the model's `associations` property with the presentation configuration (taken from the Model's attributes `includeIn` option or from Sails configuration)
     * @param  {Waterline Collection}  Model
     * @return {Array}                 Extended version of the Model.associations with `includeIn` defintions
     */
    getAssociationConfiguration(Model, style) {
        // get configured defaults or always embed full records
        let presentationDefaults = sails.config.models.associations || {
            list: 'record',
            detail: 'record'
        };
        let associations = Model.associations;
        let attributes = Model.attributes;
        associations.forEach(assoc => {
            assoc.include = _.extend({}, presentationDefaults, attributes[assoc.alias].includeIn)[style]; // extend association object with presentation configuration
            if (attributes[assoc.alias].through) {
                assoc.through = attributes[assoc.alias].through;
            }
        });
        return associations;
    },
    /**
     * helper function to populate a record with an array for indexes for associated models, running various Waterline queries on the join tables if neccessary ( defined as: include -> index )
     * @param  {Waterine Collection}   parentModel  [description]
     * @param  {Array|Integer}   ids          [description]
     * @param  {[type]}   associations [description]
     * @param  {Function} done         [description]
     */
    populateIndexes(parentModel, ids, associations, done) {
        const hash = {};
        associations.forEach(association => {
            if (association.include === 'index') {
                if (association.collection && !association.through) {
                    let assocCriteria = {};
                    let assocModel = sails.models[association.collection];
                    assocCriteria[association.via] = ids;
                    hash[association.alias] = done => {
                        assocModel.find(assocCriteria).exec(done); //it may be necessary to implement .limit() here at some point...
                    };
                }
            }
        });
        async.parallel(hash, done);
    },

    /**
     * helper function to populate a Waterline query according to the model definition include -> record
     * @param  {[type]} query        [description]
     * @param  {[type]} associations [description]
     * @return {[type]}              [description]
     */
    populateRecords(query, associations, force) {
        associations.forEach(assoc => {
            // if the associations is to be populated with the full records...
            if (assoc.include === 'record' || (assoc.through && assoc.include !== 'link') || force) {
                query.populate(assoc.alias);
            }
        });
        return query;
    },

    /**
     * Given a Waterline query, populate the appropriate/specified
     * association attributes and return it so it can be chained
     * further ( i.e. so you can .exec() it )
     *
     * @param   {Query} query               [waterline query object]
     * @param   {Request} req
     * @return {Query}
     */
    populateEach(query, req) {
        const DEFAULT_POPULATE_LIMIT = sails.config.blueprints.defaultLimit || 30;
        const _options = req.options;

        return _(_options.associations).reduce((query, association) => {
            return query.populate(association.alias, {
                limit: populationLimit
            });
        }, query);
    },

    /**
     * Subscribe deep (associations)
     * This likely needs to be refactored to fully support sails 1.0.
     * @param   {[type]} associations   [description]
     * @param   {[type]} record         [description]
     * @return {[type]}                 [description]
     */
    subscribeDeep(req, record) {
        req.options.associations.forEach(assoc => {
            // Look up identity of associated model
            let ident = assoc[assoc.type];
            let AssociatedModel = sails.models[ident];
            if (req.options.autoWatch) {
                AssociatedModel._watch(req);
            }

            // Subscribe to each associated model instance in a collection
            if (assoc.type === 'collection') {
                record[assoc.alias].forEach(associatedInstance => {
                    AssociatedModel.subscribe(req, [associatedInstance[AssociatedModel.primaryKey]]);
                });
            } else if (assoc.type === 'model' && record[assoc.alias]) {
                // If there is an associated to-one model instance, subscribe to it
                AssociatedModel.subscribe(req, [record[assoc.alias][AssociatedModel.primaryKey]]);
            }
        });
    },

    /**
     * Parse primary key value for use in a Waterline criteria
     * (e.g. for `find`, `update`, or `destroy`)
     *
     * @param   {Request} req
     * @return {Integer|String}
     */
    parsePk(req) {
        const pk = req.options.id || (req.options.where && req.options.where.id) || req.param('id');

        // TODO: make this smarter...
        // (e.g. look for actual primary key of model and look for it
        //  in the absence of `id`.)
        // See coercePK for reference (although be aware it is not currently in use)

        // exclude criteria on id field
        return _.isPlainObject(pk) ? undefined : pk;
    },

    /**
     * Parse primary key value from parameters.
     * Throw an error if it cannot be retrieved.
     *
     * @param   {Request} req
     * @return {Integer|String}
     */
    requirePk(req) {
        const pk = module.exports.parsePk(req);

        // Validate the required `id` parameter
        if (!pk) {
            const err = new Error(
                'No `id` parameter provided.' +
                    "(Note: even if the model's primary key is not named `id`- " +
                    '`id` should be used as the name of the parameter- it will be ' +
                    'mapped to the proper primary key name)'
            );
            err.status = 400;
            throw err;
        }
        return pk;
    },

    /**
     * Parse `criteria` for a Waterline `find` or `update` from all
     * request parameters.
     *
     * @param   {Request} req
     * @return {Object}                     the WHERE criteria object
     */
    parseCriteria(req) {
        const Model = module.exports.parseModel(req);
        // Allow customizable blacklist for params NOT to include as criteria.
        req.options.criteria = req.options.criteria || {};
        req.options.criteria.blacklist = req.options.criteria.blacklist || ['limit', 'skip', 'sort', 'populate'];
        // Validate blacklist to provide a more helpful error msg.
        const blacklist = req.options.criteria && req.options.criteria.blacklist;
        if (blacklist && !Array.isArray(blacklist)) {
            throw new Error(
                'Invalid `req.options.criteria.blacklist`. Should be an array of strings (parameter names.)'
            );
        }

        // Look for explicitly specified `where` parameter.
        let where = req.allParams().where;

        // If `where` parameter is a string, try to interpret it as JSON
        if (_.isString(where)) {
            where = tryToParseJSON(where);
        }

        // If `where` has not been specified, but other unbound parameter variables
        // **ARE** specified, build the `where` option using them.
        if (!where) {
            // Prune params which aren't fit to be used as `where` criteria
            // to build a proper where query
            where = req.allParams();

            // Omit built-in runtime config (like query modifiers)
            where = _.omit(where, blacklist || ['limit', 'skip', 'sort']);

            // Omit any params w/ undefined values
            where = _.omit(where, p => {
                if (_.isUndefined(p)) return true;
            });

            // Transform ids[ .., ..] request
            if (where.ids || where.id) {
                let key = where.ids ? 'ids' : 'id';
                let tmp = where.ids || where.id;
                delete where[key];
                where[Model.primaryKey] = tmp;
            }

            // Omit jsonp callback param (but only if jsonp is enabled)
            let jsonpOpts = req.options.jsonp && !req.isSocket;
            jsonpOpts = _.isObject(jsonpOpts)
                ? jsonpOpts
                : {
                      callback: JSONP_CALLBACK_PARAM
                  };
            if (jsonpOpts) {
                where = _.omit(where, [jsonpOpts.callback]);
            }
        }

        // Merge w/ req.options.where and return
        where = _.merge({}, req.options.where || {}, where) || undefined;

        return where;
    },

    /**
     * Parse `values` for a Waterline `create` or `update` from all
     * request parameters.
     *
     * @param   {Request} req
     * @return {Object}
     */
    parseValues(req, model) {
        // Create data object (monolithic combination of all parameters)
        // Omit the blacklisted params (like JSONP callback param, etc.)

        // Allow customizable blacklist for params NOT to include as values.
        req.options.values = req.options.values || {};

        // Validate blacklist to provide a more helpful error msg.
        const blacklist = req.options.values.blacklist;
        if (blacklist && !Array.isArray(blacklist)) {
            throw new Error('Invalid `req.options.values.blacklist`. Should be an array of strings (parameter names.)');
        }

        // Get valufunctiones using the model identity as resource identifier
        let values = req.param(_.kebabCase(model.globalId)) || {};

        // Omit built-in runtime config (like query modifiers)
        values = _.omit(values, blacklist || []);

        // Omit any params w/ undefined values
        values = _.omit(values, p => {
            if (_.isUndefined(p)) return true;
        });

        // Omit jsonp callback param (but only if jsonp is enabled)
        let jsonpOpts = req.options.jsonp && !req.isSocket;
        jsonpOpts = _.isObject(jsonpOpts)
            ? jsonpOpts
            : {
                  callback: JSONP_CALLBACK_PARAM
              };
        if (jsonpOpts) {
            values = _.omit(values, [jsonpOpts.callback]);
        }

        return values;
    },

    /**
     * Determine the model class to use w/ this blueprint action.
     * @param   {Request} req
     * @return {WLCollection}
     */
    parseModel(req) {
        // Ensure a model can be deduced from the request options.
        const model = req.options.model || req.options.controller;
        if (!model) throw new Error(util.format('No "model" specified in route options.'));

        const Model = req._sails.models[model];
        if (!Model)
            throw new Error(
                util.format('Invalid route option, "model".\nI don\'t know about any models named: `%s`', model)
            );

        return Model;
    },

    /**
     * @param   {Request} req
     */
    parseSort(req) {
        let sort = req.param('sort') || req.options.sort;
        if (_.isUndefined(sort)) {
            return undefined;
        }

        // If `sort` is a string, attempt to JSON.parse() it.
        // (e.g. `{"name": 1}`)
        if (_.isString(sort)) {
            try {
                sort = JSON.parse(sort);
            } catch (e) {
                // If it is not valid JSON, then fall back to interpreting it as-is.
                // (e.g. "name ASC")
            }
        }
        return sort;
    },

    /**
     * @param   {Request} req
     */
    parseLimit(req) {
        const DEFAULT_LIMIT = sails.config.blueprints.defaultLimit || false;
        let limit =
            req.param('limit') || (typeof req.options.limit !== 'undefined' ? req.options.limit : DEFAULT_LIMIT);
        if (limit) {
            limit = +limit;
        }
        return limit;
    },

    /**
     * @param   {Request} req
     */
    parseSkip(req) {
        const DEFAULT_SKIP = 0;
        let skip = req.param('skip') || (typeof req.options.skip !== 'undefined' ? req.options.skip : DEFAULT_SKIP);
        if (skip) {
            skip = +skip;
        }
        return skip;
    },

    /**
     * @param   {Request} req
     */
    parseLocals(req) {
        return typeof req.options.locals !== 'undefined' && req.options.locals !== null ? req.options.locals : null;
    }
};
<commit_msg>Add population limit to actonUtil.<commit_after>/**
 * Module dependencies
 */
const _ = require('lodash');
const util = require('util');
// Parameter used for jsonp callback is constant, as far as
// blueprints are concerned (for now.)
const JSONP_CALLBACK_PARAM = 'callback';

// TODO:
//
// Replace the following helper with the version in sails.util:

// Attempt to parse JSON
// If the parse fails, return the error object
// If JSON is falsey, return null
// (this is so that it will be ignored if not specified)
function tryToParseJSON(json) {
    if (!_.isString(json)) return null;
    try {
        return JSON.parse(json);
    } catch (e) {
        return e;
    }
}

/**
 * Utility methods used in built-in blueprint actions.
 *
 * @type {Object}
 */
module.exports = {
    /**
     * Prepare records for create / update using Sails 1.0 relationship methods
     *
     * This method will alter the DATA object! (it must prevent many relations from directly saving)
     *
     * @param {Associations} associations Definition of the associations, from `req.option.associations`
     * @param {Object} data The individual record that is about to be persisted
     * @return {Array} The returned structure can be utilized by controller actions create / update
     */
    prepareManyRelations(associations, data) {
        const targets = [];
        associations.forEach(assoc => {
            if (assoc.type === 'collection' && Array.isArray(data[assoc.alias])) {
                targets.push({
                    collection: assoc.alias,
                    values: data[assoc.alias]
                });
                delete data[assoc.alias];
            }
        });
        return targets;
    },

    /**
     * Extend the model's `associations` property with the presentation configuration (taken from the Model's attributes `includeIn` option or from Sails configuration)
     * @param  {Waterline Collection}  Model
     * @return {Array}                 Extended version of the Model.associations with `includeIn` defintions
     */
    getAssociationConfiguration(Model, style) {
        // get configured defaults or always embed full records
        let presentationDefaults = sails.config.models.associations || {
            list: 'record',
            detail: 'record'
        };
        let associations = Model.associations;
        let attributes = Model.attributes;
        associations.forEach(assoc => {
            assoc.include = _.extend({}, presentationDefaults, attributes[assoc.alias].includeIn)[style]; // extend association object with presentation configuration
            if (attributes[assoc.alias].through) {
                assoc.through = attributes[assoc.alias].through;
            }
        });
        return associations;
    },
    /**
     * helper function to populate a record with an array for indexes for associated models, running various Waterline queries on the join tables if neccessary ( defined as: include -> index )
     * @param  {Waterine Collection}   parentModel  [description]
     * @param  {Array|Integer}   ids          [description]
     * @param  {[type]}   associations [description]
     * @param  {Function} done         [description]
     */
    populateIndexes(parentModel, ids, associations, done) {
        const hash = {};
        associations.forEach(association => {
            if (association.include === 'index') {
                if (association.collection && !association.through) {
                    let assocCriteria = {};
                    let assocModel = sails.models[association.collection];
                    assocCriteria[association.via] = ids;
                    hash[association.alias] = done => {
                        assocModel.find(assocCriteria).exec(done); //it may be necessary to implement .limit() here at some point...
                    };
                }
            }
        });
        async.parallel(hash, done);
    },

    /**
     * helper function to populate a Waterline query according to the model definition include -> record
     * @param  {[type]} query        [description]
     * @param  {[type]} associations [description]
     * @return {[type]}              [description]
     */
    populateRecords(query, associations, force) {
        associations.forEach(assoc => {
            // if the associations is to be populated with the full records...
            if (assoc.include === 'record' || (assoc.through && assoc.include !== 'link') || force) {
                query.populate(assoc.alias);
            }
        });
        return query;
    },

    /**
     * Given a Waterline query, populate the appropriate/specified
     * association attributes and return it so it can be chained
     * further ( i.e. so you can .exec() it )
     *
     * @param   {Query} query               [waterline query object]
     * @param   {Request} req
     * @return {Query}
     */
    populateEach(query, req) {
        const DEFAULT_POPULATE_LIMIT = sails.config.blueprints.defaultLimit || 30;
        const _options = req.options;
        const populationLimit = req.param('limit') || _options.limit || DEFAULT_POPULATE_LIMIT;

        return _(_options.associations).reduce((query, association) => {
            return query.populate(association.alias, {
                limit: populationLimit
            });
        }, query);
    },

    /**
     * Subscribe deep (associations)
     * This likely needs to be refactored to fully support sails 1.0.
     * @param   {[type]} associations   [description]
     * @param   {[type]} record         [description]
     * @return {[type]}                 [description]
     */
    subscribeDeep(req, record) {
        req.options.associations.forEach(assoc => {
            // Look up identity of associated model
            let ident = assoc[assoc.type];
            let AssociatedModel = sails.models[ident];
            if (req.options.autoWatch) {
                AssociatedModel._watch(req);
            }

            // Subscribe to each associated model instance in a collection
            if (assoc.type === 'collection') {
                record[assoc.alias].forEach(associatedInstance => {
                    AssociatedModel.subscribe(req, [associatedInstance[AssociatedModel.primaryKey]]);
                });
            } else if (assoc.type === 'model' && record[assoc.alias]) {
                // If there is an associated to-one model instance, subscribe to it
                AssociatedModel.subscribe(req, [record[assoc.alias][AssociatedModel.primaryKey]]);
            }
        });
    },

    /**
     * Parse primary key value for use in a Waterline criteria
     * (e.g. for `find`, `update`, or `destroy`)
     *
     * @param   {Request} req
     * @return {Integer|String}
     */
    parsePk(req) {
        const pk = req.options.id || (req.options.where && req.options.where.id) || req.param('id');

        // TODO: make this smarter...
        // (e.g. look for actual primary key of model and look for it
        //  in the absence of `id`.)
        // See coercePK for reference (although be aware it is not currently in use)

        // exclude criteria on id field
        return _.isPlainObject(pk) ? undefined : pk;
    },

    /**
     * Parse primary key value from parameters.
     * Throw an error if it cannot be retrieved.
     *
     * @param   {Request} req
     * @return {Integer|String}
     */
    requirePk(req) {
        const pk = module.exports.parsePk(req);

        // Validate the required `id` parameter
        if (!pk) {
            const err = new Error(
                'No `id` parameter provided.' +
                    "(Note: even if the model's primary key is not named `id`- " +
                    '`id` should be used as the name of the parameter- it will be ' +
                    'mapped to the proper primary key name)'
            );
            err.status = 400;
            throw err;
        }
        return pk;
    },

    /**
     * Parse `criteria` for a Waterline `find` or `update` from all
     * request parameters.
     *
     * @param   {Request} req
     * @return {Object}                     the WHERE criteria object
     */
    parseCriteria(req) {
        const Model = module.exports.parseModel(req);
        // Allow customizable blacklist for params NOT to include as criteria.
        req.options.criteria = req.options.criteria || {};
        req.options.criteria.blacklist = req.options.criteria.blacklist || ['limit', 'skip', 'sort', 'populate'];
        // Validate blacklist to provide a more helpful error msg.
        const blacklist = req.options.criteria && req.options.criteria.blacklist;
        if (blacklist && !Array.isArray(blacklist)) {
            throw new Error(
                'Invalid `req.options.criteria.blacklist`. Should be an array of strings (parameter names.)'
            );
        }

        // Look for explicitly specified `where` parameter.
        let where = req.allParams().where;

        // If `where` parameter is a string, try to interpret it as JSON
        if (_.isString(where)) {
            where = tryToParseJSON(where);
        }

        // If `where` has not been specified, but other unbound parameter variables
        // **ARE** specified, build the `where` option using them.
        if (!where) {
            // Prune params which aren't fit to be used as `where` criteria
            // to build a proper where query
            where = req.allParams();

            // Omit built-in runtime config (like query modifiers)
            where = _.omit(where, blacklist || ['limit', 'skip', 'sort']);

            // Omit any params w/ undefined values
            where = _.omit(where, p => {
                if (_.isUndefined(p)) return true;
            });

            // Transform ids[ .., ..] request
            if (where.ids || where.id) {
                let key = where.ids ? 'ids' : 'id';
                let tmp = where.ids || where.id;
                delete where[key];
                where[Model.primaryKey] = tmp;
            }

            // Omit jsonp callback param (but only if jsonp is enabled)
            let jsonpOpts = req.options.jsonp && !req.isSocket;
            jsonpOpts = _.isObject(jsonpOpts)
                ? jsonpOpts
                : {
                      callback: JSONP_CALLBACK_PARAM
                  };
            if (jsonpOpts) {
                where = _.omit(where, [jsonpOpts.callback]);
            }
        }

        // Merge w/ req.options.where and return
        where = _.merge({}, req.options.where || {}, where) || undefined;

        return where;
    },

    /**
     * Parse `values` for a Waterline `create` or `update` from all
     * request parameters.
     *
     * @param   {Request} req
     * @return {Object}
     */
    parseValues(req, model) {
        // Create data object (monolithic combination of all parameters)
        // Omit the blacklisted params (like JSONP callback param, etc.)

        // Allow customizable blacklist for params NOT to include as values.
        req.options.values = req.options.values || {};

        // Validate blacklist to provide a more helpful error msg.
        const blacklist = req.options.values.blacklist;
        if (blacklist && !Array.isArray(blacklist)) {
            throw new Error('Invalid `req.options.values.blacklist`. Should be an array of strings (parameter names.)');
        }

        // Get valufunctiones using the model identity as resource identifier
        let values = req.param(_.kebabCase(model.globalId)) || {};

        // Omit built-in runtime config (like query modifiers)
        values = _.omit(values, blacklist || []);

        // Omit any params w/ undefined values
        values = _.omit(values, p => {
            if (_.isUndefined(p)) return true;
        });

        // Omit jsonp callback param (but only if jsonp is enabled)
        let jsonpOpts = req.options.jsonp && !req.isSocket;
        jsonpOpts = _.isObject(jsonpOpts)
            ? jsonpOpts
            : {
                  callback: JSONP_CALLBACK_PARAM
              };
        if (jsonpOpts) {
            values = _.omit(values, [jsonpOpts.callback]);
        }

        return values;
    },

    /**
     * Determine the model class to use w/ this blueprint action.
     * @param   {Request} req
     * @return {WLCollection}
     */
    parseModel(req) {
        // Ensure a model can be deduced from the request options.
        const model = req.options.model || req.options.controller;
        if (!model) throw new Error(util.format('No "model" specified in route options.'));

        const Model = req._sails.models[model];
        if (!Model)
            throw new Error(
                util.format('Invalid route option, "model".\nI don\'t know about any models named: `%s`', model)
            );

        return Model;
    },

    /**
     * @param   {Request} req
     */
    parseSort(req) {
        let sort = req.param('sort') || req.options.sort;
        if (_.isUndefined(sort)) {
            return undefined;
        }

        // If `sort` is a string, attempt to JSON.parse() it.
        // (e.g. `{"name": 1}`)
        if (_.isString(sort)) {
            try {
                sort = JSON.parse(sort);
            } catch (e) {
                // If it is not valid JSON, then fall back to interpreting it as-is.
                // (e.g. "name ASC")
            }
        }
        return sort;
    },

    /**
     * @param   {Request} req
     */
    parseLimit(req) {
        const DEFAULT_LIMIT = sails.config.blueprints.defaultLimit || false;
        let limit =
            req.param('limit') || (typeof req.options.limit !== 'undefined' ? req.options.limit : DEFAULT_LIMIT);
        if (limit) {
            limit = +limit;
        }
        return limit;
    },

    /**
     * @param   {Request} req
     */
    parseSkip(req) {
        const DEFAULT_SKIP = 0;
        let skip = req.param('skip') || (typeof req.options.skip !== 'undefined' ? req.options.skip : DEFAULT_SKIP);
        if (skip) {
            skip = +skip;
        }
        return skip;
    },

    /**
     * @param   {Request} req
     */
    parseLocals(req) {
        return typeof req.options.locals !== 'undefined' && req.options.locals !== null ? req.options.locals : null;
    }
};
<|endoftext|>


<commit_before>'use strict';

describe('Controller: demande', function() {
  let $controller;

  const RequestService = {
    getCompletion() {
      return true;
    },

    postAction() {
      return {
        then() {
          return true;
        }
      };
    }
  };

  const toastr = {
    error() {}
  };

  const $state = {
    go() {}
  };

  let request = {
    user: 'userId',
    prestations: [],
    status: 'en_cours',
    $update() {}
  };

  const currentUser = {};

  // load the service's module
  beforeEach(module('impactApp'));

  beforeEach(inject(function(_$controller_) {
    $controller = _$controller_;
  }));

  describe('prestations list', () => {
    let controller;

    //given
    let $scope = {};

    beforeEach(function() {
      controller = $controller('DemandeCtrl', {
        $state,
        $scope,
        currentUser,
        request,
        RequestService,
        toastr
      });
    });

    it('should have 16 prestations', function() {
      expect(controller.prestations.length, 16);
    });
  });

  describe('prestations request', () => {
    let controller;

    //given
    let $scope = {};

    beforeEach(function() {
      controller = $controller('DemandeCtrl', {
        $state,
        $scope,
        currentUser,
        request,
        RequestService,
        toastr
      });
    });

    it('should select the right prestations', function() {
      //given
      controller.cartestationnement = _.assign(controller.cartestationnement, {choice: true});
      controller.carteinvalidite = _.assign(controller.carteinvalidite, {choice: true, renouvellement: true});

      $scope.submit({$valid: true});

      //then
      expect($scope.request.renouvellements[0]).toEqual('carteinvalidite');
      expect($scope.request.prestations[0]).toEqual('cartestationnement');
    });

  });

});
<commit_msg>Add prestations list for the test<commit_after>'use strict';

describe('Controller: demande', function() {
  let $controller;

  const RequestService = {
    getCompletion() {
      return true;
    },

    postAction() {
      return {
        then() {
          return true;
        }
      };
    }
  };

  const toastr = {
    error() {}
  };

  const $state = {
    go() {}
  };

  let request = {
    user: 'userId',
    prestations: [],
    status: 'en_cours',
    $update() {}
  };

  const currentUser = {};

  const samplePrestations = {
      cartestationnement: {
        id: 'cartestationnement'
      },
      carteinvalidite: {
        id: 'carteinvalidite',
      },
      aeeh: {
        id: 'aeeh',
      },
      aah: {
        id: 'aah',
      },
      complement: {
        id: 'complement',
      },
      pch: {
        id: 'pch',
      },
      rqth: {
        id: 'rqth',
      },
      av: {
        id: 'av',
      },
      ems: {
        id: 'ems',
      },
      pps: {
        id: 'pps',
      },
      orp: {
        id: 'orp',
      },
      formation: {
        id: 'formation',
      },
      sms: {
        id: 'sms',
      },
      sms_enfant: {
        id: 'sms_enfant',
      },
      ac: {
        id: 'ac',
      },
      acfp: {
        id: 'acfp',
      }
    };

  // load the service's module
  beforeEach(module('impactApp'));

  beforeEach(inject(function(_$controller_) {
    $controller = _$controller_;
  }));

  describe('prestations list', () => {
    let controller;

    //given
    let $scope = {};

    beforeEach(function() {
      controller = $controller('DemandeCtrl', {
        $state,
        $scope,
        currentUser,
        request,
        RequestService,
        toastr,
        prestations: samplePrestations
      });
    });

    it('should have 16 prestations', function() {
      expect(controller.prestations.length, 16);
    });
  });

  describe('prestations request', () => {
    let controller;

    //given
    let $scope = {};

    beforeEach(function() {
      controller = $controller('DemandeCtrl', {
        $state,
        $scope,
        currentUser,
        request,
        RequestService,
        toastr,
        prestations: samplePrestations
      });
    });

    it('should select the right prestations', function() {
      //given
      controller.cartestationnement = _.assign(controller.cartestationnement, {choice: true});
      controller.carteinvalidite = _.assign(controller.carteinvalidite, {choice: true, renouvellement: true});

      $scope.submit({$valid: true});

      //then
      expect($scope.request.renouvellements[0]).toEqual('carteinvalidite');
      expect($scope.request.prestations[0]).toEqual('cartestationnement');
    });

  });

});
<|endoftext|>


<commit_before>define(['backbone', 'modules/shipment/models/platetype', 'utils/kvcollection'], function(PageableCollection,  PlateType, KVCollection) {

    var plate_types = [
         { name: 'Puck', capacity: 16 },

         { name: 'ReferencePlate', well_per_row: 2,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 4
         },

         { name: 'CrystalQuickX', well_per_row: 12,
             drop_per_well_x: 2, drop_per_well_y: 1,
             drop_height: 0.5, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*2
        },
         { name: 'Greiner 3 Drop', well_per_row: 12,
             drop_per_well_x: 3, drop_per_well_y: 1,
             drop_height: 0.5, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*3
        },
         { name: 'MRC Maxi', well_per_row: 6,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 0.5,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 48
        },
         { name: 'MitegenInSitu', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 0.5, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96
        },
         { name: 'MRC 2 Drop', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 2,
             drop_height: 1, drop_width: 0.5,
             drop_offset_x: 0.5, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*2
        },
         { name: 'Griener 1536', well_per_row: 12,
             drop_per_well_x: 4, drop_per_well_y: 4,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*4*4
        },
         { name: '3 Drop Square', well_per_row: 12,
             drop_per_well_x: 2, drop_per_well_y: 2,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: 3,
             capacity: 96*3
        },

         { name: 'SWISSCI 3 Drop', well_per_row: 12,
             drop_per_well_x: 2, drop_per_well_y: 2,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: 1,
             capacity: 96*3
        },
         { name: '1 drop', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 0.5, drop_width: 0.5,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96
        },
        { name: 'FilmBatch', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96
        },
         { name: 'LCP Glass', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96
        },/*
         { name: 'Linbro 24 well', well_per_row: 6,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 24
        },*/
    ]

    var PlateTypes = Backbone.Collection.extend(_.extend({}, KVCollection, {
        model: PlateType,

        keyAttribute: 'name',
        valueAttribute: 'name',

        initialize: function(options) {
            this.on('change:isSelected', this.onSelectedChanged, this);
        },

        onSelectedChanged: function(model) {
            this.each(function(model) {
                if (model.get('isSelected') === true && !model._changing) {
                    model.set({isSelected: false}, { silent: true })
                }
            })
            console.log('trigger selected change')
            this.trigger('selected:change')
        },

    }))

    return new PlateTypes(plate_types)
})<commit_msg>consider mitegen insitu as having two drops per well for imaging purposes<commit_after>define(['backbone', 'modules/shipment/models/platetype', 'utils/kvcollection'], function(PageableCollection,  PlateType, KVCollection) {

    var plate_types = [
         { name: 'Puck', capacity: 16 },

         { name: 'ReferencePlate', well_per_row: 2,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 4
         },

         { name: 'CrystalQuickX', well_per_row: 12,
             drop_per_well_x: 2, drop_per_well_y: 1,
             drop_height: 0.5, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*2
        },
         { name: 'Greiner 3 Drop', well_per_row: 12,
             drop_per_well_x: 3, drop_per_well_y: 1,
             drop_height: 0.5, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*3
        },
         { name: 'MRC Maxi', well_per_row: 6,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 0.5,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 48
        },
         { name: 'MitegenInSitu', well_per_row: 12,
             drop_per_well_x: 2, drop_per_well_y: 1,
             drop_height: 0.5, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*2
        },
         { name: 'MRC 2 Drop', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 2,
             drop_height: 1, drop_width: 0.5,
             drop_offset_x: 0.5, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*2
        },
         { name: 'Griener 1536', well_per_row: 12,
             drop_per_well_x: 4, drop_per_well_y: 4,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96*4*4
        },
         { name: '3 Drop Square', well_per_row: 12,
             drop_per_well_x: 2, drop_per_well_y: 2,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: 3,
             capacity: 96*3
        },

         { name: 'SWISSCI 3 Drop', well_per_row: 12,
             drop_per_well_x: 2, drop_per_well_y: 2,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: 1,
             capacity: 96*3
        },
         { name: '1 drop', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 0.5, drop_width: 0.5,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96
        },
        { name: 'FilmBatch', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96
        },
         { name: 'LCP Glass', well_per_row: 12,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 96
        },/*
         { name: 'Linbro 24 well', well_per_row: 6,
             drop_per_well_x: 1, drop_per_well_y: 1,
             drop_height: 1, drop_width: 1,
             drop_offset_x: 0, drop_offset_y: 0,
             well_drop: -1,
             capacity: 24
        },*/
    ]

    var PlateTypes = Backbone.Collection.extend(_.extend({}, KVCollection, {
        model: PlateType,

        keyAttribute: 'name',
        valueAttribute: 'name',

        initialize: function(options) {
            this.on('change:isSelected', this.onSelectedChanged, this);
        },

        onSelectedChanged: function(model) {
            this.each(function(model) {
                if (model.get('isSelected') === true && !model._changing) {
                    model.set({isSelected: false}, { silent: true })
                }
            })
            console.log('trigger selected change')
            this.trigger('selected:change')
        },

    }))

    return new PlateTypes(plate_types)
})<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

import './style.css';
import { Wrapper } from './style.js';

const FeaturedItems = ({ children }) =>
  <Wrapper>
    {children}
  </Wrapper>;

FeaturedItems.propTypes = {
  children: PropTypes.node.isRequired,
};

export default FeaturedItems;
<commit_msg>Remove style.css import<commit_after>import React from 'react';
import PropTypes from 'prop-types';

import { Wrapper } from './style.js';

const FeaturedItems = ({ children }) =>
  <Wrapper>
    {children}
  </Wrapper>;

FeaturedItems.propTypes = {
  children: PropTypes.node.isRequired,
};

export default FeaturedItems;
<|endoftext|>


<commit_before>/**
 * this module is here purely for compatibility with
 * modules/dc/views/getdcview.js
 */
export default {}
<commit_msg>changed comment into TODO<commit_after>/**
 * TODO: this module is here purely for compatibility with
 * modules/dc/views/getdcview.js
 */
export default {}
<|endoftext|>


<commit_before>import React from "react";
import { connect } from "react-redux";
import { push } from "react-router-redux";
import { Link } from "react-router-dom";
import { RelativeTime, ProgressBar, Icon } from "../../base";
import { find, get } from "lodash-es";
import { Panel, Table, Row, ListGroup } from "react-bootstrap";

const ReferenceHeader = ({ name, createdAt, user, refId }) => (

        <Row>
            {name}
            <Link to={{state: {newReference: true, cloneReference: true, refId}}} style={{float: "right"}}>
                <Icon name="clone" bsStyle="warning" tip="Clone Reference" />
            </Link>
        </Row>
        <Row>

                Created <RelativeTime time={createdAt} /> by {user}

        </Row>

);

const ReferenceMetadata = ({ data_type, organism, origin, latest_build, progress }) => {

    let buildInfo;

    if (progress === 100) {
        if (latest_build) {
            buildInfo = (
                <React.Fragment>

                        Latest Build

                            Created <RelativeTime time={latest_build.created_at} /> by {latest_build.user.id}

                        Index Build ID

                            {latest_build.id}

                        Index Build Version

                            {latest_build.version}

                </React.Fragment>
            );
        } else {
            buildInfo = (

                    Latest Build

                        No Build Found

            );
        }
    }

    return (

                    Data Type

                        {data_type}

                    Organism

                        {organism || "unknown"}

                    {origin.method}

                        {origin.fileName}

                {buildInfo}

    );
};

const getOrigin = (props) => {
    let origin;

    if (get(props, "imported_from", null)) {
        origin = {
            method: "Imported from file",
            fileName: props.imported_from.name
        };
    } else if (get(props, "cloned_from", null)) {
        origin = {
            method: "Cloned from",
            fileName: props.cloned_from.name
        };
    } else {
        origin = {
            method: "Created",
            fileName: "No File"
        };
    }

    return origin;
};

const ReferenceItem = (props) => {

    const origin = getOrigin(props);

    let progress = 0;
    let step;

    if (props.process && props.processes.length) {
        const process = find(props.processes, ["id", props.process.id]);
        progress = process.progress;
        step = process.step;
        progress *= 100;
    } else {
        step = "None";
        progress = 100;
    }

    return (
        <Panel className="reference-item" onClick={props.onClick}>
            <Panel.Heading>
                <ReferenceHeader name={props.name} createdAt={props.created_at} user={props.user.id} refId={props.id} />
            </Panel.Heading>

            <ReferenceMetadata {...props} origin={origin} progress={progress} />

            <Panel.Body style={{padding: 0, textAlign: "center"}}>
                <span style={{visibility: `${progress === 100 ? "hidden" : "visible"}`, fontSize: "small"}}>
                    {step}

            </Panel.Body>

            <ListGroup>
                <ProgressBar
                    bsStyle={progress === 100 ? "success" : "warning"}
                    now={progress}
                    affixed
                />
            </ListGroup>
        </Panel>
    );
};

const mapStateToProps = state => ({
    processes: state.processes.documents
});

const mapDispatchToProps = (dispatch, ownProps) => ({
    onClick: (e) => {
        if (e.target.nodeName !== "I") {
            dispatch(push(`/refs/${ownProps.id}`));
        }
    }
});

export default connect(mapStateToProps, mapDispatchToProps)(ReferenceItem);
<commit_msg>Change ref clone link color<commit_after>import React from "react";
import { connect } from "react-redux";
import { push } from "react-router-redux";
import { Link } from "react-router-dom";
import { RelativeTime, ProgressBar, Icon } from "../../base";
import { find, get } from "lodash-es";
import { Panel, Table, Row, ListGroup } from "react-bootstrap";

const ReferenceHeader = ({ name, createdAt, user, refId }) => (

        <Row>
            {name}
            <Link to={{state: {newReference: true, cloneReference: true, refId}}} style={{float: "right"}}>
                <Icon
                    name="clone"
                    tip="Clone"
                    style="default"
                />
            </Link>
        </Row>
        <Row>

                Created <RelativeTime time={createdAt} /> by {user}

        </Row>

);

const ReferenceMetadata = ({ data_type, organism, origin, latest_build, progress }) => {

    let buildInfo;

    if (progress === 100) {
        if (latest_build) {
            buildInfo = (
                <React.Fragment>

                        Latest Build

                            Created <RelativeTime time={latest_build.created_at} /> by {latest_build.user.id}

                        Index Build ID

                            {latest_build.id}

                        Index Build Version

                            {latest_build.version}

                </React.Fragment>
            );
        } else {
            buildInfo = (

                    Latest Build

                        No Build Found

            );
        }
    }

    return (

                    Data Type

                        {data_type}

                    Organism

                        {organism || "unknown"}

                    {origin.method}

                        {origin.fileName}

                {buildInfo}

    );
};

const getOrigin = (props) => {
    let origin;

    if (get(props, "imported_from", null)) {
        origin = {
            method: "Imported from file",
            fileName: props.imported_from.name
        };
    } else if (get(props, "cloned_from", null)) {
        origin = {
            method: "Cloned from",
            fileName: props.cloned_from.name
        };
    } else {
        origin = {
            method: "Created",
            fileName: "No File"
        };
    }

    return origin;
};

const ReferenceItem = (props) => {

    const origin = getOrigin(props);

    let progress = 0;
    let step;

    if (props.process && props.processes.length) {
        const process = find(props.processes, ["id", props.process.id]);
        progress = process.progress;
        step = process.step;
        progress *= 100;
    } else {
        step = "None";
        progress = 100;
    }

    return (
        <Panel className="reference-item" onClick={props.onClick}>
            <Panel.Heading>
                <ReferenceHeader name={props.name} createdAt={props.created_at} user={props.user.id} refId={props.id} />
            </Panel.Heading>

            <ReferenceMetadata {...props} origin={origin} progress={progress} />

            <Panel.Body style={{padding: 0, textAlign: "center"}}>
                <span style={{visibility: `${progress === 100 ? "hidden" : "visible"}`, fontSize: "small"}}>
                    {step}

            </Panel.Body>

            <ListGroup>
                <ProgressBar
                    bsStyle={progress === 100 ? "success" : "warning"}
                    now={progress}
                    affixed
                />
            </ListGroup>
        </Panel>
    );
};

const mapStateToProps = state => ({
    processes: state.processes.documents
});

const mapDispatchToProps = (dispatch, ownProps) => ({
    onClick: (e) => {
        if (e.target.nodeName !== "I") {
            dispatch(push(`/refs/${ownProps.id}`));
        }
    }
});

export default connect(mapStateToProps, mapDispatchToProps)(ReferenceItem);
<|endoftext|>


<commit_before>import React from "react";
import { connect } from "react-redux";
import { push } from "react-router-redux";
import { find } from "lodash-es";
import { RelativeTime, ProgressBar } from "../../base";
import { Panel, Table, Row, ListGroup } from "react-bootstrap";

const ReferenceHeader = ({ name, createdAt, user }) => (

        <Row>
            {name}
        </Row>
        <Row>

                Created <RelativeTime time={createdAt} /> by {user}

        </Row>

);

const ReferenceMetadata = ({ data_type, organism }) => (

                Data Type

                    {data_type}

                Organism

                    {organism || "unknown"}

);

const ReferenceItem = (props) => {

    let progress = 0;

    if (props.process && props.processes.length) {
        progress = find(props.processes, ["id", props.process.id]).progress;
        progress *= 100;
    }

    const barStyle = { backgroundColor: "#f5f5f5" };

    return (
        <Panel className="reference-item" onClick={props.onClick}>
            <Panel.Heading>
                <ReferenceHeader name={props.name} createdAt={props.created_at} user={props.user.id} />
            </Panel.Heading>

            <ReferenceMetadata {...props} />
            <ListGroup>
                <ProgressBar
                    bsStyle={progress === 100 ? "success" : "warning"}
                    now={progress}
                    style={barStyle}
                    affixed
                />
            </ListGroup>
        </Panel>
    );
};

const mapStateToProps = state => ({
    processes: state.processes.documents
});

const mapDispatchToProps = (dispatch, ownProps) => ({
    onClick: () => {
        dispatch(push(`/refs/${ownProps.id}`));
    }
});

export default connect(mapStateToProps, mapDispatchToProps)(ReferenceItem);
<commit_msg>Add ref card creation description<commit_after>import React from "react";
import { connect } from "react-redux";
import { push } from "react-router-redux";
import { find, get } from "lodash-es";
import { RelativeTime, ProgressBar } from "../../base";
import { Panel, Table, Row, ListGroup } from "react-bootstrap";

const ReferenceHeader = ({ name, createdAt, user }) => (

        <Row>
            {name}
        </Row>
        <Row>

                Created <RelativeTime time={createdAt} /> by {user}

        </Row>

);

const ReferenceMetadata = ({ data_type, organism, origin }) => (

                Data Type

                    {data_type}

                Organism

                    {organism || "unknown"}

                {origin.method}

                    {origin.fileName}

);

const getOrigin = (props) => {
    let origin;

    if (get(props, "imported_from", null)) {
        origin = {
            method: "Imported from file",
            fileName: props.imported_from.name
        };
    } else if (get(props, "cloned_from", null)) {
        origin = {
            method: "Cloned from",
            fileName: props.cloned_from.name
        };
    } else if (get(props, "remote_from", null)) {
        origin = {
            method: "Remote update from",
            fileName: props.remote_from.name
        };
    } else {
        origin = {
            method: "Created",
            fileName: "No File"
        };
    }

    return origin;
};

const ReferenceItem = (props) => {

    const origin = getOrigin(props);

    let progress = 0;

    if (props.process && props.processes.length) {
        progress = find(props.processes, ["id", props.process.id]).progress;
        progress *= 100;
    }

    const barStyle = { backgroundColor: "#f5f5f5" };

    return (
        <Panel className="reference-item" onClick={props.onClick}>
            <Panel.Heading>
                <ReferenceHeader name={props.name} createdAt={props.created_at} user={props.user.id} />
            </Panel.Heading>

            <ReferenceMetadata {...props} origin={origin} />
            <ListGroup>
                <ProgressBar
                    bsStyle={progress === 100 ? "success" : "warning"}
                    now={progress}
                    style={barStyle}
                    affixed
                />
            </ListGroup>
        </Panel>
    );
};

const mapStateToProps = state => ({
    processes: state.processes.documents
});

const mapDispatchToProps = (dispatch, ownProps) => ({
    onClick: () => {
        dispatch(push(`/refs/${ownProps.id}`));
    }
});

export default connect(mapStateToProps, mapDispatchToProps)(ReferenceItem);
<|endoftext|>