/**
* Ajax Manager @version 2.3
*/
(function($) {
    $.manageAjax = (function() {
        var cache = {},
			queues = {},
			presets = {},
			activeRequest = {},
			allRequests = {},
			defaults = {
			    queue: true, //clear
			    maxRequests: 1,
			    abortOld: false,
			    preventDoubbleRequests: true,
			    cacheResponse: false,
			    complete: function() { },
			    error: function(ahr, status) {
			        var opts = this;
			        if (status && status.indexOf('error') != -1) {
			            setTimeout(function() {
			                var errStr = status + ': ';
			                if (ahr.status) {
			                    errStr += 'status: ' + ahr.status + ' | ';
			                }
			                errStr += 'URL: ' + opts.url;
			                throw new Error(errStr);
			            }, 1);
			        }
			    },
			    success: function() { },
			    abort: function() { }
			}
		;
        function create(name, settings) {
            var publicMethods = {};
            presets[name] = presets[name] ||
				{};

            $.extend(true, presets[name], $.ajaxSettings, defaults, settings);
            if (!allRequests[name]) {
                allRequests[name] = {};
                activeRequest[name] = {};
                activeRequest[name].queue = [];
                queues[name] = [];
            }
            $.each($.manageAjax, function(fnName, fn) {
                if ($.isFunction(fn) && fnName.indexOf('_') !== 0) {
                    publicMethods[fnName] = function(param) {
                        fn(name, param);
                    };
                }
            });
            return publicMethods;
        }
        function complete(opts, args) {
            if (args[1] == 'success' || args[1] == 'notmodified') {
                opts.success.apply(opts, [args[0].successData, args[1]]);
                if (opts.global) {
                    $.event.trigger("ajaxSuccess", args);
                }
            }
            if (args[1] === 'abort') {
                opts.abort.apply(opts, args);
                if (opts.global) {
                    $.active--;
                    $.event.trigger("ajaxAbort", args);
                }
            }
            opts.complete.apply(opts, args);
            if (opts.global) {
                $.event.trigger("ajaxComplete", args);
            }
            if (opts.global && !$.active) {
                $.event.trigger("ajaxStop");
            }
            //args[0] = null; 
        }
        function proxy(oldFn, fn) {
            return function(xhr, s, e) {
                fn.call(this, xhr, s, e);
                oldFn.call(this, xhr, s, e);
                xhr = null;
                e = null;
            };
        }
        function callQueueFn(name) {
            var q = queues[name];
            if (q && q.length) {
                var fn = q.shift();
                if (fn) {
                    fn();
                }
            }
        }
        function add(name, opts) {
            if (!presets[name]) {
                create(name, opts);
            }
            opts = $.extend({}, presets[name], opts);
            //aliases
            var allR = allRequests[name],
				activeR = activeRequest[name],
				queue = queues[name];

            var id = opts.type + '_' + opts.url.replace(/\./g, '_'),
				oldComplete = opts.complete,
				ajaxFn = function() {
				    activeR[id] = {
				        xhr: $.ajax(opts),
				        ajaxManagerOpts: opts
				    };
				    activeR.queue.push(id);
				    return id;
				}
				;
            if (opts.data) {
                id += (typeof opts.data == 'string') ? opts.data : $.param(opts.data);
            }
            if (opts.preventDoubbleRequests && allRequests[name][id]) {
                return false;
            }
            allR[id] = true;
            opts.complete = function(xhr, s, e) {
                if (opts.abortOld) {
                    $.each(activeR.queue, function(i, activeID) {
                        if (activeID == id) {
                            return false;
                        }
                        abort(name, activeID);
                        return activeID;
                    });
                }
                oldComplete.call(this, xhr, s, e);
                //stop memory leak
                if (activeRequest[name][id]) {
                    if (activeRequest[name][id] && activeRequest[name][id].xhr) {
                        activeRequest[name][id].xhr = null;
                    }
                    activeRequest[name][id] = null;
                }
                xhr = null;
                activeRequest[name].queue = $.grep(activeRequest[name].queue, function(qid) {
                    return (qid !== id);
                });
                allR[id] = false;
                e = null;
                delete activeRequest[name][id];
            };
            if (cache[id]) {
                ajaxFn = function() {
                    activeR.queue.push(id);
                    complete(opts, cache[id]);
                    return id;
                };
            } else if (opts.cacheResponse) {
                opts.complete = proxy(opts.complete, function(xhr, s) {
                    if (s !== "success" && s !== "notmodified") {
                        return false;
                    }
                    cache[id][0].responseXML = xhr.responseXML;
                    cache[id][0].responseText = xhr.responseText;
                    cache[id][1] = s;
                    //stop memory leak
                    xhr = null;
                    return id; //strict
                });
                opts.success = proxy(opts.success, function(data, s) {
                    cache[id] = [{
                        successData: data,
                        ajaxManagerOpts: opts
                    }, s];
                    data = null;
                });
            }
            ajaxFn.ajaxID = id;
            if (opts.queue) {
                opts.complete = proxy(opts.complete, function() {

                    callQueueFn(name);
                });
                if (opts.queue === 'clear') {
                    queue = clear(name);
                }
                queue.push(ajaxFn);

                if (activeR.queue.length < opts.maxRequests) {
                    callQueueFn(name);
                }
                return id;
            }
            return ajaxFn();
        }
        function clear(name, shouldAbort) {
            $.each(queues[name], function(i, fn) {
                allRequests[name][fn.ajaxID] = false;
            });
            queues[name] = [];

            if (shouldAbort) {
                abort(name);
            }
            return queues[name];
        }
        function getXHR(name, id) {
            var ar = activeRequest[name];
            if (!ar || !allRequests[name][id]) {
                return false;
            }
            if (ar[id]) {
                return ar[id].xhr;
            }
            var queue = queues[name],
				xhrFn;
            $.each(queue, function(i, fn) {
                if (fn.ajaxID == id) {
                    xhrFn = [fn, i];
                    return false;
                }
                return xhrFn;
            });
            return xhrFn;
        }
        function abort(name, id) {
            var ar = activeRequest[name];
            if (!ar) {
                return false;
            }
            function abortID(qid) {
                if (qid !== 'queue' && ar[qid] && typeof ar[qid].xhr !== 'unedfiend' && typeof ar[qid].xhr.abort !== 'unedfiend') {
                    ar[qid].xhr.abort();
                    complete(ar[qid].ajaxManagerOpts, [ar[qid].xhr, 'abort']);
                }
                return null;
            }
            if (id) {
                return abortID(id);
            }
            return $.each(ar, abortID);
        }
        function unload() {
            $.each(presets, function(name) {
                clear(name, true);
            });
            cache = {};
        }
        return {
            defaults: defaults,
            add: add,
            create: create,
            cache: cache,
            abort: abort,
            clear: clear,
            getXHR: getXHR,
            _activeRequest: activeRequest,
            _complete: complete,
            _allRequests: allRequests,
            _unload: unload
        };
    })();
    //stop memory leaks
    $(window).unload($.manageAjax._unload);
})(jQuery);
/*
* jQuery corner plugin 1.2
*/
(function($) {
    $.fn.corner = function(o) {
        var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent);
        function sz(el, p) { return parseInt($.css(el, p)) || 0; };
        function hex2(s) {
            var s = parseInt(s).toString(16);
            return (s.length < 2) ? '0' + s : s;
        };
        function gpc(node) {
            for (; node && node.nodeName.toLowerCase() != 'html'; node = node.parentNode) {
                var v = $.css(node, 'backgroundColor');
                if (v.indexOf('rgb') >= 0) {
                    if ($.browser.safari && v == 'rgba(0, 0, 0, 0)')
                        continue;
                    var rgb = v.match(/\d+/g);
                    return '#' + hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]);
                }
                if (v && v != 'transparent')
                    return v;
            }
            return '#ffffff';
        };
        function getW(i) {
            switch (fx) {
                case 'round': return Math.round(width * (1 - Math.cos(Math.asin(i / width))));
                case 'cool': return Math.round(width * (1 + Math.cos(Math.asin(i / width))));
                case 'sharp': return Math.round(width * (1 - Math.cos(Math.acos(i / width))));
                case 'bite': return Math.round(width * (Math.cos(Math.asin((width - i - 1) / width))));
                case 'slide': return Math.round(width * (Math.atan2(i, width / i)));
                case 'jut': return Math.round(width * (Math.atan2(width, (width - i - 1))));
                case 'curl': return Math.round(width * (Math.atan(i)));
                case 'tear': return Math.round(width * (Math.cos(i)));
                case 'wicked': return Math.round(width * (Math.tan(i)));
                case 'long': return Math.round(width * (Math.sqrt(i)));
                case 'sculpt': return Math.round(width * (Math.log((width - i - 1), width)));
                case 'dog': return (i & 1) ? (i + 1) : width;
                case 'dog2': return (i & 2) ? (i + 1) : width;
                case 'dog3': return (i & 3) ? (i + 1) : width;
                case 'fray': return (i % 2) * width;
                case 'notch': return width;
                case 'bevel': return i + 1;
            }
        };
        o = (o || "").toLowerCase();
        var keep = /keep/.test(o);                       // keep borders?
        var cc = ((o.match(/cc:(#[0-9a-f]+)/) || [])[1]);  // corner color
        var sc = ((o.match(/sc:(#[0-9a-f]+)/) || [])[1]);  // strip color
        var width = parseInt((o.match(/(\d+)px/) || [])[1]) || 10; // corner width
        var re = /round|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dog/;
        var fx = ((o.match(re) || ['round'])[0]);
        var edges = { T: 0, B: 1 };
        var opts = {
            TL: /top|tl/.test(o), TR: /top|tr/.test(o),
            BL: /bottom|bl/.test(o), BR: /bottom|br/.test(o)
        };
        if (!opts.TL && !opts.TR && !opts.BL && !opts.BR)
            opts = { TL: 1, TR: 1, BL: 1, BR: 1 };
        var strip = document.createElement('div');
        strip.style.overflow = 'hidden';
        strip.style.height = '1px';
        strip.style.backgroundColor = sc || 'transparent';
        strip.style.borderStyle = 'solid';
        return this.each(function(index) {
            var pad = {
                T: parseInt($.css(this, 'paddingTop')) || 0, R: parseInt($.css(this, 'paddingRight')) || 0,
                B: parseInt($.css(this, 'paddingBottom')) || 0, L: parseInt($.css(this, 'paddingLeft')) || 0
            };
            if ($.browser.msie) this.style.zoom = 1; // force 'hasLayout' in IE
            if (!keep) this.style.border = 'none';
            strip.style.borderColor = cc || gpc(this.parentNode);
            var cssHeight = $.curCSS(this, 'height');
            for (var j in edges) {
                var bot = edges[j];
                // only add stips if needed
                if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) {
                    strip.style.borderStyle = 'none ' + (opts[j + 'R'] ? 'solid' : 'none') + ' none ' + (opts[j + 'L'] ? 'solid' : 'none');
                    var d = document.createElement('div');
                    $(d).addClass('jquery-corner');
                    var ds = d.style;
                    bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild);
                    if (bot && cssHeight != 'auto') {
                        if ($.css(this, 'position') == 'static')
                            this.style.position = 'relative';
                        ds.position = 'absolute';
                        ds.bottom = ds.left = ds.padding = ds.margin = '0';
                        if ($.browser.msie)
                            ds.setExpression('width', 'this.parentNode.offsetWidth');
                        else
                            ds.width = '100%';
                    }
                    else if (!bot && $.browser.msie) {
                        if ($.css(this, 'position') == 'static')
                            this.style.position = 'relative';
                        ds.position = 'absolute';
                        ds.top = ds.left = ds.right = ds.padding = ds.margin = '0';
                        // fix ie6 problem when blocked element has a border width
                        var bw = 0;
                        if (ie6 || !$.boxModel)
                            bw = sz(this, 'borderLeftWidth') + sz(this, 'borderRightWidth');
                        ie6 ? ds.setExpression('width', 'this.parentNode.offsetWidth - ' + bw + '+ "px"') : ds.width = '100%';
                    }
                    else {
                        ds.margin = !bot ? '-' + pad.T + 'px -' + pad.R + 'px ' + (pad.T - width) + 'px -' + pad.L + 'px' :
                            (pad.B - width) + 'px -' + pad.R + 'px -' + pad.B + 'px -' + pad.L + 'px';
                    }
                    for (var i = 0; i < width; i++) {
                        var w = Math.max(0, getW(i));
                        var e = strip.cloneNode(false);
                        e.style.borderWidth = '0 ' + (opts[j + 'R'] ? w : 0) + 'px 0 ' + (opts[j + 'L'] ? w : 0) + 'px';
                        bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild);
                    }
                }
            }
        });
    };
    $.fn.uncorner = function(o) { return $('.jquery-corner', this).remove(); };
})(jQuery);
/*
* html pager
*/
(function($) {
    $.fn.pager = function(clas, options) {
        var settings = {
            navId: 'nav',
            navClass: 'nav',
            navAttach: 'append',
            highlightClass: 'highlight',
            prevText: '&laquo;',
            nextText: '&raquo;',
            linkText: null,
            linkWrap: null,
            height: null
        }
        if (options) $.extend(settings, options);
        return this.each(function() {
            var me = $(this);
            var size;
            var i = 0;
            var navid = '#' + settings.navId;
            function init() {
                size = $(clas, me).not(navid).size();
                if (settings.height == null) {
                    settings.height = getHighest();
                }
                if (size > 1) {
                    makeNav();
                    show();
                    highlight();
                }
                sizePanel();
                if (settings.linkWrap != null) {
                    linkWrap();
                }
            }
            function makeNav() {
                var str = '<div id="' + settings.navId + '" class="' + settings.navClass + '">';
                str += '<a href="#" rel="prev">' + settings.prevText + '</a>';
                for (var i = 0; i < size; i++) {
                    var j = i + 1;
                    str += '<a href="#" rel="' + j + '">';
                    str += (settings.linkText == null) ? j : settings.linkText[j - 1];
                    str += '</a>';
                }
                str += '<a href="#" rel="next">' + settings.nextText + '</a>';
                str += '</div>';
                switch (settings.navAttach) {
                    case 'before':
                        $(me).before(str);
                        break;
                    case 'after':
                        $(me).after(str);
                        break;
                    case 'prepend':
                        $(me).prepend(str);
                        break;
                    default:
                        $(me).append(str);
                        break;
                }
            }
            function show() {
                $(me).find(clas).not(navid).hide();
                var show = $(me).find(clas).not(navid).get(i);
                $(show).show();
            }
            function highlight() {
                $(me).find(navid).find('a').removeClass(settings.highlightClass);
                var show = $(me).find(navid).find('a').get(i + 1);
                $(show).addClass(settings.highlightClass);
            }
            function sizePanel() {
                if ($.browser.msie) {
                    $(me).find(clas).not(navid).css({
                        height: settings.height
                    });
                } else {
                    $(me).find(clas).not(navid).css({
                        minHeight: settings.height
                    });
                }
            }
            function getHighest() {
                var highest = 0;
                $(me).find(clas).not(navid).each(function() {

                    if (this.offsetHeight > highest) {
                        highest = this.offsetHeight;
                    }
                });
                highest = highest + "px";
                return highest;
            }
            function getNavHeight() {
                var nav = $(navid).get(0);
                return nav.offsetHeight;
            }
            function linkWrap() {
                $(me).find(navid).find("a").wrap(settings.linkWrap);
            }
            init();
            $(this).find(navid).find("a").click(function() {
                if ($(this).attr('rel') == 'next') {
                    if (i + 1 < size) {
                        i = i + 1;
                    }
                } else if ($(this).attr('rel') == 'prev') {
                    if (i > 0) {
                        i = i - 1;
                    }
                } else {
                    var j = $(this).attr('rel');
                    i = j - 1;
                }
                show();
                highlight();
                return false;
            });
        });
    };
})(jQuery);
/*
* bgiframe Version 2.1.1
*/
(function($) {
    $.fn.bgIframe = $.fn.bgiframe = function(s) {
        // This is only for IE6
        if ($.browser.msie && /6.0/.test(navigator.userAgent)) {
            s = $.extend({
                top: 'auto', // auto == .currentStyle.borderTopWidth
                left: 'auto', // auto == .currentStyle.borderLeftWidth
                width: 'auto', // auto == offsetWidth
                height: 'auto', // auto == offsetHeight
                opacity: true,
                src: 'javascript:false;'
            }, s || {});
            var prop = function(n) { return n && n.constructor == Number ? n + 'px' : n; },
            html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="' + s.src + '"' +
                'style="display:block;position:absolute;z-index:-1;' +
                (s.opacity !== false ? 'filter:Alpha(Opacity=\'0\');' : '') +
                'top:' + (s.top == 'auto' ? 'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')' : prop(s.top)) + ';' +
                'left:' + (s.left == 'auto' ? 'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')' : prop(s.left)) + ';' +
                'width:' + (s.width == 'auto' ? 'expression(this.parentNode.offsetWidth+\'px\')' : prop(s.width)) + ';' +
                'height:' + (s.height == 'auto' ? 'expression(this.parentNode.offsetHeight+\'px\')' : prop(s.height)) + ';' +
                '"/>';
            return this.each(function() {
                if ($('> iframe.bgiframe', this).length == 0)
                    this.insertBefore(document.createElement(html), this.firstChild);
            });
        }
        return this;
    };
})(jQuery);

/*
* jQuery Impromptu
*/
jQuery.extend({
    ImpromptuDefaults: { prefix: 'jqi',
        url: "",
        message: "Are you sure?",
        width: "300",
        height: "100",
        buttons: { Ok: true },
        loaded: function() { },
        submit: function() { return true; },
        callback: function() { },
        opacity: 0.6,
        zIndex: 999,
        overlayspeed: 'slow',
        promptspeed: 'fast',
        show: 'show',
        focus: 0,
        useiframe: false
    },

    SetImpromptuDefaults: function(o) {
        jQuery.ImpromptuDefaults = jQuery.extend({}, jQuery.ImpromptuDefaults, o);
    },
    prompt: function(m, o) {
        o = jQuery.extend({}, jQuery.ImpromptuDefaults, o);

        var ie6 = (jQuery.browser.msie && jQuery.browser.version < 7);
        var b = jQuery(document.body);
        var w = jQuery(window);

        var msgbox = '<div class="' + o.prefix + 'box" id="' + o.prefix + 'box" tabindex="0">';
        if (o.useiframe && ((jQuery.browser.msie && jQuery('object, applet').length > 0) || ie6))//if you want to use the iframe uncomment these 3 lines
            msgbox += '<iframe src="javascript:;" class="' + o.prefix + 'fade" id="' + o.prefix + 'fade"></iframe>';
        else {
            if (ie6) $('select').css('visibility', 'hidden');
            msgbox += '<div class="' + o.prefix + 'fade" id="' + o.prefix + 'fade"></div>';
        }
        msgbox += '<div class="' + o.prefix + '" id="' + o.prefix + '"><div id="jqititle"><p class="pTitleMessage"><span>' + m + '</span></p><div class="' + o.prefix + 'close" title="关闭"></div></div><div class="' + o.prefix + 'container"><div class="' + o.prefix + 'message">' + m + '</div><div class="' + o.prefix + 'buttons" id="' + o.prefix + 'buttons">';
        jQuery.each(o.buttons, function(k, v) { msgbox += '<a name="' + o.prefix + 'button' + k + '" id="' + o.prefix + 'button' + k + '" value="' + v + '">' + k + '</a>' });
        msgbox += '</div></div></div></div>';

        var jqib = b.append(msgbox).children('#' + o.prefix + 'box');
        var jqi = jqib.children('#' + o.prefix);
        var jqif = jqib.children('#' + o.prefix + 'fade');
        var jqic = jqi.children('.' + o.prefix + 'container');
        var jqiclose = jqic.children('#jqititle');

        var getWindowScrollOffset = function() {
            return (document.documentElement.scrollTop || document.body.scrollTop) + 'px';
        };

        var getWindowSize = function() {
            var size = {
                width: window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth),
                height: window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight)
            };
            return size;
        };

        var ie6scroll = function() {
            jqib.css({ top: getWindowScrollOffset() });
        };

        var flashPrompt = function() {
            var i = 0;
            jqib.addClass(o.prefix + 'warning');
            var intervalid = setInterval(function() {
                jqib.toggleClass(o.prefix + 'warning');
                if (i++ > 1) {
                    clearInterval(intervalid);
                    jqib.removeClass(o.prefix + 'warning');
                }
            }, 100);
        };

        var escapeKeyClosePrompt = function(e) {
            var kC = (window.event) ? event.keyCode : e.keyCode; // MSIE or Firefox?
            var Esc = (window.event) ? 27 : e.DOM_VK_ESCAPE; // MSIE : Firefox
            if (kC == Esc) removePrompt();
        };

        var positionPrompt = function() {
            var wsize = getWindowSize();
            jqib.css({ position: (ie6) ? "absolute" : "fixed", height: wsize.height, width: "100%", top: (ie6) ? getWindowScrollOffset() : 0, left: 0, right: 0, bottom: 0 });
            jqif.css({ position: "absolute", height: wsize.height, width: "100%", top: 0, left: 0, right: 0, bottom: 0 });
            jqi.css({ position: "absolute",
                width: o.width + "px",
                height: o.height + "px",
                left: (wsize.width - o.width) / 2 + "px",
                top: (wsize.height - o.height) / 2 + "px"
            });
            jqic.css({ height: o.height - 27 + "px" });
            jqic.children('.' + o.prefix + 'message').css({ height: jqic.height() - 42 + "px" });
        };

        var stylePrompt = function() {
            jqif.css({ zIndex: o.zIndex, display: "none", opacity: o.opacity });
            jqi.css({ zIndex: o.zIndex + 1, display: "none" });
        }

        var removePrompt = function(callCallback, clicked, msg) {
            jqi.remove();
            if (ie6) b.unbind('scroll', ie6scroll); //ie6, remove the scroll event
            w.unbind('resize', positionPrompt);
            jqif.fadeOut(o.overlayspeed, function() {
                jqif.unbind('click', flashPrompt);
                jqif.remove();
                if (callCallback) o.callback(clicked, msg);
                jqib.unbind('keypress', escapeKeyClosePrompt);
                jqib.remove();
                if (ie6 && !o.useiframe) $('select').css('visibility', 'visible');
            });
        }

        positionPrompt();
        stylePrompt();

        if (o.url != '') {
            var msg = jqi.children('.' + o.prefix + 'container').children('.' + o.prefix + 'message');
            msg.load(o.url);
        } else {
            var msg = jqi.children('.' + o.prefix + 'container').children('.' + o.prefix + 'message');
            msg.html(o.message);
        }

        //Events
        jQuery('#' + o.prefix + 'buttons').children('a').click(function() {
            var msg = jqi.children('.' + o.prefix + 'container').children('.' + o.prefix + 'message');
            var clicked = o.buttons[jQuery(this).text()];
            if (o.submit(clicked, msg))
                removePrompt(true, clicked, msg);
        });
        if (ie6) w.scroll(ie6scroll); //ie6, add a scroll event to fix position:fixed
        jqif.click(flashPrompt);
        w.resize(positionPrompt);
        jqib.keypress(escapeKeyClosePrompt);

        //close window button
        jqi.find('.' + o.prefix + 'close').click(removePrompt);

        //Show it
        jqif.fadeIn(o.overlayspeed);
        jqi[o.show](o.promptspeed, o.loaded);
        jqib.focus();
        jqib.keydown(function(event) {
            if (event.keyCode == 13) {
                jqi.find('#' + o.prefix + 'buttons a:eq(' + o.focus + ')').click();
            }
        });
        return jqib;
    }
});
/**
* jTemplates 0.7.8 (http://jtemplates.tpython.com)
*/
if (window.jQuery && !window.jQuery.createTemplate) {
    (function(jQuery) {
        var Template = function(s, includes, settings) {
            this._tree = [];
            this._param = {};
            this._includes = null;
            this._templates = {};
            this._templates_code = {};

            this.settings = jQuery.extend({
                disallow_functions: false,
                filter_data: true,
                filter_params: false,
                runnable_functions: false,
                clone_data: true,
                clone_params: true
            }, settings);

            this.f_cloneData = (this.settings.f_cloneData !== undefined) ? (this.settings.f_cloneData) : (TemplateUtils.cloneData);
            this.f_escapeString = (this.settings.f_escapeString !== undefined) ? (this.settings.f_escapeString) : (TemplateUtils.escapeHTML);

            this.splitTemplates(s, includes);

            if (s) {
                this.setTemplate(this._templates_code['MAIN'], includes, this.settings);
            }

            this._templates_code = null;
        };
        Template.prototype.version = '0.7.8';
        Template.DEBUG_MODE = false;
        Template.prototype.splitTemplates = function(s, includes) {
            var reg = /\{#template *(\w*?)( .*)*\}/g;
            var iter, tname, se;
            var lastIndex = null;

            var _template_settings = [];

            while ((iter = reg.exec(s)) != null) {
                lastIndex = reg.lastIndex;
                tname = iter[1];
                se = s.indexOf('{#/template ' + tname + '}', lastIndex);
                if (se == -1) {
                    throw new Error('jTemplates: Template "' + tname + '" is not closed.');
                }
                this._templates_code[tname] = s.substring(lastIndex, se);
                _template_settings[tname] = TemplateUtils.optionToObject(iter[2]);
            }
            if (lastIndex === null) {
                this._templates_code['MAIN'] = s;
                return;
            }

            for (var i in this._templates_code) {
                if (i != 'MAIN') {
                    this._templates[i] = new Template();
                }
            }
            for (var i in this._templates_code) {
                if (i != 'MAIN') {
                    this._templates[i].setTemplate(this._templates_code[i], jQuery.extend({}, includes || {}, this._templates || {}), jQuery.extend({}, this.settings, _template_settings[i]));
                    this._templates_code[i] = null;
                }
            }
        };
        Template.prototype.setTemplate = function(s, includes, settings) {
            if (s == undefined) {
                this._tree.push(new TextNode('', 1, this));
                return;
            }
            s = s.replace(/[\n\r]/g, '');
            s = s.replace(/\{\*.*?\*\}/g, '');
            this._includes = jQuery.extend({}, this._templates || {}, includes || {});
            this.settings = new Object(settings);
            var node = this._tree;
            var op = s.match(/\{#.*?\}/g);
            var ss = 0, se = 0;
            var e;
            var literalMode = 0;
            var elseif_level = 0;

            for (var i = 0, l = (op) ? (op.length) : (0); i < l; ++i) {
                var this_op = op[i];

                if (literalMode) {
                    se = s.indexOf('{#/literal}');
                    if (se == -1) {
                        throw new Error("jTemplates: No end of literal.");
                    }
                    if (se > ss) {
                        node.push(new TextNode(s.substring(ss, se), 1, this));
                    }
                    ss = se + 11;
                    literalMode = 0;
                    i = jQuery.inArray('{#/literal}', op);
                    continue;
                }
                se = s.indexOf(this_op, ss);
                if (se > ss) {
                    node.push(new TextNode(s.substring(ss, se), literalMode, this));
                }
                var ppp = this_op.match(/\{#([\w\/]+).*?\}/);
                var op_ = RegExp.$1;
                switch (op_) {
                    case 'elseif':
                        ++elseif_level;
                        node.switchToElse();
                        //no break
                    case 'if':
                        e = new opIF(this_op, node);
                        node.push(e);
                        node = e;
                        break;
                    case 'else':
                        node.switchToElse();
                        break;
                    case '/if':
                        while (elseif_level) {
                            node = node.getParent();
                            --elseif_level;
                        }
                        //no break
                    case '/for':
                    case '/foreach':
                        node = node.getParent();
                        break;
                    case 'foreach':
                        e = new opFOREACH(this_op, node, this);
                        node.push(e);
                        node = e;
                        break;
                    case 'for':
                        e = opFORFactory(this_op, node, this);
                        node.push(e);
                        node = e;
                        break;
                    case 'continue':
                    case 'break':
                        node.push(new JTException(op_));
                        break;
                    case 'include':
                        node.push(new Include(this_op, this._includes));
                        break;
                    case 'param':
                        node.push(new UserParam(this_op));
                        break;
                    case 'cycle':
                        node.push(new Cycle(this_op));
                        break;
                    case 'ldelim':
                        node.push(new TextNode('{', 1, this));
                        break;
                    case 'rdelim':
                        node.push(new TextNode('}', 1, this));
                        break;
                    case 'literal':
                        literalMode = 1;
                        break;
                    case '/literal':
                        if (Template.DEBUG_MODE) {
                            throw new Error("jTemplates: Missing begin of literal.");
                        }
                        break;
                    default:
                        if (Template.DEBUG_MODE) {
                            throw new Error('jTemplates: unknown tag: ' + op_ + '.');
                        }
                }

                ss = se + this_op.length;
            }

            if (s.length > ss) {
                node.push(new TextNode(s.substr(ss), literalMode, this));
            }
        };
        Template.prototype.get = function(d, param, element, deep) {
            ++deep;

            var $T = d, _param1, _param2;

            if (this.settings.clone_data) {
                $T = this.f_cloneData(d, { escapeData: (this.settings.filter_data && deep == 1), noFunc: this.settings.disallow_functions }, this.f_escapeString);
            }

            if (!this.settings.clone_params) {
                _param1 = this._param;
                _param2 = param;
            } else {
                _param1 = this.f_cloneData(this._param, { escapeData: (this.settings.filter_params), noFunc: false }, this.f_escapeString);
                _param2 = this.f_cloneData(param, { escapeData: (this.settings.filter_params && deep == 1), noFunc: false }, this.f_escapeString);
            }
            var $P = jQuery.extend({}, _param1, _param2);

            var $Q = (element != undefined) ? (element) : ({});
            $Q.version = this.version;

            var ret = '';
            for (var i = 0, l = this._tree.length; i < l; ++i) {
                ret += this._tree[i].get($T, $P, $Q, deep);
            }

            --deep;
            return ret;
        };
        Template.prototype.setParam = function(name, value) {
            this._param[name] = value;
        };
        TemplateUtils = function() {
        };
        TemplateUtils.escapeHTML = function(txt) {
            return txt.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
        };
        TemplateUtils.cloneData = function(d, filter, f_escapeString) {
            if (d == null) {
                return d;
            }
            switch (d.constructor) {
                case Object:
                    var o = {};
                    for (var i in d) {
                        o[i] = TemplateUtils.cloneData(d[i], filter, f_escapeString);
                    }
                    if (!filter.noFunc) {
                        if (d.hasOwnProperty("toString"))
                            o.toString = d.toString;
                    }
                    return o;
                case Array:
                    var o = [];
                    for (var i = 0, l = d.length; i < l; ++i) {
                        o[i] = TemplateUtils.cloneData(d[i], filter, f_escapeString);
                    }
                    return o;
                case String:
                    return (filter.escapeData) ? (f_escapeString(d)) : (d);
                case Function:
                    if (filter.noFunc) {
                        if (Template.DEBUG_MODE)
                            throw new Error("jTemplates: Functions are not allowed.");
                        else
                            return undefined;
                    }
                    //no break
                default:
                    return d;
            }
        };
        TemplateUtils.optionToObject = function(optionText) {
            if (optionText === null || optionText === undefined) {
                return {};
            }

            var o = optionText.split(/[= ]/);
            if (o[0] === '') {
                o.shift();
            }

            var obj = {};
            for (var i = 0, l = o.length; i < l; i += 2) {
                obj[o[i]] = o[i + 1];
            }

            return obj;
        };
        var TextNode = function(val, literalMode, template) {
            this._value = val;
            this._literalMode = literalMode;
            this._template = template;
        };
        TextNode.prototype.get = function(d, param, element, deep) {
            var __t = this._value;
            if (!this._literalMode) {
                var __template = this._template;
                var $T = d;
                var $P = param;
                var $Q = element;
                __t = __t.replace(/\{(.*?)\}/g, function(__0, __1) {
                    try {
                        var __tmp = eval(__1);
                        if (typeof __tmp == 'function') {
                            if (__template.settings.disallow_functions || !__template.settings.runnable_functions) {
                                return '';
                            } else {
                                __tmp = __tmp($T, $P, $Q);
                            }
                        }
                        return (__tmp === undefined) ? ("") : (String(__tmp));
                    } catch (e) {
                        if (Template.DEBUG_MODE) {
                            if (e instanceof JTException)
                                e.type = "subtemplate";
                            throw e;
                        }
                        return "";
                    }
                });
            }
            __t = __t.replace(new RegExp("null", "g"), "");
            return __t;
        };
        var opIF = function(oper, par) {
            this._parent = par;
            oper.match(/\{#(?:else)*if (.*?)\}/);
            this._cond = RegExp.$1;
            this._onTrue = [];
            this._onFalse = [];
            this._currentState = this._onTrue;
        };
        opIF.prototype.push = function(e) {
            this._currentState.push(e);
        };
        opIF.prototype.getParent = function() {
            return this._parent;
        };
        opIF.prototype.switchToElse = function() {
            this._currentState = this._onFalse;
        };
        opIF.prototype.get = function(d, param, element, deep) {
            var $T = d;
            var $P = param;
            var $Q = element;
            var ret = '';

            try {
                var tab = (eval(this._cond)) ? (this._onTrue) : (this._onFalse);
                for (var i = 0, l = tab.length; i < l; ++i) {
                    ret += tab[i].get(d, param, element, deep);
                }
            } catch (e) {
                if (Template.DEBUG_MODE || (e instanceof JTException))
                    throw e;
            }
            return ret;
        };
        opFORFactory = function(oper, par, template) {
            if (oper.match(/\{#for (\w+?) *= *(\S+?) +to +(\S+?) *(?:step=(\S+?))*\}/)) {
                oper = '{#foreach opFORFactory.funcIterator as ' + RegExp.$1 + ' begin=' + (RegExp.$2 || 0) + ' end=' + (RegExp.$3 || -1) + ' step=' + (RegExp.$4 || 1) + ' extData=$T}';
                return new opFOREACH(oper, par, template);
            } else {
                throw new Error('jTemplates: Operator failed "find": ' + oper);
            }
        };
        opFORFactory.funcIterator = function(i) {
            return i;
        };
        var opFOREACH = function(oper, par, template) {
            this._parent = par;
            this._template = template;
            oper.match(/\{#foreach (.+?) as (\w+?)( .+)*\}/);
            this._arg = RegExp.$1;
            this._name = RegExp.$2;
            this._option = RegExp.$3 || null;
            this._option = TemplateUtils.optionToObject(this._option);

            this._onTrue = [];
            this._onFalse = [];
            this._currentState = this._onTrue;
        };
        opFOREACH.prototype.push = function(e) {
            this._currentState.push(e);
        };
        opFOREACH.prototype.getParent = function() {
            return this._parent;
        };
        opFOREACH.prototype.switchToElse = function() {
            this._currentState = this._onFalse;
        };
        opFOREACH.prototype.get = function(d, param, element, deep) {
            try {
                var $T = d;
                var $P = param;
                var $Q = element;
                var fcount = eval(this._arg); //array of elements in foreach
                var key = []; //only for objects
                var mode = typeof fcount;
                if (mode == 'object') {
                    var arr = [];
                    jQuery.each(fcount, function(k, v) {
                        key.push(k);
                        arr.push(v);
                    });
                    fcount = arr;
                }
                var extData = (this._option.extData !== undefined) ? (eval(this._option.extData)) : (($T != null) ? ($T) : ({}));
                var s = Number(eval(this._option.begin) || 0), e; //start, end
                var step = Number(eval(this._option.step) || 1);
                if (mode != 'function') {
                    e = fcount.length;
                } else {
                    if (this._option.end === undefined || this._option.end === null) {
                        e = Number.MAX_VALUE;
                    } else {
                        e = Number(eval(this._option.end)) + ((step > 0) ? (1) : (-1));
                    }
                }
                var ret = ''; //returned string
                var i, l; //iterators

                if (this._option.count) {
                    var tmp = s + Number(eval(this._option.count));
                    e = (tmp > e) ? (e) : (tmp);
                }
                if ((e > s && step > 0) || (e < s && step < 0)) {
                    var iteration = 0;
                    var _total = (mode != 'function') ? (Math.ceil((e - s) / step)) : undefined;
                    var ckey, cval; //current key, current value
                    for (; ((step > 0) ? (s < e) : (s > e)); s += step, ++iteration) {
                        ckey = key[s];
                        if (mode != 'function') {
                            cval = fcount[s];
                        } else {
                            cval = fcount(s);
                            if (cval === undefined || cval === null) {
                                break;
                            }
                        }
                        if ((typeof cval == 'function') && (this._template.settings.disallow_functions || !this._template.settings.runnable_functions)) {
                            continue;
                        }
                        if ((mode == 'object') && (ckey in Object)) {
                            continue;
                        }
                        var prevValue = extData[this._name];
                        extData[this._name] = cval;
                        extData[this._name + '$index'] = s;
                        extData[this._name + '$iteration'] = iteration;
                        extData[this._name + '$first'] = (iteration == 0);
                        extData[this._name + '$last'] = (s + step >= e);
                        extData[this._name + '$total'] = _total;
                        extData[this._name + '$key'] = (ckey !== undefined && ckey.constructor == String) ? (this._template.f_escapeString(ckey)) : (ckey);
                        extData[this._name + '$typeof'] = typeof cval;
                        for (i = 0, l = this._onTrue.length; i < l; ++i) {
                            try {
                                ret += this._onTrue[i].get(extData, param, element, deep);
                            } catch (ex) {
                                if (ex instanceof JTException) {
                                    switch (ex.type) {
                                        case 'continue':
                                            i = l;
                                            break;
                                        case 'break':
                                            i = l;
                                            s = e;
                                            break;
                                        default:
                                            throw e;
                                    }
                                } else {
                                    throw e;
                                }
                            }
                        }
                        delete extData[this._name + '$index'];
                        delete extData[this._name + '$iteration'];
                        delete extData[this._name + '$first'];
                        delete extData[this._name + '$last'];
                        delete extData[this._name + '$total'];
                        delete extData[this._name + '$key'];
                        delete extData[this._name + '$typeof'];
                        delete extData[this._name];
                        extData[this._name] = prevValue;
                    }
                } else {
                    for (i = 0, l = this._onFalse.length; i < l; ++i) {
                        ret += this._onFalse[i].get($T, param, element, deep);
                    }
                }
                return ret;
            } catch (e) {
                if (Template.DEBUG_MODE || (e instanceof JTException))
                    throw e;
                return "";
            }
        };
        var JTException = function(type) {
            this.type = type;
        };
        JTException.prototype = Error;
        JTException.prototype.get = function(d) {
            throw this;
        };
        var Include = function(oper, includes) {
            oper.match(/\{#include (.*?)(?: root=(.*?))?\}/);
            this._template = includes[RegExp.$1];
            if (this._template == undefined) {
                if (Template.DEBUG_MODE)
                    throw new Error('jTemplates: Cannot find include: ' + RegExp.$1);
            }
            this._root = RegExp.$2;
        };
        Include.prototype.get = function(d, param, element, deep) {
            var $T = d;
            var $P = param;
            try {
                return this._template.get(eval(this._root), param, element, deep);
            } catch (e) {
                if (Template.DEBUG_MODE || (e instanceof JTException))
                    throw e;
            }
            return '';
        };
        var UserParam = function(oper) {
            oper.match(/\{#param name=(\w*?) value=(.*?)\}/);
            this._name = RegExp.$1;
            this._value = RegExp.$2;
        };
        UserParam.prototype.get = function(d, param, element, deep) {
            var $T = d;
            var $P = param;
            var $Q = element;

            try {
                param[this._name] = eval(this._value);
            } catch (e) {
                if (Template.DEBUG_MODE || (e instanceof JTException))
                    throw e;
                param[this._name] = undefined;
            }
            return '';
        };
        var Cycle = function(oper) {
            oper.match(/\{#cycle values=(.*?)\}/);
            this._values = eval(RegExp.$1);
            this._length = this._values.length;
            if (this._length <= 0) {
                throw new Error('jTemplates: cycle has no elements');
            }
            this._index = 0;
            this._lastSessionID = -1;
        };
        Cycle.prototype.get = function(d, param, element, deep) {
            var sid = jQuery.data(element, 'jTemplateSID');
            if (sid != this._lastSessionID) {
                this._lastSessionID = sid;
                this._index = 0;
            }
            var i = this._index++ % this._length;
            return this._values[i];
        };
        jQuery.fn.setTemplate = function(s, includes, settings) {
            if (s.constructor === Template) {
                return jQuery(this).each(function() {
                    jQuery.data(this, 'jTemplate', s);
                    jQuery.data(this, 'jTemplateSID', 0);
                });
            } else {
                return jQuery(this).each(function() {
                    jQuery.data(this, 'jTemplate', new Template(s, includes, settings));
                    jQuery.data(this, 'jTemplateSID', 0);
                });
            }
        };
        jQuery.fn.setTemplateURL = function(url_, includes, settings) {
            var s = jQuery.ajax({
                url: url_,
                async: false
            }).responseText;

            return jQuery(this).setTemplate(s, includes, settings);
        };
        jQuery.fn.setTemplateElement = function(elementName, includes, settings) {
            var s = jQuery('#' + elementName).val();
            if (s == null) {
                s = jQuery('#' + elementName).html();
                s = s.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
            }

            s = jQuery.trim(s);
            s = s.replace(/^<\!\[CDATA\[([\s\S]*)\]\]>$/im, '$1');
            s = s.replace(/^<\!--([\s\S]*)-->$/im, '$1');

            return jQuery(this).setTemplate(s, includes, settings);
        };
        jQuery.fn.hasTemplate = function() {
            var count = 0;
            jQuery(this).each(function() {
                if (jQuery.getTemplate(this)) {
                    ++count;
                }
            });
            return count;
        };
        jQuery.fn.removeTemplate = function() {
            jQuery(this).processTemplateStop();
            return jQuery(this).each(function() {
                jQuery.removeData(this, 'jTemplate');
            });
        };
        jQuery.fn.setParam = function(name, value) {
            return jQuery(this).each(function() {
                var t = jQuery.getTemplate(this);
                if (t === undefined) {
                    if (Template.DEBUG_MODE)
                        throw new Error('jTemplates: Template is not defined.');
                    else
                        return;
                }
                t.setParam(name, value);
            });
        };
        jQuery.fn.processTemplate = function(d, param) {
            return jQuery(this).each(function() {
                var t = jQuery.getTemplate(this);
                if (t === undefined) {
                    if (Template.DEBUG_MODE)
                        throw new Error('jTemplates: Template is not defined.');
                    else
                        return;
                }
                jQuery.data(this, 'jTemplateSID', jQuery.data(this, 'jTemplateSID') + 1);
                jQuery(this).html(t.get(d, param, this, 0));
            });
        };
        jQuery.fn.processTemplateURL = function(url_, param, options) {
            var that = this;

            options = jQuery.extend({
                type: 'GET',
                async: true,
                cache: false
            }, options);

            jQuery.ajax({
                url: url_,
                type: options.type,
                data: options.data,
                dataFilter: options.dataFilter,
                async: options.async,
                cache: options.cache,
                timeout: options.timeout,
                dataType: 'json',
                success: function(d) {
                    var r = jQuery(that).processTemplate(d, param);
                    if (options.on_success) {
                        options.on_success(r);
                    }
                },
                error: options.on_error,
                complete: options.on_complete
            });
            return this;
        };
        var Updater = function(url, param, interval, args, objs, options) {
            this._url = url;
            this._param = param;
            this._interval = interval;
            this._args = args;
            this.objs = objs;
            this.timer = null;
            this._options = options || {};

            var that = this;
            jQuery(objs).each(function() {
                jQuery.data(this, 'jTemplateUpdater', that);
            });
            this.run();
        };
        Updater.prototype.run = function() {
            this.detectDeletedNodes();
            if (this.objs.length == 0) {
                return;
            }
            var that = this;
            jQuery.getJSON(this._url, this._args, function(d) {
                var r = jQuery(that.objs).processTemplate(d, that._param);
                if (that._options.on_success) {
                    that._options.on_success(r);
                }
            });
            this.timer = setTimeout(function() { that.run(); }, this._interval);
        };
        Updater.prototype.detectDeletedNodes = function() {
            this.objs = jQuery.grep(this.objs, function(o) {
                if (jQuery.browser.msie) {
                    var n = o.parentNode;
                    while (n && n != document) {
                        n = n.parentNode;
                    }
                    return n != null;
                } else {
                    return o.parentNode != null;
                }
            });
        };
        jQuery.fn.processTemplateStart = function(url, param, interval, args, options) {
            return new Updater(url, param, interval, args, this, options);
        };
        jQuery.fn.processTemplateStop = function() {
            return jQuery(this).each(function() {
                var updater = jQuery.data(this, 'jTemplateUpdater');
                if (updater == null) {
                    return;
                }
                var that = this;
                updater.objs = jQuery.grep(updater.objs, function(o) {
                    return o != that;
                });
                jQuery.removeData(this, 'jTemplateUpdater');
            });
        };
        jQuery.extend({
            createTemplate: function(s, includes, settings) {
                return new Template(s, includes, settings);
            },
            createTemplateURL: function(url_, includes, settings) {
                var s = jQuery.ajax({
                    url: url_,
                    async: false
                }).responseText;

                return new Template(s, includes, settings);
            },
            getTemplate: function(element) {
                return jQuery.data(element, 'jTemplate');
            },
            processTemplateToText: function(template, data, parameter) {
                return template.get(data, parameter, undefined, 0);
            },
            jTemplatesDebugMode: function(value) {
                Template.DEBUG_MODE = value;
            }
        });
    })(jQuery);
};