<commit_before>/*
 *
 * Bancha Scaffolding Library
 * Copyright 2011-2014 codeQ e.U.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @package       Bancha.scaffold
 * @copyright     Copyright 2011-2014 codeQ e.U.
 * @link          http://scaffold.bancha.io
 * @since         Bancha Scaffold v 0.0.1
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 * @author        Roland Schuetz <mail@rolandschuetz.at>
 * @version       Bancha Scaffold v PRECOMPILER_ADD_BANCHA_SCAFFOLD_RELEASE_VERSION
 *
 * For more information go to http://scaffold.bancha.io
 */

/**
 * @singleton
 * @class Bancha.scaffold.Util
 * **This is only available inside Ext JS.**
 *
 * Some scaffolding util functions for {@link Ext.grid.Panel} and {@link Ext.form.Panel}.
 *
 * @author Roland Schuetz <mail@rolandschuetz.at>
 * @docauthor Roland Schuetz <mail@rolandschuetz.at>
 */
Ext.define('Bancha.scaffold.Util', {
    singleton: true,

    /**
     * Makes the first letter of an string upper case.
     * @param {String} str The string to transform
     * @return {String} The transformed string with a first letter upper case
     */
    toFirstUpper: function (str) {
        if (typeof str !== 'string') {
            return str;
        }
        if (str.length === 1) {
            return str.toUpperCase();
        }
        // note: IE6 ad 7 doesn't allow array syntax for getting the first letter
        return str.substr(0,1).toUpperCase() + str.substr(1);
    },

    /**
     * Capitalizes the first word, turns underscores into spaces and strips
     * trailing *'_id'*.
     *
     * Also it converts camel case by finding upper case letters right after
     * lower case and replaceing the upper case with an space and lower case.
     *
     * examples:
     *     "user_name"  -> "User name"
     *     "userName"   -> "User name"
     *     "John Smith" -> "John Smith"
     *
     * @param {String} str
     * @return {String} The transformed string
     */
    humanize: function (str) {
        str = str.replace(/_id/g, ''); // delete _id from the string
        str = str.replace(/_/g, ' '); // _ to spaces
        str = str.replace(/([a-z])([A-Z])/g, function (all, first, second) {
            return first + ' ' + second.toLowerCase();
        }); // convert camel case (only)
        return this.toFirstUpper(str);
    },

    /**
     * Transforms a namespacd class name like *'Bancha.model.AwesomeArticle'*
     * and transforms it into a name you can show to users, in this case
     * *'Awesome article'*.
     *
     * For details on how the transformation, see also {@link #humanize}
     *
     * @param {String} classname A full classname to humanize
     * @return {String} The transformed string
     */
    humanizeClassName: function(classname) {
        // get the class name without any namespacing
        if(classname.indexOf('.')) {
            classname = classname.substr(classname.lastIndexOf('.')+1);
        }
        return this.humanize(classname);
    },

    /**
     * Makes every words first letter upper case.
     *
     * @param {String} str String of words, separated by space
     * @return {String} The transformed string
     */
    toTitle: function(str) {
        return str.replace(/ ([a-z])/g, function (all, letter) {
            return ' ' + letter.toUpperCase();
        });
    },

    /**
     * @private
     * This function will search for 'create', 'reset' and 'save' and will
     * properly replace them with the values from the config object
     *
     * It will also inject the scope into all elements where the scope
     * equals 'scaffold-scope-me'
     *
     * @param {String[]|Ext.button.Button[]} buttons the  button config, e.g. ['->','create','reset','save']
     * @param {Bancha.scaffold.form.Config|Bancha.scaffold.grid.Config} config The config with all necessary
     * replacements (onCreate, createButtonConfig, onReset, ...)
     * @param buttonScope the scope, that's applied to all replaced buttons
     * @return {String[]|Ext.button.Button[]} The build buttons array
     */
    replaceButtonPlaceHolders: function(buttons, config, buttonScope) {
        if(typeof buttons === 'undefined' || buttons.length===0) {
            return buttons;
        }

        for(var i=0, len=buttons.length; i<len; i++) {
            switch(buttons[i]) {
            case 'create':
                buttons[i] = Ext.apply(config.createButtonConfig, {
                    scope: buttonScope,
                    handler: config.onCreate
                });
                break;
            case 'reset':
                buttons[i] = Ext.apply(config.resetButtonConfig, {
                    scope: buttonScope,
                    handler: config.onReset
                });
                break;
            case 'save':
                buttons[i] = Ext.apply(config.saveButtonConfig, {
                    scope: buttonScope,
                    handler: config.onSave
                });
                break;
            default:
                // check if we should inject a scope
                if(buttons[i].scope === 'scaffold-scope-me') {
                    buttons[i].scope = buttonScope;
                }
            }
        }

        return buttons;
    },

    /**
     * Returns true if a model class name (or class) is given.
     *
     * Normalized Ext JS 4 and 5.
     *
     * @param {string} classOrClassName The class name or class itself.
     * @returns {boolen}
     */
    isModel: function(classOrClassName) {
        try {
            this.getModel(classOrClassName);
            return true;
        } catch(e) {
            return false;
        }
    },

    /**
     * Returns the model for a given class name (or class)
     *
     * Normalized Ext JS 4 and 5.
     *
     * @param {string} classOrClassName The class name or class itself.
     * @returns {Ext.Class|null} The corresponsing class, or null
     */
    getModel: function(classOrClassName) {
        var className = Ext.isString(classOrClassName) ? classOrClassName : Ext.ClassManager.getName(classOrClassName),
            model;
        if(!className) {
            Ext.Error.raise('No model given to Bancha.scaffold.Util.getModel("' + className + '").');
        }
        if(Ext.versions.extjs.major===4) {
            // Ext JS 4
            model = Ext.ModelManager.getModel(className);
            if(!model) {
                Ext.Error.raise('No such model "' + className + '" found.');
            }
            return model;
        }
        // Ext JS 5
        return Ext.data.schema.Schema.lookupEntity(className);
    },

    /**
     * @method
     * For separation of concerns, gets/creates a store.
     *
     * Used to build the grid store and to build associated stores.
     *
     * @param {Ext.data.Model} model A model
     * @param {Bancha.scaffold.form.Config|Bancha.scaffold.grid.Config} config (optional)
     * A config object with the properties oneStorePerModel, storeDefaults and storeDefaultClass
     * @return {Ext.data.Store} The store
     */
    getStore: (function (model, config) {
        var stores = {};

        return function (model, config) {
            var modelName = Ext.ClassManager.getName(model),
                store;
            config = config || {};
            if (config.oneStorePerModel && stores[modelName]) {
                return stores[modelName];
            }

            store = Ext.create(config.storeDefaultClass || 'Ext.data.Store', Ext.apply({
                model: modelName
            }, Ext.clone(config.storeDefaults || {})));

            if (config.oneStorePerModel) {
                stores[modelName] = store;
            }

            return store;
        };
    }()),

    /**
     * This method can be overriden by your own needs.
     *
     * Tries to find the most usefull model field for dispaying. This is
     * used in the Grid scaffolding renderer for associations and can be
     * overwritten at any time.
     *
     * By default it used the models "displayField" config, which just exists
     * in Bancha Scaffold and soon should be implemented in Bancha for CakePHP.
     *
     * @param {Ext.data.Model} model The model to look through
     * @return {String} The most accurate field name
     */
    getDisplayFieldName: function(model) {
        // if defined use the display field config (when using CakePHP)
        if(model.getDisplayField && model.getDisplayField()) {
            return model.getDisplayField();
        }

        // get the field names
        var fieldNames;
        if(Ext.versions.extjs.shortVersion < 410) {
            // legacy support
            fieldNames = Ext.Array.pluck(model.prototype.fields.items, 'name');
        } else {
            fieldNames = Ext.Array.pluck(model.getFields(), 'name');
        }

        // try to find name, title or code
        if(Ext.Array.indexOf(fieldNames, 'name') !== -1) {
            return 'name';
        }
        if(Ext.Array.indexOf(fieldNames, 'title') !== -1) {
            return 'title';
        }
        if(Ext.Array.indexOf(fieldNames, 'code') !== -1) {
            return 'code';
        }

        // nothing usefull found
        return model.idProperty || fieldNames[0];
    },

    /**
     * This function may need to be customized, it generates the expected model associations "name" value,
     * to check if this field has a association (this is used for replacing id values with actual data).
     *
     * The default uses CakePHP naming conventions, e.g.
     * fieldname *'book_author_id'* expects association name *'bookAuthors'*
     *
     * @param {String} modelFieldName The fields name of an model, e.g. 'title'
     * @param {String} associationType The association type, e.g. belongsTo
     * @return {String} The guessed association name
     */
    fieldNameToModelAssociationName: function(modelFieldName, associationType) {
        if(!Ext.isString(modelFieldName)) {
            return;
        }

        var parts = modelFieldName.split('_');
        if(parts.length<2 || parts[parts.length-1] !== 'id') {
            return;
        }
        parts.pop(); // remove 'id'

        var name = parts.shift(); // the first stays lower case
        Ext.each(parts, function(part) {
            name += part.substr(0,1).toUpperCase() + part.substr(1);
        });

        return associationType==='belongsTo' ? name : name+'s';
    },

    /**
     * Returns the corresponding association for a given field, or false.
     *
     * This will not return a reference, if defined on the field. See getBelongsToModel.
     *
     * @param {Ext.data.Field} field The model field to look for an association (belongsTo)
     * @param {Ext.data.Model} model The fields model
     * @return {Ext.data.association.belongsTo|Ext.data.schema.ManyToOne|False} The found association or false
     */
    getBelongsToAssociation: function(field, model) {
        var associationName,
            associations,
            key;

        // Ext JS 5 works differently then older versions
        if(Ext.versions.extjs && Ext.versions.extjs.major === 5) {
            associations = model.associations;
            // this will detect Bancha associations
            for(key in associations) {
                if(associations.hasOwnProperty(key) && associations[key].foreignKey === field.name) {
                    return associations[key]; // match
                }
            }
            return false; // no match
        }

        // Ext JS 4 and Sencha Touch
        associationName = this.fieldNameToModelAssociationName(field.name, 'belongsTo');
        associations = Ext.isFunction(model.getAssociations) ? model.getAssociations():
                            (model.prototype ? model.prototype.associations : false);
        if(!associationName || !associations) {
            return false;
        }
        return associations.get(associationName);
    },
    /**
     * Returns the associated model for a given field.
     *
     * Normalized the function across Ext JS 4 and 5.
     *
     * @param {Ext.data.Field} field The model field to look for an association (belongsTo)
     * @param {Ext.data.Model} model The fields model
     * @return {Ext.data.Model|null} The associated model or null
     */
    getBelongsToModel: function(field, model) {
        // this will detect new Ext JS 5 reference associations
        if(field.reference) {
            return field.reference.cls;
        }

        // below will detect associations array configurations
        var association = this.getBelongsToAssociation(field, model);

        // if no association found
        if(!association) {
            return association;
        }

        // Ext JS 5 works differently then older versions
        if(Ext.versions.extjs && Ext.versions.extjs.major === 5) {
            return Ext.ClassManager.get(association.model);
        }

        // Ext JS 4
        return association.associatedModel;
    }
});
<commit_msg>code style<commit_after>/*
 *
 * Bancha Scaffolding Library
 * Copyright 2011-2014 codeQ e.U.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @package       Bancha.scaffold
 * @copyright     Copyright 2011-2014 codeQ e.U.
 * @link          http://scaffold.bancha.io
 * @since         Bancha Scaffold v 0.0.1
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 * @author        Roland Schuetz <mail@rolandschuetz.at>
 * @version       Bancha Scaffold v PRECOMPILER_ADD_BANCHA_SCAFFOLD_RELEASE_VERSION
 *
 * For more information go to http://scaffold.bancha.io
 */

