This repository has been archived on 2026-03-31. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
franz/js/franz.js

336 lines
11 KiB
JavaScript

/* Franz.js - Client side color swatches (awesomeness)
*
* @Author Ryan McGrath (http://twitter.com/ryanmcgrath)
* @Author Dominick Pham (http://twitter.com/enotionz)
*/
if(!Array.prototype.swap) {
Array.prototype.swap = function(a, b) {
var tmp = this[a];
this[a] = this[b];
this[b] = tmp;
return true; /* For the sake of being complete */
}
}
var franz = function(franzProps) {
/* new franz({
canvas: "canvas_id",
src: "image_src",
callback: fn() { ... }
});
Stores a reference to our canvas and the canvas context for future use. Accepts
an optional callback function to tie this into further methods.
Note: Since there's no way to get the type of <canvas> support we need in Internet Explorer,
we actually branch the codepath here - IE gets a small flash applet that handles pulling color data out. We additionally serve
this if, for some reason, <canvas> support is lacking or broken in newer browser (or Firefox 2, etc).
The data is still parsed via JS by using externalInterface() callbacks into the Flash applet, so it's safe to
assume that anything occuring after getColors() is back in global-browser-use land.
*/
this.canvas_id = franzProps.canvas;
if(typeof document.createElement("canvas").getContext === "function") {
/* Canvas is natively supported, go down the normal path... append a new canvas off the viewport and draw our image onto it */
this.src = franzProps.src;
this.canvas = document.createElement("canvas");
this.canvas.width = 100;
this.canvas.height = 100;
this.canvas.style.visibility = "hidden";
this.canvas.style.position = "absolute";
this.canvas.style.top = 0 + "px";
this.canvas.style.left = "-" + 1000 + "px";
this.ctx = this.canvas.getContext('2d');
var that = this;
franz.util.loadEvent(function() {
document.body.appendChild(that.canvas);
typeof that.canvas_id !== 'undefined' ? that.draw(that.canvas_id) : that.draw();
if(typeof franzProps.callback !== "undefined" && typeof franzProps.callback === "function") franzProps.callback();
});
} else {
/* We're dealing with something that belongs in an old folks home; throw in the life support. (Embed Flash junk) :(
Inject SWFObject so we can deal with Flash embedding in a sane manner...
*/
if(typeof swfobject === "undefined") {
var newScript = document.createElement("script");
newScript.type = "text/javascript";
newScript.src = "/js/swfobject.js";
document.getElementsByTagName("head")[0].appendChild(newScript);
if(typeof franzProps.callback !== "undefined" && typeof franzProps.callback === "function") {
// Catch the callback function for IE...
newScript.onreadystatechange = function() {
if(newScript.readyState == "loaded" || newScript.readyState == "complete") franzProps.callback();
return false;
};
// Catch all other misguided browsers, just in case (Really old versions of Safari that don't have good <canvas> support will fail totally, but that's far too much to care about)
newScript.onload = function() {
franzProps.callback();
return false;
}
}
return false;
}
// If swfobject was already appended once, just catch the callback and run
if(typeof franzProps.callback !== "undefined" && typeof franzProps.callback === "function") franzProps.callback();
}
}
franz.prototype = {
rgba: [],
hsb: [],
hsl: [],
extra_canvas: null,
extra_ctx: null,
draw: function(different_canvas) {
/* If you're looking for the non-<canvas> portion of this, check the associated Flash files. */
if(typeof different_canvas !== "undefined" && typeof different_canvas === "string") {
this.extra_canvas = document.getElementById(different_canvas);
this.extra_ctx = this.extra_canvas.getContext('2d');
}
var working = this;
working.img = new Image();
working.img.onload = function() {
(working.extra_ctx === null ? working.ctx : working.extra_ctx).drawImage(working.img, 0, 0, 100, 100);
setTimeout(function() { working.getColors(); }, 100);
};
working.img.src = this.src;
},
getColors: function() {
/* If you're looking for the non-<canvas> portion of this, check the associated Flash files. */
var hidden_canvas = document.getElementById("lol_hidden"),
extra_ctx = hidden_canvas.getContext('2d');
extra_ctx.drawImage(this.img, 0, 0, 33, 33);
var imageData = extra_ctx.getImageData(1, 1, 32, 32).data;
for(var i = 0; i*4 < imageData.length; i++) {
this.rgba[i] = new Array(4);
this.rgba[i][0] = imageData[i*4];
this.rgba[i][1] = imageData[i*4 + 1];
this.rgba[i][2] = imageData[i*4 + 2];
this.rgba[i][3] = imageData[i*4 + 3];
}
/* get hue sat val array */
this.getHSB();
/* show original image */
this.displayImg();
return false;
},
/* Converts RGB to the Hue/Saturation/Brightness*/
getHSB: function() {
if ( this.rgba.length > 0 ){
for(var i = 0; i < this.rgba.length; i++) {
this.hsb[i] = franz.util.RGBtoHSB(this.rgba[i]);
this.hsb[i][3] = i; /* this is to keep the original index reference to the rgb domain */
}
return true;
} else { return false; }
},
displayColors: function(order_array) {
var docStr = "";
if (typeof order_array === 'undefined'){
for(var i = 0; i < this.rgba.length; i++) {
docStr += '<div class="color_boxd" style="background-color: rgb(' + this.rgba[i][0] + ', ' + this.rgba[i][1] + ',' + this.rgba[i][2] + ');"></div>';
}
} else {
for(var i = 0; i < this.rgba.length; i++) {
docStr += '<div class="color_boxd" style="background-color: rgb(' + this.rgba[order_array[i]][0] + ', ' + this.rgba[order_array[i]][1] + ',' + this.rgba[order_array[i]][2] + ');"></div>';
}
}
document.getElementById("log_colors").innerHTML = docStr;
if(typeof jQuery !== "undefined") {
$("#container_bottom").fadeIn("fast");
setTimeout(function() { $("#testLayout").fadeIn("slow"); }, 500);
}
else document.getElementById("container_bottom").style.display = "block";
return false;
},
displayImg: function() {
this.displayColors();
return false;
},
displayHue: function() { this.sortArray(this.hsb,0); this.displayOut(this.hsb); },
displaySat: function() { this.sortArray(this.hsb,1); this.displayOut(this.hsb);},
displayBright: function() { this.sortArray(this.hsb,2); this.displayOut(this.hsb);},
displayOut: function(array) {
var index = [];
for(var i=0;i<array.length;i++){index[i] = array[i][3];} /* rebuilding index */
this.displayColors(index);
return false;
},
/* array should be a (rgba.length)x4 2d array where position 0,1, or 2
eg: pass in hsb ==> hsb[] = [h,s,b,index][]
position = 0 ---> sort by hue
position = 1 ---> sort by saturation
position = 2 ---> sort by brightness
*/
sortArray: function(array,position) {
array.sort(function(a,b){ return a[position] - b[position]; });
},
/* calculates density of array data given interval and step size - both controlling data error
step size should ideally be < interval. Returns array with (# of intervals) as size */
getDensity: function(inputArray, step, interval, max) {
var size = max/step;
var densityArray = new Array(size);
for (var i=0; i < size; i++) {
var count = 0;
for (var j=0; j < inputArray.length; j++) {
// if entry is within current interval
if ((inputArray[j] > i*step) && (inputArray[j] < i*step + interval)){
count++;
}
}
densityArray[i] = count;
}
return densityArray;
}
}
franz.util = {
RGBtoHSB: function(rgb) {
var hsb = new Array(3);
var min = Math.min(rgb[0], Math.min(rgb[1], rgb[2])),
max = Math.max(rgb[0], Math.max(rgb[1], rgb[2])),
delta = max - min;
hsb[0] = this.getHue(rgb,min,max,delta); /* hue */
hsb[1] = delta/max; /* saturation */
hsb[2] = max; /* brightness */
return hsb;
},
RGBtoHSL: function(rgb) {
var min = Math.min(rgb[0], Math.min(rgb[1], rgb[2])),
max = Math.max(rgb[0], Math.max(rgb[1], rgb[2])),
delta = max-min;
var hsl = new Array(3), sat, lightness = 0.5*(min + max);
if(min == max) sat = 0;
if(lightness < 1/2) sat = (max-min)/(max+min);
else sat = (max - min) / (2 - (max + min));
hsl[0] = this.getHue(rgb,min,max,delta); /* hue */
hsl[1] = sat; /* saturation */
hsl[2] = lightness; /* lightness */
return hsl;
},
/* hue is shared for both hsl and hsv */
getHue: function(rgb,min,max,delta) {
var hue;
if (max === min) { return 0; } else {
if(rgb[0] === max)
hue = (rgb[1] - rgb[2]) / delta; //between yellow & magenta
else if(rgb[1] === max)
hue = 2 + (rgb[2] - rgb[0]) / delta; //between cyan & yellow
else
hue = 4 + (rgb[0] - rgb[1]) / delta; //between magenta & cyan
// hue degrees
hue = parseInt(hue * 60);
if(hue < 0) hue += 360;
}
return hue;
},
RGBtoHex: function(rgb) {
var hex = [];
if(rgb[3] == 0) return 'transparent';
for(var i = 0; i < 3; i++) {
var bit = (rgb[i] - 0).toString(16);
hex.push(bit.length == 1 ? ('0' + bit) : bit);
}
return '#' + hex.join('');
},
clone: function(obj) {
/* Utility function for deep cloning */
if(typeof obj !== "undefined") {
var returnObj = (obj instanceof Array) ? [] : {};
for(i in obj) {
if(obj[i] != null && typeof obj[i] == "object") {
returnObj[i] = franz.util.clone(obj[i]);
} else {
returnObj[i] = obj[i];
}
}
return returnObj;
}
},
loadEvent: function(func) {
if(typeof jQuery != "undefined") jQuery(document).ready(function() { func(); });
else franz.util.fallback_loadEvent(function() { func(); });
},
fallback_loadEvent: (function() {
var load_events = [], load_timer, script, done, exec, old_onload,
init = function () {
done = true;
clearInterval(load_timer);
for(i = 0; i < load_events.length; i++) {
exec = load_events.shift();
exec();
}
if(script) script.onreadystatechange = '';
};
return function(func) {
if(done) return func();
if(!load_events[0]) {
if(document.addEventListener) document.addEventListener("DOMContentLoaded", init, false);
/*@cc_on @*/ /*@if (@_win32) // Fairly obvious who this is for...
document.write("<script id=__ie_onload defer src=//0><\/scr"+"ipt>");
script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") init(); // call the onload handler
};
/*@end @*/
if(/WebKit/i.test(navigator.userAgent)) {
load_timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) init();
}, 10);
}
old_onload = window.onload;
window.onload = function() {
init();
if (old_onload) old_onload();
};
}
load_events.push(func);
}
})()
}