Last active
August 26, 2017 11:09
-
-
Save danyg/0f70c62f2fdd1cb91c20cee0697e6e69 to your computer and use it in GitHub Desktop.
Webs Color Correction
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
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