<commit_before>define(["github/adioo/bind/v0.2.0/bind", "/jquery.js"], function(Bind) {

    function List(module) {

        var self;
        var config;
        var container;
        var template;

        function processConfig(config) {
            config.options = config.options || {};
            config.template.binds = config.template.binds || [];

            var optClasses = config.options.classes || {}
            optClasses.selected = optClasses.selected || "selected";
            config.options.classes = optClasses;

            return config;
        }

        function init(conf) {

            // initialize the globals
            self = this;
            config = processConfig(conf);
            if (config.container) {
                container = $(config.container, module.dom);
            } else {
                container = module.dom;
            }
            template = $(config.template.value, module.dom);

            // **************************************
            // generate general binds from the config
            var binds = [];

            for (var i in config.controls) {
                switch (i) {
                    case "add":
                        binds.push({
                            target: ".create",
                            context: ".controls",
                            on: [{
                                name: "click",
                                emit: "requestNewItem"
                            }]
                        });
                        break;
                    case "delete":
                        binds.push({
                            target: ".delete",
                            context: ".controls",
                            on: [{
                                name: "click",
                                handler: "removeSelected"
                            }]
                        });
                        break;
                }
            }

            // run the internal binds
            for (var i in binds) {
                Bind.call(self, binds[i]);
            }

            // run the binds
            for (var i in config.binds) {
                Bind.call(self, config.binds[i]);
            }

            self.on("newItem", createItem);

            for (var miid in config.listen) {
                var miidEvents = config.listen[miid];
                for (var name in miidEvents) {
                    var handler = miidEvents[name];
                    if (typeof self[handler] === "function") {
                        self.on(name, miid, function(data) {
                            self[handler].call(self, data);
                        });
                    }
                }
            }

            if (config.options.autofetch) {
                self.read();
            }
        }

        function render(item) {
            switch (config.template.type) {
                case "selector":
                    renderSelector.call(self, item);
                case "html":
                    // TODO
                case "url":
                    // TODO
            }
        }

        function renderSelector(item) {
            var newItem = $(template).clone();
            newItem
                .removeClass("template")
                .appendTo(container)
                .show();

            for (var i in config.template.binds) {
                var bindObj = config.template.binds[i];
                bindObj.context = newItem;
                Bind.call(self, bindObj, item);
            }
        }

        // ********************************
        // Public functions ***************
        // ********************************

        function read(data) {

            self.link(config.crud.read, { data: data }, function(err, data) {

                if (err) { return; }

                for (var i in data) {
                    render.call(self, data[i]);
                }
            });
        }

        function createItem(itemData) {
            self.link(config.crud.create, { data: itemData }, function(err, data) {
                if (err) { return; }
            });
        }

        function removeItem(itemData) {
            self.link(config.crud.delete, { data: { id: itemData.id } }, function(err, data) {
                if (err) { return; }
                $("#" + itemData.id).remove();
            });
        }

        function removeSelected() {
            var ids = [];
            var selectedClass = config.options.classes.selected;
            $("." + selectedClass, container).each(function() {
                ids.push($(this).attr("id"));
            });

            self.link(config.crud.delete, { data: { ids: ids } }, function(err, data) {
                if (err) { return; }
                $("." + selectedClass, container).remove();
            });
        }

        function selectItem(dataItem) {
            var selectedClass = config.options.classes.selected;
            switch (config.options.selection) {
                case "single":
                    $("." + selectedClass, container).removeClass(selectedClass);
                    $("#" + dataItem.id, module.dom).addClass(selectedClass);
                    self.emit("selectionChanged", dataItem);
                    break;
                case "multiple":
                    $("#" + dataItem.id, module.dom).toggleClass(selectedClass);
                    break;
                default: // none
            }
        }

         return {
            init: init,
            read: read,
            removeItem: removeItem,
            removeSelected: removeSelected,
            selectItem: selectItem
        };
    }

    return function(module, config) {

        var list = new List(module);
        for (var i in list) {
            list[i] = module[i] || list[i];
        }
        list = Object.extend(list, module);

        list.init(config);

        return list;
    }
});

<commit_msg>clearList, better listener processing, added class for template item<commit_after>define(["github/adioo/bind/v0.2.0/bind", "/jquery.js"], function(Bind) {

    function List(module) {

        var self;
        var config;
        var container;
        var template;

        function processConfig(config) {
            config.options = config.options || {};
            config.template.binds = config.template.binds || [];

            var optClasses = config.options.classes || {}
            optClasses.item = optClasses.item || "item";
            optClasses.selected = optClasses.selected || "selected";
            config.options.classes = optClasses;

            return config;
        }

        function init(conf) {

            // initialize the globals
            self = this;
            config = processConfig(conf);
            if (config.container) {
                container = $(config.container, module.dom);
            } else {
                container = module.dom;
            }
            template = $(config.template.value, module.dom);

            // **************************************
            // generate general binds from the config
            var binds = [];

            for (var i in config.controls) {
                switch (i) {
                    case "add":
                        binds.push({
                            target: ".create",
                            context: ".controls",
                            on: [{
                                name: "click",
                                emit: "requestNewItem"
                            }]
                        });
                        break;
                    case "delete":
                        binds.push({
                            target: ".delete",
                            context: ".controls",
                            on: [{
                                name: "click",
                                handler: "removeSelected"
                            }]
                        });
                        break;
                }
            }

            // run the internal binds
            for (var i in binds) {
                Bind.call(self, binds[i]);
            }

            // run the binds
            for (var i in config.binds) {
                Bind.call(self, config.binds[i]);
            }

            self.on("newItem", createItem);

            // for each module instance I listen to
            for (var miid in config.listen) {
                var miidEvents = config.listen[miid];
                // for each event for this instance
                for (var name in miidEvents) {
                    var handler = miidEvents[name];
                    // if the handler is a module function name
                    if (typeof handler === "string" && typeof self[handler] === "function") {
                        self.on(name, miid, function(data) {
                            self[handler].call(self, data);
                        });
                        continue;
                    }
                    // else it must be object
                    if (handler instanceof Object) {
                        // TODO
                    }
                }
            }

            if (config.options.autofetch) {
                self.read();
            }
        }

        function render(item) {
            switch (config.template.type) {
                case "selector":
                    renderSelector.call(self, item);
                case "html":
                    // TODO
                case "url":
                    // TODO
            }
        }

        function renderSelector(item) {
            var newItem = $(template).clone();
            newItem
                .removeClass("template")
                .addClass(config.options.classes.item)
                .appendTo(container)
                .show();

            for (var i in config.template.binds) {
                var bindObj = config.template.binds[i];
                bindObj.context = newItem;
                Bind.call(self, bindObj, item);
            }
        }

        function clearList() {
            $("." + config.options.classes.item, container).remove();
        }

        // ********************************
        // Public functions ***************
        // ********************************

        function read(data) {

            clearList();

            self.link(config.crud.read, { data: data }, function(err, data) {

                if (err) { return; }

                for (var i in data) {
                    render.call(self, data[i]);
                }
            });
        }

        function createItem(itemData) {
            self.link(config.crud.create, { data: itemData }, function(err, data) {
                if (err) { return; }
            });
        }

        function removeItem(itemData) {
            self.link(config.crud.delete, { data: { id: itemData.id } }, function(err, data) {
                if (err) { return; }
                $("#" + itemData.id).remove();
            });
        }

        function removeSelected() {
            var ids = [];
            var selectedClass = config.options.classes.selected;
            $("." + selectedClass, container).each(function() {
                ids.push($(this).attr("id"));
            });

            self.link(config.crud.delete, { data: { ids: ids } }, function(err, data) {
                if (err) { return; }
                $("." + selectedClass, container).remove();
            });
        }

        function selectItem(dataItem) {
            var selectedClass = config.options.classes.selected;

            switch (config.options.selection) {

                case "single":
                    var currentItem = $("#" + dataItem.id, container);
                    if (currentItem.hasClass(selectedClass)) {
                        break;
                    }

                    $("." + selectedClass, container).removeClass(selectedClass);
                    $("#" + dataItem.id, container).addClass(selectedClass);
                    self.emit("selectionChanged", dataItem);
                    break;

                case "multiple":
                    $("#" + dataItem.id, module.dom).toggleClass(selectedClass);
                    break;

                default: // none
            }
        }

         return {
            init: init,
            read: read,
            removeItem: removeItem,
            removeSelected: removeSelected,
            selectItem: selectItem
        };
    }

    return function(module, config) {

        var list = new List(module);
        for (var i in list) {
            list[i] = module[i] || list[i];
        }
        list = Object.extend(list, module);

        list.init(config);

        return list;
    }
});

<|endoftext|>


<commit_before>/*
  lib/list.js
  Originally created on 11/29/16 by KhanCS (https://github.com/khancs).
*/

(function(context) {
    if (typeof context.ka === 'undefined') {
        context.ka = {};
    }
    var ka = context.ka;
    ka._libRoot = "https://khancs.github.io/lib/lib/";
    ka.libraries = {
        "pjs": {
            name: "Khan Academy Processing.js",
            desc: "A library to produce environments to emulate the Khan Academy Processing.js environment",
            src: "khancs/pjs.js"
        }
    };
})(this);
<commit_msg>Update list.js to use cdn.rawgit.com for files<commit_after>/*
  list.js
  Originally created on 11/29/16 by KhanCS (https://github.com/khancs).
*/

(function(context) {
    if (typeof context.ka === 'undefined') {
        context.ka = {};
    }
    var ka = context.ka;
    ka._libRoot = "https://cdn.rawgit.com/khancs/lib/v0.0.1/lib/";
    ka.libraries = {
        "pjs": {
            name: "Khan Academy Processing.js",
            desc: "A library to produce environments to emulate the Khan Academy Processing.js environment",
            src: "khancs/pjs.js"
        }
    };
})(this);
<|endoftext|>


<commit_before><commit_msg>Fix parallax example status bar background<commit_after><|endoftext|>


<commit_before>var defaultCreateOut = require('./createOut');
var extend = require('raptor-util/extend');

module.exports = function(target, renderer) {
    var renderFunc = renderer && (renderer.renderer || renderer.render || renderer);
    var createOut = target.createOut || renderer.createOut || defaultCreateOut;

    return extend(target, {
        createOut: createOut,

        renderToString: function(data, callback) {
            var localData = data || {};
            var render = renderFunc || this._;
            var out = createOut(localData.$global);

            out.global.template = this;

            if (callback) {
                out.on('finish', function() {
                       callback(null, out.toString(), out);
                   })
                   .once('error', callback);

                render(localData, out);
                return out.end();
            } else {
                out.sync();
                render(localData, out);
                return out.toString();
            }
        },

        renderSync: function(data) {
            var localData = data || {};
            var render = renderFunc || this._;
            var out = createOut(localData.$global);
            out.sync();

            out.global.template = this;

            render(localData, out);
            return out.$__getResult();
        },

        /**
         * Renders a template to either a stream (if the last
         * argument is a Stream instance) or
         * provides the output to a callback function (if the last
         * argument is a Function).
         *
         * Supported signatures:
         *
         * render(data)
         * render(data, out)
         * render(data, stream)
         * render(data, callback)
         *
         * @param  {Object} data The view model data for the template
         * @param  {AsyncStream/AsyncVDOMBuilder} out A Stream, an AsyncStream/AsyncVDOMBuilder instance, or a callback function
         * @return {AsyncStream/AsyncVDOMBuilder} Returns the AsyncStream/AsyncVDOMBuilder instance that the template is rendered to
         */
        render: function(data, out) {
            var callback;
            var finalOut;
            var finalData;
            var globalData;
            var render = renderFunc || this._;
            var shouldBuffer = this.$__shouldBuffer;
            var shouldEnd = true;

            if (data) {
                finalData = data;
                if ((globalData = data.$global)) {
                    finalData.$global = null;
                }
            } else {
                finalData = {};
            }

            if (out && out.$__isOut) {
                finalOut = out;
                shouldEnd = false;
                extend(out.global, globalData);
            } else if (typeof out === 'function') {
                finalOut = createOut(globalData);
                callback = out;
            } else {
                finalOut = createOut(
                    globalData, // global
                    out, // writer(AsyncStream) or parentNode(AsyncVDOMBuilder)
                    null, // state
                    shouldBuffer // ignored by AsyncVDOMBuilder
                );
            }

            if (callback) {
                finalOut
                    .on('finish', function() {
                        callback(null, finalOut.$__getResult());
                    })
                    .once('error', callback);
            }

            globalData = finalOut.global;

            globalData.template = globalData.template || this;

            render(finalData, finalOut);

            return shouldEnd ? finalOut.end() : finalOut;
        }
    });
};<commit_msg>remove  from input for all render methods<commit_after>var defaultCreateOut = require('./createOut');
var extend = require('raptor-util/extend');

module.exports = function(target, renderer) {
    var renderFunc = renderer && (renderer.renderer || renderer.render || renderer);
    var createOut = target.createOut || renderer.createOut || defaultCreateOut;

    return extend(target, {
        createOut: createOut,

        renderToString: function(data, callback) {
            var localData = data || {};
            var render = renderFunc || this._;
            var out = createOut(localData.$global);

            out.global.template = this;
            localData.$global = undefined;

            if (callback) {
                out.on('finish', function() {
                       callback(null, out.toString(), out);
                   })
                   .once('error', callback);

                render(localData, out);
                return out.end();
            } else {
                out.sync();
                render(localData, out);
                return out.toString();
            }
        },

        renderSync: function(data) {
            var localData = data || {};
            var render = renderFunc || this._;
            var out = createOut(localData.$global);
            out.sync();

            out.global.template = this;
            localData.$global = undefined;

            render(localData, out);
            return out.$__getResult();
        },

        /**
         * Renders a template to either a stream (if the last
         * argument is a Stream instance) or
         * provides the output to a callback function (if the last
         * argument is a Function).
         *
         * Supported signatures:
         *
         * render(data)
         * render(data, out)
         * render(data, stream)
         * render(data, callback)
         *
         * @param  {Object} data The view model data for the template
         * @param  {AsyncStream/AsyncVDOMBuilder} out A Stream, an AsyncStream/AsyncVDOMBuilder instance, or a callback function
         * @return {AsyncStream/AsyncVDOMBuilder} Returns the AsyncStream/AsyncVDOMBuilder instance that the template is rendered to
         */
        render: function(data, out) {
            var callback;
            var finalOut;
            var finalData;
            var globalData;
            var render = renderFunc || this._;
            var shouldBuffer = this.$__shouldBuffer;
            var shouldEnd = true;

            if (data) {
                finalData = data;
                if ((globalData = data.$global)) {
                    finalData.$global = undefined;
                }
            } else {
                finalData = {};
            }

            if (out && out.$__isOut) {
                finalOut = out;
                shouldEnd = false;
                extend(out.global, globalData);
            } else if (typeof out === 'function') {
                finalOut = createOut(globalData);
                callback = out;
            } else {
                finalOut = createOut(
                    globalData, // global
                    out, // writer(AsyncStream) or parentNode(AsyncVDOMBuilder)
                    null, // state
                    shouldBuffer // ignored by AsyncVDOMBuilder
                );
            }

            if (callback) {
                finalOut
                    .on('finish', function() {
                        callback(null, finalOut.$__getResult());
                    })
                    .once('error', callback);
            }

            globalData = finalOut.global;

            globalData.template = globalData.template || this;

            render(finalData, finalOut);

            return shouldEnd ? finalOut.end() : finalOut;
        }
    });
};<|endoftext|>


<commit_before>/**
 * The Base module
 * a base class for all documents that have: comments, attachments, other keyvalues
 * it inherits from Document to support some basic actions
 * @copyright CHECKROOM NV 2015
 * @module base
 */
