Skip to content

Instantly share code, notes, and snippets.

@GuoJing
Forked from froots/image_replacer.js
Created December 18, 2012 14:14
Show Gist options
  • Save GuoJing/4328306 to your computer and use it in GitHub Desktop.
Save GuoJing/4328306 to your computer and use it in GitHub Desktop.
if (typeof (AC) === "undefined") {
AC = {}
}
AC.ImageReplacer = Class.create({
_defaultOptions: {
listenToSwapView: true,
filenameRegex: /(.*)(\.[a-z]{3}($|#.*|\?.*))/i,
filenameInsert: "_☃x",
ignoreCheck: /(^http:\/\/movies\.apple\.com\/|\/105\/|\/global\/elements\/quicktime\/|_(([2-9]|[1-9][0-9]+)x|nohires)(\.[a-z]{3})($|#.*|\?.*))/i,
attribute: "data-hires",
recursive: true,
preload: false,
checkExists: true,
queueSize: 8,
debug: false
},
_lowestPriority: 2,
__replacedAttribute: "data-hires-status",
initialize: function (b) {
if (typeof b !== "object") {
b = {}
}
this.options = Object.extend(Object.clone(this._defaultOptions), b);
this.options.lowestPriority = this._lowestPriority;
this.options.replacedAttribute = this.__replacedAttribute;
if ((this.options.debug !== true) && ((typeof AC.Detector !== "undefined" && AC.Detector.isMobile()) || (AC.ImageReplacer.devicePixelRatio() <= 1))) {
return
}
if (this.options.debug === true) {
AC.ImageReplacer._devicePixelRatio = 2
}
Object.synthesize(this);
if (windowHasLoaded) {
this.__setup()
} else {
var a = this.__setup.bind(this);
Event.observe(window, "load", a)
}
},
log: function () {
if (this.__canLog !== false && this.options.debug === true) {
var a = $A(arguments);
if (a.length < 2) {
a = a[0]
}
try {
console.log(a)
} catch (b) {
this.__canLog = false
}
}
},
isReplaceable: function (c) {
if ((c.getAttribute(this.options.attribute) === "false") || (c.up("[" + this.options.attribute + '="false"]') && this.options.recursive === true)) {
return false
}
var a = (typeof c.responsiveImageObject === "undefined");
if (c.tagName.toLowerCase() === "img") {
return a
} else {
if (c.hasClassName("imageLink") && c.tagName.toLowerCase() === "a") {
return true
} else {
var b = AC.ImageReplacer.Image.removeCSSURLSyntax(c.getStyle("background-image"));
return (((b.match(AC.ImageReplacer.normalImageTypeRegex) !== null) && a))
}
}
},
potentialElements: function (g, f) {
if (typeof g === "undefined") {
g = document.body
}
var b = $(g).getElementsBySelector("[" + this.options.attribute + "]");
var a;
var c = function (h) {
if (typeof f === "undefined") {
return typeof g.up("[" + h + "]") !== "undefined"
} else {
return f.getAttribute(h) !== null || typeof f.up("[" + h + "]") !== "undefined"
}
};
if (this.options.recursive === true) {
if (g !== document.body && c(this.options.attribute)) {
b = b.concat(g)
}
a = [];
var e = this.isReplaceable.bind(this);
var d = function (h) {
if (e(h)) {
a.push(h)
}
a = a.concat(this.replaceableElementsWithinElement(h))
}.bind(this);
$A(b).each(d)
} else {
a = b
}
return a
},
prioritize: function (a) {
var c = [];
var d = function (f) {
if (typeof f.responsiveImageObject !== "undefined") {
return
}
var e = new AC.ImageReplacer.Image(f, this.options);
if (e.hiResSrc() !== null && !e.isHiRes()) {
if (typeof c[e.priority()] === "undefined") {
c[e.priority()] = []
}
c[e.priority()].push(e)
} else {
if (e.hiResSrc() && e.isHiRes()) {
e.setStatus("already-hires")
} else {
e.setStatus("not-replaceable")
}
}
}.bind(this);
$A(a).each(d);
var b;
for (b = this._lowestPriority;
b >= 0; b--) {
if (typeof c[b] === "undefined") {
c[b] = []
}
}
return c.flatten()
},
replaceableElementsWithinElement: function (d) {
d = $(d);
var a = this;
var b = d.descendants();
var c = this.isReplaceable.bind(this);
return b.findAll(c)
},
addToQueue: function (a) {
if (typeof this.__queues === "undefined") {
this.__queues = $A()
}
if (this.__queues.length === 0) {
this.__queues.push($A())
}
this.__queues[this.__queues.length - 1].push(a)
},
replace: function () {
if (typeof this.__queues === "undefined") {
this.__queues = $A()
}
if (this.__queues.length > 0 && this.__queues[0].length > 0) {
this.__queues.push($A());
var a = this.replace.bind(this);
this.__replaceNextQueue(a)
} else {
this.log("There are no queues to replace")
}
},
__replaceNextQueue: function (d) {
var a = this.__queues[0].reverse();
var b = this.log.bind(this);
this.__queues.splice(0, 1);
var c = function () {
b("Found " + a.length + " elements to replace.");
var e = function () {
var f = a.pop();
if (!f) {
b("No more images to start replacing.");
if (typeof d === "function") {
d()
}
d = Prototype.emptyFunction;
return
}
f.replace(function (g) {
b("Replaced image.", f.hiResSrc(), "status: " + f.status());
e()
})
};
$R(0, this.options.queueSize - 1).each(e)
}.bind(this);
window.setTimeout(c, 10)
},
__respondToSwapView: function (c) {
var b = c.event_data.data.incomingView.content;
var a = c.event_data.data.sender.view.view();
var d = this.addToQueue.bind(this);
this.prioritize(this.potentialElements(b, a)).each(d);
this.replace()
},
__setup: function () {
if (this.options.listenToSwapView === true && "Listener" in Event && typeof AC.ViewMaster !== "undefined") {
this.respondToSwapView = this.__respondToSwapView.bindAsEventListener(this);
Event.Listener.listenForEvent(AC.ViewMaster, "ViewMasterDidShowNotification", false, this.respondToSwapView)
}
var a = this.addToQueue.bind(this);
this.prioritize(this.potentialElements()).each(a);
this.replace()
}
});
AC.ImageReplacer.normalImageTypeRegex = /(\.jpg($|#.*|\?.*)|\.png($|#.*|\?.*)|\.gif($|#.*|\?.*))/;
AC.ImageReplacer.devicePixelRatio = function () {
if (typeof AC.ImageReplacer._devicePixelRatio !== "undefined") {
return AC.ImageReplacer._devicePixelRatio
}
if ("devicePixelRatio" in window && window.devicePixelRatio > 1) {
return AC.ImageReplacer._devicePixelRatio = 2
} else {
return AC.ImageReplacer._devicePixelRatio = 1
}
};
AC.ImageReplacer.Image = Class.create(Object.clone(AC.Synthesize), {
initialize: function (b, a) {
if (Object.isElement(b)) {
this._el = b;
this._tagName = this._el.tagName.toLowerCase();
this.options = Object.extend(Object.clone(a), AC.ImageReplacer.Image.convertParametersToOptions(this.src()));
this.setStatus("considered");
this.synthesize()
}
},
__synthesizeSetter: Prototype.emptyFunction,
preload: function (b) {
if (this._isPreloaded) {
return true
}
this.setStatus("loading");
var a = new Element("img");
a.observe("load", function () {
this._isPreloaded = true;
this.width = a.width;
this.height = a.height;
this.setStatus("replaced");
if (typeof b === "function") {
b()
}
}.bind(this));
a.observe("error", function () {
this.setStatus("404");
this._exists = false;
if (typeof b === "function") {
b()
}
}.bind(this));
a.src = this.hiResSrc()
},
replace: function (b) {
var a = this.replace.bind(this, b);
if (this._exists === false) {
this.setStatus("404");
if (typeof b === "function") {
b(false)
}
return
}
if (this.options.checkExists === true && typeof this._exists === "undefined") {
return this.requestHeaders(a)
}
if (this.isImageLink()) {
this._el.setAttribute("href", this.hiResSrc());
this.setStatus("replaced");
if (typeof b === "function") {
b(true)
}
} else {
if ((this.options.preload === true || this._tagName !== "img") && this._isPreloaded !== true) {
return this.preload(a)
}
if (this._tagName === "img") {
this._el.setAttribute("src", this.hiResSrc());
if ((this.options.preload !== true)) {
this.setStatus("loading");
this._el.observe("load", function (c) {
this.setStatus("replaced");
if (typeof b === "function") {
b(true)
}
}.bindAsEventListener(this));
this._el.observe("error", function (c) {
this.setStatus("404");
this._el.setAttribute("src", this.src());
if (typeof b === "function") {
b(false)
}
}.bindAsEventListener(this))
}
} else {
this._el.setStyle("background-image:url(" + this.hiResSrc() + ");");
this._el.setStyle("background-size:" + (this.width / AC.ImageReplacer.devicePixelRatio()) + "px " + (this.height / AC.ImageReplacer.devicePixelRatio()) + "px;");
if (typeof b === "function") {
b(true)
}
}
}
this._el.responsiveImageObject = this;
this.synthesize()
},
requestHeaders: function (c) {
var a = this;
if (typeof a._headers === "undefined") {
var b = new XMLHttpRequest();
var a = this;
src = this.hiResSrc().replace(/^http:\/\/.*\.apple\.com\//, "/");
b.open("HEAD", src, true);
b.onreadystatechange = function () {
if (b.readyState == 4) {
if (b.status === 200) {
a._exists = true;
var f = b.getAllResponseHeaders();
a._headers = {
src: src
};
var d, e;
f = f.split("\r");
for (d = 0;
d < f.length; d++) {
e = f[d].split(": ");
if (e.length > 1) {
a._headers[e[0].replace("\n", "")] = e[1]
}
}
} else {
a._exists = false;
a._headers = null
}
if (typeof c === "function") {
c(a._headers, a)
}
}
};
b.send(null)
} else {
c(a._headers, a)
}
},
requestFileSize: function (c) {
var b = this;
if (typeof b._fileSize === "undefined") {
var a = function (d) {
b._fileSize = parseFloat(d["Content-Length"]) / 1000;
if (typeof c === "function") {
c(b._fileSize, b)
}
};
this.requestHeaders(a)
} else {
c(b._fileSize, b)
}
},
src: function () {
if (typeof this._src !== "undefined") {
return this._src
}
if (this.isImageLink()) {
this._src = this._el.getAttribute("href")
} else {
if (this._tagName === "img") {
this._src = this._el.getAttribute("src")
} else {
this._src = AC.ImageReplacer.Image.removeCSSURLSyntax(this._el.getStyle("background-image"));
if (this._src === "none") {
return this._src = ""
}
}
}
return this._src
},
hiResSrc: function () {
if (typeof this._hiResSrc !== "undefined") {
return this._hiResSrc
}
var a;
if (typeof this.options.hiresFormat === "string") {
a = this.src().match(/^(.*)((\.[a-z]{3})($|#.*|\?.*))/i);
if (a !== null && a.length > 1) {
return this._hiResSrc = a[1] + "." + this.options.hiresFormat + (a[4] || "")
}
}
a = this.src().match(this.options.filenameRegex);
if (a === null) {
return this._hiResSrc = null
} else {
return this._hiResSrc = a[1] + this.options.filenameInsert.replace("☃", AC.ImageReplacer.devicePixelRatio()) + a[2]
}
},
isHiRes: function () {
if (this._isHiRes === true) {
return this._isHiRes
}
if (this.status() === "replaced") {
return this._isHiRes = true
}
var a = this.src();
if (a.match(AC.ImageReplacer.normalImageTypeRegex) === null) {
return this._isHiRes = true
}
if (a.match(this.options.ignoreCheck) !== null) {
return this._isHiRes = true
}
this._isHiRes = false
},
isImageLink: function () {
if (typeof this._isImageLink !== "undefined") {
return this._isImageLink
}
return this._isImageLink = (this._el.hasClassName("imageLink") && this._tagName === "a")
},
priority: function () {
if (typeof this._priority !== "undefined") {
return this._priority
}
if (this.options.recursive && this._el.hasAttribute(this.options.attribute) === false) {
var a = this._el.up("[" + this.options.attribute + "]");
if ( !! a) {
this._priority = parseInt(a.getAttribute(this.options.attribute))
} else {
this._priority = this.options.lowestPriority
}
} else {
this._priority = parseInt(this._el.getAttribute(this.options.attribute))
}
if (isNaN(this._priority) || this._priority > this.options.lowestPriority) {
this._priority = this.options.lowestPriority
} else {
if (this._priority < 0) {
this._priority = 0
}
}
return this._priority
},
setStatus: function (a) {
if (typeof a === "string") {
this._status = a;
this._el.setAttribute(this.options.replacedAttribute, a)
}
},
status: function () {
return this._el.getAttribute(this.options.replacedAttribute)
}
});
AC.ImageReplacer.Image.removeCSSURLSyntax = function (a) {
if (typeof a === "string" && typeof a.replace === "function") {
return a.replace(/^url\(/, "").replace(/\)$/, "")
}
return ""
};
AC.ImageReplacer.Image.convertParametersToOptions = function (b) {
if (typeof b === "string" && typeof b.toQueryParams === "function") {
var a = b.toQueryParams(),
c;
for (c in a) {
if (a.hasOwnProperty(c)) {
a[c.camelize()] = a[c]
}
}
return a
}
return {}
};
AC.ImageReplacer.autoInstance = new AC.ImageReplacer();
var windowHasLoaded = false;
Event.observe(window, "load", function () {
windowHasLoaded = true
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment