/**
 * WYSIWYG - jQuery plugin 0.5
 *
 * Copyright (c) 2008-2009 Juan M Martinez
 * http://plugins.jquery.com/project/jWYSIWYG
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * $Id: $
 */
(function($)
{
    $.fn.document = function()
    {
        var element = this[0];

        if (element.nodeName.toLowerCase() == 'iframe')
            return element.contentWindow.document;
        /*
        return ( $.browser.msie )
        ? document.frames[element.id].document
        : element.contentWindow.document // contentDocument;
        */
        else
            return $(this);
    };

    $.fn.documentSelection = function()
    {
        var element = this[0];

        if (element.contentWindow.document.selection)
            return element.contentWindow.document.selection.createRange().text;
        else
            return element.contentWindow.getSelection().toString();
    };

    $.fn.wysiwyg = function(options)
    {
        if (arguments.length > 0 && arguments[0].constructor == String)
        {
            var action = arguments[0].toString();
            var params = [];

            for (var i = 1; i < arguments.length; i++)
                params[i - 1] = arguments[i];

            if (action in Wysiwyg)
            {
                return this.each(function()
                {
                    $.data(this, 'wysiwyg')
                     .designMode();

                    Wysiwyg[action].apply(this, params);
                });
            }
            else return this;
        }

        var controls = {};

        /**
        * If the user set custom controls, we catch it, and merge with the
        * defaults controls later.
        */
        if (options && options.controls)
        {
            var controls = options.controls;
            delete options.controls;
        }

        var options = $.extend({
            html: '<' + '?xml version="1.0" encoding="UTF-8"?' + '><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">STYLE_SHEET</head><body>INITIAL_CONTENT</body></html>',
            css: {},

            debug: false,

            autoSave: true,  // http://code.google.com/p/jwysiwyg/issues/detail?id=11
            rmUnwantedBr: true,  // http://code.google.com/p/jwysiwyg/issues/detail?id=15
            brIE: true,

            controls: {},
            messages: {}
        }, options);

        options.messages = $.extend(true, options.messages, Wysiwyg.MSGS_EN);
        options.controls = $.extend(true, options.controls, Wysiwyg.TOOLBAR);

        for (var control in controls)
        {
            if (control in options.controls)
                $.extend(options.controls[control], controls[control]);
            else
                options.controls[control] = controls[control];
        }

        // not break the chain
        return this.each(function()
        {
            Wysiwyg(this, options);
        });
    };

    function Wysiwyg(element, options)
    {
        return this instanceof Wysiwyg
            ? this.init(element, options)
            : new Wysiwyg(element, options);
    }

    $.extend(Wysiwyg, {
        insertImage: function(szURL, attributes)
        {
            var self = $.data(this, 'wysiwyg');

            if (self.constructor == Wysiwyg && szURL && szURL.length > 0)
            {
                if (attributes)
                {
                    self.editorDoc.execCommand('insertImage', false, '#jwysiwyg#');
                    var img = self.getElementByAttributeValue('img', 'src', '#jwysiwyg#');

                    if (img)
                    {
                        img.src = szURL;

                        for (var attribute in attributes)
                        {
                            img.setAttribute(attribute, attributes[attribute]);
                        }
                    }
                }
                else
                {
                    self.editorDoc.execCommand('insertImage', false, szURL);
                }
            }
        },

        createLink: function(szURL)
        {
            var self = $.data(this, 'wysiwyg');

            if (self.constructor == Wysiwyg && szURL && szURL.length > 0)
            {
                var selection = $(self.editor).documentSelection();

                if (selection.length > 0)
                {
                    self.editorDoc.execCommand('unlink', false, []);
                    self.editorDoc.execCommand('createLink', false, szURL);
                }
                else if (self.options.messages.nonSelection)
                    alert(self.options.messages.nonSelection);
            }
        },

        setContent: function(newContent)
        {
            var self = $.data(this, 'wysiwyg');
            self.setContent(newContent);
            self.saveContent();
        },

        clear: function()
        {
            var self = $.data(this, 'wysiwyg');
            self.setContent('');
            self.saveContent();
        },

        MSGS_EN: {
            nonSelection: 'select the text you wish to link'
        },

        TOOLBAR: {
            bold: { visible: true, tags: ['b', 'strong'], css: { fontWeight: 'bold'} },
            italic: { visible: true, tags: ['i', 'em'], css: { fontStyle: 'italic'} },
            strikeThrough: { visible: false, tags: ['s', 'strike'], css: { textDecoration: 'line-through'} },
            underline: { visible: false, tags: ['u'], css: { textDecoration: 'underline'} },

            separator00: { visible: false, separator: true },

            justifyLeft: { visible: false, css: { textAlign: 'left'} },
            justifyCenter: { visible: false, tags: ['center'], css: { textAlign: 'center'} },
            justifyRight: { visible: false, css: { textAlign: 'right'} },
            justifyFull: { visible: false, css: { textAlign: 'justify'} },

            separator01: { visible: false, separator: true },

            indent: { visible: false },
            outdent: { visible: false },

            separator02: { visible: false, separator: true },

            subscript: { visible: false, tags: ['sub'] },
            superscript: { visible: false, tags: ['sup'] },

            separator03: { visible: false, separator: true },

            undo: { visible: false },
            redo: { visible: false },

            separator04: { visible: true, separator: true },

            insertOrderedList: { visible: true, tags: ['ol'] },
            insertUnorderedList: { visible: true, tags: ['ul'] },
            insertHorizontalRule: { visible: false, tags: ['hr'] },

            separator05: { separator: true },

            createLink: {
                visible: true,
                exec: function()
                {
                    var selection = $(this.editor).documentSelection();

                    if (selection.length > 0)
                    {
                        if ($.browser.msie)
                            this.editorDoc.execCommand('createLink', true, null);
                        else
                        {
                            var szURL = prompt('URL', 'http://');

                            if (szURL && szURL.length > 0)
                            {
                                this.editorDoc.execCommand('unlink', false, []);
                                this.editorDoc.execCommand('createLink', false, szURL);
                            }
                        }
                    }
                    else if (this.options.messages.nonSelection)
                        alert(this.options.messages.nonSelection);
                },

                tags: ['a']
            },

            insertImage: {
                visible: true,
                exec: function()
                {
                    if ($.browser.msie)
                        this.editorDoc.execCommand('insertImage', true, null);
                    else
                    {
                        var szURL = prompt('URL', 'http://');

                        if (szURL && szURL.length > 0)
                            this.editorDoc.execCommand('insertImage', false, szURL);
                    }
                },

                tags: ['img']
            },

            separator06: { separator: true },

            h1mozilla: { visible: true && $.browser.mozilla, className: 'h1', command: 'heading', arguments: ['h1'], tags: ['h1'] },
            h2mozilla: { visible: true && $.browser.mozilla, className: 'h2', command: 'heading', arguments: ['h2'], tags: ['h2'] },
            h3mozilla: { visible: true && $.browser.mozilla, className: 'h3', command: 'heading', arguments: ['h3'], tags: ['h3'] },

            h1: { visible: true && !($.browser.mozilla), className: 'h1', command: 'formatBlock', arguments: ['Heading 1'], tags: ['h1'] },
            h2: { visible: true && !($.browser.mozilla), className: 'h2', command: 'formatBlock', arguments: ['Heading 2'], tags: ['h2'] },
            h3: { visible: true && !($.browser.mozilla), className: 'h3', command: 'formatBlock', arguments: ['Heading 3'], tags: ['h3'] },

            separator07: { visible: false, separator: true },

            cut: { visible: false },
            copy: { visible: false },
            paste: { visible: false },

            separator08: { separator: true && !($.browser.msie) },

            increaseFontSize: { visible: true && !($.browser.msie), tags: ['big'] },
            decreaseFontSize: { visible: true && !($.browser.msie), tags: ['small'] },

            separator09: { separator: true },

            html: {
                visible: false,
                exec: function()
                {
                    if (this.viewHTML)
                    {
                        this.setContent($(this.original).val());
                        $(this.original).hide();
                    }
                    else
                    {
                        this.saveContent();
                        $(this.original).show();
                    }

                    this.viewHTML = !(this.viewHTML);
                }
            },

            removeFormat: {
                visible: true,
                exec: function()
                {
                    this.editorDoc.execCommand('removeFormat', false, []);
                    this.editorDoc.execCommand('unlink', false, []);
                }
            }
        }
    });

    $.extend(Wysiwyg.prototype,
    {
        original: null,
        options: {},

        element: null,
        editor: null,

        init: function(element, options)
        {
            var self = this;

            this.editor = element;
            this.options = options || {};

            $.data(element, 'wysiwyg', this);

            var newX; // = element.width || element.clientWidth;
            var newY; // = element.height || element.clientHeight;

            //if (newX == 0)
                newX = element.offsetWidth;
            //if (newY == 0)
                newY = element.offsetHeight;

            if (element.nodeName.toLowerCase() == 'textarea')
            {
                this.original = element;

                if (newX == 0 && element.cols)
                    newX = (element.cols * 8) + 21;

                if (newY == 0 && element.rows)
                    newY = (element.rows * 16) + 16;

                var editor = this.editor = $('<iframe marginheight=\"0\" marginwidth=\"0\" frameborder=\"0\"></iframe>').css({
                    minHeight: (newY - 6).toString() + 'px',
                    width: '100%'
                }).attr('id', $(element).attr('id') + 'IFrame');

                if ($.browser.msie)
                {
                    this.editor
                        .css('height', (newY).toString() + 'px');

                    /**
                    var editor = $('<span></span>').css({
                    width     : ( newX - 6 ).toString() + 'px',
                    height    : ( newY - 8 ).toString() + 'px'
                    }).attr('id', $(element).attr('id') + 'IFrame');

                    editor.outerHTML = this.editor.outerHTML;
                    */
                }
            }

            var panel = this.panel = $('<ul></ul>').addClass('panel');

            this.appendControls();
            this.element = $('<div></div>').css({
                width: (newX > 0) ? (newX).toString() + 'px' : '100%'
            }).addClass('wysiwyg')
              .append(panel)
              .append($('<div><!-- --></div>').css({ clear: 'both' }))
              .append(editor);

            $(element)
            // .css('display', 'none')
            .hide()
            .before(this.element);

            this.viewHTML = false;

            this.initialHeight = newY - 8;

            /**
            * @link http://code.google.com/p/jwysiwyg/issues/detail?id=52
            */
            this.initialContent = $(element).val();

            this.initFrame();

            if (this.initialContent.length == 0)
                this.setContent('');

            if (this.options.autoSave)
                $('form').submit(function() { self.saveContent(); });

            $('form').bind('reset', function()
            {
                self.setContent(self.initialContent);
                self.saveContent();
            });
        },

        initFrame: function()
        {
            var self = this;
            var style = '';

            /**
            * @link http://code.google.com/p/jwysiwyg/issues/detail?id=14
            */
            if (this.options.css && this.options.css.constructor == String)
                style = '<link rel="stylesheet" type="text/css" media="screen" href="' + this.options.css + '" />';

            this.editorDoc = $(this.editor).document();
            this.editorDoc_designMode = false;

            try
            {
                this.editorDoc.designMode = 'on';
                this.editorDoc_designMode = true;
            } catch (e)
            {
                // Will fail on Gecko if the editor is placed in an hidden container element
                // The design mode will be set ones the editor is focused

                $(this.editorDoc).focus(function()
                {
                    self.designMode();
                });
            }

            this.editorDoc.open();
            this.editorDoc.write(
                this.options.html
                    .replace(/INITIAL_CONTENT/, this.initialContent)
                    .replace(/STYLE_SHEET/, style)
            );
            this.editorDoc.close();
            this.editorDoc.contentEditable = 'true';

            if ($.browser.msie)
            {
                /**
                * Remove the horrible border it has on IE.
                */
                setTimeout(function() { $(self.editorDoc.body).css('border', 'none'); }, 0);
            }

            $(this.editorDoc).click(function(event)
            {
                self.checkTargets(event.target ? event.target : event.srcElement);
            });

            /**
            * @link http://code.google.com/p/jwysiwyg/issues/detail?id=20
            */
            $(this.original).focus(function()
            {
                $(self.editorDoc.body).focus();
            });

            if (this.options.autoSave)
            {
                /**
                * @link http://code.google.com/p/jwysiwyg/issues/detail?id=11
                */
                $(this.editorDoc).keydown(function() { self.saveContent(); })
                                 .keyup(function() { self.saveContent(); })
                                 .mousedown(function() { self.saveContent(); });
            }

            if (this.options.css)
            {
                setTimeout(function()
                {
                    if (self.options.css.constructor == String)
                    {
                        /**
                        * $(self.editorDoc)
                        * .find('head')
                        * .append(
                        *     $('<link rel="stylesheet" type="text/css" media="screen" />')
                        *     .attr('href', self.options.css)
                        * );
                        */
                    }
                    else
                        $(self.editorDoc).find('body').css(self.options.css);
                }, 0);
            }

            $(this.editorDoc).keydown(function(event)
            {
                if ($.browser.msie && self.options.brIE && event.keyCode == 13)
                {
                    var rng = self.getRange();
                    rng.pasteHTML('<br />');
                    rng.collapse(false);
                    rng.select();

                    return false;
                }
            });
        },

        designMode: function()
        {
            if (!(this.editorDoc_designMode))
            {
                try
                {
                    this.editorDoc.designMode = 'on';
                    this.editorDoc_designMode = true;
                } catch (e) { }
            }
        },

        getSelection: function()
        {
            return (window.getSelection) ? window.getSelection() : document.selection;
        },

        getRange: function()
        {
            var selection = this.getSelection();

            if (!(selection))
                return null;

            return (selection.rangeCount > 0) ? selection.getRangeAt(0) : selection.createRange();
        },

        getContent: function()
        {
            return $($(this.editor).document()).find('body').html();
        },

        setContent: function(newContent)
        {
            $($(this.editor).document()).find('body').html(newContent);
        },

        saveContent: function()
        {
            if (this.original)
            {
                var content = this.getContent();

                if (this.options.rmUnwantedBr)
                    content = (content.substr(-4) == '<br>') ? content.substr(0, content.length - 4) : content;

                $(this.original).val(content);
            }
        },

        appendMenu: function(cmd, args, className, fn)
        {
            var self = this;
            var args = args || [];

            $('<li></li>').append(
                $('<a><!-- --></a>').addClass(className || cmd)
            ).mousedown(function()
            {
                if (fn) fn.apply(self); else self.editorDoc.execCommand(cmd, false, args);
                if (self.options.autoSave) self.saveContent();
            }).appendTo(this.panel);
        },

        appendMenuSeparator: function()
        {
            $('<li class="separator"></li>').appendTo(this.panel);
        },

        appendControls: function()
        {
            for (var name in this.options.controls)
            {
                var control = this.options.controls[name];

                if (control.separator)
                {
                    if (control.visible !== false)
                        this.appendMenuSeparator();
                }
                else if (control.visible)
                {
                    this.appendMenu(
                        control.command || name, control.arguments || [],
                        control.className || control.command || name || 'empty', control.exec
                    );
                }
            }
        },

        checkTargets: function(element)
        {
            for (var name in this.options.controls)
            {
                var control = this.options.controls[name];
                var className = control.className || control.command || name || 'empty';

                $('.' + className, this.panel).removeClass('active');

                if (control.tags)
                {
                    var elm = element;

                    do
                    {
                        if (elm.nodeType != 1)
                            break;

                        if ($.inArray(elm.tagName.toLowerCase(), control.tags) != -1)
                            $('.' + className, this.panel).addClass('active');
                    } while (elm = elm.parentNode);
                }

                if (control.css)
                {
                    var elm = $(element);

                    do
                    {
                        if (elm[0].nodeType != 1)
                            break;

                        for (var cssProperty in control.css)
                            if (elm.css(cssProperty).toString().toLowerCase() == control.css[cssProperty])
                            $('.' + className, this.panel).addClass('active');
                    } while (elm = elm.parent());
                }
            }
        },

        getElementByAttributeValue: function(tagName, attributeName, attributeValue)
        {
            var elements = this.editorDoc.getElementsByTagName(tagName);

            for (var i = 0; i < elements.length; i++)
            {
                var value = elements[i].getAttribute(attributeName);

                if ($.browser.msie)
                {
                    /** IE add full path, so I check by the last chars. */
                    value = value.substr(value.length - attributeValue.length);
                }

                if (value == attributeValue)
                    return elements[i];
            }

            return false;
        }
    });
})(jQuery);