define([
    'jquery',
    'common',
    'api',
    'document',
    'comment',
    'attachment',
    'field'],  /** @lends Base */ function ($, common, api, Document, Comment, Attachment, Field) {

    // Some constant values
    var DEFAULTS = {
        id: "",
        modified: null,
        cover: null,
        flag: null,
        fields: {},
        comments: [],
        attachments: []
    };

    // Allow overriding the ctor during inheritance
    // http://stackoverflow.com/questions/4152931/javascript-inheritance-call-super-constructor-or-use-prototype-chain
    var tmp = function(){};
    tmp.prototype = Document.prototype;

    /**
     * @name  Base
     * @class
     * @property {ApiDataSource} dsAttachments   attachments datasource
     * @property {string} crtype                 e.g. cheqroom.types.customer
     * @property {moment} modified               last modified timestamp
     * @property {string} flag                   the document flag
     * @property {object} fields                 dictionary of document fields
     * @property {array} comments                array of Comment objects
     * @property {array} attachments             array of Attachment objects
     * @property {string} cover                  cover attachment id, default null
     * @constructor
     * @extends Document
     */
    var Base = function(opt) {
        var spec = $.extend({}, opt);
        Document.call(this, spec);

        this.dsAttachments = spec.dsAttachments;                                // ApiDataSource for the attachments coll
        this.crtype = spec.crtype;                                              // e.g. cheqroom.types.customer
        this.modified = spec.modified || DEFAULTS.modified;                     // last modified timestamp in momentjs
        this.flag = spec.flag || DEFAULTS.flag;                                 // flag
        this.fields = spec.fields || $.extend({}, DEFAULTS.fields);             // fields dictionary
        this.comments = spec.comments || DEFAULTS.comments.slice();             // comments array
        this.attachments = spec.attachments || DEFAULTS.attachments.slice();    // attachments array
        this.cover = spec.cover || DEFAULTS.cover;                              // cover attachment id, default null
    };

    Base.prototype = new tmp();
    Base.prototype.constructor = Base;

    //
    // Document overrides
    //
    Base.prototype._getDefaults = function() {
        return DEFAULTS;
    };

    /**
     * Checks if the object is empty
     * after calling reset() isEmpty() should return true
     * We'll only check for fields, comments, attachments here
     * @name  Base#isEmpty
     * @method
     * @returns {boolean}
     * @override
     */
    Base.prototype.isEmpty = function() {
        return (
            (this.flag==DEFAULTS.flag) &&
            ((this.fields==null) || (Object.keys(this.fields).length==0)) &&
            ((this.comments==null) || (this.comments.length==0)) &&
            ((this.attachments==null) || (this.attachments.length==0))
        );
    };

    /**
     * Checks if the base is dirty and needs saving
     * @name Base#isDirty
     * @returns {boolean}
     */
    Base.prototype.isDirty = function() {
        return (
        (this._isDirtyFlag()) ||
        (this._isDirtyFields()));
    };

    /**
     * Checks via the api if we can delete the document
     * @name  Base#canDelete
     * @method
     * @returns {promise}
     * @override
     */
    Base.prototype.canDelete = function() {
        // Documents can only be deleted when they have a pk
        if (this.existsInDb()) {
            return this.ds.call(this.id, 'canDelete');
        } else {
            return $.Deferred().resolve({result: false, message: ''});
        }
    };

    // Comments
    // ----
    /**
     * Adds a comment by string
     * @name  Base#addComment
     * @method
     * @param comment
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.addComment = function(comment, skipRead) {
        return this._doApiCall({
            method: 'addComment',
            params: {comment: comment},
            skipRead: skipRead
        });
    };

    /**
     * Updates a comment by id
     * @name  Base#updateComment
     * @method
     * @param id
     * @param comment
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.updateComment = function(id, comment, skipRead) {
        return this._doApiCall({
            method: 'updateComment',
            params: {commentId: id, comment: comment},
            skipRead: skipRead
        });
    };

    /**
     * Deletes a Comment by id
     * @name  Base#deleteComment
     * @method
     * @param id
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.deleteComment = function(id, skipRead) {
        return this._doApiCall({
            method: 'removeComment',
            params: {commentId: id},
            skipRead: skipRead
        });
    };

    // Field stuff
    // ----
    /**
     * Sets a custom field
     * @name Base#setField
     * @method
     * @param field
     * @param value
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.setField = function(field, value, skipRead) {
        return this._doApiCall({
            method: 'setField',
            params: {field: field, value: value},
            skipRead: skipRead
        });
    };

    /**
     * Clears a custom field
     * @name Base#clearField
     * @method
     * @param field
     * @param skipRead
     */
    Base.prototype.clearField = function(field, skipRead) {
        return this._doApiCall({
            method: 'setField',
            params: {field: field},
            skipRead: skipRead
        });
    };

    // Attachments stuff
    // ----
    /**
     * Gets an url for a user avatar
     * 'XS': (64, 64),
     * 'S': (128, 128),
     * 'M': (256, 256),
     * 'L': (512, 512)
     * @param size {string} default null is original size
     * @param groupId {string} Group primary key (only when you're passing an attachment)
     * @param att {string} attachment primary key, by default we take the cover
     * @param bustCache {boolean}
     * @returns {string}
     */
    Base.prototype.getImageUrl = function(size, groupId, att, bustCache) {
        var attachment = att || this.cover;
        return (
        (attachment!=null) &&
        (attachment.length>0)) ?
            this.helper.getImageCDNUrl(groupId, attachment, size) :
            this.helper.getImageUrl(this.ds, this.id, size, bustCache);
    };

    /**
     * Set the cover image to an Attachment
     * @name  Base#setCover
     * @method
     * @param att
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.setCover = function(att, skipRead) {
        return this._doApiCall({
            method: 'setCover',
            params: {attachmentId: att._id},
            skipRead: skipRead
        });
    };

    /**
     * Clears the cover image
     * @name  Base#clearCover
     * @method
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.clearCover = function(skipRead) {
        return this._doApiCall({
            method: 'clearCover',
            params: {},
            skipRead: skipRead
        });
    };

    /**
     * attaches an Attachment object
     * @name  Base#attach
     * @method
     * @param attachmentId
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.attach = function(attachmentId, skipRead) {
        if (this.existsInDb()) {
            return this._doApiCall({
                method: 'attach',
                params: {attachments: [attachmentId]},
                skipRead: skipRead
            });
        } else {
            return $.Deferred().reject(new api.ApiError('Cannot attach attachment, id is empty or null'));
        }
    };

    /**
     * detaches an Attachment by kvId (guid)
     * @name  Base#detach
     * @method
     * @param attachmentId
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.detach = function(attachmentId, skipRead) {
        if (this.existsInDb()) {
            return this._doApiCall({
                method: 'detach',
                params: {attachments: [attachmentId]},
                skipRead: skipRead
            });
        } else {
            return $.Deferred().reject(new api.ApiError('Cannot detach attachment, id is empty or null'));
        }
    };

    // Flags stuff
    // ----

    /**
     * Sets the flag of an item
     * @name Base#setFlag
     * @param flag
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.setFlag = function(flag, skipRead) {
        return this._doApiCall({
            method: 'setFlag',
            params: { flag: flag },
            skipRead: skipRead});
    };

    /**
     * Clears the flag of an item
     * @name Base#clearFlag
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.clearFlag = function (skipRead) {
        return this._doApiCall({
            method: 'clearFlag',
            params: {},
            skipRead: skipRead
        });
    };

    /**
     * Returns a list of Field objects
     * @param fieldDefs     array of field definitions
     * @return {array}
     */
    Base.prototype.getSortedFields = function(fieldDefs, onlyFormFields){
        var that = this,
            fields = [];

        // Work on copy of fieldDefs array
        fieldDefs = fieldDefs.slice();

        // Return only form field definitions?
        fieldDefs = fieldDefs.filter(function(def){ return onlyFormFields == true?def.form:true; });

        // Create a Field object for each field definition
        for(var i=0;i<fieldDefs.length;i++){
            var fieldDef = fieldDefs[i];
            var fieldValue = that.fields[fieldDef.name];

            fields.push(that._getField($.extend({ value: fieldValue }, fieldDef)));
        }

        return fields;
    }

    /**
     * Update item fields based on the given Field objects
     * @param {array} fields    array of Field objects
     */
    Base.prototype.setSortedFields = function(fields){
        for(var i=0;i<fields.length;i++){
            var field = fields[i];
            this.fields[field.name] = field.value;
        }
    }

    /**
     * Checks if all item fields are valid
     * @param  {array}  fields
     * @return {Boolean}
     */
    Base.prototype.validateSortedFields = function(fields){
        for(var i=0;i<fields.length;i++){
            if(!fields[i].isValid()){
                return false;
            }
        }
        return true;
    }

    // Implementation
    // ----

    /**
     * Checks if the flag is dirty compared to the raw response
     * @returns {boolean}
     * @private
     */
    Base.prototype._isDirtyFlag = function() {
        if (this.raw) {
            return (this.flag != this.raw.flag);
        } else {
            return false;
        }
    };

    /**
     * Checks if the fields are dirty compared to the raw response
     * @returns {boolean}
     * @private
     */
    Base.prototype._isDirtyFields = function() {
        if (this.raw) {
            return !(common.areEqual(this.fields, this.raw.fields));
        } else {
            return false;
        }
    };

    /**
     * Runs over the fields that are dirty and calls `setField
     * @returns {*}
     * @private
     */
    Base.prototype._updateFields = function() {
        var calls = [];

        if (this.raw) {
            for (var key in this.fields) {
                if (this.fields[key] != this.raw.fields[key]) {
                    calls.push(this.setField(key, this.fields[key], true));
                }
            }
        }

        if (calls.length>0) {
            return $.when(calls);
        } else {
            return $.Deferred().resolve(this);
        }
    };

    // toJson, fromJson
    // ----

    /**
     * _toJson, makes a dict of params to use during create / update
     * @param options
     * @returns {{}}
     * @private
     */
    Base.prototype._toJson = function(options) {
        return Document.prototype._toJson.call(this, options);
    };

    /**
     * _fromJson: read some basic information
     * @method
     * @param {object} data the json response
     * @param {object} options dict
     * @private
     */
    Base.prototype._fromJson = function(data, options) {
        var that = this;
        return Document.prototype._fromJson.call(this, data, options)
            .then(function() {
                that.flag = data.flag || DEFAULTS.flag;
                that.fields = (data.fields!=null) ? $.extend({}, data.fields) : $.extend({}, DEFAULTS.fields);
                that.modified = data.modified || DEFAULTS.modified;

                return that._fromCommentsJson(data, options)
                    .then(function() {
                        return that._fromAttachmentsJson(data, options);
                    });
            });
    };

    /**
     * _toJsonFields: makes json which can be used to set fields during `create`
     * @method
     * @param options
     * @returns {{}}
     * @private
     */
    Base.prototype._toJsonFields = function(options) {
        var fields = {};
        if (this.fields) {
            for (var key in this.fields) {
                fields["fields__"+key] = this.fields[key];
            }
        }
        return fields;
    };

    /**
     * _fromCommentsJson: reads the data.comments
     * @param data
     * @param options
     * @returns {*}
     * @private
     */
    Base.prototype._fromCommentsJson = function(data, options) {
        var obj = null,
            that = this;

        this.comments = DEFAULTS.comments.slice();

        if( (data.comments) &&
            (data.comments.length>0)) {
            $.each(data.comments, function(i, comment) {
                obj = that._getComment(comment, options);
                if (obj) {
                    that.comments.push(obj);
                }
            });
        }

        return $.Deferred().resolve(data);
    };

    /**
     * _fromAttachmentsJson: reads the data.attachments
     * @param data
     * @param options
     * @returns {*}
     * @private
     */
    Base.prototype._fromAttachmentsJson = function(data, options) {
        var obj = null,
            that = this;

        this.attachments = DEFAULTS.attachments.slice();

        if( (data.attachments) &&
            (data.attachments.length>0)) {
            $.each(data.attachments, function(i, att) {
                obj = that._getAttachment(att, options);
                if (obj) {
                    that.attachments.push(obj);
                }
            });
        }

        return $.Deferred().resolve(data);
    };

    Base.prototype._getComment = function(data, options) {
        var spec = $.extend({ds: this.ds}, options || {}, data);
        return new Comment(spec);
    };

    Base.prototype._getAttachment = function(data, options) {
        var spec = $.extend({ds: this.ds}, options || {}, data);
        return new Attachment(spec);
    };

    Base.prototype._getField = function(data, options){
        var spec = $.extend({}, options || {}, data);
        return new Field(spec);
    }

    return Base;

});
<commit_msg>Added an extra limit param to getSortedFields<commit_after>/**
 * The Base module
 * a base class for all documents that have: comments, attachments, other keyvalues
 * it inherits from Document to support some basic actions
 * @copyright CHECKROOM NV 2015
 * @module base
 */
