Skip to content

Instantly share code, notes, and snippets.

@danyg
Last active August 26, 2017 11:09
Show Gist options
  • Save danyg/0f70c62f2fdd1cb91c20cee0697e6e69 to your computer and use it in GitHub Desktop.
Save danyg/0f70c62f2fdd1cb91c20cee0697e6e69 to your computer and use it in GitHub Desktop.
Webs Color Correction
let template = `
<div id="__COLOR__">
<label>
<strong>Contrast:</strong>
<input id="__COLOR__contrast" type ="range" min ="0" max="200" step ="1" value="100" data-initial="100"/>
</label>
<label>
<strong>Brightness:</strong>
<input id="__COLOR__brightness" type ="range" min ="0" max="200" step ="1" value="100" data-initial="100"/>
</label>
<label>
<strong>Polarity:</strong>
<input id="__COLOR__invert" type ="range" min ="0" max="100" step ="1" value="0" data-initial="0"/>
</label>
<label>
<strong>Background:</strong>
<input id="__COLOR__background" type ="range" min ="0" max="255" step ="1" value="255" data-initial="255"/>
</label>
<label>
<strong>Darkness:</strong>
<input id="__COLOR__darkness" type ="range" min ="0" max="255" step ="1" value="255" data-initial="255"/>
</label>
<label>
<strong>Actions:</strong>
<button id="__COLOR__reset_btn">Reset</button>
</label>
</div>
`;
let styles = `
#__COLOR__ {
position: fixed;
z-index: 99999999999999999;
top: 0;
right: 0;
padding: 5px;
width: 1px;
height: 1px;
background: #222;
color: #eee;
overflow: hidden;
transition: width 500ms, height 300ms;
display: flex;
flex-direction: column;
}
#__COLOR__:hover {
width: 400px;
height: 250px;
}
#__COLOR__ label {
display: flex;
flex-grow: 1;
align-items: center;
color: #eee;
}
#__COLOR__ label strong {
color: #eee;
flex-grow: 1;
min-width: 6em;
}
#__COLOR__ label input {
flex-grow: 9;
}
`;
class ObjectIter extends Object {
forEach(cbk) {
let keys = Object.keys(this);
keys.forEach((key, ix) => {
return cbk(this[key], key, this);
});
}
map(cbk) {
let keys = Object.keys(this);
let newObj = new ObjectIter();
keys.forEach((key, ix) => {
newObj[key] = cbk(this[key], key, this);
});
return newObj;
}
}
StyleSheetList.prototype.forEach = ObjectIter.prototype.forEach;
CSSRuleList.prototype.forEach = ObjectIter.prototype.forEach;
class ColorCSSRule {
constructor(rule) {
this.originalColor = rule.style.color;
this.originalBackgroundColor = rule.style.backgroundColor;
this.style = rule.style;
this.selectorText = rule.selectorText;
}
}
class Color {
constructor(template, styles) {
this.BODY = document.body;
this.HTML = this.BODY.parentElement;
this.ELMS = new ObjectIter();
this.rgbRegex = /rgb\(([\d]{1,3}),\s([\d]{1,3}),\s([\d]{1,3})\)|rgba\(([\d]{1,3}),\s([\d]{1,3}),\s([\d]{1,3}),\s([\d]|[\d]\.[\d]+|\.[\d]+)\)/;
this.FILTERS = localStorage.hasOwnProperty('__COLORS__') ?
JSON.parse(localStorage.getItem('__COLORS__')) :
{}
;
this.setStyles(styles);
let h = document.createElement('div');
h.innerHTML = template;
this.__COLOR__ = h.querySelectorAll('#__COLOR__')[0];
this.HTML.appendChild(this.__COLOR__);
// this.ELMS.contrast = this.__COLOR__.querySelectorAll('#__COLOR__contrast')[0];
// this.ELMS.brightness = this.__COLOR__.querySelectorAll('#__COLOR__brightness')[0];
// this.ELMS.invert = this.__COLOR__.querySelectorAll('#__COLOR__invert')[0];
// this.ELMS.color = this.__COLOR__.querySelectorAll('#__COLOR__color')[0];
this.ELMS.resetBtn = this.__COLOR__.querySelectorAll('#__COLOR__reset_btn')[0];
this.bindSlider('contrast');
this.bindSlider('brightness');
this.bindSlider('invert');
this.bindSlider('background');
this.bindSlider('darkness');
// this.bindColorSlider('color');
this._originalBk = window.getComputedStyle(document.body).backgroundColor;
this._originalColor = window.getComputedStyle(document.body).color;
this.ELMS.resetBtn.addEventListener('click', this.reset.bind(this));
this._init();
}
setStyles(styles) {
this.readPageStyles();
let styleElm = document.createElement('style');
styleElm.type = 'text/css';
styleElm.innerHTML = styles;
document.body.appendChild(styleElm);
}
readPageStyles() {
this.cssRules = [];
let globalSelector = ['*', 'html *', 'body *', 'body', 'html', 'html body'];
let globalRule = false;
document.styleSheets.forEach(styleSheet => {
styleSheet.rules.forEach(rule => {
let color=false, bk=false;
if(rule.style.color !== "" || rule.style.backgroundColor !== "") {
this.cssRules.push(new ColorCSSRule(rule));
if(rule.style.color !== "") {
color=true;
}
if(rule.style.backgroundColor !== "") {
bk=true;
}
}
if(color && bk && globalSelector.indexOf(rule.selectorText)) {
globalRule = true;
}
});
});
if(document.styleSheets[0]) {
document.styleSheets[0].insertRule('body {background: rgb(255, 255, 255); color: rgb(0, 0, 0);}', 0);
let rule = document.styleSheets[0].rules[0];
this.cssRules.unshift(new ColorCSSRule(rule));
}
}
reset(n) {
this.FILTERS = {};
this._setFilters();
localStorage.removeItem('_BK_');
this.BODY.style.backgroundColor = this._originalBk;
this.BODY.style.color = this._originalColor;
this.ELMS.forEach((elm) => {
let iValue;
if( (iValue = elm.getAttribute('data-initial')) ) {
elm.value = iValue;
}
})
}
invertColor(n) {
return Math.abs(n - 256);
}
observeSlider(target, cbk) {
let myC = () => {
cbk(target.value);
};
target.addEventListener('input', myC);
target.addEventListener('change', myC);
}
bindSlider(filter) {
let slider = this.ELMS[filter] = this.__COLOR__.querySelectorAll(`#__COLOR__${filter}`)[0];
let methodName = 'set' + filter.charAt(0).toUpperCase() + filter.slice(1);
if(typeof this[methodName] === 'function') {
this.observeSlider(slider, (v) => {
this[methodName](v);
});
} else {
this.observeSlider(slider, (v) => {
this.setFilter(filter, v);
});
}
}
bindColorSlider(id){
let slider = this.ELMS['color'];
this.observeSlider(slider, (v) => {
this.setBK(v);
});
}
setDarkness(v) {
this.readPageStyles();
this.cssRules.forEach((rule) => {
if(rule.style.color !== '') {
rule.style.color = this.mathDarkColor(rule.originalColor, this.invertColor(v), 1);
}
if(rule.style.backgroundColor !== '') {
rule.style.backgroundColor = this.mathDarkColor(rule.originalBackgroundColor, v, -1);
}
console.log(rule.selectorText, rule.color, rule.backgroundColor);
});
}
mathDarkColor(original, darkLevel, dir) {
let match = original.match(this.rgbRegex),
C = new ObjectIter(),
A
;
if(match !== null && !!match[1]) {
// rgb
C.R = parseInt(match[1], 10);
C.G = parseInt(match[2], 10);
C.B = parseInt(match[3], 10);
} else if(match !== null && !!match[4]) {
//rgba
C.R = parseInt(match[4], 10);
C.G = parseInt(match[5], 10);
C.B = parseInt(match[6], 10);
A = match[7];
} else {
throw new Error(`COLORS: problem with given color ${original}, not able to determine rgb/rgba data`);
}
let cDarkLvl = ((C.R + C.G + C.B)) / 3;
let con = dir < 1 ? (cDarkLvl > darkLevel) : (cDarkLvl < darkLevel);
if(con) {
let proportion = (cDarkLvl - darkLevel);
// do not destroy the relation between RGB cutting off one channel
// proportion gets accomodated in order to mantain relation
proportion = C.R < proportion ? C.R :
C.G < proportion ? C.G :
C.B < proportion ? C.B :
proportion
;
C = C.map(v => {
return v - proportion;
});
}
return A ?
`rgba(${C.R}, ${C.G}, ${C.B}, ${A})` :
`rgb(${C.R}, ${C.G}, ${C.B})`
;
}
updateSlider(filter, v) {
if(this.ELMS[filter].value !== v) {
this.ELMS[filter].value = v;
}
}
setBackground(v) {
let iv = this.invertColor(v);
this.BODY.style.backgroundColor = `rgb(${v},${v},${v})`;
this.BODY.style.color = `rgb(${iv},${iv},${iv})`;
localStorage.setItem('_BK_', v);
}
setFilter(filter, v) {
let val = v/100.0;
this.FILTERS[filter] = val;
this._setFilters();
this.updateSlider(filter, v);
}
_setFilters() {
let str = '';
let istr = '';
Object.keys(this.FILTERS).forEach((filter) => {
let v = this.FILTERS[filter];
str += `${(str === '' ? '' : ' ')}${filter}(${v.toFixed(2)})`;
});
this.BODY.style.filter = str;
localStorage.setItem('__COLORS__', JSON.stringify(this.FILTERS));
}
_init() {
// this._setFilters();
Object.keys(this.FILTERS).forEach((filter) => {
this.setFilter(filter, this.FILTERS[filter]);
});
if(localStorage.hasOwnProperty('_BK_')){
this.setBackground(localStorage.getItem('_BK_'))
}
}
}
window.__COLOR__ = new Color(template, styles);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment