/**
* jquery.writeCapture.js 
* 
* Note that this file only provides the jQuery plugin functionality, you still
* need writeCapture.js. The compressed version will contain both as as single
* file.
*
* @author noah <noah.sloan@gmail.com>
* 
*/
(function ($, wc, noop) {
    // methods that take HTML content (according to API)
    var methods = {
        html: html
    };
    // TODO wrap domManip instead?
    $.each(['append', 'prepend', 'after', 'before', 'wrap', 'wrapAll', 'replaceWith',
		'wrapInner'], function () { methods[this] = makeMethod(this); });

    function isString(s) {
        return Object.prototype.toString.call(s) == "[object String]";
    }

    function executeMethod(method, content, options, cb) {
        if (arguments.length == 0) return proxyMethods.call(this);

        var m = methods[method];
        if (method == 'load') {
            return load.call(this, content, options, cb);
        }
        if (!m) error(method);
        return doEach.call(this, content, options, m);
    }

    $.fn.writeCapture = executeMethod;

    var PROXIED = '__writeCaptureJsProxied-fghebd__';
    // inherit from the jQuery instance, proxying the HTML injection methods
    // so that the HTML is sanitized
    function proxyMethods() {
        if (this[PROXIED]) return this;

        var jq = this;
        function F() {
            var _this = this, sanitizing = false;
            this[PROXIED] = true;
            $.each(methods, function (method) {
                var _super = jq[method];
                if (!_super) return;
                _this[method] = function (content, options, cb) {
                    // if it's unsanitized HTML, proxy it
                    if (!sanitizing && isString(content)) {
                        try {
                            sanitizing = true;
                            return executeMethod.call(_this, method, content,
								options, cb);
                        } finally {
                            sanitizing = false;
                        }
                    }
                    return _super.apply(_this, arguments); // else delegate
                };
            });
            // wrap pushStack so that the new jQuery instance is also wrapped
            this.pushStack = function () {
                return proxyMethods.call(jq.pushStack.apply(_this, arguments));
            };
            this.endCapture = function () { return jq; };
        }
        F.prototype = jq;
        return new F();
    }

    function doEach(content, options, action) {
        var done, self = this;
        if (options && options.done) {
            done = options.done;
            delete options.done;
        } else if ($.isFunction(options)) {
            done = options;
            options = null;
        }
        wc.sanitizeSerial($.map(this, function (el) {
            return {
                html: content,
                options: options,
                action: function (text) {
                    action.call(el, text);
                }
            };
        }), done && function () { done.call(self); } || done);
        return this;
    }


    function html(safe) {
        $(this).html(safe);
    }

    function makeMethod(method) {
        return function (safe) {
            $(this)[method](safe);
        };
    }

    function load(url, options, callback) {
        var self = this, selector, off = url.indexOf(' ');
        if (off >= 0) {
            selector = url.slice(off, url.length);
            url = url.slice(0, off);
        }
        if ($.isFunction(callback)) {
            options = options || {};
            options.done = callback;
        }
        return $.ajax({
            url: url,
            type: options && options.type || "GET",
            dataType: "html",
            data: options && options.params,
            complete: loadCallback(self, options, selector)
        });
    }

    function loadCallback(self, options, selector) {
        return function (res, status) {
            if (status == "success" || status == "notmodified") {
                var text = getText(res.responseText, selector);
                doEach.call(self, text, options, html);
            }
        };
    }

    var PLACEHOLDER = /jquery-writeCapture-script-placeholder-(\d+)-wc/g;
    function getText(text, selector) {
        if (!selector || !text) return text;

        var id = 0, scripts = {};
        return $('<div/>').append(
			text.replace(/<script(.|\s)*?\/script>/g, function (s) {
			    scripts[id] = s;
			    return "jquery-writeCapture-script-placeholder-" + (id++) + '-wc';
			})
		).find(selector).html().replace(PLACEHOLDER, function (all, id) {
		    return scripts[id];
		});
    }

    function error(method) {
        throw "invalid method parameter " + method;
    }

    // expose core
    $.writeCapture = wc;
})(jQuery, writeCapture.noConflict());
