// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/*
 * @package    atto_accessibilityhelper
 * @copyright  2014 Damyon Wiese  <damyon@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

/**
 * @module moodle-atto_accessibilityhelper-button
 */

/**
 * Atto text editor accessibilityhelper plugin.
 *
 * This plugin adds some functions to do things that screen readers do not do well.
 * Specifically, listing the active styles for the selected text,
 * listing the images in the page, listing the links in the page.
 *
 *
 * @namespace M.atto_accessibilityhelper
 * @class Button
 * @extends M.editor_atto.EditorPlugin
 */

var COMPONENT = 'atto_accessibilityhelper',
    TEMPLATE = '' +
        // The list of styles.
        '<div><p id="{{elementid}}_{{CSS.STYLESLABEL}}">' +
            '{{get_string "liststyles" component}}<br/>' +
            '<span aria-labelledby="{{elementid}}_{{CSS.STYLESLABEL}}" />' +
        '</p></div>' +
        '<span class="listStyles"></span>' +

        '<p id="{{elementid}}_{{CSS.LINKSLABEL}}">' +
            '{{get_string "listlinks" component}}<br/>' +
            '<span aria-labelledby="{{elementid}}_{{CSS.LINKSLABEL}}"/>' +
        '</p>' +
        '<span class="listLinks"></span>' +

        '<p id="{{elementid}}_{{CSS.IMAGESLABEL}}">' +
            '{{get_string "listimages" component}}<br/>' +
            '<span aria-labelledby="{{elementid}}_{{CSS.IMAGESLABEL}}"/>' +
        '</p>' +
        '<span class="listImages"></span>',

    CSS = {
        STYLESLABEL: COMPONENT + '_styleslabel',
        LINKSLABEL: COMPONENT + '_linkslabel',
        IMAGESLABEL: COMPONENT + '_imageslabel'
    };

Y.namespace('M.atto_accessibilityhelper').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {

    initializer: function() {
        this.addButton({
            icon: 'e/screenreader_helper',
            callback: this._displayDialogue
        });
    },

    /**
     * Display the Accessibility Helper tool.
     *
     * @method _displayDialogue
     * @private
     */
    _displayDialogue: function() {
        var dialogue = this.getDialogue({
            headerContent: M.util.get_string('pluginname', COMPONENT),
            width: '800px',
            focusAfterHide: true
        });

        // Set the dialogue content, and then show the dialogue.
        dialogue.set('bodyContent', this._getDialogueContent())
                .show();
    },

    /**
     * Return the dialogue content for the tool, attaching any required
     * events.
     *
     * @method _getDialogueContent
     * @private
     * @return {Node} The content to place in the dialogue.
     */
    _getDialogueContent: function() {
        var template = Y.Handlebars.compile(TEMPLATE),
            content = Y.Node.create(template({
                CSS: CSS,
                component: COMPONENT
            }));

        // Add the data.
        content.one('.listStyles')
                .empty()
                .appendChild(this._listStyles());
        content.one('.listLinks')
                .empty()
                .appendChild(this._listLinks());
        content.one('.listImages')
                .empty()
                .appendChild(this._listImages());

        return content;
    },

    /**
     * List the styles present for the selection.
     *
     * @method _listStyles
     * @return {String} The list of styles in use.
     * @private
     */
    _listStyles: function() {
        // Clear the status node.
        var list = [],
            host = this.get('host'),
            current = host.getSelectionParentNode(),
            tagname;

        if (current) {
            current = Y.one(current);
        }

        while (current && (current !== this.editor)) {
            tagname = current.get('tagName');
            if (typeof tagname !== 'undefined') {
                list.push(Y.Escape.html(tagname));
            }
            current = current.ancestor();
        }
        if (list.length === 0) {
            list.push(M.util.get_string('nostyles', COMPONENT));
        }

        list.reverse();

        // Append the list of current styles.
        return list.join(', ');
    },

    /**
     * List the links for the current editor
     *
     * @method _listLinks
     * @return {string}
     * @private
     */
    _listLinks: function() {
        var list = Y.Node.create('<ol />'),
            listitem,
            selectlink;

        this.editor.all('a').each(function(link) {
            selectlink = Y.Node.create('<a href="#" title="' +
                    M.util.get_string('selectlink', COMPONENT) + '">' +
                    Y.Escape.html(link.get('text')) +
                    '</a>');

            selectlink.setData('sourcelink', link);
            selectlink.on('click', this._linkSelected, this);

            listitem = Y.Node.create('<li></li>');
            listitem.appendChild(selectlink);

            list.appendChild(listitem);
        }, this);

        if (!list.hasChildNodes()) {
            list.append('<li>' + M.util.get_string('nolinks', COMPONENT) + '</li>');
        }

        // Append the list of current styles.
        return list;
    },

    /**
     * List the images used in the editor.
     *
     * @method _listImages
     * @return {Node} A Node containing all of the images present in the editor.
     * @private
     */
    _listImages: function() {
        var list = Y.Node.create('<ol/>'),
            listitem,
            selectimage;

        this.editor.all('img').each(function(image) {
            // Get the alt or title or img url of the image.
            var imgalt = image.getAttribute('alt');
            if (imgalt === '') {
                imgalt = image.getAttribute('title');
                if (imgalt === '') {
                    imgalt = image.getAttribute('src');
                }
            }

            selectimage = Y.Node.create('<a href="#" title="' +
                    M.util.get_string('selectimage', COMPONENT) + '">' +
                    Y.Escape.html(imgalt) +
                    '</a>');

            selectimage.setData('sourceimage', image);
            selectimage.on('click', this._imageSelected, this);

            listitem = Y.Node.create('<li></li>');
            listitem.append(selectimage);
            list.append(listitem);
        }, this);
        if (!list.hasChildNodes()) {
            list.append('<li>' + M.util.get_string('noimages', COMPONENT) + '</li>');
        }

        // Append the list of current styles.
        return list;
    },

    /**
     * Event handler for selecting an image.
     *
     * @method _imageSelected
     * @param {EventFacade} e
     * @private
     */
    _imageSelected: function(e) {
        e.preventDefault();

        this.getDialogue({
            focusAfterNode: null
        }).hide();

        var host = this.get('host'),
            target = e.target.getData('sourceimage');

        this.editor.focus();
        host.setSelection(host.getSelectionFromNode(target));
    },

    /**
     * Event handler for selecting a link.
     *
     * @method _linkSelected
     * @param {EventFacade} e
     * @private
     */
    _linkSelected: function(e) {
        e.preventDefault();

        this.getDialogue({
            focusAfterNode: null
        }).hide();

        var host = this.get('host'),
            target = e.target.getData('sourcelink');

        this.editor.focus();
        host.setSelection(host.getSelectionFromNode(target));
    }
});