define([
    'jquery',
    'common',
    'api',
    'document',
    'comment',
    'attachment',
    'field'],  /** @lends Base */ function ($, common, api, Document, Comment, Attachment, Field) {

    // Some constant values
    var DEFAULTS = {
        id: "",
        modified: null,
        cover: null,
        flag: null,
        fields: {},
        comments: [],
        attachments: []
    };

    // Allow overriding the ctor during inheritance
    // http://stackoverflow.com/questions/4152931/javascript-inheritance-call-super-constructor-or-use-prototype-chain
    var tmp = function(){};
    tmp.prototype = Document.prototype;

    /**
     * @name  Base
     * @class
     * @property {ApiDataSource} dsAttachments   attachments datasource
     * @property {string} crtype                 e.g. cheqroom.types.customer
     * @property {moment} modified               last modified timestamp
     * @property {string} flag                   the document flag
     * @property {object} fields                 dictionary of document fields
     * @property {array} comments                array of Comment objects
     * @property {array} attachments             array of Attachment objects
     * @property {string} cover                  cover attachment id, default null
     * @constructor
     * @extends Document
     */
    var Base = function(opt) {
        var spec = $.extend({}, opt);
        Document.call(this, spec);

        this.dsAttachments = spec.dsAttachments;                                // ApiDataSource for the attachments coll
        this.crtype = spec.crtype;                                              // e.g. cheqroom.types.customer
        this.modified = spec.modified || DEFAULTS.modified;                     // last modified timestamp in momentjs
        this.flag = spec.flag || DEFAULTS.flag;                                 // flag
        this.fields = spec.fields || $.extend({}, DEFAULTS.fields);             // fields dictionary
        this.comments = spec.comments || DEFAULTS.comments.slice();             // comments array
        this.attachments = spec.attachments || DEFAULTS.attachments.slice();    // attachments array
        this.cover = spec.cover || DEFAULTS.cover;                              // cover attachment id, default null
    };

    Base.prototype = new tmp();
    Base.prototype.constructor = Base;

    //
    // Document overrides
    //
    Base.prototype._getDefaults = function() {
        return DEFAULTS;
    };

    /**
     * Checks if the object is empty
     * after calling reset() isEmpty() should return true
     * We'll only check for fields, comments, attachments here
     * @name  Base#isEmpty
     * @method
     * @returns {boolean}
     * @override
     */
    Base.prototype.isEmpty = function() {
        return (
            (this.flag==DEFAULTS.flag) &&
            ((this.fields==null) || (Object.keys(this.fields).length==0)) &&
            ((this.comments==null) || (this.comments.length==0)) &&
            ((this.attachments==null) || (this.attachments.length==0))
        );
    };

    /**
     * Checks if the base is dirty and needs saving
     * @name Base#isDirty
     * @returns {boolean}
     */
    Base.prototype.isDirty = function() {
        return (
        (this._isDirtyFlag()) ||
        (this._isDirtyFields()));
    };

    /**
     * Checks via the api if we can delete the document
     * @name  Base#canDelete
     * @method
     * @returns {promise}
     * @override
     */
    Base.prototype.canDelete = function() {
        // Documents can only be deleted when they have a pk
        if (this.existsInDb()) {
            return this.ds.call(this.id, 'canDelete');
        } else {
            return $.Deferred().resolve({result: false, message: ''});
        }
    };

    // Comments
    // ----
    /**
     * Adds a comment by string
     * @name  Base#addComment
     * @method
     * @param comment
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.addComment = function(comment, skipRead) {
        return this._doApiCall({
            method: 'addComment',
            params: {comment: comment},
            skipRead: skipRead
        });
    };

    /**
     * Updates a comment by id
     * @name  Base#updateComment
     * @method
     * @param id
     * @param comment
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.updateComment = function(id, comment, skipRead) {
        return this._doApiCall({
            method: 'updateComment',
            params: {commentId: id, comment: comment},
            skipRead: skipRead
        });
    };

    /**
     * Deletes a Comment by id
     * @name  Base#deleteComment
     * @method
     * @param id
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.deleteComment = function(id, skipRead) {
        return this._doApiCall({
            method: 'removeComment',
            params: {commentId: id},
            skipRead: skipRead
        });
    };

    // Field stuff
    // ----
    /**
     * Sets a custom field
     * @name Base#setField
     * @method
     * @param field
     * @param value
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.setField = function(field, value, skipRead) {
        return this._doApiCall({
            method: 'setField',
            params: {field: field, value: value},
            skipRead: skipRead
        });
    };

    /**
     * Clears a custom field
     * @name Base#clearField
     * @method
     * @param field
     * @param skipRead
     */
    Base.prototype.clearField = function(field, skipRead) {
        return this._doApiCall({
            method: 'setField',
            params: {field: field},
            skipRead: skipRead
        });
    };

    // Attachments stuff
    // ----
    /**
     * Gets an url for a user avatar
     * 'XS': (64, 64),
     * 'S': (128, 128),
     * 'M': (256, 256),
     * 'L': (512, 512)
     * @param size {string} default null is original size
     * @param groupId {string} Group primary key (only when you're passing an attachment)
     * @param att {string} attachment primary key, by default we take the cover
     * @param bustCache {boolean}
     * @returns {string}
     */
    Base.prototype.getImageUrl = function(size, groupId, att, bustCache) {
        var attachment = att || this.cover;
        return (
        (attachment!=null) &&
        (attachment.length>0)) ?
            this.helper.getImageCDNUrl(groupId, attachment, size) :
            this.helper.getImageUrl(this.ds, this.id, size, bustCache);
    };

    /**
     * Set the cover image to an Attachment
     * @name  Base#setCover
     * @method
     * @param att
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.setCover = function(att, skipRead) {
        return this._doApiCall({
            method: 'setCover',
            params: {attachmentId: att._id},
            skipRead: skipRead
        });
    };

    /**
     * Clears the cover image
     * @name  Base#clearCover
     * @method
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.clearCover = function(skipRead) {
        return this._doApiCall({
            method: 'clearCover',
            params: {},
            skipRead: skipRead
        });
    };

    /**
     * attaches an Attachment object
     * @name  Base#attach
     * @method
     * @param attachmentId
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.attach = function(attachmentId, skipRead) {
        if (this.existsInDb()) {
            return this._doApiCall({
                method: 'attach',
                params: {attachments: [attachmentId]},
                skipRead: skipRead
            });
        } else {
            return $.Deferred().reject(new api.ApiError('Cannot attach attachment, id is empty or null'));
        }
    };

    /**
     * detaches an Attachment by kvId (guid)
     * @name  Base#detach
     * @method
     * @param attachmentId
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.detach = function(attachmentId, skipRead) {
        if (this.existsInDb()) {
            return this._doApiCall({
                method: 'detach',
                params: {attachments: [attachmentId]},
                skipRead: skipRead
            });
        } else {
            return $.Deferred().reject(new api.ApiError('Cannot detach attachment, id is empty or null'));
        }
    };

    // Flags stuff
    // ----

    /**
     * Sets the flag of an item
     * @name Base#setFlag
     * @param flag
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.setFlag = function(flag, skipRead) {
        return this._doApiCall({
            method: 'setFlag',
            params: { flag: flag },
            skipRead: skipRead});
    };

    /**
     * Clears the flag of an item
     * @name Base#clearFlag
     * @param skipRead
     * @returns {promise}
     */
    Base.prototype.clearFlag = function (skipRead) {
        return this._doApiCall({
            method: 'clearFlag',
            params: {},
            skipRead: skipRead
        });
    };

    /**
     * Returns a list of Field objects
     * @param fieldDefs         array of field definitions
     * @param onlyFormFields    should return only form fields
     * @param limit             return no more than x fields
     * @return {Array}
     */
    Base.prototype.getSortedFields = function(fieldDefs, onlyFormFields, limit) {
        var that = this,
            fields = [],
            fieldDef = null,
            fieldValue = null;

        // Work on copy of fieldDefs array
        fieldDefs = fieldDefs.slice();

        // Return only form field definitions?
        fieldDefs = fieldDefs.filter(function(def) { return onlyFormFields == true ? def.form : true; });

        // Create a Field object for each field definition
        for (var i=0;i<fieldDefs.length;i++) {
            fieldDef = fieldDefs[i];
            fieldValue = that.fields[fieldDef.name];

            if( (limit==null) ||
                (limit>fields.length)) {
                fields.push(that._getField($.extend({ value: fieldValue }, fieldDef)));
            }
        }

        return fields;
    };

    /**
     * Update item fields based on the given Field objects
     * @param {Array} fields    array of Field objects
     */
    Base.prototype.setSortedFields = function(fields) {
        for (var i=0;i<fields.length;i++) {
            var field = fields[i];
            this.fields[field.name] = field.value;
        }
    };

    /**
     * Checks if all item fields are valid
     * @param  {Array}  fields
     * @return {Boolean}
     */
    Base.prototype.validateSortedFields = function(fields) {
        for (var i=0;i<fields.length;i++) {
            if (!fields[i].isValid()) {
                return false;
            }
        }
        return true;
    };

    // Implementation
    // ----

    /**
     * Checks if the flag is dirty compared to the raw response
     * @returns {boolean}
     * @private
     */
    Base.prototype._isDirtyFlag = function() {
        if (this.raw) {
            return (this.flag != this.raw.flag);
        } else {
            return false;
        }
    };

    /**
     * Checks if the fields are dirty compared to the raw response
     * @returns {boolean}
     * @private
     */
    Base.prototype._isDirtyFields = function() {
        if (this.raw) {
            return !(common.areEqual(this.fields, this.raw.fields));
        } else {
            return false;
        }
    };

    /**
     * Runs over the fields that are dirty and calls `setField
     * @returns {*}
     * @private
     */
    Base.prototype._updateFields = function() {
        var calls = [];

        if (this.raw) {
            for (var key in this.fields) {
                if (this.fields[key] != this.raw.fields[key]) {
                    calls.push(this.setField(key, this.fields[key], true));
                }
            }
        }

        if (calls.length>0) {
            return $.when(calls);
        } else {
            return $.Deferred().resolve(this);
        }
    };

    // toJson, fromJson
    // ----

    /**
     * _toJson, makes a dict of params to use during create / update
     * @param options
     * @returns {{}}
     * @private
     */
    Base.prototype._toJson = function(options) {
        return Document.prototype._toJson.call(this, options);
    };

    /**
     * _fromJson: read some basic information
     * @method
     * @param {object} data the json response
     * @param {object} options dict
     * @private
     */
    Base.prototype._fromJson = function(data, options) {
        var that = this;
        return Document.prototype._fromJson.call(this, data, options)
            .then(function() {
                that.flag = data.flag || DEFAULTS.flag;
                that.fields = (data.fields!=null) ? $.extend({}, data.fields) : $.extend({}, DEFAULTS.fields);
                that.modified = data.modified || DEFAULTS.modified;

                return that._fromCommentsJson(data, options)
                    .then(function() {
                        return that._fromAttachmentsJson(data, options);
                    });
            });
    };

    /**
     * _toJsonFields: makes json which can be used to set fields during `create`
     * @method
     * @param options
     * @returns {{}}
     * @private
     */
    Base.prototype._toJsonFields = function(options) {
        var fields = {};
        if (this.fields) {
            for (var key in this.fields) {
                fields["fields__"+key] = this.fields[key];
            }
        }
        return fields;
    };

    /**
     * _fromCommentsJson: reads the data.comments
     * @param data
     * @param options
     * @returns {*}
     * @private
     */
    Base.prototype._fromCommentsJson = function(data, options) {
        var obj = null,
            that = this;

        this.comments = DEFAULTS.comments.slice();

        if( (data.comments) &&
            (data.comments.length>0)) {
            $.each(data.comments, function(i, comment) {
                obj = that._getComment(comment, options);
                if (obj) {
                    that.comments.push(obj);
                }
            });
        }

        return $.Deferred().resolve(data);
    };

    /**
     * _fromAttachmentsJson: reads the data.attachments
     * @param data
     * @param options
     * @returns {*}
     * @private
     */
    Base.prototype._fromAttachmentsJson = function(data, options) {
        var obj = null,
            that = this;

        this.attachments = DEFAULTS.attachments.slice();

        if( (data.attachments) &&
            (data.attachments.length>0)) {
            $.each(data.attachments, function(i, att) {
                obj = that._getAttachment(att, options);
                if (obj) {
                    that.attachments.push(obj);
                }
            });
        }

        return $.Deferred().resolve(data);
    };

    Base.prototype._getComment = function(data, options) {
        var spec = $.extend({ds: this.ds}, options || {}, data);
        return new Comment(spec);
    };

    Base.prototype._getAttachment = function(data, options) {
        var spec = $.extend({ds: this.ds}, options || {}, data);
        return new Attachment(spec);
    };

    Base.prototype._getField = function(data, options){
        var spec = $.extend({}, options || {}, data);
        return new Field(spec);
    }

    return Base;

});
<|endoftext|>


