提交 | 用户 | 时间
|
58d006
|
1 |
/* |
A |
2 |
|
|
3 |
Holder - 2.0 - client side image placeholders |
|
4 |
(c) 2012-2013 Ivan Malopinsky / http://imsky.co |
|
5 |
|
|
6 |
Provided under the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
Commercial use requires attribution. |
|
8 |
|
|
9 |
*/ |
|
10 |
|
|
11 |
var Holder = Holder || {}; |
|
12 |
(function (app, win) { |
|
13 |
|
|
14 |
var preempted = false, |
|
15 |
fallback = false, |
|
16 |
canvas = document.createElement('canvas'); |
|
17 |
|
|
18 |
//getElementsByClassName polyfill |
|
19 |
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}) |
|
20 |
|
|
21 |
//getComputedStyle polyfill |
|
22 |
window.getComputedStyle||(window.getComputedStyle=function(e,t){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}) |
|
23 |
|
|
24 |
//http://javascript.nwbox.com/ContentLoaded by Diego Perini with modifications |
|
25 |
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)}}; |
|
26 |
|
|
27 |
//https://gist.github.com/991057 by Jed Schmidt with modifications |
|
28 |
function selector(a){ |
|
29 |
a=a.match(/^(\W)?(.*)/);var b=document["getElement"+(a[1]?a[1]=="#"?"ById":"sByClassName":"sByTagName")](a[2]); |
|
30 |
var ret=[]; b!=null&&(b.length?ret=b:b.length==0?ret=b:ret=[b]); return ret; |
|
31 |
} |
|
32 |
|
|
33 |
//shallow object property extend |
|
34 |
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} |
|
35 |
|
|
36 |
//hasOwnProperty polyfill |
|
37 |
if (!Object.prototype.hasOwnProperty) |
|
38 |
Object.prototype.hasOwnProperty = function(prop) { |
|
39 |
var proto = this.__proto__ || this.constructor.prototype; |
|
40 |
return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]); |
|
41 |
} |
|
42 |
|
|
43 |
function text_size(width, height, template) { |
|
44 |
height = parseInt(height,10); |
|
45 |
width = parseInt(width,10); |
|
46 |
var bigSide = Math.max(height, width) |
|
47 |
var smallSide = Math.min(height, width) |
|
48 |
var scale = 1 / 12; |
|
49 |
var newHeight = Math.min(smallSide * 0.75, 0.75 * bigSide * scale); |
|
50 |
return { |
|
51 |
height: Math.round(Math.max(template.size, newHeight)) |
|
52 |
} |
|
53 |
} |
|
54 |
|
|
55 |
function draw(ctx, dimensions, template, ratio) { |
|
56 |
var ts = text_size(dimensions.width, dimensions.height, template); |
|
57 |
var text_height = ts.height; |
|
58 |
var width = dimensions.width * ratio, |
|
59 |
height = dimensions.height * ratio; |
|
60 |
var font = template.font ? template.font : "sans-serif"; |
|
61 |
canvas.width = width; |
|
62 |
canvas.height = height; |
|
63 |
ctx.textAlign = "center"; |
|
64 |
ctx.textBaseline = "middle"; |
|
65 |
ctx.fillStyle = template.background; |
|
66 |
ctx.fillRect(0, 0, width, height); |
|
67 |
ctx.fillStyle = template.foreground; |
|
68 |
ctx.font = "bold " + text_height + "px " + font; |
|
69 |
var text = template.text ? template.text : (Math.floor(dimensions.width) + "x" + Math.floor(dimensions.height)); |
|
70 |
var text_width = ctx.measureText(text).width; |
|
71 |
if (text_width / width >= 0.75) { |
|
72 |
text_height = Math.floor(text_height * 0.75 * (width/text_width)); |
|
73 |
} |
|
74 |
//Resetting font size if necessary |
|
75 |
ctx.font = "bold " + (text_height * ratio) + "px " + font; |
|
76 |
ctx.fillText(text, (width / 2), (height / 2), width); |
|
77 |
return canvas.toDataURL("image/png"); |
|
78 |
} |
|
79 |
|
|
80 |
function render(mode, el, holder, src) { |
|
81 |
var dimensions = holder.dimensions, |
|
82 |
theme = holder.theme, |
|
83 |
text = holder.text ? decodeURIComponent(holder.text) : holder.text; |
|
84 |
var dimensions_caption = dimensions.width + "x" + dimensions.height; |
|
85 |
theme = (text ? extend(theme, { |
|
86 |
text: text |
|
87 |
}) : theme); |
|
88 |
theme = (holder.font ? extend(theme, { |
|
89 |
font: holder.font |
|
90 |
}) : theme); |
|
91 |
if (mode == "image") { |
|
92 |
el.setAttribute("data-src", src); |
|
93 |
el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption); |
|
94 |
if (fallback || !holder.auto) { |
|
95 |
el.style.width = dimensions.width + "px"; |
|
96 |
el.style.height = dimensions.height + "px"; |
|
97 |
} |
|
98 |
if (fallback) { |
|
99 |
el.style.backgroundColor = theme.background; |
|
100 |
} else { |
|
101 |
el.setAttribute("src", draw(ctx, dimensions, theme, ratio)); |
|
102 |
} |
|
103 |
} else if (mode == "background") { |
|
104 |
if (!fallback) { |
|
105 |
el.style.backgroundImage = "url(" + draw(ctx, dimensions, theme, ratio) + ")"; |
|
106 |
el.style.backgroundSize = dimensions.width + "px " + dimensions.height + "px"; |
|
107 |
} |
|
108 |
} else if (mode == "fluid") { |
|
109 |
el.setAttribute("data-src", src); |
|
110 |
el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption); |
|
111 |
if (dimensions.height.substr(-1) == "%") { |
|
112 |
el.style.height = dimensions.height |
|
113 |
} else { |
|
114 |
el.style.height = dimensions.height + "px" |
|
115 |
} |
|
116 |
if (dimensions.width.substr(-1) == "%") { |
|
117 |
el.style.width = dimensions.width |
|
118 |
} else { |
|
119 |
el.style.width = dimensions.width + "px" |
|
120 |
} |
|
121 |
if (el.style.display == "inline" || el.style.display == "") { |
|
122 |
el.style.display = "block"; |
|
123 |
} |
|
124 |
if (fallback) { |
|
125 |
el.style.backgroundColor = theme.background; |
|
126 |
} else { |
|
127 |
el.holderData = holder; |
|
128 |
fluid_images.push(el); |
|
129 |
fluid_update(el); |
|
130 |
} |
|
131 |
} |
|
132 |
}; |
|
133 |
|
|
134 |
function fluid_update(element) { |
|
135 |
var images; |
|
136 |
if (element.nodeType == null) { |
|
137 |
images = fluid_images; |
|
138 |
} else { |
|
139 |
images = [element] |
|
140 |
} |
|
141 |
for (i in images) { |
|
142 |
var el = images[i] |
|
143 |
if (el.holderData) { |
|
144 |
var holder = el.holderData; |
|
145 |
el.setAttribute("src", draw(ctx, { |
|
146 |
height: el.clientHeight, |
|
147 |
width: el.clientWidth |
|
148 |
}, holder.theme, ratio)); |
|
149 |
} |
|
150 |
} |
|
151 |
} |
|
152 |
|
|
153 |
function parse_flags(flags, options) { |
|
154 |
|
|
155 |
var ret = { |
|
156 |
theme: settings.themes.gray |
|
157 |
}, render = false; |
|
158 |
|
|
159 |
for (sl = flags.length, j = 0; j < sl; j++) { |
|
160 |
var flag = flags[j]; |
|
161 |
if (app.flags.dimensions.match(flag)) { |
|
162 |
render = true; |
|
163 |
ret.dimensions = app.flags.dimensions.output(flag); |
|
164 |
} else if (app.flags.fluid.match(flag)) { |
|
165 |
render = true; |
|
166 |
ret.dimensions = app.flags.fluid.output(flag); |
|
167 |
ret.fluid = true; |
|
168 |
} else if (app.flags.colors.match(flag)) { |
|
169 |
ret.theme = app.flags.colors.output(flag); |
|
170 |
} else if (options.themes[flag]) { |
|
171 |
//If a theme is specified, it will override custom colors |
|
172 |
ret.theme = options.themes[flag]; |
|
173 |
} else if (app.flags.text.match(flag)) { |
|
174 |
ret.text = app.flags.text.output(flag); |
|
175 |
} else if (app.flags.font.match(flag)) { |
|
176 |
ret.font = app.flags.font.output(flag); |
|
177 |
} else if (app.flags.auto.match(flag)) { |
|
178 |
ret.auto = true; |
|
179 |
} |
|
180 |
} |
|
181 |
|
|
182 |
return render ? ret : false; |
|
183 |
|
|
184 |
}; |
|
185 |
|
|
186 |
|
|
187 |
|
|
188 |
if (!canvas.getContext) { |
|
189 |
fallback = true; |
|
190 |
} else { |
|
191 |
if (canvas.toDataURL("image/png") |
|
192 |
.indexOf("data:image/png") < 0) { |
|
193 |
//Android doesn't support data URI |
|
194 |
fallback = true; |
|
195 |
} else { |
|
196 |
var ctx = canvas.getContext("2d"); |
|
197 |
} |
|
198 |
} |
|
199 |
|
|
200 |
var dpr = 1, bsr = 1; |
|
201 |
|
|
202 |
if(!fallback){ |
|
203 |
dpr = window.devicePixelRatio || 1, |
|
204 |
bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; |
|
205 |
} |
|
206 |
|
|
207 |
var ratio = dpr / bsr; |
|
208 |
|
|
209 |
var fluid_images = []; |
|
210 |
|
|
211 |
var settings = { |
|
212 |
domain: "holder.js", |
|
213 |
images: "img", |
|
214 |
bgnodes: ".holderjs", |
|
215 |
themes: { |
|
216 |
"gray": { |
|
217 |
background: "#eee", |
|
218 |
foreground: "#aaa", |
|
219 |
size: 12 |
|
220 |
}, |
|
221 |
"social": { |
|
222 |
background: "#3a5a97", |
|
223 |
foreground: "#fff", |
|
224 |
size: 12 |
|
225 |
}, |
|
226 |
"industrial": { |
|
227 |
background: "#434A52", |
|
228 |
foreground: "#C2F200", |
|
229 |
size: 12 |
|
230 |
} |
|
231 |
}, |
|
232 |
stylesheet: "" |
|
233 |
}; |
|
234 |
|
|
235 |
|
|
236 |
app.flags = { |
|
237 |
dimensions: { |
|
238 |
regex: /^(\d+)x(\d+)$/, |
|
239 |
output: function (val) { |
|
240 |
var exec = this.regex.exec(val); |
|
241 |
return { |
|
242 |
width: +exec[1], |
|
243 |
height: +exec[2] |
|
244 |
} |
|
245 |
} |
|
246 |
}, |
|
247 |
fluid: { |
|
248 |
regex: /^([0-9%]+)x([0-9%]+)$/, |
|
249 |
output: function (val) { |
|
250 |
var exec = this.regex.exec(val); |
|
251 |
return { |
|
252 |
width: exec[1], |
|
253 |
height: exec[2] |
|
254 |
} |
|
255 |
} |
|
256 |
}, |
|
257 |
colors: { |
|
258 |
regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i, |
|
259 |
output: function (val) { |
|
260 |
var exec = this.regex.exec(val); |
|
261 |
return { |
|
262 |
size: settings.themes.gray.size, |
|
263 |
foreground: "#" + exec[2], |
|
264 |
background: "#" + exec[1] |
|
265 |
} |
|
266 |
} |
|
267 |
}, |
|
268 |
text: { |
|
269 |
regex: /text\:(.*)/, |
|
270 |
output: function (val) { |
|
271 |
return this.regex.exec(val)[1]; |
|
272 |
} |
|
273 |
}, |
|
274 |
font: { |
|
275 |
regex: /font\:(.*)/, |
|
276 |
output: function (val) { |
|
277 |
return this.regex.exec(val)[1]; |
|
278 |
} |
|
279 |
}, |
|
280 |
auto: { |
|
281 |
regex: /^auto$/ |
|
282 |
} |
|
283 |
} |
|
284 |
|
|
285 |
for (var flag in app.flags) { |
|
286 |
if (!app.flags.hasOwnProperty(flag)) continue; |
|
287 |
app.flags[flag].match = function (val) { |
|
288 |
return val.match(this.regex) |
|
289 |
} |
|
290 |
} |
|
291 |
|
|
292 |
app.add_theme = function (name, theme) { |
|
293 |
name != null && theme != null && (settings.themes[name] = theme); |
|
294 |
return app; |
|
295 |
}; |
|
296 |
|
|
297 |
app.add_image = function (src, el) { |
|
298 |
var node = selector(el); |
|
299 |
if (node.length) { |
|
300 |
for (var i = 0, l = node.length; i < l; i++) { |
|
301 |
var img = document.createElement("img") |
|
302 |
img.setAttribute("data-src", src); |
|
303 |
node[i].appendChild(img); |
|
304 |
} |
|
305 |
} |
|
306 |
return app; |
|
307 |
}; |
|
308 |
|
|
309 |
app.run = function (o) { |
|
310 |
var options = extend(settings, o), |
|
311 |
images = [], imageNodes = [], bgnodes = []; |
|
312 |
|
|
313 |
if(typeof(options.images) == "string"){ |
|
314 |
imageNodes = selector(options.images); |
|
315 |
} |
|
316 |
else if (window.NodeList && options.images instanceof window.NodeList) { |
|
317 |
imageNodes = options.images; |
|
318 |
} else if (window.Node && options.images instanceof window.Node) { |
|
319 |
imageNodes = [options.images]; |
|
320 |
} |
|
321 |
|
|
322 |
if(typeof(options.bgnodes) == "string"){ |
|
323 |
bgnodes = selector(options.bgnodes); |
|
324 |
} else if (window.NodeList && options.elements instanceof window.NodeList) { |
|
325 |
bgnodes = options.bgnodes; |
|
326 |
} else if (window.Node && options.bgnodes instanceof window.Node) { |
|
327 |
bgnodes = [options.bgnodes]; |
|
328 |
} |
|
329 |
|
|
330 |
preempted = true; |
|
331 |
|
|
332 |
for (i = 0, l = imageNodes.length; i < l; i++) images.push(imageNodes[i]); |
|
333 |
|
|
334 |
var holdercss = document.getElementById("holderjs-style"); |
|
335 |
if (!holdercss) { |
|
336 |
holdercss = document.createElement("style"); |
|
337 |
holdercss.setAttribute("id", "holderjs-style"); |
|
338 |
holdercss.type = "text/css"; |
|
339 |
document.getElementsByTagName("head")[0].appendChild(holdercss); |
|
340 |
} |
|
341 |
|
|
342 |
if (!options.nocss) { |
|
343 |
if (holdercss.styleSheet) { |
|
344 |
holdercss.styleSheet.cssText += options.stylesheet; |
|
345 |
} else { |
|
346 |
holdercss.appendChild(document.createTextNode(options.stylesheet)); |
|
347 |
} |
|
348 |
} |
|
349 |
|
|
350 |
var cssregex = new RegExp(options.domain + "\/(.*?)\"?\\)"); |
|
351 |
|
|
352 |
for (var l = bgnodes.length, i = 0; i < l; i++) { |
|
353 |
var src = window.getComputedStyle(bgnodes[i], null) |
|
354 |
.getPropertyValue("background-image"); |
|
355 |
var flags = src.match(cssregex); |
|
356 |
var bgsrc = bgnodes[i].getAttribute("data-background-src"); |
|
357 |
|
|
358 |
if (flags) { |
|
359 |
var holder = parse_flags(flags[1].split("/"), options); |
|
360 |
if (holder) { |
|
361 |
render("background", bgnodes[i], holder, src); |
|
362 |
} |
|
363 |
} |
|
364 |
else if(bgsrc != null){ |
|
365 |
var holder = parse_flags(bgsrc.substr(bgsrc.lastIndexOf(options.domain) + options.domain.length + 1) |
|
366 |
.split("/"), options); |
|
367 |
if(holder){ |
|
368 |
render("background", bgnodes[i], holder, src); |
|
369 |
} |
|
370 |
} |
|
371 |
} |
|
372 |
|
|
373 |
for (l = images.length, i = 0; i < l; i++) { |
|
374 |
|
|
375 |
var attr_src = attr_data_src = src = null; |
|
376 |
|
|
377 |
try{ |
|
378 |
attr_src = images[i].getAttribute("src"); |
|
379 |
attr_datasrc = images[i].getAttribute("data-src"); |
|
380 |
}catch(e){} |
|
381 |
|
|
382 |
if (attr_datasrc == null && !! attr_src && attr_src.indexOf(options.domain) >= 0) { |
|
383 |
src = attr_src; |
|
384 |
} else if ( !! attr_datasrc && attr_datasrc.indexOf(options.domain) >= 0) { |
|
385 |
src = attr_datasrc; |
|
386 |
} |
|
387 |
|
|
388 |
if (src) { |
|
389 |
var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1) |
|
390 |
.split("/"), options); |
|
391 |
if (holder) { |
|
392 |
if (holder.fluid) { |
|
393 |
render("fluid", images[i], holder, src) |
|
394 |
} else { |
|
395 |
render("image", images[i], holder, src); |
|
396 |
} |
|
397 |
} |
|
398 |
} |
|
399 |
} |
|
400 |
return app; |
|
401 |
}; |
|
402 |
|
|
403 |
contentLoaded(win, function () { |
|
404 |
if (window.addEventListener) { |
|
405 |
window.addEventListener("resize", fluid_update, false); |
|
406 |
window.addEventListener("orientationchange", fluid_update, false); |
|
407 |
} else { |
|
408 |
window.attachEvent("onresize", fluid_update) |
|
409 |
} |
|
410 |
preempted || app.run(); |
|
411 |
}); |
|
412 |
|
|
413 |
if (typeof define === "function" && define.amd) { |
|
414 |
define("Holder", [], function () { |
|
415 |
return app; |
|
416 |
}); |
|
417 |
} |
|
418 |
|
|
419 |
})(Holder, window); |