metanohi-misc-subsites/nohix/modernart.js

437 lines
15 KiB
JavaScript

/*
* modernart.js -- Transforming ordinary pictures into dynamic art
* Copyright (C) 2010 Niels Serup
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License is available at
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
/*
* Version 0.1.1
* Maintainer: Niels Serup <ns@metanohi.name>
*/
/*
* There is no real documentation for this script. Look at
* <http://nohix.metanohi.org/modernart.htm> to get an idea on how to
* implement it. It's very easy.
* This has currently only been tested in Conkeror, IceCat (like
* Firefox) 3.67 and Chromium.
*/
/*****************
*** CHANGELOG ***
0.1.1, September 25 2010 -- Now also works with images with absolute or
fixed positions.
0.1.0, August 5 2010 -- Initial release.
*****************/
createCode = function(dict) {
for (x in dict) {
try {
dict[x].prototype.objectParent = dict;
} catch (e) {};
}
return dict;
};
var modernart = createCode({
ModernArtRunner: function(imgElem, boxElems, lineElems,
colorFunction) {
this.imgElem = imgElem;
this.boxElems = boxElems;
this.lineElems = lineElems;
this.colorFunctions = this.objectParent.colorFunctions;
this.colorFunction = this.colorFunctions[colorFunction];
this.parent = this.imgElem.parentNode;
this.timers = [];
for (var i in this.boxElems) {
for (var j in this.boxElems[i]) {
this.boxElems[i][j].objectParent = this;
this.parent.appendChild(this.boxElems[i][j]);
}
}
for (var i in this.lineElems)
this.parent.appendChild(this.lineElems[i]);
var self = this;
var timerPause = function() {
for (var i in self.timers)
self.timers[i].pause();
};
var timerResume = function() {
for (var i in self.timers)
self.timers[i].resume();
};
var timerToggle = function() {
for (var i in self.timers)
self.timers[i].toggle();
};
this.imgElem.pause = timerPause;
this.imgElem.resume = timerResume;
this.imgElem.toggle = timerToggle;
this.parent.pause = timerPause;
this.parent.resume = timerResume;
this.parent.toggle = timerToggle;
if (this.objectParent.customFunction)
this.objectParent.customFunction(this);
this.rgb = [0, 255, 0];
this.colorFunction();
},
Timer: function(func, delay) {
this.func = func;
this.delay = delay;
this.resume();
},
colorFunctions: {
flow: function() {
var power = parseInt(this.imgElem.getSetting(
'modernflowpower')) || 25;
var flowType = this.imgElem.getSetting(
'modernflowtype') || 'stop';
var flowIgnore = flowType == 'stop';
var xPower = parseInt(power / this.boxElems[0].length);
var yPower = parseInt(power / this.boxElems.length);
var flowRun = function() {
var r = this.rgb[0];
var g = this.rgb[1];
var b = this.rgb[2];
if (r < 255 && g <= 0 && b >= 255)
r += 10;
else if (r > 0 && g >= 255 && b <= 0)
r -= 10;
else if (r >= 255 && g < 255 && b <= 0)
g += 10;
else if (r <= 0 && g > 0 && b >= 255)
g -= 10;
else if (r <= 0 && g >= 255 & b < 255)
b += 10;
else if (r >= 255 && g <= 0 && b > 0)
b -= 10;
this.rgb[0] = r;
this.rgb[1] = g;
this.rgb[2] = b;
var tr, tg, tb;
for (var i in this.boxElems) {
for (var j in this.boxElems[i]) {
if (flowIgnore) {
if (r > 255) tr = 255;
else tr = r;
if (g > 255) tg = 255;
else tg = g;
if (b > 255) tb = 255;
else tb = b;
this.boxElems[i][j].style.setBackground(
tr, tg, tb);
}
else
this.boxElems[i][j].style.setBackground(
r % 256, g % 256, b % 256);
r += xPower;
g += xPower;
b += xPower;
}
r += yPower;
g += yPower;
b += yPower;
}
};
var self = this;
this.timers.push(new this.objectParent.Timer(
function() {flowRun.apply(self);}, 100));
},
individual: function() {
var IndividualBox = function(boxElem) {
this.elem = boxElem;
this.objectParent = this.elem.objectParent;
this.topParent = this.objectParent.objectParent;
this.rgb = [this.topParent.randInt(255),
this.topParent.randInt(255),
this.topParent.randInt(255)];
this.way = [this.topParent.randInt(1) || -1,
this.topParent.randInt(1) || -1,
this.topParent.randInt(1) || -1];
this.generateSpeed();
var self = this;
this.objectParent.timers.push(
new this.topParent.Timer(function() {self.run()}, 100));
};
IndividualBox.prototype.generateSpeed = function() {
this.spd = [this.topParent.randInt(5, 15),
this.topParent.randInt(5, 15),
this.topParent.randInt(5, 15)];
};
IndividualBox.prototype.run = function() {
var rgb = this.rgb;
var i;
this.elem.style.setBackground(rgb[0], rgb[1], rgb[2]);
for (i = 0; i < 3; i++) {
rgb[i] += this.spd[i] * this.way[i];
if (this.rgb[i] >= 255) {
rgb[i] = 255 - (this.spd[i] - (rgb[i] - 255));
this.generateSpeed();
this.way[i] *= -1;
}
else if (this.rgb[i] <= 0) {
rgb[i] = this.rgb[i] + this.spd[i];
this.generateSpeed();
this.way[i] *= -1;
}
}
for (i in rgb)
this.rgb[i] = rgb[i];
};
for (var i in this.boxElems) {
for (var j in this.boxElems[i])
new IndividualBox(this.boxElems[i][j]);
}
}
},
applyEffectToImages: function() {
var candidates = document.getElementsByTagName('img');
for (var i = 0, node; node = candidates[i++];) {
if (node.className.indexOf('modernart') != -1) {
if (arguments.length > 0)
this.applyEffectToImage(node, arguments[0]);
else
this.applyEffectToImage(node);
}
}
},
applyEffectToImage: function(imgElem) {
var i, j;
var size = [imgElem.offsetWidth, imgElem.offsetHeight];
var parent = imgElem.parentNode;
var colorFunction = imgElem.getSetting('moderncolor') || 'flow';
var rectSize = parseInt(imgElem.getSetting('modernrect')) || 20;
var lineWidth = parseInt(imgElem.getSetting('modernline'));
if (isNaN(lineWidth))
lineWidth = 5;
var opacity = parseFloat(imgElem.getSetting('modernopacity')) || 0.5;
var numbers = parseInt(imgElem.getSetting('modernnumbers'));
if (arguments.length > 1)
this.customFunction = arguments[1];
else
this.customFunction = null;
if (!numbers) {
numbers = [];
var f = rectSize + lineWidth;
for (i = 0; i < 2; i++)
numbers.push(parseInt((size[i] + lineWidth) / f));
}
else {
var relativeTo = imgElem.getSetting(
'modernnumbersrel') || 'x';
if (relativeTo == 'x') {
numbers = [numbers,
parseInt(size[1] / (size[0] / numbers))];
rectSize = ((size[0] + lineWidth) /
numbers[0] - lineWidth);
}
else { // y
numbers = [parseInt(size[0] / (size[1] / numbers)),
numbers];
rectSize = ((size[1] + lineWidth) /
numbers[1] - lineWidth);
}
}
if (lineWidth == 0) {
rectSize = [];
for (i = 0; i < 2; i++)
rectSize.push(size[i] / numbers[i]);
lineWidth = [0, 0];
}
else {
lineWidth = [];
for (i = 0; i < 2; i++)
lineWidth.push((size[i] - numbers[i] * rectSize) /
(numbers[i] - 1));
rectSize = [rectSize, rectSize];
}
var container = document.createElement('div');
container.className = imgElem.className;
container.style.set('width', size[0] + 'px');
container.style.set('height', size[1] + 'px');
container.style.set('padding', '0');
container.style.set('display', 'inline-block');
container.style.set('float', 'none');
found_pos = imgElem.style.getPropertyValue('position');
container.style.set('position', imgElem.getSetting('modernposition')
|| found_pos || 'relative');
try {
var property, value;
for (i in imgElem.style) {
property = imgElem.style[i];
value = imgElem.style.getPropertyValue(property);
if (value)
container.style.set(property, value);
}
} catch (e) {};
imgElem.style.set('border', 'none');
imgElem.style.set('margin', '0');
imgElem.style.set('left', '0');
imgElem.style.set('top', '0');
imgElem.style.set('background-color', '#000');
parent.insertBefore(container, imgElem);
parent.removeChild(imgElem);
container.appendChild(imgElem);
var elemPos = [0, 0];
var boxElems = [];
for (i = 0; i < numbers[1]; i++) {
boxes = [];
for (j = 0; j < numbers[0]; j++) {
elem = document.createElement('div');
elem.style.set('width', rectSize[0] + 'px');
elem.style.set('height', rectSize[1] + 'px');
elem.style.set('background-color', 'transparent');
elem.style.set('position', 'absolute');
elem.style.set('left', elemPos[0] + 'px');
elem.style.set('top', elemPos[1] + 'px');
elem.style.set('margin', '0');
elem.style.set('padding', '0');
elem.style.set('border', 'none');
elem.style.setOpacity(opacity);
boxes.push(elem);
elemPos[0] += rectSize[0] + lineWidth[0];
}
boxElems.push(boxes);
elemPos[1] += rectSize[1] + lineWidth[1];
elemPos[0] = 0;
}
var lineElems = [];
if (lineWidth[0] > 0) {
var tempWidth, tempHeight, tempTop, tempLeft;
for (i = 0; i < 2; i++) {
elemPos = rectSize[i];
if (i == 0) {
tempWidth = lineWidth[0];
tempHeight = size[1];
}
else {
tempWidth = size[0];
tempHeight = lineWidth[1];
}
for (j = 0; j < numbers[i] - 1; j++) {
if (i == 0) {
tempLeft = elemPos;
tempTop = 0;
}
else {
tempLeft = 0;
tempTop = elemPos;
}
elem = document.createElement('div');
elem.style.set('width', tempWidth + 'px');
elem.style.set('height', tempHeight + 'px');
elem.style.set('background-color', '#000');
elem.style.set('position', 'absolute');
elem.style.set('left', tempLeft + 'px');
elem.style.set('top', tempTop + 'px');
elem.style.set('margin', '0');
elem.style.set('padding', '0');
elem.style.set('border', 'none');
lineElems.push(elem);
elemPos += rectSize[i] + lineWidth[i];
}
}
}
new this.ModernArtRunner(imgElem, boxElems, lineElems, colorFunction);
},
randInt: function() { // function([min, ]max)
if (arguments.length == 0)
return 0;
var min, max;
if (arguments.length > 1) {
min = arguments[0];
max = arguments[1];
}
else {
min = 0;
max = arguments[0];
}
return Math.floor(Math.random() * (max + 1)) + min;
}
});
delete createCode;
modernart.Timer.prototype.resume = function() {
this.interval = setInterval(this.func, this.delay);
this.running = true;
};
modernart.Timer.prototype.pause = function() {
clearInterval(this.interval);
delete this.interval;
this.running = false;
};
modernart.Timer.prototype.toggle = function() {
if (this.running)
this.pause();
else
this.resume();
};
HTMLElement.prototype.getSetting = function(property) {
var string = this.className;
var start, end;
start = string.indexOf(property + ':');
if (start != -1) {
end = string.indexOf(' ', start);
if (end == -1)
return string.substring(start + property.length + 1);
else
return string.substring(start + property.length + 1, end);
}
};
CSSStyleDeclaration.prototype.set = function(property, value) {
this.setProperty(property, value, null);
};
CSSStyleDeclaration.prototype.setOpacity = function(val) {
this.set('opacity', val);
this.set('-moz-opacity', val);
this.set('-khtml-opacity', val);
this.filter = 'alpha(opacity=' + val*100 + ')';
};
CSSStyleDeclaration.prototype.setBackground = function(r, g, b) {
this.set('background-color', 'rgb('+r+', '+g+', '+b+')');
};