Created
April 9, 2014 20:47
-
-
Save uniqname/10313382 to your computer and use it in GitHub Desktop.
polyPict - <picture> polyfill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
window.addEventListener("DOMContentLoaded", function() { | |
//Use live-node-lists here so we don't have to query the DOM everytime polyPict runs. | |
var picturesLNL = document.getElementsByTagName("picture"), | |
imgLNL = document.getElementsByTagName("img"), | |
size2Px = (function() { | |
var testEl = document.createElement("div"); | |
testEl.style.position = "absolute"; | |
testEl.style.zIndex = -1000; | |
testEl.style.height = 0; | |
testEl.style.opacity = 0; | |
document.body.appendChild(testEl); | |
return function(size) { | |
testEl.style.width = size; | |
return parseInt(window.getComputedStyle(testEl).width, 10); | |
}; | |
})(), | |
devicePxRatio = window.devicePixelRatio || 1, | |
parseSizesAttr = function(sizes) { | |
var parsed = {}, | |
mq, | |
size; | |
sizes = sizes.split(/,\s*/); | |
sizes.forEach(function(sizePair) { | |
sizePair = sizePair.split(/\)\s*/); | |
if (sizePair.length === 1) { | |
//a blank or white-space only mq is always true, | |
// so we use this as the fallback mq; | |
parsed[" "] = sizePair[0]; | |
} else if (sizePair.length === 2) { | |
parsed[sizePair[0] + ")"] = sizePair[1]; | |
} | |
}); | |
return parsed; | |
}, | |
isMatchingSize = function(sizesObj) { | |
var mq, size; | |
for (mq in sizesObj) { | |
if (sizesObj.hasOwnProperty(mq)) { | |
size = sizesObj[mq]; | |
if (matchMedia(mq).matches) { | |
return size; | |
} | |
} | |
} | |
return undefined; | |
}, | |
parseSrcsetAttr = function(srcset) { | |
var parsed = {}, | |
src, | |
w, | |
res; | |
srcset = srcset.split(/,\s*/); | |
srcset.forEach(function(srcPair) { | |
var w, res, | |
properProperty = function(value, srcsetObj) { | |
if (value.match(/w$/)) { | |
srcsetObj.width = parseInt(value.replace(/w$/, ""), 10); | |
} else if (value.match(/x$/)) { | |
srcsetObj.pixelRatio = parseFloat(value.replace(/x$/, "")); | |
} | |
}; | |
srcPair = srcPair.split(/\s+/); | |
if (srcPair.length === 1) { | |
parsed. | |
default = {}; | |
properProperty(srcPair[0], parsed. | |
default); | |
} else if (srcPair.length === 2) { | |
parsed[srcPair[0]] = {}; | |
properProperty(srcPair[1], parsed[srcPair[0]]); | |
} | |
}); | |
return parsed; | |
}, | |
getSrc = function(srcsetObj, size) { | |
var imgSize = size2Px(size); | |
}, | |
polyPict = function() { | |
//unique list of images with either sizes or srcset attrs. | |
var pictyImgs = Array.prototype.filter.call(imgLNL, function(img) { | |
return img.getAttribute("sizes") != void 0; | |
}), | |
getFinalSrc = function(source) { | |
var sizes = parseSizesAttr(source.getAttribute("sizes")), | |
mqs = Object.keys(sizes), | |
mq = mqs.find(function(mq) { | |
return matchMedia(mq).matches; | |
}), | |
//note mq's companion `size` from selected <source> || <img> | |
//convert `size` to px -- this is the desired img src size | |
size = size2Px(sizes[mq]), | |
src, w, x, | |
currentW, currentX, | |
finalSrc; | |
if (mq) { | |
//from matched <source> || <img> | |
srcset = parseSrcsetAttr(source.getAttribute("srcset")); | |
for (src in srcset) { | |
if (srcset.hasOwnProperty(src)) { | |
//if `srcset` attr has `w` descriptors | |
if (srcset[src].width) { | |
currentW = srcset[src].width; | |
//find `w` descriptor that has smallest value > `size` | |
if (currentW >= size && (currentW < w || !w)) { | |
window.console.log(currentW + " >= " + size + " | " + currentW + " < " + w); | |
w = currentW; | |
//use companion `src` as controlling <img /> src. | |
window.console.log(finalSrc + " > " + src + " > size"); | |
finalSrc = src; | |
} | |
//if `srcset` attr has device-pixel-ratio descriptors (`x`) | |
} else if (srcset[src].pixelRatio) { | |
currentX = srcset[src].pixelRatio; | |
//find `x` descriptor that has smallest value > device-px-ratio | |
if (currentX >= devicePxRatio && (crrentX < x || !x)) { | |
x = currentX; | |
//use companion `src` as controlling <img /> src. | |
finalSrc = src; | |
} | |
} | |
} | |
} | |
if (this.src !== finalSrc) { | |
window.console.log("switching to " + finalSrc + "on ", this); | |
this.src = finalSrc; | |
} | |
} | |
return mq; | |
}; | |
pictyImgs.forEach(function(img) { | |
getFinalSrc.call(img, img); | |
}); | |
Array.prototype.slice.call(picturesLNL, 0).forEach(function(pic) { | |
// we reverse the array to select the last match first. | |
var controllingImg = pic.querySelector("img"), | |
sources = Array.prototype.slice.call(pic.querySelectorAll("source"), 0).reverse(); | |
//select last <source> with matching mq in `sizes` attr | |
sources.find(getFinalSrc, controllingImg); | |
}); | |
}; | |
polyPict(); | |
window.addEventListener("resize", polyPict); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment