API Docs for: 1.0.0
Show:

File: Resources/public/js/alloyeditor/plugins/embed.js

/*
 * Copyright (C) eZ Systems AS. All rights reserved.
 * For full copyright and license information view LICENSE file distributed with this source code.
 */
/* global CKEDITOR */
YUI.add('ez-alloyeditor-plugin-embed', function (Y) {
    "use strict";

    var IMAGE_TYPE_CLASS = 'ez-embed-type-image',
        DATA_ALIGNMENT_ATTR = 'ezalign';

    if (CKEDITOR.plugins.get('ezembed')) {
        return;
    }

    /**
     * CKEditor plugin to configure the widget plugin so that it recognizes the
     * `div[data-ezelement="embed"]` elements as widget.
     *
     * @class ezembed
     * @namespace CKEDITOR.plugins
     * @constructor
     */
    CKEDITOR.plugins.add('ezembed', {
        requires: 'widget,ezaddcontent',

        init: function (editor) {
            editor.widgets.add('ezembed', {
                defaults: {
                    href: "ezcontent://57",
                    content: "home",
                    view: "embed",
                },
                draggable: false,
                template: '<div data-ezelement="ezembed" data-href="{href}" data-ezview="{view}">{content}</div>',
                requiredContent: 'div',

                upcast: function (element) {
                    return (
                        element.name === 'div' &&
                        element.attributes['data-ezelement'] === 'ezembed'
                    );
                },

                /**
                 * Insert an `ezembed` widget in the editor. It overrides the
                 * default implementation to make sure that in the case where an
                 * embed widget is focused, a new one is added after it.
                 *
                 * @method insert
                 */
                insert: function () {
                    var element = CKEDITOR.dom.element.createFromHtml(this.template.output(this.defaults)),
                        wrapper = editor.widgets.wrapElement(element, this.name),
                        temp = new CKEDITOR.dom.documentFragment(wrapper.getDocument()),
                        instance;

                    temp.append(wrapper);
                    editor.widgets.initOn(element, this.name);
                    editor.eZ.appendElement(wrapper);

                    instance = editor.widgets.getByElement(wrapper);
                    instance.ready = true;
                    instance.fire('ready');
                    instance.focus();
                },

                /**
                 * It's not possible to *edit* an embed widget in AlloyEditor,
                 * so `edit` directly calls `insert` instead. This is needed
                 * because by default, the CKEditor engine calls this method
                 * when an embed widget has the focus and the `ezembed` command
                 * is executed. In AlloyEditor, we want to insert a new widget,
                 * not to `edit` the focused widget as the editing process is
                 * provided by the style toolbar.
                 *
                 * @method edit
                 */
                edit: function () {
                    this.insert();
                },

                init: function () {
                    this.on('focus', this._fireEditorInteraction);
                    this._syncAlignment();
                    this._getEzConfigElement();
                    this.setWidgetContent('');
                    this._cancelEditEvents();
                },

                /**
                 * Cancels the widget events that trigger the `edit` event as
                 * an embed widget can not be edited in a *CKEditor way*.
                 *
                 * @method _cancelEditEvents
                 * @private
                 */
                _cancelEditEvents: function () {
                    var cancel = function (e) {
                            e.cancel();
                        };

                    this.on('doubleclick', cancel, null, null, 5);
                    this.on('key', cancel, null, null, 5);
                },

                /**
                 * Initializes the alignment on the widget wrapper if the widget
                 * is aligned.
                 *
                 * @method _syncAlignment
                 * @protected
                 */
                _syncAlignment: function () {
                    var align = this.element.data(DATA_ALIGNMENT_ATTR);

                    if ( align ) {
                        this._setAlignment(align);
                    } else {
                        this._unsetAlignment();
                    }
                },

                /**
                 * Sets the alignment of the embed widget to `type`. The
                 * alignment is set by adding the `data-ezalign` attribute
                 * on the widget wrapper and the widget element.
                 *
                 * @method _setAlignment
                 * @protected
                 * @param {String} type
                 */
                _setAlignment: function (type) {
                    this.wrapper.data(DATA_ALIGNMENT_ATTR, type);
                    this.element.data(DATA_ALIGNMENT_ATTR, type);
                },

                /**
                 * Sets the alignment of the embed widget to `type` and fires
                 * the corresponding `editorInteraction` event.
                 *
                 * @method setAlignment
                 * @param {String} type
                 */
                setAlignment: function (type, fireEvent) {
                    this._setAlignment(type);
                    this._fireEditorInteraction('setAlignment' + type);
                },

                /**
                 * Removes the alignment of the widget.
                 *
                 * @method _unsetAlignment
                 * @protected
                 */
                _unsetAlignment: function () {
                    this.wrapper.data(DATA_ALIGNMENT_ATTR, false);
                    this.element.data(DATA_ALIGNMENT_ATTR, false);
                },

                /**
                 * Removes the alignment of the widget and fires the
                 * corresponding `editorInteraction` event.
                 *
                 * @method unsetAlignment
                 */
                unsetAlignment: function () {
                    this._unsetAlignment();
                    this._fireEditorInteraction('unsetAlignment');
                },

                /**
                 * Checks whether the embed is aligned with `type` alignment.
                 *
                 * @method isAligned
                 * @param {String} type
                 * @return {Boolean}
                 */
                isAligned: function (type) {
                    return (this.wrapper.data(DATA_ALIGNMENT_ATTR) === type);
                },

                /**
                 * Set the embed as an embed representing an image
                 *
                 * @method setImageType
                 * @return {CKEDITOR.plugins.widget}
                 */
                setImageType: function () {
                    this.element.addClass(IMAGE_TYPE_CLASS);
                    return this;
                },

                /**
                 * Check whether the embed widget represents an image or not.
                 *
                 * @method isImage
                 * @return {Boolean}
                 */
                isImage: function () {
                    return this.element.hasClass(IMAGE_TYPE_CLASS);
                },

                /**
                 * Sets the `href` of the embed is URI to the embed content or
                 * location. (ezcontent://32 for instance).
                 *
                 * @method setHref
                 * @param {String} href
                 * @return {CKEDITOR.plugins.widget}
                 */
                setHref: function (href) {
                    this.element.data('href', href);
                    return this;
                },

                /**
                 * Returns the `href`of the embed.
                 *
                 * @method getHref
                 * @return {String}
                 */
                getHref: function () {
                    return this.element.data('href');
                },

                /**
                 * Sets the widget content. It makes sure the config element is
                 * not overwritten.
                 *
                 * @method setWidgetContent
                 * @param {String|CKEDITOR.dom.node} content
                 * @return {CKEDITOR.plugins.widget}
                 */
                setWidgetContent: function (content) {
                    var element = this.element.getFirst(), next;

                    while ( element ) {
                        next = element.getNext();
                        if ( !element.data || !element.data('ezelement') ) {
                            element.remove();
                        }
                        element = next;
                    }
                    if ( content instanceof CKEDITOR.dom.node ) {
                        this.element.append(content);
                    } else {
                        this.element.appendText(content);
                    }
                    return this;
                },

                /**
                 * Moves the widget after the given element. It also fires the
                 * `editorInteraction` event so that the UI can respond to that
                 * change.
                 *
                 * @method moveAfter
                 * @param {CKEDITOR.dom.element} element
                 */
                moveAfter: function (element) {
                    this.wrapper.insertAfter(element);
                    this._fireEditorInteraction('moveAfter');
                },

                /**
                 * Moves the widget before the given element. It also fires the
                 * `editorInteraction` event so that the UI can respond to that
                 * change.
                 *
                 * @method moveAfter
                 * @param {CKEDITOR.dom.element} element
                 */
                moveBefore: function (element) {
                    this.wrapper.insertBefore(element);
                    this._fireEditorInteraction('moveBefore');
                },

                /**
                 * Sets a config value under the `key` for the embed.
                 *
                 * @method setConfig
                 * @param {String} key
                 * @param {String} value
                 * @return {CKEDITOR.plugins.widget}
                 */
                setConfig: function (key, value) {
                    var valueElement = this._getValueElement(key);

                    if ( !valueElement ) {
                        valueElement = new CKEDITOR.dom.element('span');
                        valueElement.data('ezelement', 'ezvalue');
                        valueElement.data('ezvalue-key', key);
                        this._getEzConfigElement().append(valueElement);
                    }
                    valueElement.setText(value);
                    return this;
                },

                /**
                 * Returns the config value for the `key` or undefined if the
                 * config key is not found.
                 *
                 * @method getConfig
                 * @return {String}
                 */
                getConfig: function (key) {
                    var valueElement = this._getValueElement(key);

                    return valueElement ? valueElement.getText() : undefined;
                },

                /**
                 * Returns the Element holding the config under `key`
                 *
                 * @method _getValueElement
                 * @param {String} key
                 * @return {CKEDITOR.dom.element}
                 */
                _getValueElement: function (key) {
                    return this.element.findOne('[data-ezelement="ezvalue"][data-ezvalue-key="' + key + '"]');
                },

                /**
                 * Returns the element used as a container the config values. if
                 * it does not exist, it is created.
                 *
                 * @method _getEzConfigElement
                 * @private
                 * @return {CKEDITOR.dom.element}
                 */
                _getEzConfigElement: function () {
                    var config = this.element.findOne('[data-ezelement="ezconfig"]');

                    if ( !config ) {
                        config = new CKEDITOR.dom.element('span');
                        config.data('ezelement', 'ezconfig');
                        this.element.append(config, true);
                    }
                    return config;
                },

                /**
                 * Fires the editorInteraction event so that AlloyEditor editor
                 * UI remains visible and is updated. This method also computes
                 * `selectionData.region` and the `pageX` and `pageY` properties
                 * so that the add toolbar is correctly positioned on the
                 * widget.
                 *
                 * @method _fireEditorInteraction
                 * @protected
                 * @param {Object|String} evt this initial event info object or
                 * the event name for which the `editorInteraction` is fired.
                 */
                _fireEditorInteraction: function (evt) {
                    var wrapperRegion = this._getWrapperRegion(),
                        name = evt.name || evt,
                        e = {
                            editor: editor,
                            target: this.element.$,
                            name: "widget" + name,
                            pageX: wrapperRegion.left,
                            pageY: wrapperRegion.top + wrapperRegion.height,
                        };

                    editor.focus();
                    this.focus();

                    editor.fire('editorInteraction', {
                        nativeEvent: e,
                        selectionData: {
                            element: this.element,
                            region: wrapperRegion,
                        },
                    });
                },

                /**
                 * Returns the wrapper element region.
                 *
                 * @method _getWrapperRegion
                 * @private
                 * @return {Object}
                 */
                _getWrapperRegion: function () {
                    var scroll = this.wrapper.getWindow().getScrollPosition(),
                        region = this.wrapper.getClientRect();

                    region.top += scroll.y;
                    region.bottom += scroll.y;
                    region.left += scroll.x;
                    region.right += scroll.x;
                    region.direction = CKEDITOR.SELECTION_TOP_TO_BOTTOM;
                    return region;
                },
            });
        },
    });
});