API Docs for: 1.8.0
Show:

File: src/base.js

/*
 * Copyright (c) 2014-2015, Wanadev <http://www.wanadev.fr/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, this
 *     list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   * Neither the name of Wanadev nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without specific
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authored by: Fabien LOISON <http://flozz.fr/>
 */

/**
 * PhotonUI - Javascript Web User Interface.
 *
 * @module PhotonUI
 * @namespace photonui
 */

var Class = require("abitbol");
var uuid = require("uuid");

var Helpers = require("./helpers.js");

/**
 * Base class for all PhotonUI Classes.
 *
 * wEvents:
 *
 *   * destroy:
 *      - description: called before the widget was destroyed.
 *      - callback:    function (widget)
 *
 * @class Base
 * @constructor
 * @param {Object} params An object that can contain any property that will be set to the class (optional).
 */
var Base = Class.$extend({

    // Constructor
    __init__: function (params) {
        // New instances for object properties
        this.__events = {};
        this._data = {};

        // wEvents
        this._registerWEvents(["destroy"]);

        // Apply params
        params = params || {};
        for (var param in params) {
            if (this.$map.computedProperties[param]) {
                this[param] = params[param];
            }
        }

        // Update properties
        for (var propName in this.$map.computedProperties) {
            var prop = this.$map.computedProperties[propName];
            if (params[propName] === undefined &&
                prop.annotations["photonui-update"] &&
                prop.get && prop.set) {
                this[propName] = this[propName];
            }
        }

        // Register callbacks
        var ev = null;
        var i = 0;
        var evId = "";
        if (params.callbacks) {
            for (var wEvent in params.callbacks) {
                ev = params.callbacks[wEvent];
                if (typeof(ev) == "function") {
                    this.registerCallback(uuid.v4(), wEvent, ev);
                } else if (ev instanceof Array) {
                    for (i = 0 ; i < ev.length ; i++) {
                        this.registerCallback(uuid.v4(), wEvent, ev[i]);
                    }
                } else {
                    for (evId in ev) {
                        this.registerCallback(evId, wEvent, ev[evId]);
                    }
                }
            }
        }
    },

    //////////////////////////////////////////
    // Properties and Accessors             //
    //////////////////////////////////////////

    // ====== Public properties ======

    /**
     * Arbitrary data
     *
     * @property data
     * @type object
     * @default {}
     */
    _data: null,

    getData: function () {
        return this._data;
    },

    setData: function (data) {
        this._data = data;
    },

    // ====== Private properties ======

    /**
     * Object containing references javascript events binding (for widget
     * internal use).
     *
     * @property __events
     * @type Object
     * @private
     */
    __events: null,    // Javascript internal event

    /**
     * Object containing references to registered callbacks.
     *
     * @property __callbacks
     * @type Object
     * @private
     */
    __callbacks: null,  // Registered callback

    //////////////////////////////////////////
    // Methods                              //
    //////////////////////////////////////////

    // ====== Public methods ======

    /**
     * Destroy the class.
     *
     * @method destroy
     */
    destroy: function () {
        this._callCallbacks("destroy");
        for (var id in this.__events) {
            this._unbindEvent(id);
        }
    },

    /**
     * Register a callback for any PhotonUI/Widget event (called wEvent).
     *
     * Callback signature:
     *
     *     function (Object(Base/Widget) [, arg1 [, arg2 [, ...]]])
     *
     * @method registerCallback
     * @param {String} id An unique id for the callback.
     * @param {String} wEvent the PhotonUI/Widget event name.
     * @param {Function} callback The callback function.
     * @param {Object} thisArg The value of this (optionnal, default = current widget).
     */
    registerCallback: function (id, wEvent, callback, thisArg) {
        if (!this.__callbacks[wEvent]) {
            Helpers.log("error", "This widget has no '" + wEvent + "' wEvent.");
            return;
        }
        this.__callbacks[wEvent][id] = {
            callback: callback,
            thisArg: thisArg || null
        };
    },

    /**
     * Remove a registered callback.
     *
     * @method removeCallback
     * @param {String} id The id of the callback.
     */
    removeCallback: function (id) {
        for (var wEvent in this.__callbacks) {
            if (this.__callbacks[wEvent][id]) {
                delete this.__callbacks[wEvent][id];
            }
        }
    },

    // ====== Private methods ======

    /**
     * Force the update of the given properties.
     *
     * This method is deprecated.
     * One should use '@photonui-update' abitbol's annotation on concerned properties.
     *
     * @method _updateProperties
     * @private
     * @deprecated
     * @param {Array} properties The properties to update.
     */
    _updateProperties: function (properties) {
        Helpers.log("warn", "'photonui.Base._updateProperties()' is deprecated." +
                            " One should use '@photonui-update' abitbol's annotation on concerned properties.");

        for (var i = 0 ; i < properties.length ; i++) {
            this[properties[i]] = this[properties[i]];
        }
    },

    /**
     * Javascript event binding (for internal use).
     *
     * @method _bindEvent
     * @private
     * @param {String} id An unique id for the event.
     * @param {DOMElement} element The element on which the event will be bind.
     * @param {String} evName The event name (e.g. "mousemove", "click",...).
     * @param {Function} callback The function that will be called when the event occured.
     * @param {Object} [options] options for `addEventListener`
     */
    _bindEvent: function (id, element, evName, callback, options) {
        if (options === undefined) {
            options = false;
        }
        this._unbindEvent(id);
        this.__events[id] = {
            evName: evName,
            element: element,
            callback: callback
        };
        this.__events[id].element.addEventListener(
                this.__events[id].evName,
                this.__events[id].callback,
                options
        );
    },

    /**
     * Unbind javascript event.
     *
     * @method _unbindEvent
     * @private
     * @param {String} id The id of the event.
     */
    _unbindEvent: function (id) {
        if (!this.__events[id]) {
            return;
        }
        this.__events[id].element.removeEventListener(
                this.__events[id].evName,
                this.__events[id].callback,
                false
        );
        delete this.__events[id];
    },

    /**
     * Register available wEvent.
     *
     * @method _registerWEvents
     * @private
     * @param {Array} wEvents
     */
    _registerWEvents: function (wEvents) {
        if (this.__callbacks === null) {
            this.__callbacks = {};
        }
        for (var i in wEvents) {
            this.__callbacks[wEvents[i]] = {};
        }
    },

    /**
     * Call all callbacks for the given wEvent.
     *
     * NOTE: the first argument passed to the callback is the current widget.
     * NOTEĀ²: if the thisArg of the callback is null, this will be binded to the current widget.
     *
     * @method _callCallbacks
     * @private
     * @param {String} wEvent The widget event.
     * @param {Array} params Parametters that will be sent to the callbacks.
     */
    _callCallbacks: function (wEvent, params) {
        params = params || [];
        for (var id in this.__callbacks[wEvent]) {
            this.__callbacks[wEvent][id].callback.apply(
                    this.__callbacks[wEvent][id].thisArg || this,
                    [this].concat(params)
            );
        }
    }
});

module.exports = Base;