<commit_before>/*
	Base is extended from Scale, it is used for the basic/ folder section. All mixined or extended modules will have all its behavior. Where you define your absolutely needed methods for the APIs. Internal use only, never use it for displaying a chart.

	Initalization:
		initialize: function(containerInfo) {
			var chart =  this.mixin('Base',  axisG, {
				info: containerInfo,
				x: 'ordinal',
				y: 'log',
				width: this.base.attr('width'),  // optional
				height: this.base.attr('height')   // optional
			});
		}

		// Passing options object as a third argument. Options is required.

	Arguments:
		Attributes
			since it inherit from scale.js, it will have all its private & public attr. Read core/scale.js Arguments section for details

	APIs:  ( defined in the constructor level )
	# THOSE APIs EXIST IN ALL EXTENDED MODULES
		constructor.box(_width, _height)
			# setter/getter container width value and height values, when only one value, height is the same as width

		constructor.w(_width)
			# setter/getter container width value  // always recommend to use constructor.box() instead

		constructor.h(_height)
			# setter/getter container height value  // always recommend to use constructor.box() instead

	PRIVATE APIs:
		_bindMouseEnterOutEvents(chart, single)
			# when it is entering, binding datum with each element, single element will bind 'mouseenter' event with our custom 'd3maMouseenter', bind 'mouseout' with our custom event 'd3maMouseout'.
			For example, quick internal binding to reduce duplicated code in modules like circle.js, bars.js etc

		1. _resize()  # called from d3.ma.onResize() or d3.ma.resize()
		2. _onWindowResize(chart, single)  # called from each individual module. It has to defined in each module basis. Since it has to pass the chart, single info in this.layer lifeevent enter event. No way to know its context in base.js level.
		3. _redraw(e, chart, single)  # called from each individual module
		4. _unbind(e, chart, single) # called from each individual module
		5. _updateScale(_width, _height) # called from each individual module
		6. _update(_width, _height, chart, single)  # this methods should be handled by each module, normally defined for special cases

		Here is the logic on those private methods above.
		When each instance attach an function of resize(context, context ...), E.G. d3.ma.onResize(line._resize, line); `1. _resize()` is called automatically, it will figure it out current width and height, dispatch 'd3maOnWindowResize' or 'd3maOffWindowResize' accordingly, passing the correct data object to the handlers.

		Each module ( like line, bar, circle etc ) will handle it to trigger `2. _onWindowResize()` this._onWindowResize() like Line, Axis or chart._onWindowResize(chart, this) inside this.layer enter lifeevent. _onWindowResize defined the global handler on 'd3maOnWindowResize' or 'd3maOffWindowResize', passing three arguments (e, chart, single).
		e is the object received from 'd3maOnWindowResize' or 'd3maOffWindowResize'.
		chart refer to this context, used it to access xScale, yScale, width, height, etc. chart property.
		single refer to each individual group just appended by insert command.

		`3. _redraw(e, chart, single)` will be triggered on 'd3maOnWindowResize'. First, it will trigger `5. _updateScale(_width, _height)`, based on the current xScale string value, yScale string value, update the xScale, yScale range array. ( defined in scale.js ) and update the module box width and height attribute.  Second, it will dispatch another global d3.ma event. 'd3maSingleWindowResize', passing (chart, single) as its arguments.  Third, defined a recommended method called `6. _update()`. E.G. this._update( _width, _height, chart, single ).  so any module could hook into this method and attach its own custom updates based on current context and its useful values like width, height, chart info and single info

		`4. _unbind(e, chart, single)` will be triggered on 'd3maOffWindowResize'. It will auto trigger `5. _updateScale(_width, _height)` and `6. _update( _width, _height, chart, single )` for modifying the scale range and domain

		'd3maSingleWindowResize': `3. _redraw(e, chart, single)` dispatch an event of `d3maSingleWindowResize`, each instance of the module should handle it separately based on its settings. (like in index.html).
			E.G.
				circles.dispatch.on('d3maSingleWindowResize', function(chart, single){
					var chart = chart || this;
					single.attr({
						'cx': area.line.x(),
						'cy': area.line.y()
					});
				});

	Events:  ( defined in the instance level )
		# Used for constructor.dispatch.on trigger events  E.G: Custom d3maMouseenter and mouseout events
		# syntax: constructor.dispatch.on('d3maMouseenter', function(e){ });

		# Currently support:
			d3maMouseenter   # each individual element mouse enter event
			d3maMouseout	# each individual element mouse out event
			d3maSingleWindowResize   # each individual element window resize event, normally, update scale domain, individual attrs
			d3maOnWindowResize		# window resize event, elements need to update its scales, and other attrs
			d3maOffWindowResize		# window resize event, elements do not update its scale, attrs, reset to original
 */