/**
 * @singleton
 * @class Bancha.scaffold.Util
 * **This is only available inside Ext JS.**
 *
 * Some scaffolding util functions for {@link Ext.grid.Panel} and {@link Ext.form.Panel}.
 *
 * @author Roland Schuetz <mail@rolandschuetz.at>
 * @docauthor Roland Schuetz <mail@rolandschuetz.at>
 */
Ext.define('Bancha.scaffold.Util', {
    singleton: true,

    /**
     * Makes the first letter of an string upper case.
     * @param {String} str The string to transform
     * @return {String} The transformed string with a first letter upper case
     */
    toFirstUpper: function (str) {
        if (typeof str !== 'string') {
            return str;
        }
        if (str.length === 1) {
            return str.toUpperCase();
        }
        // note: IE6 ad 7 doesn't allow array syntax for getting the first letter
        return str.substr(0,1).toUpperCase() + str.substr(1);
    },

    /**
     * Capitalizes the first word, turns underscores into spaces and strips
     * trailing *'_id'*.
     *
     * Also it converts camel case by finding upper case letters right after
     * lower case and replaceing the upper case with an space and lower case.
     *
     * examples:
     *     "user_name"  -> "User name"
     *     "userName"   -> "User name"
     *     "John Smith" -> "John Smith"
     *
     * @param {String} str
     * @return {String} The transformed string
     */
    humanize: function (str) {
        str = str.replace(/_id/g, ''); // delete _id from the string
        str = str.replace(/_/g, ' '); // _ to spaces
        str = str.replace(/([a-z])([A-Z])/g, function (all, first, second) {
            return first + ' ' + second.toLowerCase();
        }); // convert camel case (only)
        return this.toFirstUpper(str);
    },

    /**
     * Transforms a namespacd class name like *'Bancha.model.AwesomeArticle'*
     * and transforms it into a name you can show to users, in this case
     * *'Awesome article'*.
     *
     * For details on how the transformation, see also {@link #humanize}
     *
     * @param {String} classname A full classname to humanize
     * @return {String} The transformed string
     */
    humanizeClassName: function(classname) {
        // get the class name without any namespacing
        if(classname.indexOf('.')) {
            classname = classname.substr(classname.lastIndexOf('.')+1);
        }
        return this.humanize(classname);
    },

    /**
     * Makes every words first letter upper case.
     *
     * @param {String} str String of words, separated by space
     * @return {String} The transformed string
     */
    toTitle: function(str) {
        return str.replace(/ ([a-z])/g, function (all, letter) {
            return ' ' + letter.toUpperCase();
        });
    },

    /**
     * @private
     * This function will search for 'create', 'reset' and 'save' and will
     * properly replace them with the values from the config object
     *
     * It will also inject the scope into all elements where the scope
     * equals 'scaffold-scope-me'
     *
     * @param {String[]|Ext.button.Button[]} buttons the  button config, e.g. ['->','create','reset','save']
     * @param {Bancha.scaffold.form.Config|Bancha.scaffold.grid.Config} config The config with all necessary
     * replacements (onCreate, createButtonConfig, onReset, ...)
     * @param buttonScope the scope, that's applied to all replaced buttons
     * @return {String[]|Ext.button.Button[]} The build buttons array
     */
    replaceButtonPlaceHolders: function(buttons, config, buttonScope) {
        if(typeof buttons === 'undefined' || buttons.length===0) {
            return buttons;
        }

        for(var i=0, len=buttons.length; i<len; i++) {
            switch(buttons[i]) {
            case 'create':
                buttons[i] = Ext.apply(config.createButtonConfig, {
                    scope: buttonScope,
                    handler: config.onCreate
                });
                break;
            case 'reset':
                buttons[i] = Ext.apply(config.resetButtonConfig, {
                    scope: buttonScope,
                    handler: config.onReset
                });
                break;
            case 'save':
                buttons[i] = Ext.apply(config.saveButtonConfig, {
                    scope: buttonScope,
                    handler: config.onSave
                });
                break;
            default:
                // check if we should inject a scope
                if(buttons[i].scope === 'scaffold-scope-me') {
                    buttons[i].scope = buttonScope;
                }
            }
        }

        return buttons;
    },

    /**
     * Returns true if a model class name (or class) is given.
     *
     * Normalized Ext JS 4 and 5.
     *
     * @param {string} classOrClassName The class name or class itself.
     * @returns {boolen}
     */
    isModel: function(classOrClassName) {
        try {
            // some errors are jsut thrown in debug mode, so check return value
            return !!this.getModel(classOrClassName);
        } catch(e) {
            return false;
        }
    },

    /**
     * Returns the model for a given class name (or class)
     *
     * Normalized Ext JS 4 and 5.
     *
     * @param {string} classOrClassName The class name or class itself.
     * @returns {Ext.Class|null} The corresponsing class, or null
     */
    getModel: function(classOrClassName) {
        var className = Ext.isString(classOrClassName) ? classOrClassName : Ext.ClassManager.getName(classOrClassName),
            model;
        if(!className) {
            Ext.Error.raise('No model given to Bancha.scaffold.Util.getModel("' + className + '").');
        }
        if(Ext.versions.extjs.major === 4) {
            // Ext JS 4
            model = Ext.ModelManager.getModel(className);
            if(!model) {
                Ext.Error.raise('No such model "' + className + '" found.');
            }
            return model;
        }
        // Ext JS 5
        return Ext.data.schema.Schema.lookupEntity(className);
    },

    /**
     * @method
     * For separation of concerns, gets/creates a store.
     *
     * Used to build the grid store and to build associated stores.
     *
     * @param {Ext.data.Model} model A model
     * @param {Bancha.scaffold.form.Config|Bancha.scaffold.grid.Config} config (optional)
     * A config object with the properties oneStorePerModel, storeDefaults and storeDefaultClass
     * @return {Ext.data.Store} The store
     */
    getStore: (function (model, config) {
        var stores = {};

        return function (model, config) {
            var modelName = Ext.ClassManager.getName(model),
                store;
            config = config || {};
            if (config.oneStorePerModel && stores[modelName]) {
                return stores[modelName];
            }

            store = Ext.create(config.storeDefaultClass || 'Ext.data.Store', Ext.apply({
                model: modelName
            }, Ext.clone(config.storeDefaults || {})));

            if (config.oneStorePerModel) {
                stores[modelName] = store;
            }

            return store;
        };
    }()),

    /**
     * This method can be overriden by your own needs.
     *
     * Tries to find the most usefull model field for dispaying. This is
     * used in the Grid scaffolding renderer for associations and can be
     * overwritten at any time.
     *
     * By default it used the models "displayField" config, which just exists
     * in Bancha Scaffold and soon should be implemented in Bancha for CakePHP.
     *
     * @param {Ext.data.Model} model The model to look through
     * @return {String} The most accurate field name
     */
    getDisplayFieldName: function(model) {
        // if defined use the display field config (when using CakePHP)
        if(model.getDisplayField && model.getDisplayField()) {
            return model.getDisplayField();
        }

        // get the field names
        var fieldNames;
        if(Ext.versions.extjs.shortVersion < 410) {
            // legacy support
            fieldNames = Ext.Array.pluck(model.prototype.fields.items, 'name');
        } else {
            fieldNames = Ext.Array.pluck(model.getFields(), 'name');
        }

        // try to find name, title or code
        if(Ext.Array.indexOf(fieldNames, 'name') !== -1) {
            return 'name';
        }
        if(Ext.Array.indexOf(fieldNames, 'title') !== -1) {
            return 'title';
        }
        if(Ext.Array.indexOf(fieldNames, 'code') !== -1) {
            return 'code';
        }

        // nothing usefull found
        return model.idProperty || fieldNames[0];
    },

    /**
     * This function may need to be customized, it generates the expected model associations "name" value,
     * to check if this field has a association (this is used for replacing id values with actual data).
     *
     * The default uses CakePHP naming conventions, e.g.
     * fieldname *'book_author_id'* expects association name *'bookAuthors'*
     *
     * @param {String} modelFieldName The fields name of an model, e.g. 'title'
     * @param {String} associationType The association type, e.g. belongsTo
     * @return {String} The guessed association name
     */
    fieldNameToModelAssociationName: function(modelFieldName, associationType) {
        if(!Ext.isString(modelFieldName)) {
            return;
        }

        var parts = modelFieldName.split('_');
        if(parts.length<2 || parts[parts.length-1] !== 'id') {
            return;
        }
        parts.pop(); // remove 'id'

        var name = parts.shift(); // the first stays lower case
        Ext.each(parts, function(part) {
            name += part.substr(0,1).toUpperCase() + part.substr(1);
        });

        return associationType==='belongsTo' ? name : name+'s';
    },

    /**
     * Returns the corresponding association for a given field, or false.
     *
     * This will not return a reference, if defined on the field. See getBelongsToModel.
     *
     * @param {Ext.data.Field} field The model field to look for an association (belongsTo)
     * @param {Ext.data.Model} model The fields model
     * @return {Ext.data.association.belongsTo|Ext.data.schema.ManyToOne|False} The found association or false
     */
    getBelongsToAssociation: function(field, model) {
        var associationName,
            associations,
            key;

        // Ext JS 5 works differently then older versions
        if(Ext.versions.extjs && Ext.versions.extjs.major === 5) {
            associations = model.associations;
            // this will detect Bancha associations
            for(key in associations) {
                if(associations.hasOwnProperty(key) && associations[key].foreignKey === field.name) {
                    return associations[key]; // match
                }
            }
            return false; // no match
        }

        // Ext JS 4 and Sencha Touch
        associationName = this.fieldNameToModelAssociationName(field.name, 'belongsTo');
        associations = Ext.isFunction(model.getAssociations) ? model.getAssociations():
                            (model.prototype ? model.prototype.associations : false);
        if(!associationName || !associations) {
            return false;
        }
        return associations.get(associationName);
    },
    /**
     * Returns the associated model for a given field.
     *
     * Normalized the function across Ext JS 4 and 5.
     *
     * @param {Ext.data.Field} field The model field to look for an association (belongsTo)
     * @param {Ext.data.Model} model The fields model
     * @return {Ext.data.Model|null} The associated model or null
     */
    getBelongsToModel: function(field, model) {
        // this will detect new Ext JS 5 reference associations
        if(field.reference) {
            return field.reference.cls;
        }

        // below will detect associations array configurations
        var association = this.getBelongsToAssociation(field, model);

        // if no association found
        if(!association) {
            return association;
        }

        // Ext JS 5 works differently then older versions
        if(Ext.versions.extjs && Ext.versions.extjs.major === 5) {
            return Ext.ClassManager.get(association.model);
        }

        // Ext JS 4
        return association.associatedModel;
    }
});
<|endoftext|>


