info@54: /* info@54: info@54: Holder - 2.2 - client side image placeholders info@54: (c) 2012-2013 Ivan Malopinsky / http://imsky.co info@54: info@54: Provided under the MIT License. info@54: Commercial use requires attribution. info@54: info@54: */ info@54: info@54: var Holder = Holder || {}; info@54: (function (app, win) { info@54: info@54: var preempted = false, info@54: fallback = false, info@54: canvas = document.createElement('canvas'); info@54: var dpr = 1, bsr = 1; info@54: var resizable_images = []; info@54: info@54: if (!canvas.getContext) { info@54: fallback = true; info@54: } else { info@54: if (canvas.toDataURL("image/png") info@54: .indexOf("data:image/png") < 0) { info@54: //Android doesn't support data URI info@54: fallback = true; info@54: } else { info@54: var ctx = canvas.getContext("2d"); info@54: } info@54: } info@54: info@54: if(!fallback){ info@54: dpr = window.devicePixelRatio || 1, info@54: bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; info@54: } info@54: info@54: var ratio = dpr / bsr; info@54: info@54: var settings = { info@54: domain: "holder.js", info@54: images: "img", info@54: bgnodes: ".holderjs", info@54: themes: { info@54: "gray": { info@54: background: "#eee", info@54: foreground: "#aaa", info@54: size: 12 info@54: }, info@54: "social": { info@54: background: "#3a5a97", info@54: foreground: "#fff", info@54: size: 12 info@54: }, info@54: "industrial": { info@54: background: "#434A52", info@54: foreground: "#C2F200", info@54: size: 12 info@54: }, info@54: "sky": { info@54: background: "#0D8FDB", info@54: foreground: "#fff", info@54: size: 12 info@54: }, info@54: "vine": { info@54: background: "#39DBAC", info@54: foreground: "#1E292C", info@54: size: 12 info@54: }, info@54: "lava": { info@54: background: "#F8591A", info@54: foreground: "#1C2846", info@54: size: 12 info@54: } info@54: }, info@54: stylesheet: "" info@54: }; info@54: app.flags = { info@54: dimensions: { info@54: regex: /^(\d+)x(\d+)$/, info@54: output: function (val) { info@54: var exec = this.regex.exec(val); info@54: return { info@54: width: +exec[1], info@54: height: +exec[2] info@54: } info@54: } info@54: }, info@54: fluid: { info@54: regex: /^([0-9%]+)x([0-9%]+)$/, info@54: output: function (val) { info@54: var exec = this.regex.exec(val); info@54: return { info@54: width: exec[1], info@54: height: exec[2] info@54: } info@54: } info@54: }, info@54: colors: { info@54: regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i, info@54: output: function (val) { info@54: var exec = this.regex.exec(val); info@54: return { info@54: size: settings.themes.gray.size, info@54: foreground: "#" + exec[2], info@54: background: "#" + exec[1] info@54: } info@54: } info@54: }, info@54: text: { info@54: regex: /text\:(.*)/, info@54: output: function (val) { info@54: return this.regex.exec(val)[1]; info@54: } info@54: }, info@54: font: { info@54: regex: /font\:(.*)/, info@54: output: function (val) { info@54: return this.regex.exec(val)[1]; info@54: } info@54: }, info@54: auto: { info@54: regex: /^auto$/ info@54: }, info@54: textmode: { info@54: regex: /textmode\:(.*)/, info@54: output: function(val){ info@54: return this.regex.exec(val)[1]; info@54: } info@54: } info@54: } info@54: info@54: //getElementsByClassName polyfill info@54: 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= 0.75) { info@54: text_height = Math.floor(text_height * 0.75 * (width / text_width)); info@54: } info@54: //Resetting font size if necessary info@54: ctx.font = "bold " + (text_height * ratio) + "px " + font; info@54: ctx.fillText(text, (width / 2), (height / 2), width); info@54: return canvas.toDataURL("image/png"); info@54: } info@54: info@54: function render(mode, el, holder, src) { info@54: info@54: var dimensions = holder.dimensions, info@54: theme = holder.theme, info@54: text = holder.text ? decodeURIComponent(holder.text) : holder.text; info@54: var dimensions_caption = dimensions.width + "x" + dimensions.height; info@54: theme = (text ? extend(theme, { info@54: text: text info@54: }) : theme); info@54: theme = (holder.font ? extend(theme, { info@54: font: holder.font info@54: }) : theme); info@54: el.setAttribute("data-src", src); info@54: holder.theme = theme; info@54: el.holder_data = holder; info@54: info@54: if (mode == "image") { info@54: el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption); info@54: if (fallback || !holder.auto) { info@54: el.style.width = dimensions.width + "px"; info@54: el.style.height = dimensions.height + "px"; info@54: } info@54: if (fallback) { info@54: el.style.backgroundColor = theme.background; info@54: } else { info@54: el.setAttribute("src", draw({ctx: ctx, dimensions: dimensions, template: theme, ratio:ratio, holder: holder})); info@54: info@54: if(holder.textmode && holder.textmode == "exact"){ info@54: resizable_images.push(el); info@54: resizable_update(el); info@54: } info@54: info@54: } info@54: } else if (mode == "background") { info@54: if (!fallback) { info@54: el.style.backgroundImage = "url(" + draw({ctx:ctx, dimensions: dimensions, template: theme, ratio: ratio, holder: holder}) + ")"; info@54: el.style.backgroundSize = dimensions.width + "px " + dimensions.height + "px"; info@54: } info@54: } else if (mode == "fluid") { info@54: el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption); info@54: if (dimensions.height.slice(-1) == "%") { info@54: el.style.height = dimensions.height info@54: } else { info@54: el.style.height = dimensions.height + "px" info@54: } info@54: if (dimensions.width.slice(-1) == "%") { info@54: el.style.width = dimensions.width info@54: } else { info@54: el.style.width = dimensions.width + "px" info@54: } info@54: if (el.style.display == "inline" || el.style.display === "" || el.style.display == "none") { info@54: el.style.display = "block"; info@54: } info@54: if (fallback) { info@54: el.style.backgroundColor = theme.background; info@54: } else { info@54: resizable_images.push(el); info@54: resizable_update(el); info@54: } info@54: } info@54: } info@54: info@54: function dimension_check(el, callback) { info@54: var dimensions = { info@54: height: el.clientHeight, info@54: width: el.clientWidth info@54: }; info@54: if (!dimensions.height && !dimensions.width) { info@54: if (el.hasAttribute("data-holder-invisible")) { info@54: throw new Error("Holder: placeholder is not visible"); info@54: } else { info@54: el.setAttribute("data-holder-invisible", true) info@54: setTimeout(function () { info@54: callback.call(this, el) info@54: }, 1) info@54: return null; info@54: } info@54: } else { info@54: el.removeAttribute("data-holder-invisible") info@54: } info@54: return dimensions; info@54: } info@54: info@54: function resizable_update(element) { info@54: var images; info@54: if (element.nodeType == null) { info@54: images = resizable_images; info@54: } else { info@54: images = [element] info@54: } info@54: for (var i in images) { info@54: if (!images.hasOwnProperty(i)) { info@54: continue; info@54: } info@54: var el = images[i] info@54: if (el.holder_data) { info@54: var holder = el.holder_data; info@54: var dimensions = dimension_check(el, resizable_update) info@54: if(dimensions){ info@54: if(holder.fluid){ info@54: el.setAttribute("src", draw({ info@54: ctx: ctx, info@54: dimensions: dimensions, info@54: template: holder.theme, info@54: ratio: ratio, info@54: holder: holder info@54: })) info@54: } info@54: if(holder.textmode && holder.textmode == "exact"){ info@54: holder.exact_dimensions = dimensions; info@54: el.setAttribute("src", draw({ info@54: ctx: ctx, info@54: dimensions: holder.dimensions, info@54: template: holder.theme, info@54: ratio: ratio, info@54: holder: holder info@54: })) info@54: } info@54: } info@54: } info@54: } info@54: } info@54: info@54: function parse_flags(flags, options) { info@54: var ret = { info@54: theme: extend(settings.themes.gray, {}) info@54: }; info@54: var render = false; info@54: for (sl = flags.length, j = 0; j < sl; j++) { info@54: var flag = flags[j]; info@54: if (app.flags.dimensions.match(flag)) { info@54: render = true; info@54: ret.dimensions = app.flags.dimensions.output(flag); info@54: } else if (app.flags.fluid.match(flag)) { info@54: render = true; info@54: ret.dimensions = app.flags.fluid.output(flag); info@54: ret.fluid = true; info@54: } else if (app.flags.textmode.match(flag)) { info@54: ret.textmode = app.flags.textmode.output(flag) info@54: } else if (app.flags.colors.match(flag)) { info@54: ret.theme = app.flags.colors.output(flag); info@54: } else if (options.themes[flag]) { info@54: //If a theme is specified, it will override custom colors info@54: if(options.themes.hasOwnProperty(flag)){ info@54: ret.theme = extend(options.themes[flag], {}); info@54: } info@54: } else if (app.flags.font.match(flag)) { info@54: ret.font = app.flags.font.output(flag); info@54: } else if (app.flags.auto.match(flag)) { info@54: ret.auto = true; info@54: } else if (app.flags.text.match(flag)) { info@54: ret.text = app.flags.text.output(flag); info@54: } info@54: } info@54: return render ? ret : false; info@54: } info@54: info@54: for (var flag in app.flags) { info@54: if (!app.flags.hasOwnProperty(flag)) continue; info@54: app.flags[flag].match = function (val) { info@54: return val.match(this.regex) info@54: } info@54: } info@54: app.add_theme = function (name, theme) { info@54: name != null && theme != null && (settings.themes[name] = theme); info@54: return app; info@54: }; info@54: app.add_image = function (src, el) { info@54: var node = selector(el); info@54: if (node.length) { info@54: for (var i = 0, l = node.length; i < l; i++) { info@54: var img = document.createElement("img") info@54: img.setAttribute("data-src", src); info@54: node[i].appendChild(img); info@54: } info@54: } info@54: return app; info@54: }; info@54: app.run = function (o) { info@54: preempted = true; info@54: info@54: var options = extend(settings, o), info@54: images = [], info@54: imageNodes = [], info@54: bgnodes = []; info@54: if (typeof (options.images) == "string") { info@54: imageNodes = selector(options.images); info@54: } else if (window.NodeList && options.images instanceof window.NodeList) { info@54: imageNodes = options.images; info@54: } else if (window.Node && options.images instanceof window.Node) { info@54: imageNodes = [options.images]; info@54: } info@54: info@54: if (typeof (options.bgnodes) == "string") { info@54: bgnodes = selector(options.bgnodes); info@54: } else if (window.NodeList && options.elements instanceof window.NodeList) { info@54: bgnodes = options.bgnodes; info@54: } else if (window.Node && options.bgnodes instanceof window.Node) { info@54: bgnodes = [options.bgnodes]; info@54: } info@54: for (i = 0, l = imageNodes.length; i < l; i++) images.push(imageNodes[i]); info@54: var holdercss = document.getElementById("holderjs-style"); info@54: if (!holdercss) { info@54: holdercss = document.createElement("style"); info@54: holdercss.setAttribute("id", "holderjs-style"); info@54: holdercss.type = "text/css"; info@54: document.getElementsByTagName("head")[0].appendChild(holdercss); info@54: } info@54: if (!options.nocss) { info@54: if (holdercss.styleSheet) { info@54: holdercss.styleSheet.cssText += options.stylesheet; info@54: } else { info@54: holdercss.appendChild(document.createTextNode(options.stylesheet)); info@54: } info@54: } info@54: var cssregex = new RegExp(options.domain + "\/(.*?)\"?\\)"); info@54: for (var l = bgnodes.length, i = 0; i < l; i++) { info@54: var src = window.getComputedStyle(bgnodes[i], null) info@54: .getPropertyValue("background-image"); info@54: var flags = src.match(cssregex); info@54: var bgsrc = bgnodes[i].getAttribute("data-background-src"); info@54: if (flags) { info@54: var holder = parse_flags(flags[1].split("/"), options); info@54: if (holder) { info@54: render("background", bgnodes[i], holder, src); info@54: } info@54: } else if (bgsrc != null) { info@54: var holder = parse_flags(bgsrc.substr(bgsrc.lastIndexOf(options.domain) + options.domain.length + 1) info@54: .split("/"), options); info@54: if (holder) { info@54: render("background", bgnodes[i], holder, src); info@54: } info@54: } info@54: } info@54: for (l = images.length, i = 0; i < l; i++) { info@54: var attr_data_src, attr_src; info@54: attr_src = attr_data_src = src = null; info@54: try { info@54: attr_src = images[i].getAttribute("src"); info@54: attr_datasrc = images[i].getAttribute("data-src"); info@54: } catch (e) {} info@54: if (attr_datasrc == null && !! attr_src && attr_src.indexOf(options.domain) >= 0) { info@54: src = attr_src; info@54: } else if ( !! attr_datasrc && attr_datasrc.indexOf(options.domain) >= 0) { info@54: src = attr_datasrc; info@54: } info@54: if (src) { info@54: var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1) info@54: .split("/"), options); info@54: if (holder) { info@54: if (holder.fluid) { info@54: render("fluid", images[i], holder, src) info@54: } else { info@54: render("image", images[i], holder, src); info@54: } info@54: } info@54: } info@54: } info@54: return app; info@54: }; info@54: contentLoaded(win, function () { info@54: if (window.addEventListener) { info@54: window.addEventListener("resize", resizable_update, false); info@54: window.addEventListener("orientationchange", resizable_update, false); info@54: } else { info@54: window.attachEvent("onresize", resizable_update) info@54: } info@54: preempted || app.run(); info@54: }); info@54: if (typeof define === "function" && define.amd) { info@54: define([], function () { info@54: return app; info@54: }); info@54: } info@54: info@54: })(Holder, window);