API Docs for: 3.11.0-git
Show:

File: src/sm-editor/js/extensions/editor-queue.js

/*jshint expr:true, onevar:false */

/**
Provides the `Editor.Queue` extension.

@module gallery-sm-editor
@submodule gallery-sm-editor-queue
**/

/**
Extension for `Editor.Base` that queues commands

@class Editor.Queue
@extends Base
@extensionfor Editor.Base
**/

(function() {

var EditorQueue = Y.Base.create('editorStyle', Y.Base, [], {
    // -- Public Properties ----------------------------------------------------

    /**
    Key commands related to queue functionality.

    @property {Object} queueKeyCommands
    **/
    queueKeyCommands: {
        'down'     : {fn: '_clearCommandQueue', allowDefault: true},
        'end'      : {fn: '_clearCommandQueue', allowDefault: true},
        'esc'      : {fn: '_clearCommandQueue', allowDefault: true},
        'home'     : {fn: '_clearCommandQueue', allowDefault: true},
        'left'     : {fn: '_clearCommandQueue', allowDefault: true},
        'pgdown'   : {fn: '_clearCommandQueue', allowDefault: true},
        'pgup'     : {fn: '_clearCommandQueue', allowDefault: true},
        'right'    : {fn: '_clearCommandQueue', allowDefault: true},
        'tab'      : {fn: '_clearCommandQueue', allowDefault: true},
        'up'       : {fn: '_clearCommandQueue', allowDefault: true}
    },


    // -- Protected Properties -------------------------------------------------

    /**
    Hash of commands queued for execution on the next keypress.

    Workaround for webkit bug where it wont place a caret in an empty node
    or between nodes.

    @property {Object} _commandQueue
    **/


    // -- Lifecycle ------------------------------------------------------------

    initializer: function () {
        if (this.keyCommands) {
            this.keyCommands = Y.merge(this.keyCommands, this.queueKeyCommands);
        }

        this._attachQueueEvents();
    },


    destructor: function () {
        this._detachQueueEvents();
    },


    // -- Protected Methods ----------------------------------------------------

    /**
    Attaches queue events.

    @method _attachQueueEvents
    @protected
    **/
    _attachQueueEvents: function() {
        if (this._queueEvents) {
            return;
        }

        var container = this.get('container');

        this._queueEvents = [
            container.delegate('keypress', this._onKeyPress, this.selectors.input, this),
            Y.Do.before(this._queueDoBeforeCommand, this, 'command', this),
            Y.Do.before(this._queueDoBeforeQuery, this, 'query', this)
        ];
    },


    /**
    Clears the command queue

    @method _clearCommandQueue
    @protected
    **/
    _clearCommandQueue: function() {
        this._commandQueue = null;
    },


    /**
    Detaches queue events.

    @method _detachQueueEvents
    @protected
    **/
    _detachQueueEvents: function() {
        if (this._queueEvents) {
            new Y.EventHandle(this._queueEvents).detach();
            this._queueEvents = null;
        }
    },


    /**
    Executes all commands on the command queue

    @method _flushCommandQueue
    @protected
    **/
    _flushCommandQueue: function() {
        if (!this._commandQueue) {
            return;
        }

        // need to create a copy because the commandQueue
        // will be flushed after the first command is executed
        var queue = Y.merge(this._commandQueue);

        Y.Object.each(queue, function(value, cmd) {
            this.command(cmd, value);
        }, this);

        this._clearCommandQueue();
    },


    /**
    Adds a command to the queue

    @method _queueCommand
    @protected
    **/
    _queueCommand: function(name, value) {
        var command = this.commands[name];

        if (!this._commandQueue) {
            this._commandQueue = {};
        }

        if (command.style && Y.Lang.isValue(command.style.value)) {
            value = this._commandQueue[name] ? '' : command.style.value;
        }

        this._commandQueue[name] = value;

        return value;
    },


    // -- Protected Event Handlers ---------------------------------------------

    /**
    Handles `keypress` events on the editor

    @method _onKeyPress
    @param {EventFacade} e
    @protected
    **/
    _onKeyPress: function(e) {
        if (e.metaKey) {
            return;
        }

        var selection = this.selection,
            range = selection.range(),
            node;

        if (range.shrink().isCollapsed() && this._commandQueue) {
            e.preventDefault();

            node = range.insertNode(Y.Node.create(String.fromCharCode(e.charCode)));

            selection.select(range.selectNode(node));

            this._flushCommandQueue();

            selection.select(range.selectNode(node).shrink().collapse());
        } else {
            this._clearCommandQueue();
        }
    },


    /**
    Wrapper for `Editor.Base#_execCommand()`.

    @method _queueBeforeExecStyleCommand
    @param {String} name Command name.
    @param {Boolean|String} value Command value.
    @protected
    **/
    _queueDoBeforeCommand: function (name, value) {
        var styleCmd = this.styleCommands[name],
            range, ret;

        if (styleCmd && 'block' !== styleCmd.type) {
            range = this.selection.range();

            if (range && range.isCollapsed()) {
                ret = this._queueCommand(name, value);
                this._updateSelection({force: true});
                return new Y.Do.Halt('Editor.Queue prevented command', ret);
            }
        } else {
            this._clearCommandQueue();
        }
    },


    /**
    Wrapper for `Editor.Base#_queryCommandValue()`.

    @method _queueDoBeforeQueryCommandValue
    @param {String} name Command name.
    @protected
    **/
    _queueDoBeforeQuery: function (name) {
        var qCmd = this._commandQueue && this._commandQueue[name],
            styleCmd = this.styleCommands[name];

        if (styleCmd && Y.Lang.isValue(qCmd)) { // because qCmd could be ''
            if (Y.Lang.isValue(styleCmd.style.value)) {
                qCmd = qCmd === styleCmd.style.value;
            }

            return new Y.Do.Halt('Editor.Queue prevented query', qCmd);
        }
    }
});

Y.namespace('Editor').Queue = EditorQueue;

}());