/*

 Holder - 2.1 - client side image placeholders
 (c) 2012-2013 Ivan Malopinsky / http://imsky.co

 Provided under the MIT License.
 Commercial use requires attribution.

 */

var Holder = Holder || {};
(function (app, win) {

    var preempted = false,
        fallback = false,
        canvas = document.createElement('canvas');

    if (!canvas.getContext) {
        fallback = true;
    } else {
        if (canvas.toDataURL("image/png")
            .indexOf("data:image/png") < 0) {
            //Android doesn't support data URI
            fallback = true;
        } else {
            var ctx = canvas.getContext("2d");
        }
    }

    var dpr = 1, bsr = 1;

    if (!fallback) {
        dpr = window.devicePixelRatio || 1,
            bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
    }

    var ratio = dpr / bsr;

//getElementsByClassName polyfill
    document.getElementsByClassName || (document.getElementsByClassName = function (e) {
        var t = document, n, r, i, s = [];
        if (t.querySelectorAll)return t.querySelectorAll("." + e);
        if (t.evaluate) {
            r = ".//*[contains(concat(' ', @class, ' '), ' " + e + " ')]", n = t.evaluate(r, t, null, 0, null);
            while (i = n.iterateNext())s.push(i)
        } else {
            n = t.getElementsByTagName("*"), r = new RegExp("(^|\\s)" + e + "(\\s|$)");
            for (i = 0; i < n.length; i++)r.test(n[i].className) && s.push(n[i])
        }
        return s
    })

//getComputedStyle polyfill
    window.getComputedStyle || (window.getComputedStyle = function (e) {
        return this.el = e, this.getPropertyValue = function (t) {
            var n = /(\-([a-z]){1})/g;
            return t == "float" && (t = "styleFloat"), n.test(t) && (t = t.replace(n, function () {
                return arguments[2].toUpperCase()
            })), e.currentStyle[t] ? e.currentStyle[t] : null
        }, this
    })

//http://javascript.nwbox.com/ContentLoaded by Diego Perini with modifications
    function contentLoaded(n, t) {
        var l = "complete", s = "readystatechange", u = !1, h = u, c = !0, i = n.document, a = i.documentElement, e = i.addEventListener ? "addEventListener" : "attachEvent", v = i.addEventListener ? "removeEventListener" : "detachEvent", f = i.addEventListener ? "" : "on", r = function (e) {
            (e.type != s || i.readyState == l) && ((e.type == "load" ? n : i)[v](f + e.type, r, u), !h && (h = !0) && t.call(n, null))
        }, o = function () {
            try {
                a.doScroll("left")
            } catch (n) {
                setTimeout(o, 50);
                return
            }
            r("poll")
        };
        if (i.readyState == l)t.call(n, "lazy"); else {
            if (i.createEventObject && a.doScroll) {
                try {
                    c = !n.frameElement
                } catch (y) {
                }
                c && o()
            }
            i[e](f + "DOMContentLoaded", r, u), i[e](f + s, r, u), n[e](f + "load", r, u)
        }
    }

//https://gist.github.com/991057 by Jed Schmidt with modifications
    function selector(a) {
        a = a.match(/^(\W)?(.*)/);
        var b = document["getElement" + (a[1] ? a[1] == "#" ? "ById" : "sByClassName" : "sByTagName")](a[2]);
        var ret = [];
        b !== null && (b.length ? ret = b : b.length === 0 ? ret = b : ret = [b]);
        return ret;
    }

//shallow object property extend
    function extend(a, b) {
        var c = {};
        for (var d in a)c[d] = a[d];
        for (var e in b)c[e] = b[e];
        return c
    }

//hasOwnProperty polyfill
    if (!Object.prototype.hasOwnProperty)
    /*jshint -W001, -W103 */
        Object.prototype.hasOwnProperty = function (prop) {
            var proto = this.__proto__ || this.constructor.prototype;
            return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]);
        }
    /*jshint +W001, +W103 */

    function text_size(width, height, template) {
        height = parseInt(height, 10);
        width = parseInt(width, 10);
        var bigSide = Math.max(height, width)
        var smallSide = Math.min(height, width)
        var scale = 1 / 12;
        var newHeight = Math.min(smallSide * 0.75, 0.75 * bigSide * scale);
        return {
            height: Math.round(Math.max(template.size, newHeight))
        }
    }

    function draw(ctx, dimensions, template, ratio, literal) {
        var ts = text_size(dimensions.width, dimensions.height, template);
        var text_height = ts.height;
        var width = dimensions.width * ratio,
            height = dimensions.height * ratio;
        var font = template.font ? template.font : "sans-serif";
        canvas.width = width;
        canvas.height = height;
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillStyle = template.background;
        ctx.fillRect(0, 0, width, height);
        ctx.fillStyle = template.foreground;
        ctx.font = "bold " + text_height + "px " + font;
        var text = template.text ? template.text : (Math.floor(dimensions.width) + "x" + Math.floor(dimensions.height));
        if (literal) {
            text = template.literalText;
        }
        var text_width = ctx.measureText(text).width;
        if (text_width / width >= 0.75) {
            text_height = Math.floor(text_height * 0.75 * (width / text_width));
        }
        //Resetting font size if necessary
        ctx.font = "bold " + (text_height * ratio) + "px " + font;
        ctx.fillText(text, (width / 2), (height / 2), width);
        return canvas.toDataURL("image/png");
    }

    function render(mode, el, holder, src) {
        var dimensions = holder.dimensions,
            theme = holder.theme,
            text = holder.text ? decodeURIComponent(holder.text) : holder.text;
        var dimensions_caption = dimensions.width + "x" + dimensions.height;
        theme = (text ? extend(theme, {
            text: text
        }) : theme);
        theme = (holder.font ? extend(theme, {
            font: holder.font
        }) : theme);
        el.setAttribute("data-src", src);
        theme.literalText = dimensions_caption;
        holder.originalTheme = holder.theme;
        holder.theme = theme;

        if (mode == "image") {
            el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption);
            if (fallback || !holder.auto) {
                el.style.width = dimensions.width + "px";
                el.style.height = dimensions.height + "px";
            }
            if (fallback) {
                el.style.backgroundColor = theme.background;
            } else {
                el.setAttribute("src", draw(ctx, dimensions, theme, ratio));
            }
        } else if (mode == "background") {
            if (!fallback) {
                el.style.backgroundImage = "url(" + draw(ctx, dimensions, theme, ratio) + ")";
                el.style.backgroundSize = dimensions.width + "px " + dimensions.height + "px";
            }
        } else if (mode == "fluid") {
            el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption);
            if (dimensions.height.slice(-1) == "%") {
                el.style.height = dimensions.height
            } else {
                el.style.height = dimensions.height + "px"
            }
            if (dimensions.width.slice(-1) == "%") {
                el.style.width = dimensions.width
            } else {
                el.style.width = dimensions.width + "px"
            }
            if (el.style.display == "inline" || el.style.display === "") {
                el.style.display = "block";
            }
            if (fallback) {
                el.style.backgroundColor = theme.background;
            } else {
                el.holderData = holder;
                fluid_images.push(el);
                fluid_update(el);
            }
        }
    }

    function fluid_update(element) {
        var images;
        if (element.nodeType == null) {
            images = fluid_images;
        } else {
            images = [element]
        }
        for (var i in images) {
            var el = images[i]
            if (el.holderData) {
                var holder = el.holderData;
                el.setAttribute("src", draw(ctx, {
                    height: el.clientHeight,
                    width: el.clientWidth
                }, holder.theme, ratio, !!holder.literal));
            }
        }
    }

    function parse_flags(flags, options) {
        var ret = {
            theme: settings.themes.gray
        };
        var render = false;
        for (sl = flags.length, j = 0; j < sl; j++) {
            var flag = flags[j];
            if (app.flags.dimensions.match(flag)) {
                render = true;
                ret.dimensions = app.flags.dimensions.output(flag);
            } else if (app.flags.fluid.match(flag)) {
                render = true;
                ret.dimensions = app.flags.fluid.output(flag);
                ret.fluid = true;
            } else if (app.flags.literal.match(flag)) {
                ret.literal = true;
            } else if (app.flags.colors.match(flag)) {
                ret.theme = app.flags.colors.output(flag);
            } else if (options.themes[flag]) {
                //If a theme is specified, it will override custom colors
                ret.theme = options.themes[flag];
            } else if (app.flags.font.match(flag)) {
                ret.font = app.flags.font.output(flag);
            } else if (app.flags.auto.match(flag)) {
                ret.auto = true;
            } else if (app.flags.text.match(flag)) {
                ret.text = app.flags.text.output(flag);
            }
        }
        return render ? ret : false;
    }

    var fluid_images = [];
    var settings = {
        domain: "holder.js",
        images: "img",
        bgnodes: ".holderjs",
        themes: {
            "gray": {
                background: "#eee",
                foreground: "#aaa",
                size: 12
            },
            "social": {
                background: "#3a5a97",
                foreground: "#fff",
                size: 12
            },
            "industrial": {
                background: "#434A52",
                foreground: "#C2F200",
                size: 12
            }
        },
        stylesheet: ""
    };
    app.flags = {
        dimensions: {
            regex: /^(\d+)x(\d+)$/,
            output: function (val) {
                var exec = this.regex.exec(val);
                return {
                    width: +exec[1],
                    height: +exec[2]
                }
            }
        },
        fluid: {
            regex: /^([0-9%]+)x([0-9%]+)$/,
            output: function (val) {
                var exec = this.regex.exec(val);
                return {
                    width: exec[1],
                    height: exec[2]
                }
            }
        },
        colors: {
            regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i,
            output: function (val) {
                var exec = this.regex.exec(val);
                return {
                    size: settings.themes.gray.size,
                    foreground: "#" + exec[2],
                    background: "#" + exec[1]
                }
            }
        },
        text: {
            regex: /text\:(.*)/,
            output: function (val) {
                return this.regex.exec(val)[1];
            }
        },
        font: {
            regex: /font\:(.*)/,
            output: function (val) {
                return this.regex.exec(val)[1];
            }
        },
        auto: {
            regex: /^auto$/
        },
        literal: {
            regex: /^literal$/
        }
    }
    for (var flag in app.flags) {
        if (!app.flags.hasOwnProperty(flag)) continue;
        app.flags[flag].match = function (val) {
            return val.match(this.regex)
        }
    }
    app.add_theme = function (name, theme) {
        name != null && theme != null && (settings.themes[name] = theme);
        return app;
    };
    app.add_image = function (src, el) {
        var node = selector(el);
        if (node.length) {
            for (var i = 0, l = node.length; i < l; i++) {
                var img = document.createElement("img")
                img.setAttribute("data-src", src);
                node[i].appendChild(img);
            }
        }
        return app;
    };
    app.run = function (o) {
        var options = extend(settings, o),
            images = [],
            imageNodes = [],
            bgnodes = [];
        if (typeof (options.images) == "string") {
            imageNodes = selector(options.images);
        } else if (window.NodeList && options.images instanceof window.NodeList) {
            imageNodes = options.images;
        } else if (window.Node && options.images instanceof window.Node) {
            imageNodes = [options.images];
        }
        if (typeof (options.bgnodes) == "string") {
            bgnodes = selector(options.bgnodes);
        } else if (window.NodeList && options.elements instanceof window.NodeList) {
            bgnodes = options.bgnodes;
        } else if (window.Node && options.bgnodes instanceof window.Node) {
            bgnodes = [options.bgnodes];
        }
        preempted = true;
        for (i = 0, l = imageNodes.length; i < l; i++) images.push(imageNodes[i]);
        var holdercss = document.getElementById("holderjs-style");
        if (!holdercss) {
            holdercss = document.createElement("style");
            holdercss.setAttribute("id", "holderjs-style");
            holdercss.type = "text/css";
            document.getElementsByTagName("head")[0].appendChild(holdercss);
        }
        if (!options.nocss) {
            if (holdercss.styleSheet) {
                holdercss.styleSheet.cssText += options.stylesheet;
            } else {
                holdercss.appendChild(document.createTextNode(options.stylesheet));
            }
        }
        var cssregex = new RegExp(options.domain + "\/(.*?)\"?\\)");
        for (var l = bgnodes.length, i = 0; i < l; i++) {
            var src = window.getComputedStyle(bgnodes[i], null)
                .getPropertyValue("background-image");
            var flags = src.match(cssregex);
            var bgsrc = bgnodes[i].getAttribute("data-background-src");
            if (flags) {
                var holder = parse_flags(flags[1].split("/"), options);
                if (holder) {
                    render("background", bgnodes[i], holder, src);
                }
            } else if (bgsrc != null) {
                var holder = parse_flags(bgsrc.substr(bgsrc.lastIndexOf(options.domain) + options.domain.length + 1)
                    .split("/"), options);
                if (holder) {
                    render("background", bgnodes[i], holder, src);
                }
            }
        }
        for (l = images.length, i = 0; i < l; i++) {
            var attr_data_src, attr_src;
            attr_src = attr_data_src = src = null;
            try {
                attr_src = images[i].getAttribute("src");
                attr_datasrc = images[i].getAttribute("data-src");
            } catch (e) {
            }
            if (attr_datasrc == null && !!attr_src && attr_src.indexOf(options.domain) >= 0) {
                src = attr_src;
            } else if (!!attr_datasrc && attr_datasrc.indexOf(options.domain) >= 0) {
                src = attr_datasrc;
            }
            if (src) {
                var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1)
                    .split("/"), options);
                if (holder) {
                    if (holder.fluid) {
                        render("fluid", images[i], holder, src)
                    } else {
                        render("image", images[i], holder, src);
                    }
                }
            }
        }
        return app;
    };
    contentLoaded(win, function () {
        if (window.addEventListener) {
            window.addEventListener("resize", fluid_update, false);
            window.addEventListener("orientationchange", fluid_update, false);
        } else {
            window.attachEvent("onresize", fluid_update)
        }
        preempted || app.run();
    });
    if (typeof define === "function" && define.amd) {
        define([], function () {
            return app;
        });
    }

})(Holder, window);