<commit_before>define(["./jquery", "./utils"], function($, utils) {
    "use strict";

    function ArrayDataSource(data, delay) {
        if(delay) {
            setTimeout(this.load.bind(this, [data]), delay);
        } else {
            if(data) {
                this.load(data);
            }
        }
    }

    ArrayDataSource.prototype = {
        load: function(data) {
            if(data !== undefined) {
              this.data = data;
            }

            for(var x = 0, l = this.data.length;x<l;x++) {
                if(this.data[x].id === undefined) {
                    this.data[x].id = x;
                }
            }

            this.ready = true;
            $(this).trigger("dataloaded");
        },

        recordCount: function() {
            this.assertReady();
            return this.data.length;
        },

        getRowByIndex: function(idx) {
            this.assertReady();
            return this.data[idx];
        },

        getRecordById: function(id) {
            this.assertReady();
            for(var x=0,l=this.data.length; x<l; x++) {
                if(this.data[x].id == id) return this.data[x];
            }
        },

        getData: function(start, end) {
            this.assertReady();
            if(start === undefined && end === undefined) return this.data;
            if(start === undefined || start === null) start = 0;
            if(end === undefined) end = this.recordCount();
            return this.data.slice(start, end);
        },

        setValue: function(rowId, key, value) {
            this.assertReady();
            utils.setValue(this.getRecordById(rowId), key, value);
            $(this).trigger("datachanged", { values: [ { id: rowId, key: key } ] });
        },

        assertReady: function() {
            if(!this.isReady()) throw Error("Datasource not ready yet");
        },

        isReady: function() {
            return this.ready;
        },

        sort: function(comparator) {
            this.assertReady();
            this.data.sort(comparator);
            $(this).trigger("dataloaded");
        },

        replace(record) {
            var data = this.data,
                idx = data.find(function(r) { return r.id == record.id; });
            if(idx !== undefined) {
                data.splice(idx, 1, record);
                $(this).trigger("datachanged", { rows: [record] })
            }
        }
    };

    return ArrayDataSource;
});
<commit_msg>Update arraydatasource.js<commit_after>define(["./jquery", "./utils"], function($, utils) {
    "use strict";

    function ArrayDataSource(data, delay) {
        if(delay) {
            setTimeout(this.load.bind(this, [data]), delay);
        } else {
            if(data) {
                this.load(data);
            }
        }
    }

    ArrayDataSource.prototype = {
        load: function(data) {
            if(data !== undefined) {
              this.data = data;
            }

            for(var x = 0, l = this.data.length;x<l;x++) {
                if(this.data[x].id === undefined) {
                    this.data[x].id = x;
                }
            }

            this.ready = true;
            $(this).trigger("dataloaded");
        },

        recordCount: function() {
            this.assertReady();
            return this.data.length;
        },

        getRowByIndex: function(idx) {
            this.assertReady();
            return this.data[idx];
        },

        getRecordById: function(id) {
            this.assertReady();
            for(var x=0,l=this.data.length; x<l; x++) {
                if(this.data[x].id == id) return this.data[x];
            }
        },

        getData: function(start, end) {
            this.assertReady();
            if(start === undefined && end === undefined) return this.data;
            if(start === undefined || start === null) start = 0;
            if(end === undefined) end = this.recordCount();
            return this.data.slice(start, end);
        },

        setValue: function(rowId, key, value) {
            this.assertReady();
            utils.setValue(this.getRecordById(rowId), key, value);
            $(this).trigger("datachanged", { values: [ { id: rowId, key: key } ] });
        },

        assertReady: function() {
            if(!this.isReady()) throw Error("Datasource not ready yet");
        },

        isReady: function() {
            return this.ready;
        },

        sort: function(comparator) {
            this.assertReady();
            this.data.sort(comparator);
            $(this).trigger("dataloaded");
        },

        replace: function(record) {
            var data = this.data,
                idx = data.find(function(r) { return r.id == record.id; });
            if(idx !== undefined) {
                data.splice(idx, 1, record);
                $(this).trigger("datachanged", { rows: [record] })
            }
        }
    };

    return ArrayDataSource;
});
<|endoftext|>


<commit_before>/*
 * TEST HOOKS, INTEGRATION OF BETWEEN TOOLS, MODELS AND DATA FETCHING
 */

//This test depends on models entities and time (not entirely self contained)

