/*! Copyright (c) 2008 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Version 0.2-pre
 */
(function ($) {

    /**
    * Creates an instance of a SpellChecker for each matched element.
    * The SpellChecker has several configurable options.
    *  - lang: the 2 letter language code, defaults to en for english
    *  - events: a space separated string of events to use, default is 'keypress blur paste'
    *  - autocheck: number of milliseconds to check spelling after a key event, default is 750.
    *  - url: url of the spellcheck service on your server, default is spellcheck.php
    *  - ignorecaps: 1 to ignore words with all caps, 0 to check them
    *  - ignoredigits: 1 to ignore digits, 0 to check them
    */
    $.fn.spellcheck = function (options) {
        return this.each(function () {
            var $this = $(this);
            if (!$this.is('[type=password]') && !$(this).data('spellchecker'))
                $(this).data('spellchecker', new $.SpellChecker(this, options));
        });
    };

    /**
    * Forces a spell check on an element that has an instance of SpellChecker.
    */
    $.fn.checkspelling = function () {
        return this.each(function () {
            var spellchecker = $(this).data('spellchecker');
            spellchecker && spellchecker.checkSpelling();
        });
    };


    $.SpellChecker = function (element, options) {
        this.$element = $(element);
        this.options = $.extend({
            lang: 'en',
            autocheck: 100,
            events: 'keypress blur paste click',
            url: '/jquery/plugins/spellcheck/spellcheck.aspx',
            ignorecaps: 1,
            ignoredigits: 1
        }, options);
        this.bindEvents();
    };

    $.SpellChecker.prototype = {
        bindEvents: function () {
            if (!this.options.events) return;
            var self = this, timeout;
            this.$element.bind(this.options.events, function (event) {
                if (/^key[press|up|down]/.test(event.type)) {
                    if (timeout) clearTimeout(timeout);
                    timeout = setTimeout(function () { self.checkSpelling(); }, self.options.autocheck);
                } else
                    self.checkSpelling();
            });
        },

        checkSpelling: function () {
            var prevText = this.text, text = this.$element.val(), self = this;

            var $spellcheckresults = $('#spellcheckresults')
            if ($spellcheckresults.length > 0 && $spellcheckresults.css("display") == "none")
                $spellcheckresults.css("display", "block");
            else if (prevText === text) return;

            this.text = this.$element.val();
            $.get(this.options.url, $.extend({ text: this.text }, this.options), function (r) { self.parseResults(r); });
        },

        parseResults: function (results) {


            if (jQuery.browser.msie) {
                var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
                xmlDoc.loadXML(results);
                results = xmlDoc;
            }


            var self = this;
            this.results = [];
            $(results).find('c').each(function () {
                var $this = $(this),
				offset = $this.attr('o'),
				length = $this.attr('l');
                self.results.push({
                    word: self.text.substr(offset, length),
                    suggestions: $this.text().split(/\s/)
                });
            });
            this.displayResults();
        },

        displayResults: function () {
            $('#spellcheckresults').remove();
            if (!this.results.length) return;
            var $container = $('<div id="spellcheckresults"></div>').appendTo('body'),
			dl = [], self = this, offset = this.$element.offset(), height = this.$element[0].offsetHeight, i, k;
            for (i = 0; i < this.results.length; i++) {
                var result = this.results[i], suggestions = result.suggestions;
                dl.push('<dl><dt>' + result.word + '</dt>');
                for (k = 0; k < suggestions.length; k++)
                    dl.push('<dd>' + suggestions[k] + '</dd>');
                dl.push('<dd class="ignore">ignore</dd></dl>');
            }
            // add close button
            dl.push('<a id="closeSpellChecker" href="#">x</a>');

            $container.append(dl.join('')).find('dd').bind('click', function (event) {
                var $this = $(this), $parent = $this.parent();
                if (!$this.is('.ignore'))
                    self.$element.val(self.$element.val().replace($parent.find('dt').text(), $this.text()));
                $parent.remove();
                if ($('#spellcheckresults').is(':empty'))
                    $('#spellcheckresults').remove();
                this.blur();
            }).end().css({ top: offset.top + height, left: offset.left });

            // close spell checker div
            $("#closeSpellChecker").click(function () {
                $('#spellcheckresults').css("display", "none");
                return false;
            });
        }

    };

})(jQuery);