d3.chart('Scale').extend('Base', {

	initialize: function(options) {

		options = options || {};

		this.box(this.width, this.height);

		this.dispatch = d3.dispatch('d3maMouseenter', 'd3maMouseout', 'd3maOnWindowResize', 'd3maOffWindowResize', 'd3maSingleWindowResize');
	},

	w: function(_width) {
		if (arguments.length === 0) {
			return this.width;
		}
		this.width = _width;
		this.base.attr('width', this.width);
		return this;
	},

	h: function(_height) {
		if (arguments.length === 0) {
			return this.height;
		}
		this.height = _height;
		this.base.attr('height', this.height);
		return this;
	},

	box: function(_width, _height) {
		if(!arguments.length) {
			return {
				'width': this.width,
				'height': this.height
			};
		}

		this.w(_width);
		this.h((_height) ? _height : _width);

		return this;
	},

	// chart  # refer to this context, used it to access xScale, yScale, width, height, etc. chart property
	// this    # refer to each individual group just appended by insert command
	_bindMouseEnterOutEvents: function(chart, single) {
		var chart = chart || this;

		single.on('mouseenter', function(d, i){
			d3.select(this).classed('hover', true);
			var obj = {};
			if(chart.onDataMouseenter) {
				obj = chart.onDataMouseenter(d, i, chart);
			}
			chart.dispatch.d3maMouseenter(obj);
		});

		single.on('mouseout', function(d, i){
			d3.select(this).classed('hover', false);
			var obj = {};
			if(chart.onDataMouseout) {
				obj = chart.onDataMouseout(d, i, chart);
			}
			chart.dispatch.d3maMouseout(obj);
		});
	},

	_resize: function() {
		// NOTE: this here is the context where you definied in the 2nd param when initialized
		// ex: d3.ma.onResize(line._resize, line);
		// in this case, the context here is  line

		var windowWidth = d3.ma.windowSize().width,
			windowHeight = d3.ma.windowSize().height,
			containerInfo = this.info;

		if( windowWidth < containerInfo.containerW || windowHeight < containerInfo.containerH ) {
			var onObj = {
				width: ( windowWidth < containerInfo.containerW ) ? windowWidth : containerInfo.containerW,
				height: ( windowHeight < containerInfo.containerH ) ? windowHeight : containerInfo.containerH
			};
			this.dispatch.d3maOnWindowResize(onObj);
		} else {
			var offObj = {
				width: containerInfo.canvasW,
				height: containerInfo.canvasH
			};
			var origObj = {
				width: containerInfo.containerW,
				height: containerInfo.containerH
			};
			// dispatch the _redraw back to the original container box
			this.dispatch.d3maOnWindowResize(origObj);
			// unbind the window resize event
			this.dispatch.d3maOffWindowResize(offObj);
		}
	},

	// it is the handler for the internal _resize() which definied above this one
	_onWindowResize: function(chart, single){
		var self = this;

		this.dispatch.on('d3maOnWindowResize', function(e){
			self._redraw(e, chart, single);
		});

		this.dispatch.on('d3maOffWindowResize', function(e){
			self._unbind(e, chart, single);
		});
	},

	// this will trigger the _update internal fn
	// look at the line.js _update fn for details
	_redraw: function(e, chart, single) {
		var containerInfo = this.info,
			_width = e.width - containerInfo.marginLeft - containerInfo.marginRight,
			_height = e.height - containerInfo.marginTop - containerInfo.marginBottom;

		//handle this in base.js below this fn. dealing with Scale updates
		this._updateScale(_width, _height);

		//dealing with the single element, trigger 'd3maSingleWindowResize'
		//handle event in the each instance for details updates.
		//usage on rects, circles, like multiple repeated elements.
		this.dispatch.d3maSingleWindowResize(chart, single);

		// handle this in individual modules
		// Optional step, if defined in each module, could
		// setup the global default in this module, or setup global attrs
		if (this._update)
			this._update( _width, _height, chart, single );
	},

	// this will trigger the _update internal fn
	// look at the line.js _update fn for details
	_unbind: function(e, chart, single) {
		// find out the current width & height of line g container. convert it to number
		var containerInfo = this.info,
			currentWidth = +(this.base.attr('width')),
			currentHeight = +(this.base.attr('height')),
			_canvasW = containerInfo.canvasW,
			_canvasH = containerInfo.canvasH;

		if( currentWidth !== _canvasW || currentHeight !== _canvasH)  {
			// same usage like _redraw fn for  _updateScale  &  _update
			this._updateScale(_canvasW, _canvasH);

			if (this._update)
				this._update(_canvasW, _canvasH, chart, single);
		}
	},

	// Update Scale, Box Size
	_updateScale: function(_width, _height) {
		// trigger the Scale in scale.js
		this._switchXScale(this._x, _width);
		this._switchYScale(this._y, _height);

		this.box(_width, _height);
	}

});
<commit_msg>update the base resize function to handle the buffer room on resizing<commit_after>/*
	Base is extended from Scale, it is used for the basic/ folder section. All mixined or extended modules will have all its behavior. Where you define your absolutely needed methods for the APIs. Internal use only, never use it for displaying a chart.

	Initalization:
		initialize: function(containerInfo) {
			var chart =  this.mixin('Base',  axisG, {
				info: containerInfo,
				x: 'ordinal',
				y: 'log',
				width: this.base.attr('width'),  // optional
				height: this.base.attr('height')   // optional
			});
		}

		// Passing options object as a third argument. Options is required.

	Arguments:
		Attributes
			since it inherit from scale.js, it will have all its private & public attr. Read core/scale.js Arguments section for details

	APIs:  ( defined in the constructor level )
	# THOSE APIs EXIST IN ALL EXTENDED MODULES
		constructor.box(_width, _height)
			# setter/getter container width value and height values, when only one value, height is the same as width

		constructor.w(_width)
			# setter/getter container width value  // always recommend to use constructor.box() instead

		constructor.h(_height)
			# setter/getter container height value  // always recommend to use constructor.box() instead

	PRIVATE APIs:
		_bindMouseEnterOutEvents(chart, single)
			# when it is entering, binding datum with each element, single element will bind 'mouseenter' event with our custom 'd3maMouseenter', bind 'mouseout' with our custom event 'd3maMouseout'.
			For example, quick internal binding to reduce duplicated code in modules like circle.js, bars.js etc

		1. _resize()  # called from d3.ma.onResize() or d3.ma.resize()
		2. _onWindowResize(chart, single)  # called from each individual module. It has to defined in each module basis. Since it has to pass the chart, single info in this.layer lifeevent enter event. No way to know its context in base.js level.
		3. _redraw(e, chart, single)  # called from each individual module
		4. _unbind(e, chart, single) # called from each individual module
		5. _updateScale(_width, _height) # called from each individual module
		6. _update(_width, _height, chart, single)  # this methods should be handled by each module, normally defined for special cases

		Here is the logic on those private methods above.
		When each instance attach an function of resize(context, context ...), E.G. d3.ma.onResize(line._resize, line); `1. _resize()` is called automatically, it will figure it out current width and height, dispatch 'd3maOnWindowResize' or 'd3maOffWindowResize' accordingly, passing the correct data object to the handlers.

		Each module ( like line, bar, circle etc ) will handle it to trigger `2. _onWindowResize()` this._onWindowResize() like Line, Axis or chart._onWindowResize(chart, this) inside this.layer enter lifeevent. _onWindowResize defined the global handler on 'd3maOnWindowResize' or 'd3maOffWindowResize', passing three arguments (e, chart, single).
		e is the object received from 'd3maOnWindowResize' or 'd3maOffWindowResize'.
		chart refer to this context, used it to access xScale, yScale, width, height, etc. chart property.
		single refer to each individual group just appended by insert command.

		`3. _redraw(e, chart, single)` will be triggered on 'd3maOnWindowResize'. First, it will trigger `5. _updateScale(_width, _height)`, based on the current xScale string value, yScale string value, update the xScale, yScale range array. ( defined in scale.js ) and update the module box width and height attribute.  Second, it will dispatch another global d3.ma event. 'd3maSingleWindowResize', passing (chart, single) as its arguments.  Third, defined a recommended method called `6. _update()`. E.G. this._update( _width, _height, chart, single ).  so any module could hook into this method and attach its own custom updates based on current context and its useful values like width, height, chart info and single info

		`4. _unbind(e, chart, single)` will be triggered on 'd3maOffWindowResize'. It will auto trigger `5. _updateScale(_width, _height)` and `6. _update( _width, _height, chart, single )` for modifying the scale range and domain

		'd3maSingleWindowResize': `3. _redraw(e, chart, single)` dispatch an event of `d3maSingleWindowResize`, each instance of the module should handle it separately based on its settings. (like in index.html).
			E.G.
				circles.dispatch.on('d3maSingleWindowResize', function(chart, single){
					var chart = chart || this;
					single.attr({
						'cx': area.line.x(),
						'cy': area.line.y()
					});
				});

	Events:  ( defined in the instance level )
		# Used for constructor.dispatch.on trigger events  E.G: Custom d3maMouseenter and mouseout events
		# syntax: constructor.dispatch.on('d3maMouseenter', function(e){ });

		# Currently support:
			d3maMouseenter   # each individual element mouse enter event
			d3maMouseout	# each individual element mouse out event
			d3maSingleWindowResize   # each individual element window resize event, normally, update scale domain, individual attrs
			d3maOnWindowResize		# window resize event, elements need to update its scales, and other attrs
			d3maOffWindowResize		# window resize event, elements do not update its scale, attrs, reset to original
 */
