Source: ui/tooltip/TooltipComponentModel.js

// Copyright 2012 Tart. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview
 */

goog.provide('tart.ui.TooltipComponentModel');
goog.require('tart.ui.ComponentModel');
goog.require('tart.StateMachine');



/**
 * @constructor
 * @extends {tart.ui.ComponentModel}
 */
tart.ui.TooltipComponentModel = function(options) {
    options = options || {};
    this.options = {};
    this.options.timeout = options.timeout || this.timeout;
    this.options.type = options.type || this.type;
    this.options.direction = options.direction || this.direction;
    this.initStateMachine();
};
goog.inherits(tart.ui.TooltipComponentModel, tart.ui.ComponentModel);


/**
 * How long it should take the tooltip to appear for a given event type, in milliseconds.
 * Timeout 0 is instant activation.
 *
 * @type {number}
 */
tart.ui.TooltipComponentModel.prototype.timeout = 0;


/**
 * Actions to trigger this tooltip. It can be triggered on click or on hover. Default is hover.
 *
 * @enum {string}
 */
tart.ui.TooltipComponentModel.Type = {
    CLICK: 'click',
    HOVER: 'hover'
};


/**
 * Tooltip is triggered on hover by default.
 *
 * @type {tart.ui.TooltipComponentModel.Type}
 */
tart.ui.TooltipComponentModel.prototype.type = tart.ui.TooltipComponentModel.Type.HOVER;


/**
 * How many pixels away the tip should be with respect to the reference element. For top and bottom directions, this
 * is horizontal. For left and right this is vertical. Element (box) positioning is fluid, but this distance should
 * always be kept.
 *
 * @type {number}
 */
tart.ui.TooltipComponentModel.prototype.tipOffset = 20;


/**
 * How many pixels away the box (element) should be positioned with respect to the reference element.
 *
 * @type {number}
 */
tart.ui.TooltipComponentModel.prototype.boxOffset = 3;


/**
 * If the size of the reference element is greater than this threshold, the tip should be placed at a distance of
 * tipOffset. Else, the tip should point to the center of the reference element.
 *
 * @type {number}
 */
tart.ui.TooltipComponentModel.prototype.offsetThreshold = 30;


/**
 * Events that this model dispatches at corresponding states
 *
 * @enum {string}
 */
tart.ui.TooltipComponentModel.EventType = {
    INIT: 'init',
    SHOW: 'show',
    CLICK_WAIT: 'clickWait',
    HOVER_WAIT: 'hoverWait'
};


/**
 * Transition events for the internal state machine
 * @enum {string}
 */
tart.ui.TooltipComponentModel.SMEventType = {
    TIMEOUT: 'timeout',
    BODY_CLICK: 'bodyClick',
    MOUSEOVER: goog.events.EventType.MOUSEOVER,
    MOUSEOUT: goog.events.EventType.MOUSEOUT,
    CLICK: goog.events.EventType.CLICK
};


/**
 * Direction this tooltip will be shown regarding the reference element.
 * @enum {string}
 */
tart.ui.TooltipComponentModel.Direction = {
    TOP: 'top',
    BOTTOM: 'bottom',
    LEFT: 'left',
    RIGHT: 'right',
    TOP_LEFT: 'topLeft'
};

tart.ui.TooltipComponentModel.prototype.direction = tart.ui.TooltipComponentModel.Direction.TOP;


/**
 * @protected
 */
tart.ui.TooltipComponentModel.prototype.initStateMachine = function() {
    var that = this;
    this.mEvents = tart.ui.TooltipComponentModel.EventType;

    /** @protected */
    this.stateMachine = new tart.StateMachine();
    this.stateMachine.smEvents = tart.ui.TooltipComponentModel.SMEventType;

    this.timeoutHandler = false;

    this.stateMachine.createStates = function() {
        var sm = this;

        var INIT = new tart.State(function() {
            clearTimeout(that.timeoutHandler);
            that.dispatchEvent({
                type: that.mEvents.INIT
            });
        });
        var CLICK_WAIT = new tart.State(function() {
            that.dispatchEvent({
                type: that.mEvents.CLICK_WAIT
            });
            that.timeoutHandler = setTimeout(function() {
                sm.publish(sm.smEvents.TIMEOUT);
            }, that.options.timeout);
        });
        var SHOW = new tart.State(function() {
            clearTimeout(that.timeoutHandler);
            that.dispatchEvent({
                type: that.mEvents.SHOW
            });
        });
        var HOVER_WAIT = new tart.State(function() {
            that.dispatchEvent({
                type: that.mEvents.HOVER_WAIT
            });
            that.timeoutHandler = setTimeout(function() {
                sm.publish(sm.smEvents.TIMEOUT);
            }, that.options.timeout);
        });

        switch (that.options.type) {
            case tart.ui.TooltipComponentModel.Type.CLICK:
                INIT.transitions[this.smEvents.CLICK] = CLICK_WAIT;
                CLICK_WAIT.transitions[this.smEvents.BODY_CLICK] = INIT;
                CLICK_WAIT.transitions[this.smEvents.TIMEOUT] = SHOW;
                SHOW.transitions[this.smEvents.BODY_CLICK] = INIT;
                SHOW.transitions[this.smEvents.CLICK] = INIT;

                break;

            case tart.ui.TooltipComponentModel.Type.HOVER:
            default:
                INIT.transitions[this.smEvents.MOUSEOVER] = HOVER_WAIT;
                HOVER_WAIT.transitions[this.smEvents.MOUSEOUT] = INIT;
                HOVER_WAIT.transitions[this.smEvents.TIMEOUT] = SHOW;
                SHOW.transitions[this.smEvents.MOUSEOUT] = INIT;
        }

        this.addState(INIT);
        this.addState(CLICK_WAIT);
        this.addState(SHOW);
        this.addState(HOVER_WAIT);
        return INIT;
    }

    this.stateMachine.startMachine();
};


/**
 * @param {string} type
 */
tart.ui.TooltipComponentModel.prototype.handleEvent = function(type) {
    this.stateMachine.publish(type);
};


tart.ui.TooltipComponentModel.prototype.reset = function() {
    this.stateMachine.reset();
};