Source: controls/Control.js

/**
 * @requirefiles {Iterator.js,controls/GlobalControlManager.js}
 */

$(window).bind('contextmenu', null, Control_ContextMenu);
$(window).bind('keydown', null, Control_Keydown);
$(window).bind('keypress', null, Control_Keypress);
$(window).bind('keyup', null, Control_Keyup);
$(window).bind('mousedown', null, Control_Mousedown);
$(window).bind('mouseup', null, Control_Mouseup);
$(window).bind('mousewheel', null, Control_Mousewheel);

/**
 * Creates an instance of a new Control, should only be called when creating derivative controls.
 * @param {Object} [settings] The properties object used in the creation of the control.
 * @abstract
 * @class
 */
function Control(settings) {

}

/**
 * Causes control instance to be blurred and fire off associated events, while bubbling up through containing controls.
 * @function
 * @name Control#Blur
 */
function Control_Blur() {
    if (this.Focused === true) {
        this.Focused = false;
        if (this.Focusables !== null) {
            if (this.Focusables instanceof Array) {
                for (let i = 0; i < this.Focusables.length; i++) {
                    if (this.Focusables[i].hasClass('focus')) {
                        this.Focusables[i].removeClass('focus');
                    }
                }
            } else {
                this.Focusables.removeClass('focus');
            }
        }
        if (this.ParentControl !== null) {
            this.ParentControl.Blur();
        }
    }
    if (this.AfterBlur) { this.AfterBlur(); }
}

/**
 * Fires off a context menu request for the control if available, otherwise bubbles up to containing controls.
 * @function
 * @name Control#Blur
 * @param {IJQueryEventObject} event The contextmenu event object.
 */
function Control_ContextMenu(event) {
    if(window.ControlGlobal.DirtyContextMenu === true) {
        window.ControlGlobal.DirtyContextMenu = false;
        event.stopPropagation();
    } else {
        let ctl = window.ControlGlobal.Controls[window.ControlGlobal.Controls.length - 1];
        if ((ctl !== null) && (ctl.ContextMenu) && (ctl.ContextMenu !== Control_ContextMenu)) {
            event.data = ctl;
            ctl.ContextMenu(event);
            event.stopPropagation();
        }
    }
    event.preventDefault();
}

/**
 * Disposes of the base control instance and frees up allocated resources.
 * @function
 * @name Control#Dispose
 */
function Control_Dispose() {
    if (this.Focusables != null) {
        if (this.Focusables instanceof Array) {
            for (let i = 0; i < this.Focusables.length; i++) {
                this.Focusables[i].unbind('contextmenu', Control_ContextMenu);
                this.Focusables[i].unbind('mousedown', Control_Mousedown);
                this.Focusables[i].unbind('mousewheel', Control_Mousewheel);
            }
        } else {
            this.Focusables.unbind('contextmenu', Control_ContextMenu);
            this.Focusables.unbind('mousedown', Control_Mousedown);
            this.Focusables.unbind('mousewheel', Control_Mousewheel);
        }
    }
    window.ControlGlobal.Iterator.recycleSync(this.ControlUniqueID);
    this.ControlUniqueID = null;
}

/**
 * Causes control instance to be focused if available while firing off associated events, while bubbling up through containing controls when the control instance cannot receive focus.
 * @function
 * @name Control#Focus
 * @param {boolean} [last=false] Causes focus to bubble down into contained controls.
 */
function Control_Focus(last) {
    if (arguments.length < 1) { last = false; }
    if ((this.ParentControl !== null) && (this.ParentControl.Focused === false)) {
        this.ParentControl.Focus();
    }
    if (this.Focused === false) {
        this.Focused = true;
        if (this.Focusables != null) {
            if (this.Focusables instanceof Array) {
                for (let i = 0; i < this.Focusables.length; i++) {
                    if ((this.Focusables[i] !== null) && (!this.Focusables[i].hasClass('focus'))) {
                        this.Focusables[i].addClass('focus');
                    }
                }
            } else {
                this.Focusables.addClass('focus');
            }
        }
    }
    if (last === true) {
        for (let i = window.ControlGlobal.Controls.length - 1; i >= 0; i--) {
            if (window.ControlGlobal.Controls[i].ControlUniqueID !== this.ControlUniqueID) {
                if ((window.ControlGlobal.Controls[i].ParentControl !== null) && (window.ControlGlobal.Controls[i].ParentControl.ControlUniqueID === this.ControlUniqueID)) {
                    window.ControlGlobal.Controls[i].Focus(true);
                    break;
                }
            }
        }
    }
    if (this.AfterFocus) { this.AfterFocus(); }
}

/**
 * Initializes an instance of a control.
 * @function
 * @name Control#Initialize
 * @param {Array} args Arguments used to instantiate derived control.
 * @param {Control} parentControl Parent control, if applicable.
 * @param {JQuery[]} focusables Array of focusable elements.
 */