d3.chart('Scale').extend('Base', {

	initialize: function(options) {

		options = options || {};

		this.box(this.width, this.height);

		this.dispatch = d3.dispatch('d3maMouseenter', 'd3maMouseout', 'd3maOnWindowResize', 'd3maOffWindowResize', 'd3maSingleWindowResize');
	},

	w: function(_width) {
		if (arguments.length === 0) {
			return this.width;
		}
		this.width = _width;
		this.base.attr('width', this.width);
		return this;
	},

	h: function(_height) {
		if (arguments.length === 0) {
			return this.height;
		}
		this.height = _height;
		this.base.attr('height', this.height);
		return this;
	},

	box: function(_width, _height) {
		if(!arguments.length) {
			return {
				'width': this.width,
				'height': this.height
			};
		}

		this.w(_width);
		this.h((_height) ? _height : _width);

		return this;
	},

	// chart  # refer to this context, used it to access xScale, yScale, width, height, etc. chart property
	// this    # refer to each individual group just appended by insert command
	_bindMouseEnterOutEvents: function(chart, single) {
		var chart = chart || this;

		single.on('mouseenter', function(d, i){
			d3.select(this).classed('hover', true);
			var obj = {};
			if(chart.onDataMouseenter) {
				obj = chart.onDataMouseenter(d, i, chart);
			}
			chart.dispatch.d3maMouseenter(obj);
		});

		single.on('mouseout', function(d, i){
			d3.select(this).classed('hover', false);
			var obj = {};
			if(chart.onDataMouseout) {
				obj = chart.onDataMouseout(d, i, chart);
			}
			chart.dispatch.d3maMouseout(obj);
		});
	},

	_resize: function() {
		// NOTE: this here is the context where you definied in the 2nd param when initialized
		// ex: d3.ma.onResize(line._resize, line);
		// in this case, the context here is  line

		var containerInfo = this.info,
			widthOffset = d3.ma.$$(containerInfo.parentNode).offsetLeft + containerInfo.marginLeft + containerInfo.marginRight,
			heightOffset = d3.ma.$$(containerInfo.parentNode).offsetTop + containerInfo.marginTop + containerInfo.marginBottom,
			windowWidth = d3.ma.windowSize().width - widthOffset,
			windowHeight = d3.ma.windowSize().height - heightOffset;

		if( windowWidth < containerInfo.containerW || windowHeight < containerInfo.containerH ) {
			var onObj = {
				width: ( windowWidth < containerInfo.containerW ) ? windowWidth : containerInfo.containerW,
				height: ( windowHeight < containerInfo.containerH ) ? windowHeight : containerInfo.containerH
			};
			this.dispatch.d3maOnWindowResize(onObj);
		} else {
			var offObj = {
				width: containerInfo.canvasW,
				height: containerInfo.canvasH
			};
			var origObj = {
				width: containerInfo.containerW,
				height: containerInfo.containerH
			};
			// dispatch the _redraw back to the original container box
			this.dispatch.d3maOnWindowResize(origObj);
			// unbind the window resize event
			this.dispatch.d3maOffWindowResize(offObj);
		}
	},

	// it is the handler for the internal _resize() which definied above this one
	_onWindowResize: function(chart, single){
		var self = this;

		this.dispatch.on('d3maOnWindowResize', function(e){
			self._redraw(e, chart, single);
		});

		this.dispatch.on('d3maOffWindowResize', function(e){
			self._unbind(e, chart, single);
		});
	},

	// this will trigger the _update internal fn
	// look at the line.js _update fn for details
	_redraw: function(e, chart, single) {
		var containerInfo = this.info,
			_width = e.width - containerInfo.marginLeft - containerInfo.marginRight,
			_height = e.height - containerInfo.marginTop - containerInfo.marginBottom;

		//handle this in base.js below this fn. dealing with Scale updates
		this._updateScale(_width, _height);

		//dealing with the single element, trigger 'd3maSingleWindowResize'
		//handle event in the each instance for details updates.
		//usage on rects, circles, like multiple repeated elements.
		this.dispatch.d3maSingleWindowResize(chart, single);

		// handle this in individual modules
		// Optional step, if defined in each module, could
		// setup the global default in this module, or setup global attrs
		if (this._update)
			this._update( _width, _height, chart, single );
	},

	// this will trigger the _update internal fn
	// look at the line.js _update fn for details
	_unbind: function(e, chart, single) {
		// find out the current width & height of line g container. convert it to number
		var containerInfo = this.info,
			currentWidth = +(this.base.attr('width')),
			currentHeight = +(this.base.attr('height')),
			_canvasW = containerInfo.canvasW,
			_canvasH = containerInfo.canvasH;

		if( currentWidth !== _canvasW || currentHeight !== _canvasH)  {
			// same usage like _redraw fn for  _updateScale  &  _update
			this._updateScale(_canvasW, _canvasH);

			if (this._update)
				this._update(_canvasW, _canvasH, chart, single);
		}
	},

	// Update Scale, Box Size
	_updateScale: function(_width, _height) {
		// trigger the Scale in scale.js
		this._switchXScale(this._x, _width);
		this._switchYScale(this._y, _height);

		this.box(_width, _height);
	}

});
<|endoftext|>


<commit_before>'use babel';

/**
 * Copyright (C) 2016 Ivan Kravets. All rights reserved.
 *
 * This source file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

import fs from 'fs';
import path from 'path';
import ini from 'ini';
import {clone} from './utils';

export class PlatformIOBuildProvider {
  constructor(cwd) {
    this.cwd = cwd;
    this.platformioIniPath = path.join(this.cwd, 'platformio.ini');
    this.title = 'PlatformIO';
    this.targetNamePrefix = this.title + ': ';

    this.targetsBaseSettings = [
      {
        name: 'Build',
        args: ['run'],
        keymap: 'ctrl-alt-b',
      },
      {
        name: 'Clean',
        args: ['run', '--target', 'clean'],
        keymap: 'ctrl-alt-c',
      },
      {
        name: 'Upload',
        args: ['run', '--target', 'upload'],
        keymap: 'ctrl-alt-u',
      },
      {
        name: 'Upload using Programmer',
        args: ['run', '--target', 'program'],
      },
      {
        name: 'Upload SPIFFS image',
        args: ['run', '--target', 'uploadfs'],
      }
    ];
  }

  getNiceName() {
    return this.title;
  }

  isEligible() {
    return fs.statSyncNoException(this.platformioIniPath);
  }

  settings() {
    return new Promise((resolve, reject) => {
      fs.readFile(this.platformioIniPath, (err, data) => {
        if (err) reject(err);

        let envs = [];
        const envNamePrefix = 'env:';
        const configSections = Object.keys(ini.parse(data.toString()));
        for (let section of configSections) {
          if (section.indexOf(envNamePrefix) === 0) {
            envs.push(section.slice(envNamePrefix.length));
          }
        }

        let settings = this.prepareSettings(this.targetsBaseSettings);

        if (envs.length > 1) {
          for (let env of envs) {
            let envSettings = this
              .prepareSettings(this.targetsBaseSettings)
              .map(makeEnvSpecificTarget(env));
            settings = settings.concat(envSettings);
          }
        }
        resolve(settings);
      });
    });

    function makeEnvSpecificTarget(env) {
      return function(base) {
        let item = clone(base);
        item.name += ` (env:${env})`;
        item.args.push('--environment');
        item.args.push(env);
        delete item.keymap;
        delete item.atomCommandName;
        return item;
      };
    }
  }

  prepareSettings(baseSettings) {
    return baseSettings.map(base => {
      let item = clone(base);
      item.name = this.targetNamePrefix + base.name;
      item.exec = "platformio";
      item.sh = false;
      item.env = Object.create(process.env);
      item.env.PLATFORMIO_FORCE_COLOR = "true";
      item.env.PLATFORMIO_DISABLE_PROGRESSBAR = "true";
      item.env.PLATFORMIO_SETTING_ENABLE_PROMPTS = "false";
      item.errorMatch = [
        "\n(?<file>src[\\/0-9a-zA-Z\\._\\\\]+):(?<line>\\d+):(?<col>\\d+)"
      ];
      item.atomCommandName = `platformio-ide:target:${base.name.toLowerCase()}-${this.cwd}`;
      return item;
    });
  }
}
<commit_msg>Hide platform-specific upload targets on envs that do not support them<commit_after>'use babel';

/**
 * Copyright (C) 2016 Ivan Kravets. All rights reserved.
 *
 * This source file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

import fs from 'fs';
import path from 'path';
import ini from 'ini';
import {clone} from './utils';

export class PlatformIOBuildProvider {
  constructor(cwd) {
    this.cwd = cwd;
    this.platformioIniPath = path.join(this.cwd, 'platformio.ini');
    this.title = 'PlatformIO';
    this.targetNamePrefix = this.title + ': ';

    this.targetsBaseSettings = [
      {
        name: 'Build',
        args: ['run'],
        keymap: 'ctrl-alt-b',
      },
      {
        name: 'Clean',
        args: ['run', '--target', 'clean'],
        keymap: 'ctrl-alt-c',
      },
      {
        name: 'Upload',
        args: ['run', '--target', 'upload'],
        keymap: 'ctrl-alt-u',
      },
      {
        name: 'Upload using Programmer',
        args: ['run', '--target', 'program'],
      },
      {
        name: 'Upload SPIFFS image',
        args: ['run', '--target', 'uploadfs'],
      }
    ];
  }

  getNiceName() {
    return this.title;
  }

  isEligible() {
    return fs.statSyncNoException(this.platformioIniPath);
  }

  settings() {
    return new Promise((resolve, reject) => {
      fs.readFile(this.platformioIniPath, (err, data) => {
        if (err) reject(err);

        let envs = [];
        const envNamePrefix = 'env:';
        const config = ini.parse(data.toString());
        for (let section of Object.keys(config)) {
          if (section.indexOf(envNamePrefix) === 0) {
            envs.push(section.slice(envNamePrefix.length));
          }
        }

        let settings = this.prepareSettings(this.targetsBaseSettings);

        if (envs.length > 1) {
          for (let env of envs) {
            let envSettings = this
              .prepareSettings(this.targetsBaseSettings)
              .map(makeEnvSpecificTarget(env));
            if ('espressif' !== config[envNamePrefix + env].platform) {
              envSettings = envSettings.filter((item) => item.args.indexOf('uploadfs') === -1);
            } else if ('atmelavr' !== config[envNamePrefix + env].platform) {
              envSettings = envSettings.filter((item) => item.args.indexOf('program') === -1);
            }
            settings = settings.concat(envSettings);
          }
        }
        resolve(settings);
      });
    });

    function makeEnvSpecificTarget(env) {
      return function(base) {
        let item = clone(base);
        item.name += ` (env:${env})`;
        item.args.push('--environment');
        item.args.push(env);
        delete item.keymap;
        delete item.atomCommandName;
        return item;
      };
    }
  }

  prepareSettings(baseSettings) {
    return baseSettings.map(base => {
      let item = clone(base);
      item.name = this.targetNamePrefix + base.name;
      item.exec = "platformio";
      item.sh = false;
      item.env = Object.create(process.env);
      item.env.PLATFORMIO_FORCE_COLOR = "true";
      item.env.PLATFORMIO_DISABLE_PROGRESSBAR = "true";
      item.env.PLATFORMIO_SETTING_ENABLE_PROMPTS = "false";
      item.errorMatch = [
        "\n(?<file>src[\\/0-9a-zA-Z\\._\\\\]+):(?<line>\\d+):(?<col>\\d+)"
      ];
      item.atomCommandName = `platformio-ide:target:${base.name.toLowerCase()}-${this.cwd}`;
      return item;
    });
  }
}
<|endoftext|>


<commit_before>/*
 * Keeps track of files
 * - emits events about changes when .poll is called
 * - events are: delete, create
 *
 * Usage:
 *
 *     var tracker = changeTracker.create(checkFiles, files);
 *
 * - checkFiles is a function to fetch the current state of the files you want
 *   to watch. It should return a list of objects with a unique 'name'.
 *
 * - files is the current list of files, as given by running checkFiles now
 *
 *     tracker.on("create", createListener);
 *     tracker.on("delete", deleteListener);
 *     tracker.poll();
 *
 * When calling poll, checkFiles is called, the result is compared to the old
 * list, and events are emitted.
 *
 */