describe("* Base: Hooks", function() {

    var placeholder;
    var utils;
    var t;
    var TimeModel;
    var InfoDisplay;
    var MyTool;
    var MyReader;
    var options_data;

    beforeAll(function(done) {

        initializeDOM();
        placeholder = document.getElementById("vzbp-placeholder");
        utils = Vizabi.utils;

        //create a new component fo info display and register
        Vizabi.Component.unregister('info-display');
        InfoDisplay = Vizabi.Component.extend('info-display', {
            init: function(config, parent) {
                this.name = "info-display";
                this.template = "

";
                this.model_expects = [{
                    name: 'time',
                    type: 'time'
                }, {
                    name: 'label',
                    type: 'model'
                }, {
                    name: 'info',
                    type: 'model'
                }];
                var _this = this;
                this.model_binds = {
                    'change:time': function() {
                        _this.update();
                    },
                    'ready': function() {
                        _this.update();
                    }
                };
                this._super(config, parent);
            },

            update: function(evt) {
                var time = this.model.time.value;
                var items = this.model.label.getItems({
                    time: time
                });
                var html = "";
                for (var i in items) {
                    var d = items[i];
                    html += '
' + this.model.label.getValue(d) + '-' + this.model.info.getValue(d) + '
';
                }
                this.element.innerHTML = html;
            }
        });

        //create a new component class
        Vizabi.Tool.unregister('MyTool');
        MyTool = Vizabi.Tool.extend('MyTool', {
            init: function(placeholder, options) {
                this.name = 'MyTool';
                this.template = '

';
                this.components = [{
                    component: 'info-display',
                    placeholder: '.display',
                    model: ['state.time', 'state.marker.label',
                        'state.marker.info'
                    ]
                }];

                //default options
                this.default_options = {
                    state: {
                        _type_: "model",
                        _defs_: {
                            //timespan of the visualization
                            time: {
                                _type_: "model",
                                _defs_: {
                                    start: "1991",
                                    end: "1994",
                                    value: "1994"
                                }
                            },
                            //entities we want to show
                            entities: {
                                _type_: "model",
                                _defs_: {
                                    show: {
                                        _type_: "model",
                                        _defs_: {
                                            dim: {
                                                _type_: "string",
                                                _defs_: "geo"
                                            },
                                            filter: {
                                                _type_: "object",
                                                _defs_: {
                                                    "geo": ["*"]
                                                }
                                            }
                                        }
                                    }
                                }
                            },
                            //how we show it
                            marker: {
                                _type_: "model",
                                _defs_: {
                                    dimensions: {
                                        _type_: "array",
                                        _defs_: ["entities", "time"]
                                    },
                                    label: {
                                        _type_: "hook",
                                        _defs_: {
                                            use: {
                                                _type_: "string",
                                                _defs_: "property",
                                                _opts_: ["property", "indicator", "value"]
                                            },
                                            which: {
                                                _type_: "string",
                                                _defs_: "geo"
                                            }
                                        }
                                    },
                                    info: {
                                        _type_: "hook",
                                        _defs_: {
                                            use: {
                                                _type_: "string",
                                                _defs_: "indicator",
                                                _opts_: ["property", "indicator", "value"]
                                            },
                                            which: {
                                                _type_: "string",
                                                _defs_: "pop"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },

                    data: {
                        _type_: "model",
                        _defs_: {
                            reader: {
                                _type_: "string",
                                _defs_: "myreader"
                            }
                        }
                    },

                    ui: {
                        _type_: "model",
                        _defs_: {
                            'displayColor': {
                                _type_: "string",
                                _defs_: "#ffcc00"
                            }
                        }
                    },

                    //language properties
                    language: {
                        _type_: "model",
                        _defs_: {
                            id: {
                                _type_: "string",
                                _defs_: "en"
                            },
                            strings: {
                                _type_: "object",
                                _defs_: {
                                    en: {
                                        "title": "Information"
                                    }
                                }
                            }
                        }
                    }
                };

                this._super(placeholder, options);
            }
        });

        //create a new component fo ryear display and register
        Vizabi.Reader.unregister('myreader');
        MyReader = Vizabi.Reader.extend('myreader', {
            init: function(reader_info) {
                this.name = "myreader";
                this._super(reader_info);
            },
            read: function() {
                var p = new Vizabi.Promise;
                setTimeout(function() {
                    p.resolve(); //simulate async reading
                }, 500);
                return p;
            }
        });

        var options_data = {
            reader: 'myreader',
            data: [{
                geo: "swe",
                time: "1991",
                pop: "1",
                lex: "30"
            }, {
                geo: "swe",
                time: "1992",
                pop: "2",
                lex: "31"
            }, {
                geo: "swe",
                time: "1993",
                pop: "3",
                lex: "32"
            }, {
                geo: "swe",
                time: "1994",
                pop: "4",
                lex: "33"
            }, {
                geo: "nor",
                time: "1991",
                pop: "10",
                lex: "40"
            }, {
                geo: "nor",
                time: "1992",
                pop: "20",
                lex: "41"
            }, {
                geo: "nor",
                time: "1993",
                pop: "30",
                lex: "42"
            }, {
                geo: "nor",
                time: "1994",
                pop: "40",
                lex: "43"
            }]
        };

        t = Vizabi('MyTool', placeholder, {
            data: options_data,
            bind: {
                'ready': function() {
                    done();
                }
            }
        });

    });

    it("should initialize tool", function() {
        expect(Vizabi.Tool.isTool(t)).toBe(true);
    });

    it("should render tool with correct info", function() {
        expect(placeholder.innerHTML).toContain('
swe-4

nor-40
');
    });

    it("should change view instantaneously if nothing needs to be loaded (year changed)", function() {
        t.model.state.time.value = "1993";
        expect(placeholder.innerHTML).toContain('
swe-3

nor-30
');
        t.model.state.time.value = "1992";
        expect(placeholder.innerHTML).toContain('
swe-2

nor-20
');
    });

    describe("- Changing Hooks", function() {

        beforeAll(function(done) {

            t.on('ready', function() {
                //make sure done is called one frame after update
                setTimeout(function() {
                    done();
                }, 1);
            });

            t.setOptions({
                state: {
                    marker: {
                        info: {
                            which: 'lex'
                        }
                    }
                }
            });
        });

        it("should have changed indicator values", function() {
            expect(placeholder.innerHTML).toContain('
swe-31

nor-41
');
        });

    });

});<commit_msg>Fix tests for hooks<commit_after>/*
 * TEST HOOKS, INTEGRATION OF BETWEEN TOOLS, MODELS AND DATA FETCHING
 */

//This test depends on models entities and time (not entirely self contained)

describe("* Base: Hooks", function() {

    var placeholder;
    var utils;
    var t;
    var TimeModel;
    var InfoDisplay;
    var MyTool;
    var MyReader;
    var options_data;

    beforeAll(function(done) {

        initializeDOM();
        placeholder = document.getElementById("vzbp-placeholder");
        utils = Vizabi.utils;

        //create a new component fo info display and register
        Vizabi.Component.unregister('info-display');
        InfoDisplay = Vizabi.Component.extend('info-display', {
            init: function(config, parent) {
                this.name = "info-display";
                this.template = "

";
                this.model_expects = [{
                    name: 'time',
                    type: 'time'
                }, {
                    name: 'label',
                    type: 'model'
                }, {
                    name: 'info',
                    type: 'model'
                }];
                var _this = this;
                this.model_binds = {
                    'change:time': function() {
                        _this.update();
                    },
                    'ready': function() {
                        _this.update();
                    }
                };
                this._super(config, parent);
            },

            update: function(evt) {
                var time = this.model.time.value;
                var items = this.model.label.getItems({
                    time: time
                });
                var html = "";
                for (var i in items) {
                    var d = items[i];
                    html += '
' + this.model.label.getValue(d) + '-' + this.model.info.getValue(d) + '
';
                }
                this.element.innerHTML = html;
            }
        });

        //create a new component class
        Vizabi.Tool.unregister('MyTool');
        MyTool = Vizabi.Tool.extend('MyTool', {
            init: function(placeholder, options) {
                this.name = 'MyTool';
                this.template = '

';
                this.components = [{
                    component: 'info-display',
                    placeholder: '.display',
                    model: ['state.time', 'state.marker.label',
                        'state.marker.info'
                    ]
                }];

                //default options
                this.default_options = {
                    state: {
                        //timespan of the visualization
                        time: {
                            start: "1991",
                            end: "1994",
                            value: "1994"
                        },
                        //entities we want to show
                        entities: {
                            show: {
                                dim: "geo",
                                filter: {
                                    _defs_: {
                                        "geo": ["*"]
                                    }
                                }
                            }
                        },
                        //how we show it
                        marker: {
                            dimensions: ["entities", "time"],
                            label: {
                                use: "property",
                                which: "geo"
                            },
                            info: {
                                use: "indicator",
                                which: "pop"
                            }
                        }
                    },

                    data: {
                        reader: "myreader"
                    },

                    ui: {
                        'displayColor': "#ffcc00"
                    },

                    //language properties
                    language: {
                        id: "en",
                        strings: {
                            en: {
                                "title": "Information"
                            }
                        }
                    }
                };

                this._super(placeholder, options);
            }
        });

        //create a new component fo ryear display and register
        Vizabi.Reader.unregister('myreader');
        MyReader = Vizabi.Reader.extend('myreader', {
            init: function(reader_info) {
                this.name = "myreader";
                this._super(reader_info);
            },
            read: function() {
                var p = new Vizabi.Promise;
                setTimeout(function() {
                    p.resolve(); //simulate async reading
                }, 500);
                return p;
            }
        });

        var options_data = {
            reader: 'myreader',
            data: [{
                geo: "swe",
                time: "1991",
                pop: "1",
                lex: "30"
            }, {
                geo: "swe",
                time: "1992",
                pop: "2",
                lex: "31"
            }, {
                geo: "swe",
                time: "1993",
                pop: "3",
                lex: "32"
            }, {
                geo: "swe",
                time: "1994",
                pop: "4",
                lex: "33"
            }, {
                geo: "nor",
                time: "1991",
                pop: "10",
                lex: "40"
            }, {
                geo: "nor",
                time: "1992",
                pop: "20",
                lex: "41"
            }, {
                geo: "nor",
                time: "1993",
                pop: "30",
                lex: "42"
            }, {
                geo: "nor",
                time: "1994",
                pop: "40",
                lex: "43"
            }]
        };

        t = Vizabi('MyTool', placeholder, {
            data: options_data,
            bind: {
                'ready': function() {
                    done();
                }
            }
        });

    });

    it("should initialize tool", function() {
        expect(Vizabi.Tool.isTool(t)).toBe(true);
    });

    it("should render tool with correct info", function() {
        expect(placeholder.innerHTML).toContain('
swe-4

nor-40
');
    });

    it("should change view instantaneously if nothing needs to be loaded (year changed)", function() {
        t.model.state.time.value = "1993";
        expect(placeholder.innerHTML).toContain('
swe-3

nor-30
');
        t.model.state.time.value = "1992";
        expect(placeholder.innerHTML).toContain('
swe-2

nor-20
');
    });

    describe("- Changing Hooks", function() {

        beforeAll(function(done) {

            t.on('ready', function() {
                //make sure done is called one frame after update
                setTimeout(function() {
                    done();
                }, 1);
            });

            t.setOptions({
                state: {
                    marker: {
                        info: {
                            which: 'lex'
                        }
                    }
                }
            });
        });

        it("should have changed indicator values", function() {
            expect(placeholder.innerHTML).toContain('
swe-31

nor-41
');
        });

    });

});<|endoftext|>


<commit_before>/*
 *  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * bkCellMenuPluginManager
 * bkCellMenuPluginManager load and manages loaded cell menu plugins.
 */
(function() {
  'use strict';
  var module = angular.module('bk.cellMenuPluginManager', [
    'bk.utils',
    'bk.helper'  // This is only for ensuring that window.bkHelper is set, don't use bkHelper directly
  ]);
  module.factory('bkCellMenuPluginManager', function(bkUtils) {
    // loaded plugins
    var _cellMenuPlugins = {};
    return {
      reset: function() {
        var self = this;
        bkUtils.httpGet('../beaker/rest/util/getCellMenuPlugins')
            .success(function(menuUrls) {
              menuUrls.forEach(self.loadPlugin);
            });
      },
      loadPlugin: function(url) {
        return bkUtils.loadModule(url).then(function(ex) {
          _cellMenuPlugins[ex.cellType] = ex.plugin; // XXX should append not replace?
          return ex.plugin;
        });
      },
      getPlugin: function(cellType) {
        return _cellMenuPlugins[cellType];
      },
      getMenu: function(cellType, model) {
        return _cellMenuPlugins[cellType](model);
      }
    };
  });
})();
<commit_msg>Remove a unused deprecate function<commit_after>/*
 *  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * bkCellMenuPluginManager
 * bkCellMenuPluginManager load and manages loaded cell menu plugins.
 */
(function() {
  'use strict';
  var module = angular.module('bk.cellMenuPluginManager', [
    'bk.utils',
    'bk.helper'  // This is only for ensuring that window.bkHelper is set, don't use bkHelper directly
  ]);
  module.factory('bkCellMenuPluginManager', function(bkUtils) {
    // loaded plugins
    var _cellMenuPlugins = {};
    return {
      reset: function() {
        var self = this;
        bkUtils.httpGet('../beaker/rest/util/getCellMenuPlugins')
            .success(function(menuUrls) {
              menuUrls.forEach(self.loadPlugin);
            });
      },
      loadPlugin: function(url) {
        return bkUtils.loadModule(url).then(function(ex) {
          _cellMenuPlugins[ex.cellType] = ex.plugin; // XXX should append not replace?
          return ex.plugin;
        });
      },
      getPlugin: function(cellType) {
        return _cellMenuPlugins[cellType];
      }
    };
  });
})();
<|endoftext|>


<commit_before>/**

@property maria.View

@parameter model {Object} Optional

@parameter controller {Object} Optional

@description

A constructor function to create new view objects.

    var view = new maria.View();

This constructor function takes two optional arguments.

    var model = new maria.Model();
    var controller = new maria.Controller();
    var view = new maria.View(model, controller);

The null or undefined value can be passed for either of these two
parameters to skip setting it.

A view is has a model. You can get the current model which might
be undefined.

    view.getModel();

You can set the model.

    view.setModel(model);

When a view object's model object is set, the view will, by convention,
observe the model's "change" event. When a "change" event is dispatched
on the model, the view's "update" method will be called.

Your application will redefine or more likely override the update method.

    maria.View.prototype.update = function(evt) {
        alert('the model changed');
    };

If necessary, you can change the events and methods that the view will
observe when the model is set by redefining or overriding the
getModelActions method.

    maria.View.prototype.getModelActions = function() {
        return {
            'squashed': 'onSquashed',
            'squished': 'onSquished'
        };
    };

When the model is set, if the view had a previous model then the view
will unsubscribe from the events it subscribed to on the prevous model
when the previous model was set.

A view has a controller. You can get the current controller.

    view.getController();

The view's controller is created lazily the first time the
getController method is called. The view's
getDefaultControllerConstructor method returns the constructor function
to create the controller object and the getDefaultController actually
calls that constructor. Your application may redefine or override
either of these methods.

A view has a destroy method which should be called before your
application looses its last reference to the view.

An view object is a composite view. This means the view can have child
views added and removed. This functionality is provided by the Hijos
library. Briefly,

    var childView1 = new maria.View();
    var childView2 = new maria.View();
    view.appendChild(childView1);
    view.replaceChild(childView2, childView1);
    view.insertBefore(childView1, childView2);
    view.removeChild(childView2);
    view.childNodes;
    view.firstChild;
    view.lastChild;
    childView1.nextSibling;
    childView1.previousSibling;
    childView1.parentNode;

When a view's destroy method executes, it calls each child's destroy
method.

The maria.View constructor is relatively abstract. It is most likely
that your application can use maria.ElementView; however, if you are
creating a new type of view where maria.ElementView is not a good fit,
for example a view that represents part of a bitmap drawing on a canvas
element, then you may want to use maria.View as the "superclass" of
your new view constructor. The following example shows how this can be
done at a low level. See maria.View.subclass for a more compact way to
accomplish the same.

    myapp.MyView = function() {
        maria.View.apply(this, arguments);
    };
    myapp.MyView.prototype = maria.create(maria.View.prototype);
    myapp.MyView.prototype.constructor = myapp.MyView;
    myapp.MyView.prototype.getModelActions = function() {
        return {
            'squashed': 'onSquashed',
            'squished': 'onSquished'
        };
    };
    maria.MyView.prototype.onSquished = function(evt) {
        this.getController().onSquished(evt);
    };
    maria.MyView.prototype.onSquashed = function() {
    this.getController().onSquashed(evt);
    };
    myapp.MyView.prototype.getDefaultControllerConstructor = function() {
        return myapp.MyController;
    };
    myapp.MyView.prototype.anotherMethod = function() {
        alert('another method');
    };

*/
maria.View = function(model, controller) {
    hijos.Node.call(this);
    this.setModel(model);
    this.setController(controller);
};

hijos.Node.mixin(maria.View.prototype);

maria.View.prototype.destroy = function() {
    maria.purgeEventListener(this);
    this._model = null;
    if (this._controller) {
        this._controller.destroy();
        this._controller = null;
    }
    hijos.Node.prototype.destroy.call(this);
};

maria.View.prototype.update = function() {
    // to be overridden by concrete view subclasses
};

maria.View.prototype.getModel = function() {
    return this._model;
};

maria.View.prototype.setModel = function(model) {
    this._setModelAndController(model, this._controller);
};

maria.View.prototype.getDefaultControllerConstructor = function() {
    return maria.Controller;
};

maria.View.prototype.getDefaultController = function() {
    var constructor = this.getDefaultControllerConstructor();
    return new constructor();
};

maria.View.prototype.getController = function() {
    if (!this._controller) {
        this.setController(this.getDefaultController());
    }
    return this._controller;
};

maria.View.prototype.setController = function(controller) {
    this._setModelAndController(this._model, controller);
};

maria.View.prototype.getModelActions = function() {
    return {'change': 'update'};
};

maria.View.prototype._setModelAndController = function(model, controller) {
    var type, eventMap;
    if (this._model !== model) {
        if (this._model) {
            eventMap = this._lastModelActions;
            for (type in eventMap) {
                if (Object.prototype.hasOwnProperty.call(eventMap, type)) {
                    maria.removeEventListener(this._model, type, this, eventMap[type]);
                }
            }
            delete this._lastModelActions;
        }
        if (model) {
            eventMap = this._lastModelActions = this.getModelActions() || {};
            for (type in eventMap) {
                if (Object.prototype.hasOwnProperty.call(eventMap, type)) {
                    maria.addEventListener(model, type, this, eventMap[type]);
                }
            }
        }
        this._model = model;
    }
    if (controller) {
        controller.setView(this);
        controller.setModel(model);
    }
    this._controller = controller;
};
<commit_msg>maria.View was not inheriting from anything through prototype inheritance. The only thing that maria.View might want to inherit from is hijos.Node. Using prototype inheritance rather than mixin inheritance increases flexibility. An hijos plugin that adds properties to hijos.Node.prototype will automatically appear on maria.View.prototype.<commit_after>/**

@property maria.View

@parameter model {Object} Optional

@parameter controller {Object} Optional

@description

A constructor function to create new view objects.

    var view = new maria.View();

This constructor function takes two optional arguments.

    var model = new maria.Model();
    var controller = new maria.Controller();
    var view = new maria.View(model, controller);

The null or undefined value can be passed for either of these two
parameters to skip setting it.

A view is has a model. You can get the current model which might
be undefined.

    view.getModel();

You can set the model.

    view.setModel(model);

When a view object's model object is set, the view will, by convention,
observe the model's "change" event. When a "change" event is dispatched
on the model, the view's "update" method will be called.

Your application will redefine or more likely override the update method.

    maria.View.prototype.update = function(evt) {
        alert('the model changed');
    };

If necessary, you can change the events and methods that the view will
observe when the model is set by redefining or overriding the
getModelActions method.

    maria.View.prototype.getModelActions = function() {
        return {
            'squashed': 'onSquashed',
            'squished': 'onSquished'
        };
    };

When the model is set, if the view had a previous model then the view
will unsubscribe from the events it subscribed to on the prevous model
when the previous model was set.

A view has a controller. You can get the current controller.

    view.getController();

The view's controller is created lazily the first time the
getController method is called. The view's
getDefaultControllerConstructor method returns the constructor function
to create the controller object and the getDefaultController actually
calls that constructor. Your application may redefine or override
either of these methods.

A view has a destroy method which should be called before your
application looses its last reference to the view.

An view object is a composite view. This means the view can have child
views added and removed. This functionality is provided by the Hijos
library. Briefly,

    var childView1 = new maria.View();
    var childView2 = new maria.View();
    view.appendChild(childView1);
    view.replaceChild(childView2, childView1);
    view.insertBefore(childView1, childView2);
    view.removeChild(childView2);
    view.childNodes;
    view.firstChild;
    view.lastChild;
    childView1.nextSibling;
    childView1.previousSibling;
    childView1.parentNode;

When a view's destroy method executes, it calls each child's destroy
method.

The maria.View constructor is relatively abstract. It is most likely
that your application can use maria.ElementView; however, if you are
creating a new type of view where maria.ElementView is not a good fit,
for example a view that represents part of a bitmap drawing on a canvas
element, then you may want to use maria.View as the "superclass" of
your new view constructor. The following example shows how this can be
done at a low level. See maria.View.subclass for a more compact way to
accomplish the same.

    myapp.MyView = function() {
        maria.View.apply(this, arguments);
    };
    myapp.MyView.prototype = maria.create(maria.View.prototype);
    myapp.MyView.prototype.constructor = myapp.MyView;
    myapp.MyView.prototype.getModelActions = function() {
        return {
            'squashed': 'onSquashed',
            'squished': 'onSquished'
        };
    };
    maria.MyView.prototype.onSquished = function(evt) {
        this.getController().onSquished(evt);
    };
    maria.MyView.prototype.onSquashed = function() {
    this.getController().onSquashed(evt);
    };
    myapp.MyView.prototype.getDefaultControllerConstructor = function() {
        return myapp.MyController;
    };
    myapp.MyView.prototype.anotherMethod = function() {
        alert('another method');
    };

*/
maria.View = function(model, controller) {
    hijos.Node.call(this);
    this.setModel(model);
    this.setController(controller);
};

maria.View.prototype = maria.create(hijos.Node.prototype);
maria.View.prototype.constructor = maria.View;

maria.View.prototype.destroy = function() {
    maria.purgeEventListener(this);
    this._model = null;
    if (this._controller) {
        this._controller.destroy();
        this._controller = null;
    }
    hijos.Node.prototype.destroy.call(this);
};

maria.View.prototype.update = function() {
    // to be overridden by concrete view subclasses
};

maria.View.prototype.getModel = function() {
    return this._model;
};

maria.View.prototype.setModel = function(model) {
    this._setModelAndController(model, this._controller);
};

maria.View.prototype.getDefaultControllerConstructor = function() {
    return maria.Controller;
};

maria.View.prototype.getDefaultController = function() {
    var constructor = this.getDefaultControllerConstructor();
    return new constructor();
};

maria.View.prototype.getController = function() {
    if (!this._controller) {
        this.setController(this.getDefaultController());
    }
    return this._controller;
};

maria.View.prototype.setController = function(controller) {
    this._setModelAndController(this._model, controller);
};

maria.View.prototype.getModelActions = function() {
    return {'change': 'update'};
};

maria.View.prototype._setModelAndController = function(model, controller) {
    var type, eventMap;
    if (this._model !== model) {
        if (this._model) {
            eventMap = this._lastModelActions;
            for (type in eventMap) {
                if (Object.prototype.hasOwnProperty.call(eventMap, type)) {
                    maria.removeEventListener(this._model, type, this, eventMap[type]);
                }
            }
            delete this._lastModelActions;
        }
        if (model) {
            eventMap = this._lastModelActions = this.getModelActions() || {};
            for (type in eventMap) {
                if (Object.prototype.hasOwnProperty.call(eventMap, type)) {
                    maria.addEventListener(model, type, this, eventMap[type]);
                }
            }
        }
        this._model = model;
    }
    if (controller) {
        controller.setView(this);
        controller.setModel(model);
    }
    this._controller = controller;
};
<|endoftext|>


<commit_before>// Ideally we would want to use aframe's stringifying system without rewriting our own
stringifyComponent = function (componentName, properties) {
  var componentValue;

  if (_.contains(COORDINATE_SYSTEMS, componentName)) {
    properties = _.map(properties, function(propValue, propName) {
      return propValue;
    });
    componentValue = properties.join(' ');
  } else {
    properties = _.map(properties, function(propValue, propName) {
      return propName + ': ' + propValue;
    });
    componentValue = properties.join('; ');
  }

  return componentValue;
}

setAttributes = function (data, templateName, attributeNames, primitiveName) {
  var helpers = {}
    attributes = [];

  _.each(attributeNames, function(attributeName){
      var component = {},
        attributeValue = data[attributeName];
      if (attributeValue) {
        if (_.isObject(attributeValue)) {
          attributeValue = stringifyComponent(attributeName, attributeValue);
        }
        component[attributeName] = attributeValue;
        attributes.push(component);
      }
  });

  if (primitiveName && PRIMITIVES[primitiveName] && PRIMITIVES[primitiveName].primitive) {
    var geometryValue = '',
      geometryKey = false;

    _.each(attributes, function(attribute, key, list){
      if (attribute.geometry) {
        geometryValue = attribute.geometry + ';';
        geometryKey = key;
      }
    });

    geometryValue += 'primitive:' + PRIMITIVES[primitiveName].primitive;
console.log(geometryValue)
    if (geometryKey) {
      attributes[geometryKey].geometry = geometryValue;
    } else {
      attributes.push({ geometry: geometryValue })
    }
  }

  Template[templateName].helpers({
    attributes: function () {
      return attributes;
    }
  });
}<commit_msg>Remove console.log statement<commit_after>// Ideally we would want to use aframe's stringifying system without rewriting our own
stringifyComponent = function (componentName, properties) {
  var componentValue;

  if (_.contains(COORDINATE_SYSTEMS, componentName)) {
    properties = _.map(properties, function(propValue, propName) {
      return propValue;
    });
    componentValue = properties.join(' ');
  } else {
    properties = _.map(properties, function(propValue, propName) {
      return propName + ': ' + propValue;
    });
    componentValue = properties.join('; ');
  }

  return componentValue;
}

setAttributes = function (data, templateName, attributeNames, primitiveName) {
  var helpers = {}
    attributes = [];

  _.each(attributeNames, function(attributeName){
      var component = {},
        attributeValue = data[attributeName];
      if (attributeValue) {
        if (_.isObject(attributeValue)) {
          attributeValue = stringifyComponent(attributeName, attributeValue);
        }
        component[attributeName] = attributeValue;
        attributes.push(component);
      }
  });

  if (primitiveName && PRIMITIVES[primitiveName] && PRIMITIVES[primitiveName].primitive) {
    var geometryValue = '',
      geometryKey = false;

    _.each(attributes, function(attribute, key, list){
      if (attribute.geometry) {
        geometryValue = attribute.geometry + ';';
        geometryKey = key;
      }
    });

    geometryValue += 'primitive:' + PRIMITIVES[primitiveName].primitive;

    if (geometryKey) {
      attributes[geometryKey].geometry = geometryValue;
    } else {
      attributes.push({ geometry: geometryValue })
    }
  }

  Template[templateName].helpers({
    attributes: function () {
      return attributes;
    }
  });
}<|endoftext|>


<commit_before>var LATENCY = 500;

function basicEventChecker(type, object, flag, extra) {
    object[flag] = false;
    window.addEventListener(type, function handler(c) {
        window.removeEventListener(type, handler, false);
        object[flag] = true;
        if (extra)
            extra();
    }, false);
}

context('Basics', function() {
    should('have public methods', function() {
        ok(window.openDataCache !== undefined, "window.openDataCache exists");
    });

    should('have constants', function() {
        ok(typeof DataCache.IDLE === 'number', "DataCache.IDLE exists");
        ok(typeof DataCache.READY === 'number', "DataCache.IDLE exists");
        ok(typeof DataCache.OBSOLETE === 'number', "DataCache.IDLE exists");
    });
});

context('Resolving Absolute URLs', function() {
    var resolve = DataCache.resolveAbsoluteFromBase;
    var location = { href: 'http://example.com/foo/bar.txt', host: 'example.com' }

    should('resolve properly', function() {

        // Already Full
        ok(resolve(location, 'http://example.com/foo/bar.txt') === '/foo/bar.txt');
        ok(resolve(location, 'http://example.com/foo/bar/baz.txt') === '/foo/bar/baz.txt');

        // Absolute, from host
        ok(resolve(location, '/foo/bar.txt') === '/foo/bar.txt');
        ok(resolve(location, '/foo/bar/baz.txt') === '/foo/bar/baz.txt');

        // Relative from Current Directory
        ok(resolve(location, 'foo/bar.txt') === '/foo/foo/bar.txt');
        ok(resolve(location, 'foo/bar/baz.txt') === '/foo/foo/bar/baz.txt');

        // Relative with tricks
        ok(resolve(location, 'foo/../bar.txt') === '/foo/bar.txt');
        ok(resolve(location, 'foo/../bar/baz.txt') === '/foo/bar/baz.txt');
        ok(resolve(location, 'a/b/c/../../d/../../bar/baz.txt') === '/foo/bar/baz.txt');

        // Absolute + Relative attempt to go above host
        ok(resolve(location, '../../../bar.txt') === '/bar.txt');
        ok(resolve(location, '/../../../bar.txt') === '/bar.txt');

        // Strips the #hash fragment
        ok(resolve(location, 'foo/../bar.txt#hash') === '/foo/bar.txt');
        ok(resolve(location, 'http://example.com/foo/bar.txt#hash') === '/foo/bar.txt');

    });
});

context('Parsing Headers', function() {
    var parse = CacheTransaction.prototype.parseHeaders;
    var headersText = [
        'Date: Sun, 06 Dec 2009 05:19:04 GMT',     // typical data
        'Content-Encoding: gzip',                  // typical data
        'Content-Length: 3447',                    // typical data
        'Content-Type: text/html; charset=UTF-8',  // typical data
        'Cache-Control: private, max-age=0',       // typical data
        'X-Xss-Protection:   0',                   // ignore left pad after colon
        '  Server: gws',                           // ignore left pad
        '  ',                                      // ignore blank line
        'Expires: -2',                             // gets overwritten later on
        'Expires: -1'
    ].join("\n");

    should('create a nice object', function() {
        var obj = parse(headersText);
        ok(obj['Cache-Control'] === "private, max-age=0");
        ok(obj['Content-Encoding'] === "gzip");
        ok(obj['Content-Length'] === "3447");
        ok(obj['Content-Type'] === "text/html; charset=UTF-8");
        ok(obj['Date'] === "Sun, 06 Dec 2009 05:19:04 GMT");
        ok(obj['Expires'] === "-1");
        ok(obj['Server'] === "gws");
        ok(obj['X-Xss-Protection'] === "0");
    });
});

// Testing private code
context('DataCacheGroup/Host', function() {
    var cache = window.openDataCache();
    var version = cache.version;
    var group = cache.group;

    should('have window as the host', function() {
        equals(group.host.realHost, window, "window is host");
    });

    should('return most current for relativeCache', function() {
        var newCache = group.create();
        var newVersion = newCache.version;

        ok(cache.group === newCache.group, "Same Group");
        ok(version < newVersion, "Version are in the proper order");

        ok(group.effectiveCache !== newCache, "Effective is not Newest");
        ok(group.relevantCache  === newCache, "Relevant is Newest");
        group.remove(newCache);
        ok(group.effectiveCache === cache, "Effective is newest");
        ok(group.relevantCache  === cache, "Relevant is newest");
    });

    should('not be not be obsolete', function() {
        equals(group.obsolete, false, "Not obsolete");
    });
});

context('DataCache', function() {
    should('be the same', function() {
        ok(window.openDataCache() === window.openDataCache(), 'Same returned');
    });
});

context('Offline Transaction', function() {
    var flags = {};
    var eventCache = null;
    var callbackFlag = false;

    should('trigger off-line-updating event', function() {
        stop();
        basicEventChecker('off-line-updating', flags, 'firedOfflineUpdating', function(c) {
            eventCache = c;
        });

        var cache = window.openDataCache();
        cache.offlineTransaction(function(tx) {
            flags.callbackFlag = true;
        });

        setTimeout(function() {
            ok(flags.firedOfflineUpdating, "did fire");
            ok(flags.callbackFlag, "did fire");
            ok(eventCache !== cache, "new cache");
            start();
        }); // no latency needed, these are all queued setTimeout's
    });
});

context('Offline Capture', function() {
    var body = 'Hello, World!';
    var uri = 'blah.html';

    should('capture and manage a resource', function() {
        stop();

        var flags = {};
        basicEventChecker('captured', flags, 'firedCapturedEvent');

        var cache = window.openDataCache();
        cache.offlineTransaction(function(tx) {
            tx.capture(uri, body, 'text/plain', ['GET']);
        });

        function verify() {
            cache.swapCache();
            var item = window.openDataCache().getItem(uri);
            ok(item.body === body, "proper data");
            ok(item.readyState === CacheItem.CACHED);
        }

        setTimeout(function() {
            ok(flags.firedCapturedEvent === true);
            verify();
            start();
        });
    });

    should('capture and release a resource', function() {
        stop();
        var txCache = null;

        var flags = {};
        basicEventChecker('captured', flags, 'firedCapturedEvent');
        basicEventChecker('released', flags, 'firedReleasedEvent');

        var cache = window.openDataCache();
        cache.offlineTransaction(function(tx) {
            tx.capture(uri, body, 'text/plain', ['GET']);
            tx.release(uri);
            txCache = tx.cache;
        });

        setTimeout(function() {
            ok(flags.firedCapturedEvent, 'did fire');
            ok(flags.firedReleasedEvent, 'did fire');
            ok(txCache.getItem(uri).readyState === CacheItem.GONE, 'no longer stored');
            start();
        });
    });
});

context('Online Transaction', function() {
    var uri = 'data.txt';
    var asyncData = null;
    var syncData = null;

    should('work asynchronously', function() {
        stop();
        var cache = window.openDataCache();
        var txCache = null;
        cache.transaction(function(tx) {
            tx.capture(uri);
            txCache = tx.cache;
        });
        setTimeout(function() {
            ok(txCache.getItem(uri).body === 'Hello, World!');
            start();
        }, LATENCY);
    });

    should('work synchronously', function() {
        stop();
        var cache = window.openDataCache();
        var tx = cache.transactionSync();
        tx.capture(uri);
        setTimeout(function() {
            ok(tx.cache.getItem(uri).body === 'Hello, World!');
            start();
        }, LATENCY);
    });
});

context('Online Transaction with 4xx or 5xx error', function() {
    var uri = 'code.php?code=500';
    should('fire error event', function() {
        stop();

        var flags = {};
        basicEventChecker('error', flags, 'firedErrorEvent');

        var cache = window.openDataCache();
        cache.transaction(function(tx) {
            tx.capture(uri);
        });

        setTimeout(function() {
            ok(flags.firedErrorEvent, "did fire");
            start();
        }, LATENCY);
    });
});

context('Online Transaction with 401', function() {
    var uri = 'code.php?code=401';
    var cache = window.openDataCache();

    should('make fire obsolete event and make obsolete', function() {
        stop();

        var flags = {};
        basicEventChecker('obsolete', flags, 'firedObsoleteEvent');

        cache.transaction(function(tx) {
            tx.capture(uri);
        });

        setTimeout(function() {
            ok(flags.firedObsoleteEvent, "did fire");
            ok(cache.group.status === DataCache.OBSOLETE, "group became obsolete");
            start();
        }, LATENCY);
    });
});
<commit_msg>Added more Online Transaction tests<commit_after>var LATENCY = 500;

function basicEventChecker(type, object, flag, extra) {
    object[flag] = false;
    window.addEventListener(type, function handler(c) {
        window.removeEventListener(type, handler, false);
        object[flag] = true;
        if (extra)
            extra();
    }, false);
}

context('Basics', function() {
    should('have public methods', function() {
        ok(window.openDataCache !== undefined, "window.openDataCache exists");
    });

    should('have constants', function() {
        ok(typeof DataCache.IDLE === 'number', "DataCache.IDLE exists");
        ok(typeof DataCache.READY === 'number', "DataCache.IDLE exists");
        ok(typeof DataCache.OBSOLETE === 'number', "DataCache.IDLE exists");
    });
});

context('Resolving Absolute URLs', function() {
    var resolve = DataCache.resolveAbsoluteFromBase;
    var location = { href: 'http://example.com/foo/bar.txt', host: 'example.com' }

    should('resolve properly', function() {

        // Already Full
        ok(resolve(location, 'http://example.com/foo/bar.txt') === '/foo/bar.txt');
        ok(resolve(location, 'http://example.com/foo/bar/baz.txt') === '/foo/bar/baz.txt');

        // Absolute, from host
        ok(resolve(location, '/foo/bar.txt') === '/foo/bar.txt');
        ok(resolve(location, '/foo/bar/baz.txt') === '/foo/bar/baz.txt');

        // Relative from Current Directory
        ok(resolve(location, 'foo/bar.txt') === '/foo/foo/bar.txt');
        ok(resolve(location, 'foo/bar/baz.txt') === '/foo/foo/bar/baz.txt');

        // Relative with tricks
        ok(resolve(location, 'foo/../bar.txt') === '/foo/bar.txt');
        ok(resolve(location, 'foo/../bar/baz.txt') === '/foo/bar/baz.txt');
        ok(resolve(location, 'a/b/c/../../d/../../bar/baz.txt') === '/foo/bar/baz.txt');

        // Absolute + Relative attempt to go above host
        ok(resolve(location, '../../../bar.txt') === '/bar.txt');
        ok(resolve(location, '/../../../bar.txt') === '/bar.txt');

        // Strips the #hash fragment
        ok(resolve(location, 'foo/../bar.txt#hash') === '/foo/bar.txt');
        ok(resolve(location, 'http://example.com/foo/bar.txt#hash') === '/foo/bar.txt');

    });
});

context('Parsing Headers', function() {
    var parse = CacheTransaction.prototype.parseHeaders;
    var headersText = [
        'Date: Sun, 06 Dec 2009 05:19:04 GMT',     // typical data
        'Content-Encoding: gzip',                  // typical data
        'Content-Length: 3447',                    // typical data
        'Content-Type: text/html; charset=UTF-8',  // typical data
        'Cache-Control: private, max-age=0',       // typical data
        'X-Xss-Protection:   0',                   // ignore left pad after colon
        '  Server: gws',                           // ignore left pad
        '  ',                                      // ignore blank line
        'Expires: -2',                             // gets overwritten later on
        'Expires: -1'
    ].join("\n");

    should('create a nice object', function() {
        var obj = parse(headersText);
        ok(obj['Cache-Control'] === "private, max-age=0");
        ok(obj['Content-Encoding'] === "gzip");
        ok(obj['Content-Length'] === "3447");
        ok(obj['Content-Type'] === "text/html; charset=UTF-8");
        ok(obj['Date'] === "Sun, 06 Dec 2009 05:19:04 GMT");
        ok(obj['Expires'] === "-1");
        ok(obj['Server'] === "gws");
        ok(obj['X-Xss-Protection'] === "0");
    });
});

// Testing private code
context('DataCacheGroup/Host', function() {
    var cache = window.openDataCache();
    var version = cache.version;
    var group = cache.group;

    should('have window as the host', function() {
        equals(group.host.realHost, window, "window is host");
    });

    should('return most current for relativeCache', function() {
        var newCache = group.create();
        var newVersion = newCache.version;

        ok(cache.group === newCache.group, "Same Group");
        ok(version < newVersion, "Version are in the proper order");

        ok(group.effectiveCache !== newCache, "Effective is not Newest");
        ok(group.relevantCache  === newCache, "Relevant is Newest");
        group.remove(newCache);
        ok(group.effectiveCache === cache, "Effective is newest");
        ok(group.relevantCache  === cache, "Relevant is newest");
    });

    should('not be not be obsolete', function() {
        equals(group.obsolete, false, "Not obsolete");
    });
});

context('DataCache', function() {
    should('be the same', function() {
        ok(window.openDataCache() === window.openDataCache(), 'Same returned');
    });
});

context('Offline Transaction', function() {
    var flags = {};
    var eventCache = null;
    var callbackFlag = false;

    should('trigger off-line-updating event', function() {
        stop();
        basicEventChecker('off-line-updating', flags, 'firedOfflineUpdating', function(c) {
            eventCache = c;
        });

        var cache = window.openDataCache();
        cache.offlineTransaction(function(tx) {
            flags.callbackFlag = true;
        });

        setTimeout(function() {
            ok(flags.firedOfflineUpdating, "did fire");
            ok(flags.callbackFlag, "did fire");
            ok(eventCache !== cache, "new cache");
            start();
        }); // no latency needed, these are all queued setTimeout's
    });
});

context('Offline Capture', function() {
    var body = 'Hello, World!';
    var uri = 'blah.html';

    should('capture and manage a resource', function() {
        stop();

        var flags = {};
        basicEventChecker('captured', flags, 'firedCapturedEvent');

        var cache = window.openDataCache();
        cache.offlineTransaction(function(tx) {
            tx.capture(uri, body, 'text/plain', ['GET']);
        });

        function verify() {
            cache.swapCache();
            var item = window.openDataCache().getItem(uri);
            ok(item.body === body, "proper data");
            ok(item.readyState === CacheItem.CACHED);
        }

        setTimeout(function() {
            ok(flags.firedCapturedEvent === true);
            verify();
            start();
        });
    });

    should('capture and release a resource', function() {
        stop();
        var txCache = null;

        var flags = {};
        basicEventChecker('captured', flags, 'firedCapturedEvent');
        basicEventChecker('released', flags, 'firedReleasedEvent');

        var cache = window.openDataCache();
        cache.offlineTransaction(function(tx) {
            tx.capture(uri, body, 'text/plain', ['GET']);
            tx.release(uri);
            txCache = tx.cache;
        });

        setTimeout(function() {
            ok(flags.firedCapturedEvent, 'did fire');
            ok(flags.firedReleasedEvent, 'did fire');
            ok(txCache.getItem(uri).readyState === CacheItem.GONE, 'no longer stored');
            start();
        });
    });
});

context('Online Transaction', function() {
    var uri = 'data.txt';
    var body = 'Hello, World!';
    var asyncData = null;
    var syncData = null;

    should('work asynchronously', function() {
        stop();
        var cache = window.openDataCache();
        var txCache = null;
        cache.transaction(function(tx) {
            tx.capture(uri);
            txCache = tx.cache;
        });
        setTimeout(function() {
            ok(txCache.getItem(uri).body === body);
            start();
        }, LATENCY);
    });

    should('work synchronously', function() {
        stop();
        var cache = window.openDataCache();
        var tx = cache.transactionSync();
        tx.capture(uri);
        setTimeout(function() {
            ok(tx.offline === false);
            ok(tx.cache.getItem(uri).body === body);
            ok(tx.cache.getItem(uri).readyState === CacheItem.CACHED);
            start();
        }, LATENCY);
    });

    should('have the same resources as the last online', function() {
        var cache = window.openDataCache();
        var tx = cache.transactionSync();
        ok(tx.cache.getItem(uri).body === body);
    });

    should('release a resource', function() {
        stop();

        var flags = {};
        basicEventChecker('released', flags, 'firedReleasedEvent');

        var cache = window.openDataCache();
        var tx = cache.transactionSync();
        tx.release(uri);

        setTimeout(function() {
            ok(flags.firedReleasedEvent, 'did fire');
            ok(tx.cache.getItem(uri).readyState === CacheItem.GONE);
            start();
        });
    });
});

context('Online Transaction with 4xx or 5xx error', function() {
    var uri = 'code.php?code=500';
    should('fire error event', function() {
        stop();

        var flags = {};
        basicEventChecker('error', flags, 'firedErrorEvent');

        var cache = window.openDataCache();
        cache.transaction(function(tx) {
            tx.capture(uri);
        });

        setTimeout(function() {
            ok(flags.firedErrorEvent, "did fire");
            start();
        }, LATENCY);
    });
});

context('Online Transaction with 401', function() {
    var uri = 'code.php?code=401';
    var cache = window.openDataCache();

    should('make fire obsolete event and make obsolete', function() {
        stop();

        var flags = {};
        basicEventChecker('obsolete', flags, 'firedObsoleteEvent');

        cache.transaction(function(tx) {
            tx.capture(uri);
        });

        setTimeout(function() {
            ok(flags.firedObsoleteEvent, "did fire");
            ok(cache.group.status === DataCache.OBSOLETE, "group became obsolete");
            start();
        }, LATENCY);
    });
});
<|endoftext|>