function Control_Initialize(args, parentControl, focusables) {
    /**
     * The containing control.
     * @name Control#ParentControl
     * @type {Control}
     */
    this.ParentControl = ((parentControl !== null) && (parentControl !== undefined) ? parentControl : null);
    /**
     * List of elements which should have focus related CSS classes set and events hooked.
     * @name Control#Focusables
     * @type {JQuery[]}
     */
    this.Focusables = ((focusables !== null) && (focusables !== undefined) && (focusables instanceof Array) ? focusables : []);
    /**
     * Whether or not the control actively has focus.
     * @name Control#Focused
     * @type {boolean}
     */
    this.Focused = false;
    /**
     * Determines whether or not the control may receive focus.
     * @name Control#CanGetFocus
     * @type {boolean}
     */
    if (!this.hasOwnProperty('CanGetFocus')) { this.CanGetFocus = false; }
    /**
     * Determines whether or not the control may receive focus via the tab key.
     * @name Control#CanTabFocus
     * @type {boolean}
     */
    if (!this.hasOwnProperty('CanTabFocus')) { this.CanTabFocus = false; }
    //this.AfterBlur = null;
    //this.AfterFocus = null;
    /**
     * The globally-unique ID of the control.
     * @name Control#ControlUniqueID
     * @type {number}
     */
    this.ControlUniqueID = window.ControlGlobal.Iterator.getNextIdSync();
    window.ControlGlobal.Controls.push(this);
    /**
     * Arguments used to instantiate the control.
     * @name Control#Arguments
     * @type {Array}
     */
    this.Arguments = ((args !== null) && (args !== undefined) && (args instanceof Array) ? args : []);
    /**
     * Control which should receive focus in place of this one.
     * @name Control#ForFocus
     * @type {Control}
     */
    this.ForFocus = ((this.Arguments !== undefined) && (this.Arguments[0] !== undefined) && this.Arguments[0].hasOwnProperty('ForFocus') ? this.Arguments[0]['ForFocus'] : null);
    /**
     * The tab index of the control determines which order controls may shift focus via the tab key.
     * @name Control#TabIndex
     * @type {number}
     */
    this.TabIndex = (this.CanTabFocus === true ? ((this.Arguments !== undefined) && (this.Arguments[0] !== undefined) && this.Arguments[0].hasOwnProperty('TabIndex') ? parseInt(this.Arguments[0]['TabIndex']) : 0) : false);
    if (this.Focusables !== null) {
        if (this.Focusables instanceof Array) {
            for (let i = 0; i < this.Focusables.length; i++) {
                this.Focusables[i].bind('contextmenu', this, Control_ContextMenu);
                this.Focusables[i].bind('mousedown', this, Control_Mousedown);
                this.Focusables[i].bind('mouseup', this, Control_Mouseup);
                this.Focusables[i].bind('mousewheel', this, Control_Mousewheel);
            }
        } else {
            this.Focusables.bind('contextmenu', this, Control_ContextMenu);
            this.Focusables.bind('mousedown', this, Control_Mousedown);
            this.Focusables.bind('mouseup', this, Control_Mouseup);
            this.Focusables.bind('mousewheel', this, Control_Mousewheel);
        }
    }
}

/**
 * Handles the keydown event for a control.
 * @function
 * @name Control#Keydown
 * @param {IJQueryEventObject} event The keydown event object.
 */
function Control_Keydown(event) {
    if (ContextMenuGlobal.Active !== null) {
        if (ContextMenuGlobal.Active.Keydown(event) === true) {
            event.preventDefault();
            return (false);
        } else { return; }
    }
    for (let i = window.ControlGlobal.Controls.length - 1; i >= 0; i--) {
        let ctl = window.ControlGlobal.Controls[i];
        if (ctl !== null) {
            let returnCode = 0;
            if ((ctl.Keydown) && (ctl.Keydown !== Control_Keydown)) {
                returnCode = ctl.Keydown(event);
            }
            if ((returnCode !== false) && (returnCode !== true) && (event.keyCode === 9)) {
                if (ctl.hasOwnProperty('ParentControl') && (ctl['ParentControl'] !== null)) {
                    if (event.shiftKey === true) {
                        ctl.ParentControl.Controls.FocusPrevious();
                    } else {
                        ctl.ParentControl.Controls.FocusNext();
                    }
                    event.preventDefault();
                    return;
                }
            } else { break; }
        }
    }
    if ((event.ctrlKey === true) && (event.keyCode === 65)) {
        event.preventDefault();
    }
}

/**
 * Handles the keypress event for a control.
 * @function
 * @name Control#Keypress
 * @param {IJQueryEventObject} event The keypress event object.
 */
function Control_Keypress(event) {
    if (ContextMenuGlobal.Active !== null) {
        if (ContextMenuGlobal.Active.Keypress(event) === true) {
            event.preventDefault();
            return (false);
        } else {return; }
    }
    for (let i = window.ControlGlobal.Controls.length - 1; i >= 0; i--) {
        let ctl = window.ControlGlobal.Controls[window.ControlGlobal.Controls.length - 1];
        if ((ctl !== null) && (ctl.Keypress) && (ctl.Keypress !== Control_Keypress)) {
            ctl.Keypress(event);
            break;
        }
    }
}