var EventEmitter = require("events").EventEmitter;
var when = require("when");

function create(checkFiles, files) {
  var instance = Object.create(this);
  instance.checkFiles = checkFiles;
  instance.files = files;
  return instance;
}

function eq(file1) {
  return function (file2) { return file1.name === file2.name; };
}

function notIn(coll) {
  return function (item) { return !coll.some(eq(item)); };
}

function poll() {
  var before = this.files;
  var deferred = when.defer();

  this.checkFiles(function (err, after) {
    if (err) { return deferred.reject(err); }

    var created = after.filter(notIn(before));
    var deleted = before.filter(notIn(after));

    created.forEach(this.emit.bind(this, "create"));
    deleted.forEach(this.emit.bind(this, "delete"));

    this.files = after;

    deferred.resolve();
  }.bind(this));

  return deferred.promise;
}

module.exports = new EventEmitter();
module.exports.create = create;
module.exports.poll = poll;
<commit_msg>Clarify that change-tracker is not about files.<commit_after>/*
 * Keeps track of items being created or deleted in a list
 * - emits events about changes when .poll is called
 * - events are: delete, create
 *
 * Usage:
 *
 *     var tracker = changeTracker.create(updateItems, items);
 *
 * - updateItems is a function to fetch the current state of the items you want
 *   to watch. It should return a list of objects with a unique 'name'.
 *
 * - items is the current list, as given by running updateItems now
 *
 *     tracker.on("create", createListener);
 *     tracker.on("delete", deleteListener);
 *     tracker.poll();
 *
 * When calling poll, updateItems is called, the result is compared to the old
 * list, and events are emitted.
 *
 */

var EventEmitter = require("events").EventEmitter;
var when = require("when");

function create(updateItems, items) {
  var instance = Object.create(this);
  instance.updateItems = updateItems;
  instance.items = items;
  return instance;
}

function eq(item1) {
  return function (item2) { return item1.name === item2.name; };
}

function notIn(coll) {
  return function (item) { return !coll.some(eq(item)); };
}

function poll() {
  var before = this.items;
  var deferred = when.defer();

  this.updateItems(function (err, after) {
    if (err) { return deferred.reject(err); }

    var created = after.filter(notIn(before));
    var deleted = before.filter(notIn(after));

    created.forEach(this.emit.bind(this, "create"));
    deleted.forEach(this.emit.bind(this, "delete"));

    this.items = after;

    deferred.resolve();
  }.bind(this));

  return deferred.promise;
}

module.exports = new EventEmitter();
module.exports.create = create;
module.exports.poll = poll;
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////
/**
 * @module geojs
 */

/*jslint devel: true, forin: true, newcap: true, plusplus: true*/
/*jslint white: true, continue:true, indent: 2*/

/*global vgl, ogs, vec4, inherit, $*/
//////////////////////////////////////////////////////////////////////////////

if(typeof ogs === 'undefined') {
  var ogs = {};
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Create namespace for the given name
 *
 * @param ns_string
 * @returns {*|{}}
 */
//////////////////////////////////////////////////////////////////////////////
ogs.namespace = function(ns_string) {
  'use strict';

  var parts = ns_string.split('.'), parent = ogs, i;

  // strip redundant leading global
  if (parts[0] === "ogs") {
    parts = parts.slice(1);
  }
  for (i = 0; i < parts.length; i += 1) {
    // create a property if it doesn't exist
    if (typeof parent[parts[i]] === "undefined") {
      parent[parts[i]] = {};
    }
    parent = parent[parts[i]];
  }
  return parent;
};

/** geo namespace */
var geo = ogs.namespace("geo");

geo.renderers = {};
geo.features = {};

//////////////////////////////////////////////////////////////////////////////
/**
 * Convenient function to define JS inheritance
 *
 * @param C
 * @param P
 */
//////////////////////////////////////////////////////////////////////////////
function inherit(C, P) {
  "use strict";

  var F = function() {
  };
  F.prototype = P.prototype;
  C.prototype = new F();
  C.uber = P.prototype;
  C.prototype.constructor = C;
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Convenient function to get size of an object
 *
 * @param obj
 * @returns {number} *
 */
//////////////////////////////////////////////////////////////////////////////
Object.size = function(obj) {
  "use strict";

  var size = 0, key = null;
  for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      size++;
    }
  }
  return size;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Register a new renderer type
 */
//////////////////////////////////////////////////////////////////////////////
geo.registerRenderer = function(name, func) {
  if (geo.renderers === undefined) {
    geo.renderers = {};
  }

  geo.renderers[name] = func;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create new instance of the renderer
 */
//////////////////////////////////////////////////////////////////////////////
geo.createRenderer  = function(name, layer, canvas) {
  if (name in geo.renderers) {
    var ren = geo.renderers[name]({'layer': layer, 'canvas': canvas});
    ren._init();
    return ren;
  }
  return null;
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Register a new feature type
 */
//////////////////////////////////////////////////////////////////////////////
geo.registerFeature = function(category, name, func) {

  if (geo.features === undefined) {
    geo.features = {};
  }

  if (!(category in geo.features)) {
    geo.features[category] = {};
  }

  // TODO Add warning if the name already exists
  geo.features[category][name] = func;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create new instance of the renderer
 */
//////////////////////////////////////////////////////////////////////////////
geo.createFeature  = function(name, layer, renderer) {
  var category = renderer.api();
  if (category in geo.features && name in geo.features[category]) {
    return geo.features[category][name](
      {'layer':layer, 'renderer': renderer});
  }
  return null;
}
<commit_msg>prevent renderer from being initialized twice<commit_after>//////////////////////////////////////////////////////////////////////////////
/**
 * @module geojs
 */

/*jslint devel: true, forin: true, newcap: true, plusplus: true*/
/*jslint white: true, continue:true, indent: 2*/

/*global vgl, ogs, vec4, inherit, $*/
//////////////////////////////////////////////////////////////////////////////

if(typeof ogs === 'undefined') {
  var ogs = {};
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Create namespace for the given name
 *
 * @param ns_string
 * @returns {*|{}}
 */
//////////////////////////////////////////////////////////////////////////////
ogs.namespace = function(ns_string) {
  'use strict';

  var parts = ns_string.split('.'), parent = ogs, i;

  // strip redundant leading global
  if (parts[0] === "ogs") {
    parts = parts.slice(1);
  }
  for (i = 0; i < parts.length; i += 1) {
    // create a property if it doesn't exist
    if (typeof parent[parts[i]] === "undefined") {
      parent[parts[i]] = {};
    }
    parent = parent[parts[i]];
  }
  return parent;
};

/** geo namespace */
var geo = ogs.namespace("geo");

geo.renderers = {};
geo.features = {};

//////////////////////////////////////////////////////////////////////////////
/**
 * Convenient function to define JS inheritance
 *
 * @param C
 * @param P
 */
//////////////////////////////////////////////////////////////////////////////
function inherit(C, P) {
  "use strict";

  var F = function() {
  };
  F.prototype = P.prototype;
  C.prototype = new F();
  C.uber = P.prototype;
  C.prototype.constructor = C;
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Convenient function to get size of an object
 *
 * @param obj
 * @returns {number} *
 */
//////////////////////////////////////////////////////////////////////////////
Object.size = function(obj) {
  "use strict";

  var size = 0, key = null;
  for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      size++;
    }
  }
  return size;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Register a new renderer type
 */
//////////////////////////////////////////////////////////////////////////////
geo.registerRenderer = function(name, func) {
  if (geo.renderers === undefined) {
    geo.renderers = {};
  }

  geo.renderers[name] = func;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create new instance of the renderer
 */
//////////////////////////////////////////////////////////////////////////////
geo.createRenderer  = function(name, layer, canvas) {
  if (name in geo.renderers) {
    var ren = geo.renderers[name]({'layer': layer, 'canvas': canvas});
    return ren;
  }
  return null;
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Register a new feature type
 */
//////////////////////////////////////////////////////////////////////////////
geo.registerFeature = function(category, name, func) {

  if (geo.features === undefined) {
    geo.features = {};
  }

  if (!(category in geo.features)) {
    geo.features[category] = {};
  }

  // TODO Add warning if the name already exists
  geo.features[category][name] = func;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create new instance of the renderer
 */
//////////////////////////////////////////////////////////////////////////////
geo.createFeature  = function(name, layer, renderer) {
  var category = renderer.api();
  if (category in geo.features && name in geo.features[category]) {
    return geo.features[category][name](
      {'layer':layer, 'renderer': renderer});
  }
  return null;
}
<|endoftext|>