Source: category.js

/**
 * The Category module
 * @copyright CHECKROOM NV 2015
 * @module category
 */
define([
    'jquery',
    'common',
    'api',
    'document'],  /** @lends Document */ function ($, common, api, Document) {

    /*
    id = StringField(primary_key=True)  # category id in reverse domain format cheqroom.types.customer
    name = StringField()  # A friendly name for the category
    count = IntField(default=0)  # How many categories are under this parent
    defs = ListField(EmbeddedDocumentField(KeyValueDef))  # a list of allowed KeyValueDefs
    parent = ReferenceField("Category")  # the parent category
    by = ReferenceField(User)  # The user that add / updated this meta
    modified = DateTimeField(default=DateHelper.getNow)  # The data when it was added / update
     */

    // Some constant values
    var DEFAULTS = {
        id: "",
        name: "",
        count: 0,
        defs: [],
        parent: "",
        modified: null
    };

    // 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;

    /**
     * Category describes a category which can trigger on certain events (signals)
     * @name  Category
     * @class
     * @property {string} name          the category name
     * @property {string} count         how many categories are under this node
     * @property {array} defs           a list of allowed keyvalue definitions
     * @property {string} parent        the primary key of the parent category
     * @property {Moment} modified      the modified date of the category
     * @constructor
     * @extends Document
     */
    var Category = function(opt) {
        var spec = $.extend({}, opt);
        Document.call(this, spec);

        this.name = spec.name || DEFAULTS.name;
        this.count = spec.count || DEFAULTS.count;
        this.defs = spec.defs || DEFAULTS.defs.slice();
        this.parent = spec.parent || DEFAULTS.parent;
        this.modified = spec.modified || DEFAULTS.modified;
    };

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

    //
    // Specific validators
    /**
     * Checks if name is valid
     * @name Category#isValidName
     * @method
     * @return {Boolean}
     */
    Category.prototype.isValidName = function() {
        this.name = $.trim(this.name);
        if (this.name.length>=3) {
            var nospecial = this.name.latinise().replace(/[`~!@#$%^&*()_|+\-=?;:'",.<\{\}\[\]\\\/]/gi, '');
            return (this.name == nospecial);
        } else {
            return false;
        }
    };

    //
    // Document overrides
    //
    /**
     * Checks if the category has any validation errors
     * @name Category#isValid
     * @method
     * @returns {boolean}
     * @override
     */
    Category.prototype.isValid = function() {
        return this.isValidName();
    };

    Category.prototype._getDefaults = function() {
        return DEFAULTS;
    };

    /**
     * Checks if the object is empty, it never is
     * @name  Category#isEmpty
     * @method
     * @returns {boolean}
     * @override
     */
    Category.prototype.isEmpty = function() {
        return (
            (Document.prototype.isEmpty.call(this)) &&
                (this.name==DEFAULTS.name));
    };

    /**
     * Checks if the category is dirty and needs saving
     * @returns {boolean}
     * @override
     */
    Category.prototype.isDirty = function() {
        var isDirty = Document.prototype.isDirty.call(this);
        if( (!isDirty) &&
            (this.raw)) {
            isDirty = (
                (this.name!=this.raw.name));
        }
        return isDirty;
    };

    /**
     * Checks via the api if we can delete the Category document
     * @name  Category#canDelete
     * @method
     * @returns {promise}
     * @override
     */
    Category.prototype.canDelete = function() {
        return this.ds.call(this.id, 'canDeleteCategory', {omitFields: true});
    };

    /**
     * Checks via the api if we can rename the Category document
     * e.g. if it would not clash with any existing categories
     * @method
     * @param name
     * @returns {promise}
     */
    Category.prototype.canChangeName = function(name) {
        return this.ds.call(this.id, 'canChangeName', {name: name});
    };

    /**
     * Changes the name of a category
     * @method
     * @param name
     * @returns {promise}
     */
    Category.prototype.changeName = function(name) {
        return this._doApiCall({pk: this.id, method: 'changeName', params: {name: name}});
    };

    /**
     * Checks via the api if we can change the parent of a Category document
     * e.g. if it would not clash with any existing categories
     * @param parentId
     * @returns {promise}
     */
    Category.prototype.canChangeParent = function(parentId) {
        return this.ds.call(this.id, 'canChangeParent', {parent: parentId, omitFields: true});
    };

    /**
     * Changes the parent of a category
     * @param parentId
     * @returns {promise}
     */
    Category.prototype.changeParent = function(parentId) {
        return this._doApiCall({pk: this.id, method: 'changeParent', params: {parent: parentId, omitFields: true}});
    };

    // toJson, fromJson
    // ----

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

    /**
     * _fromJson: read some basic information
     * @method
     * @param {object} data the json response
     * @param {object} options dict
     * @returns {promise}
     * @private
     */
    Category.prototype._fromJson = function(data, options) {
        var that = this;
        return Document.prototype._fromJson.call(this, data, options)
            .then(function() {
                that.name = data.name || DEFAULTS.name;
                that.count = data.count || DEFAULTS.count;
                that.defs = data.defs || DEFAULTS.defs.slice();
                that.parent = data.parent || DEFAULTS.parent;
                that.modified = data.modified || DEFAULTS.modified;
                return data;
            });
    };

    return Category;

});