/**
 * Handles the keyup event for a control.
 * @function
 * @name Control#Keyup
 * @param {IJQueryEventObject} event The keyup event object.
 */
function Control_Keyup(event) {
    if (ContextMenuGlobal.Active !== null) {
        if (ContextMenuGlobal.Active.Keyup(event) === true) {
            event.preventDefault();
            return (false);
        } else { return; }
    }
    for (let i = window.ControlGlobal.Controls.length - 1; i >= 0; i--) {
        let ctl = window.ControlGlobal.Controls[window.ControlGlobal.Controls.length - 1];
        if ((ctl !== null) && (ctl.Keyup) && (ctl.Keyup !== Control_Keyup)) {
            ctl.Keyup(event);
            break;
        }
    }
}

/**
 * Handles the mousedown event for a control.
 * @function
 * @name Control#Mousedown
 * @param {IJQueryEventObject} event The mousedown event object.
 */
function Control_Mousedown(event) {
    if (event.data !== null) {
        if (window.ControlGlobal.DirtyMouseDown === false) {
            window.ControlGlobal.DirtyMouseDown = true;
            window.ControlGlobal.Controls.Activate(event.data);
            let ret = undefined;
            if (event.data.Mousedown && (event.data.Mousedown !== Control_Mousedown)) { ret = event.data.Mousedown(event); }
            if (ret !== undefined) {
                return (ret);
            }
        }
        //event.preventDefault();
    } else {
        window.ControlGlobal.DirtyMouseDown = false;
    }
}

/**
 * Handles the mouseup event for a control.
 * @function
 * @name Control#Mouseup
 * @param {IJQueryEventObject} event The mouseup event object.
 */
function Control_Mouseup(event) {
    if (event.data !== null) {
        if (window.ControlGlobal.DirtyMouseDown === false) {
            window.ControlGlobal.DirtyMouseDown = true;
            window.ControlGlobal.Controls.Activate(event.data);
            let ret = undefined;
            if (event.data.Mouseup && (event.data.Mouseup !== Control_Mouseup)) { ret = event.data.Mouseup(event); }
            if (ret !== undefined) { return (ret); }
        }
        //event.preventDefault();
    } else {
        window.ControlGlobal.DirtyMouseDown = false;
    }
}

/**
 * Handles the mousewheel event for a control
 * @function
 * @name Control#Mousewheel
 * @param {IJQueryEventObject} event The mousewheel event object.
 */
function Control_Mousewheel(event) {
    if (event.data !== null) {
        let ret = undefined;
        if (event.data.Mousewheel && (event.data.Mousewheel !== Control_Mousewheel)) { ret = event.data.Mousewheel(event); }
        if (ret !== undefined) { return (ret); }
    }
}
Control.prototype.constructor = Control;
Control.prototype.Blur = Control_Blur;
Control.prototype.ContextMenu = Control_ContextMenu;
Control.prototype.Dispose = Control_Dispose;
Control.prototype.Focus = Control_Focus;
Control.prototype.Initialize = Control_Initialize;
Control.prototype.Keydown = Control_Keydown;
Control.prototype.Keypress = Control_Keypress;
Control.prototype.Keyup = Control_Keyup;
Control.prototype.Mousedown = Control_Mousedown;
Control.prototype.Mouseup = Control_Mouseup;
Control.prototype.Mousewheel = Control_Mousewheel;
/**
 * Tells a control to render itself in an activated state.
 * @name Control#Activate
 * @function
 */
/**
 * Tells a control to render itself in a deactivated state.
 * @name Control#Deactivate
 * @function
 */

/**
 * JQuery event object used in conjunction with keyboard and mouse events.
 * @interface IJQueryEventObject
 */
/**
 * Prevents the default action of the event.
 * @function IJQueryEventObject#preventDefault
 * @instance
 */
/**
 * Stops propagation of the event.
 * @function IJQueryEventObject#stopPropagation
 * @instance
 */
/**
 * Whether or not the ALT key was down during the event.
 * @name IJQueryEventObject#altKey
 * @type {boolean}
 */
/**
 * Whether or not the CTRL key was down during the event.
 * @name IJQueryEventObject#ctrlKey
 * @type {boolean}
 */
/**
 * Whether or not the META key was down during the event.
 * @name IJQueryEventObject#metaKey
 * @type {boolean}
 */
/**
 * Whether or not the SHIFT key was down during the event.
 * @name IJQueryEventObject#shiftKey
 * @type {boolean}
 */
/**
 * The keycode of the event.
 * @name IJQueryEventObject#keyCode
 * @type {number}
 */
/**
 * Data object associated with the event.
 * @name IJQueryEventObject#data
 * @type {*}
 */