/*
 * jQuery Taconite plugin - A port of the Taconite framework by Ryan Asleson and
 *     Nathaniel T. Schutta: http://taconite.sourceforge.net/
 *
 * Examples and documentation at: http://malsup.com/jquery/taconite/
 * Copyright (c) 2007 M. Alsup
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * Thanks to Kenton Simpson for contributing some good ideas!
 *
 * @author: M. Alsup
 * @version: 2.0.8 (2/23/2007)
 * @requires jQuery v1.0.4 or later
 */

(function($) {

// add 'replace' and 'replaceContent' plugins (conditionally)
if (typeof $.fn.replace == 'undefined')
    $.fn.replace = function(a) { return this.after(a).remove(); };
if (typeof $.fn.replaceContent == 'undefined')
    $.fn.replaceContent = function(a) { return this.empty().append(a); };

/**
 *  Processes a Taconite XML command document.
 *  @name taconite
 *  @param Document|String command document
 */
$.taconite = $.xmlExec = function(xml) { $.taconite.impl.process(xml); }
$.taconite.version = [2,0,8]; // major,minor,point revision nums
$.taconite.debug = 0;    // set to true to enable debug logging to Firebug
$.taconite.lastTime = 0; // processing time for most recent document
$.taconite._httpData = $.httpData; // original jQuery httpData function

// auto-detection method (replaces jQuery's httpData method when auto-detection is enabled)
$.httpData = $.taconite.detect = function(xhr, type) {
    var ct = xhr.getResponseHeader('content-type');
    $.taconite.log('[AJAX response] content-type: ', ct, ';  status: ', xhr.status, ' ', xhr.statusText, ';  has responseXML: ', xhr.responseXML != null);
    var data = $.taconite._httpData(xhr, type); // call original method
    if (data && data.documentElement) {
        var root = data.documentElement.tagName;
        $.taconite.log('XML document root: ', root);
        if (root == 'taconite') {
            $.taconite.log('taconite command document detected');
            $.taconite(data);
        }
    }
    else $.taconite.log('httpData: response is not XML (or not "valid" XML)');
    return data;
};

// allow auto-detection to be enabled/disabled on-demand
$.taconite.enableAutoDetection = function(b) {
    // reset jQuery's httpData method
    $.httpData = b ? $.taconite.detect : $.taconite._httpData;
};

$.taconite.log = function() {
    if (!$.taconite.debug || !window.console || !window.console.log) return;
    if (!$.taconite.log.count++)
        $.taconite.log('Plugin Version: ' + $.taconite.version.join('.'));
    window.console.log('[taconite] ' + [].join.call(arguments,''));
};
$.taconite.log.count = 0;

// these are for extending Taconite with custom commands
$.taconite.addElementCommand   = function(s) { $.taconite.impl.elHash[s] = 1 };
$.taconite.addValueCommand     = function(s) { $.taconite.impl.vHash[s]  = 1 };
$.taconite.addNameValueCommand = function(s) { $.taconite.impl.nvHash[s] = 1 };

$.taconite.impl = {
    elHash: {after:1,append:1,before:1,insertAfter:1,insertBefore:1,prepend:1,wrap:1,replace:1,replaceContent:1},
    vHash:  {addClass:1,removeClass:1,toggleClass:1,val:1,removeAttr:1,text:1,height:1,width:1,fadeIn:1,fadeOut:1,slideDown:1,slideUp:1,slideToggle:1,hide:1,show:1},
    nvHash: {attr:1,css:1},
    clHash: {wrap:1},
    // convert string to xml document
    convert: function(s) {
        var doc;
        $.taconite.log('attempting string to document conversion');
        try {
            if (window.ActiveXObject) {
                doc = new ActiveXObject('Microsoft.XMLDOM');
                doc.async = 'false';
                doc.loadXML(s);
            }
            else {
                var parser = new DOMParser();
                doc = parser.parseFromString(s, 'text/xml');
            }
        }
        catch(e) {
            if (window.console && window.console.error)
                window.console.error('[taconite] ERROR parsing XML string for conversion: ' + e);
            throw e;
        }
        var ok = doc && doc.documentElement && doc.documentElement.tagName != 'parsererror';
        $.taconite.log('conversion ', ok ? 'successful!' : 'FAILED');
        return doc;
    },
    process: function(xml) {
        if (typeof xml == 'string')
            xml = this.convert(xml);
        if (!xml || !xml.documentElement) {
            $.taconite.log('$.taconite invoked without valid document; nothing to process');
            return;
        }
        try {
            var t = new Date().getTime();
            // process the document
            $.taconite.impl.process1(xml.documentElement.childNodes);
            $.taconite.lastTime = (new Date().getTime()) - t;
            $.taconite.log('time to process response: ' + $.taconite.lastTime + 'ms');
        } catch(e) {
            if (window.console && window.console.error)
                window.console.error('[taconite] ERROR processing document: ' + e);
            throw e;
        }
    },
    process1: function(commands) {
        for(var i=0; i < commands.length; i++) {
            if (commands[i].nodeType != 1)
                continue; // commands are elements (discard everything else)

            var cmdNode = commands[i];
            var cmd = cmdNode.tagName;

            if (cmd == 'eval') {
                var js = (cmdNode.firstChild ? cmdNode.firstChild.nodeValue : null);
                $.taconite.log('invoking "eval" command: ', js);
                if (js) $.globalEval(js);
                continue;
            }

            var q = cmdNode.getAttribute('select');
            var jq = $(q);
            if (!jq[0]) {
                $.taconite.log('No matching targets for selector: ', q);
                continue;
            }

            var v = cmdNode.getAttribute('value');

            // element command
            if (this.elHash[cmd]) {
                for (var j=0, els=[]; j < cmdNode.childNodes.length; j++)
                    els[j] = this.createNode(cmdNode.childNodes[j]);
                if (cmd == 'replace-content') cmd = 'replaceContent'; // xmlExec compatibility
                $.taconite.log('invoking element command: $("', q, '").', cmd, '(elements)');
                jq[cmd](this.clHash[cmd] ? this.cleanse(els) : els);
            }
            // value command
            else if (typeof v != 'undefined' && this.vHash[cmd]) {
                $.taconite.log('invoking value command: $("', q, '").', cmd, '("', v, '")');
                jq[cmd](v);
            }
            // name/value command
            else if (this.nvHash[cmd]) {
                var n = cmdNode.getAttribute('name');
                $.taconite.log('invoking name/value command: $("', q, '").', cmd, '("', n, '", "', v, '")');
                jq[cmd](n,v);
            }
            // no-arg command
            else if (typeof jq[cmd] == 'function') {
                $.taconite.log('invoking no-arg command: $("', q, '").', cmd,'()');
                jq[cmd]();
            }
        }
    },
    cleanse: function(els) {
        for (var i=0, a=[]; i < els.length; i++)
            if (els[i].nodeType == 1) a.push(els[i]);
        return a;
    },
    createNode: function (node) {
        var type = node.nodeType;
        if (type == 1) return this.createElement(node);
        if (type == 3) return this.fixTextNode(node.nodeValue);
        if (type == 4) return document.createTextNode(node.nodeValue);
        return null;
    },
    fixTextNode: function(s) {
        if ($.browser.msie) s = s.replace(/\n/g, '\r');
        return document.createTextNode(s);
    },
    createElement: function (node) {
        var e, tag = node.tagName.toLowerCase();
        if ($.browser.msie && (tag == 'input' || tag == 'button')) {
            var type = node.getAttribute('type');
            if (type == 'radio' || type == 'checkbox')
                return document.createElement('<input ' + this.copyAttrs(null, node, true) + '>');
            else if (tag == 'button')
                e = document.createElement('<button ' + this.copyAttrs(null, node, true) + '>');
        }
        if (!e) {
            e = document.createElement(tag);
            this.copyAttrs(e, node);
        }

        // IE fix; script tag not allowed to have children
        if($.browser.msie && !e.canHaveChildren) {
            if(node.childNodes.length > 0)
                e.text = node.text;
        }
        else {
            for(var i=0, max=node.childNodes.length; i < max; i++) {
                var child = this.createNode (node.childNodes[i]);
                if(child) e.appendChild(child);
            }
        }
        return e;
    },
    copyAttrs: function (dest, src, inline) {
        for (var i=0, attr=''; i < src.attributes.length; i++) {
            var a = src.attributes[i], n = $.trim(a.name), v = $.trim(a.value);
            if (inline) attr += (n + '="' + v + '" ');
            else if (n == 'style') { // IE workaround
                dest.style.cssText = v;
                dest.setAttribute(n, v);
            }
            else $.attr(dest, n, v);
        }
        return attr;
    }
};

})(jQuery);

