You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1398 lines
50 KiB
JavaScript

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

;(function(window, undefined){
"use strict"
var _data = window.ColorPicker, // will be deleted in buildView() and holds:
// window.ColorPicker = { // comes from colorPicker.data.js and will be overwritten.
// _html: ..., // holds the HTML markup of colorPicker
// _cssFunc: ..., // CSS for all the sliders
// _cssMain: ..., // CSS of the GUI
// _horizontalPng: ..., // horizontal background images for sliders
// _verticalPng: ..., // vertical background images for sliders
// _patchesPng: ..., // background images for square sliders in RGB mode
// _iconsPng: ..., // some icon sprite images
// _bgsPng: ..., // some more icon sprite images
// _blankPng: ... // the blank 16x16px image for the transparent cursor
// }
_devMode = !_data, // if no _data we assume that colorPicker.data.js is missing (for development)
_isIE = document.createStyleSheet !== undefined && document.getElementById || !!window.MSInputMethodContext,
_doesOpacity = typeof document.body.style.opacity !== 'undefined',
// _isIE8 = _isIE && document.querySelectorAll,
_valueRanges = {}, // will be assigned in initInstance() by Colors instance
// _valueRanges = {
// rgb: {r: [0, 255], g: [0, 255], b: [0, 255]},
// hsv: {h: [0, 360], s: [0, 100], v: [0, 100]},
// hsl: {h: [0, 360], s: [0, 100], l: [0, 100]},
// cmyk: {c: [0, 100], m: [0, 100], y: [0, 100], k: [0, 100]},
// cmy: {c: [0, 100], m: [0, 100], y: [0, 100]},
// XYZ: {X: [0, 100], Y: [0, 100], Z: [0, 100]},
// Lab: {L: [0, 100], a: [-128, 127], b: [-128, 127]},
// alpha: {alpha: [0, 1]},
// HEX: {HEX: [0, 16777215]}
// },
_bgTypes = {w: 'White', b: 'Black', c: 'Custom'},
_mouseMoveAction, // current mouseMove handler assigned on mouseDown
_action = '', // needed for action callback; needed due to minification of javaScript
_mainTarget, // target on mouseDown, might be parent element though...
_valueType, // check this variable; gets missused/polutet over time
_delayState = 1, // mouseMove offset (y-axis) in display elements // same here...
_startCoords = {},
_targetOrigin = {},
_renderTimer, // animationFrame/interval variable
_newData = true,
// _txt = {
// selection: document.selection || window.getSelection(),
// range: (document.createRange ? document.createRange() : document.body.createTextRange())
// },
_renderVars = {}, // used only in renderAll and convertColors
_cashedVars = {}, // reset in initSliders
_colorPicker,
_previousInstance, // only used for recycling purposes in buildView()
_colorInstance = {},
_colors = {},
_options = {},
_nodes = {},
animationFrame = 'AnimationFrame', // we also need this later
requestAnimationFrame = 'request' + animationFrame,
cancelAnimationFrame = 'cancel' + animationFrame,
vendors = ['ms', 'moz', 'webkit', 'o'],
ColorPicker = function(options) { // as tiny as possible...
this.options = {
color: 'rgba(204, 82, 37, 0.8)',
mode: 'rgb-b',
fps: 60, // 1000 / 60 = ~16.7ms
delayOffset: 8,
CSSPrefix: 'cp-',
allMixDetails: true,
alphaBG: 'w',
imagePath: ''
// devPicker: false // uses existing HTML for development...
// noAlpha: true,
// customBG: '#808080'
// size: 0,
// cmyOnly: false,
// initStyle: 'display: none',
// memoryColors: "'rgba(82,80,151,1)','rgba(100,200,10,0.5)','rgba(100,0,0,1)','rgba(0,0,0,1)'"
// memoryColors: [{r: 100, g: 200, b: 10, a: 0.5}] //
// opacityPositionRelative: undefined,
// customCSS: undefined,
// appenTo: document.body,
// noRangeBackground: false,
// textRight: false, ?????
// noHexButton: false,
// noResize: false,
// noRGBr: false,
// noRGBg: false,
// noRGBb: false,
// ------ CSSStrength: 'div.',
// XYZMatrix: XYZMatrix,
// XYZReference: {},
// grey: grey,
// luminance: luminance,
// renderCallback: undefined,
// actionCallback: undefined,
// convertCallback: undefined,
};
initInstance(this, options || {});
};
window.ColorPicker = ColorPicker; // export differently
ColorPicker.addEvent = addEvent;
ColorPicker.removeEvent = removeEvent;
ColorPicker.getOrigin = getOrigin;
ColorPicker.limitValue = limitValue;
ColorPicker.changeClass = changeClass;
// ------------------------------------------------------ //
ColorPicker.prototype.setColor = function(newCol, type, alpha, forceRender) {
focusInstance(this);
_valueType = true; // right cursor...
// https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
preRenderAll(_colorInstance.setColor.apply(_colorInstance, arguments));
if (forceRender) {
this.startRender(true);
}
};
ColorPicker.prototype.saveAsBackground = function() {
focusInstance(this);
return saveAsBackground(true);
};
ColorPicker.prototype.setCustomBackground = function(col) {
focusInstance(this); // needed???
return _colorInstance.setCustomBackground(col);
};
ColorPicker.prototype.startRender = function(oneTime) {
focusInstance(this);
if (oneTime) {
_mouseMoveAction = false; // prevents window[requestAnimationFrame] in renderAll()
renderAll();
this.stopRender();
} else {
_mouseMoveAction = 1;
_renderTimer = window[requestAnimationFrame](renderAll);
}
};
ColorPicker.prototype.stopRender = function() {
focusInstance(this); // check again
window[cancelAnimationFrame](_renderTimer);
if (_valueType) {
// renderAll();
_mouseMoveAction = 1;
stopChange(undefined, 'external');
// _valueType = undefined;
}
};
ColorPicker.prototype.setMode = function(mode) { // check again ... right cursor
focusInstance(this);
setMode(mode);
initSliders();
renderAll();
};
ColorPicker.prototype.destroyAll = function() { // check this again...
var html = this.nodes.colorPicker,
destroyReferences = function(nodes) {
for (var n in nodes) {
if (nodes[n] && nodes[n].toString() === '[object Object]' || nodes[n] instanceof Array) {
destroyReferences(nodes[n]);
}
nodes[n] = null;
delete nodes[n];
}
};
this.stopRender();
installEventListeners(this, true);
destroyReferences(this);
html.parentNode.removeChild(html);
html = null;
};
ColorPicker.prototype.renderMemory = function(memory) {
var memos = this.nodes.memos,
tmp = [];
if (typeof memory === 'string') { // revisit!!!
memory = memory.replace(/^'|'$/g, '').replace(/\s*/, '').split('\',\'');
}
for (var n = memos.length; n--; ) { // check again how to handle alpha...
if (memory && typeof memory[n] === 'string') {
tmp = memory[n].replace('rgba(', '').replace(')', '').split(',');
memory[n] = {r: tmp[0], g: tmp[1], b: tmp[2], a: tmp[3]}
}
memos[n].style.cssText = 'background-color: ' + (memory && memory[n] !== undefined ?
color2string(memory[n]) + ';' + getOpacityCSS(memory[n]['a'] || 1) : 'rgb(0,0,0);');
}
};
// ------------------------------------------------------ //
function initInstance(THIS, options) {
var exporter, // do something here..
mode = '',
CSSPrefix = '',
optionButtons;
for (var option in options) { // deep copy ??
THIS.options[option] = options[option];
}
_colorInstance = new Colors(THIS.options);
// We transfer the responsibility to the instance of Color (to save space and memory)
delete THIS.options;
_options = _colorInstance.options;
_options.scale = 1;
CSSPrefix = _options.CSSPrefix;
THIS.color = _colorInstance; // check this again...
_valueRanges = _options.valueRanges;
THIS.nodes = _nodes = getInstanceNodes(buildView(THIS), THIS); // ha, ha,... make this different
setMode(_options.mode);
focusInstance(THIS);
saveAsBackground();
mode = ' ' + _options.mode.type + '-' + _options.mode.z;
_nodes.slds.className += mode;
_nodes.panel.className += mode;
//_nodes.colorPicker.className += ' cmy-' + _options.cmyOnly;
if (_options.noHexButton) {
changeClass(_nodes.HEX_butt, CSSPrefix + 'butt', CSSPrefix + 'labl');
}
if (_options.size !== undefined) {
resizeApp(undefined, _options.size);
}
optionButtons = {
alphaBG: _nodes.alpha_labl,
cmyOnly: _nodes.HEX_labl // test... take out
};
for (var n in optionButtons) {
if (_options[n] !== undefined) {
buttonActions({target: optionButtons[n], data: _options[n]});
}
}
if (_options.noAlpha) {
_nodes.colorPicker.className += ' no-alpha'; // IE6 ??? maybe for IE6 on document.body
}
THIS.renderMemory(_options.memoryColors);
installEventListeners(THIS);
_mouseMoveAction = true;
stopChange(undefined, 'init');
if (_previousInstance) {
focusInstance(_previousInstance);
renderAll();
}
}
function focusInstance(THIS) {
_newData = true;
if (_colorPicker !== THIS) {
_colorPicker = THIS;
_colors = THIS.color.colors;
_options = THIS.color.options;
_nodes = THIS.nodes;
_colorInstance = THIS.color;
_cashedVars = {};
preRenderAll(_colors);
}
}
function getUISizes() {
var sizes = ['L', 'S', 'XS', 'XXS'];
_options.sizes = {};
_nodes.testNode.style.cssText = 'position:absolute;left:-1000px;top:-1000px;';
document.body.appendChild(_nodes.testNode);
for (var n = sizes.length; n--; ) {
_nodes.testNode.className = _options.CSSPrefix + 'app ' + sizes[n];
_options.sizes[sizes[n]] = [_nodes.testNode.offsetWidth, _nodes.testNode.offsetHeight];
}
if (_nodes.testNode.removeNode) { // old IEs
_nodes.testNode.removeNode(true);
} else {
document.body.removeChild(_nodes.testNode);
}
}
function buildView(THIS) {
var app = document.createElement('div'),
prefix = _options.CSSPrefix,
urlData = 'data:image/png;base64,',
addStyleSheet = function(cssText, id) {
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
if (id) {
style.setAttribute('id', id);
}
if (!style.styleSheet) {
style.appendChild(document.createTextNode(cssText));
}
document.getElementsByTagName('head')[0].appendChild(style);
if (style.styleSheet) { // IE compatible
document.styleSheets[document.styleSheets.length-1].cssText = cssText;
}
},
processCSS = function(doesBAS64){
// CSS - system
_data._cssFunc = _data._cssFunc.
replace(/§/g, prefix).
replace('_patches.png', doesBAS64 ? urlData + _data._patchesPng : _options.imagePath + '_patches.png').
replace('_vertical.png', doesBAS64 ? urlData + _data._verticalPng : _options.imagePath + '_vertical.png').
replace('_horizontal.png', doesBAS64 ? urlData + _data._horizontalPng :
_options.imagePath + '_horizontal.png');
addStyleSheet(_data._cssFunc, 'colorPickerCSS');
// CSS - main
if (!_options.customCSS) {
_data._cssMain = _data._cssMain.
replace(/§/g, prefix).
replace('_bgs.png', doesBAS64 ? urlData + _data._bgsPng : _options.imagePath + '_bgs.png').
replace('_icons.png', doesBAS64 ? urlData + _data._iconsPng : _options.imagePath + '_icons.png').
replace('_blank.png', !_isIE ? urlData + _data._blankPng : _options.imagePath + '_blank.cur').
// replace('"Courier New",', !_isIE ? '' : '"Courier New",').
replace(/opacity:(\d*\.*(\d+))/g, function($1, $2){
return !_doesOpacity ? '-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=' +
Math.round(+$2 * 100) + ')";filter: alpha(opacity=' + Math.round(+$2 * 100) + ')' :
'-moz-opacity: ' + $2 + '; -khtml-opacity: ' + $2 + '; opacity: ' + $2;
});
// style.appendChild(document.createTextNode(_data._cssFunc));
addStyleSheet(_data._cssMain);
}
for (var n in _data) { // almost 25k of memory ;o)
_data[n] = null;
}
},
test = document.createElement('img');
// development mode
if (_devMode) {
return THIS.color.options.devPicker;
}
// HTML
if (_previousInstance = _colorPicker) {
// we need to be careful with recycling HTML as slider calssNames might have been changed...
initSliders();
}
// app.innerHTML = _colorPicker ? _colorPicker.nodes.colorPicker.outerHTML : _data._html.replace(/§/g, prefix);
// faster ... FF8.0 (2011) though (but IE4)
// outerHTML ... FF11 (2013)
app.insertAdjacentHTML('afterbegin',
_colorPicker ? _colorPicker.nodes.colorPicker.outerHTML : _data._html.replace(/§/g, prefix));
// _html = null;
// CSS
if (!document.getElementById('colorPickerCSS')) { // only once needed
test.onload = test.onerror = function(){
if (_data._cssFunc) {
processCSS(this.width === 1 && this.height === 1);
}
};
test.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
}
app = app.children[0];
app.style.cssText = _options.initStyle || ''; // for initial hiding...
// get a better addClass for this....
// app.className = app.className.split(' ')[0]; // cleanup for multy instances
return (_options.appenTo || document.body).appendChild(app);
}
function getInstanceNodes(colorPicker, THIS) { // check nodes again... are they all needed?
var all = colorPicker.getElementsByTagName('*'),
nodes = {colorPicker: colorPicker}, // length ?? // rename nodes.colorPicker
node,
className,
memoCounter = 0,
regexp = new RegExp(_options.CSSPrefix);
// nodes.displayStyles = {}; // not needed ... or change to CSS
nodes.styles = {};
// nodes.styles.displays = {};
nodes.textNodes = {};
nodes.memos = [];
nodes.testNode = document.createElement('div');
for (var n = 0, m = all.length; n < m; n++) {
node = all[n];
if ((className = node.className) && regexp.test(className)) {
className = className.split(' ')[0].replace(_options.CSSPrefix, '').replace(/-/g, '_');
if (/_disp/.test(className)) {
className = className.replace('_disp', '');
// nodes.styles.displays[className] = node.style;
nodes.styles[className] = node.style;
nodes.textNodes[className] = node.firstChild;
node.contentEditable = true; // does this slow down rendering??
} else {
if (!(/(?:hs|cmyk|Lab).*?(?:butt|labl)/.test(className))) {
nodes[className] = node;
}
if (/(?:cur|sld[^s]|opacity|cont|col)/.test(className)) {
nodes.styles[className] = /(?:col\d)/.test(className) ? node.children[0].style : node.style;
}
}
} else if (/memo/.test(node.parentNode.className)) {
nodes.memos.push(node);
}
}
// Chrome bug: focuses contenteditable on mouse over while dragging
nodes.panelCover = nodes.panel.appendChild(document.createElement('div'));
return nodes;
}
// ------------------------------------------------------ //
// ---- Add event listners to colorPicker and window ---- //
// -------------------------------------------------------//
function installEventListeners(THIS, off) {
var onOffEvent = off ? removeEvent : addEvent;
onOffEvent(_nodes.colorPicker, 'mousedown', function(e) {
var event = e || window.event,
page = getPageXY(event),
target = event.target || event.srcElement,
className = target.className;
focusInstance(THIS);
_mainTarget = target;
stopChange(undefined, 'resetEventListener');
_action = ''; // needed due to minification of javaScript
if (target === _nodes.sldl_3 || target === _nodes.curm) {
_mainTarget = _nodes.sldl_3;
_mouseMoveAction = changeXYValue;
_action = 'changeXYValue';
changeClass(_nodes.slds, 'do-drag');
} else if (/sldr/.test(className) || target === _nodes.curl || target === _nodes.curr) {
_mainTarget = _nodes.sldr_4;
_mouseMoveAction = changeZValue;
_action = 'changeZValue';
} else if (target === _nodes.opacity.children[0] || target === _nodes.opacity_slider) {
_mainTarget = _nodes.opacity;
_mouseMoveAction = changeOpacityValue;
_action = 'changeOpacityValue';
} else if (/-disp/.test(className) && !/HEX-/.test(className)) {
_mouseMoveAction = changeInputValue;
_action = 'changeInputValue';
(target.nextSibling.nodeType === 3 ? target.nextSibling.nextSibling : target.nextSibling).
appendChild(_nodes.nsarrow); // nextSibling for better text selection
_valueType = className.split('-disp')[0].split('-');
_valueType = {type: _valueType[0], z: _valueType[1] || ''};
changeClass(_nodes.panel, 'start-change');
_delayState = 0;
} else if (target === _nodes.resize && !_options.noResize) {
if (!_options.sizes) {
getUISizes();
}
_mainTarget = _nodes.resizer;
_mouseMoveAction = resizeApp;
_action = 'resizeApp';
} else {
_mouseMoveAction = undefined;
}
if (_mouseMoveAction) {
_startCoords = {pageX: page.X, pageY: page.Y};
_mainTarget.style.display = 'block'; // for resizer...
_targetOrigin = getOrigin(_mainTarget);
_targetOrigin.width = _nodes.opacity.offsetWidth; // ???????
_targetOrigin.childWidth = _nodes.opacity_slider.offsetWidth; // ???????
_mainTarget.style.display = ''; // ??? for resizer...
_mouseMoveAction(event);
addEvent(_isIE ? document.body : window, 'mousemove', _mouseMoveAction);
_renderTimer = window[requestAnimationFrame](renderAll);
} else {
// console.log(className)
// console.log(THIS.nodes[className.split(' ')[0].replace('cp-', '').replace('-', '_')])
// resize, button states, etc...
}
// if (_mouseMoveAction !== changeInputValue) preventDefault(event);
if (!/-disp/.test(className)) {
return preventDefault(event);
// document.activeElement.blur();
}
});
onOffEvent(_nodes.colorPicker, 'click', function(e) {
focusInstance(THIS);
buttonActions(e);
});
onOffEvent(_nodes.colorPicker, 'dblclick', buttonActions);
onOffEvent(_nodes.colorPicker, 'keydown', function(e) {
focusInstance(THIS);
keyControl(e);
});
// keydown is before keypress and focuses already
onOffEvent(_nodes.colorPicker, 'keypress', keyControl);
// onOffEvent(_nodes.colorPicker, 'keyup', keyControl);
onOffEvent(_nodes.colorPicker, 'paste', function(e) {
e.target.firstChild.data = e.clipboardData.getData('Text');
return preventDefault(e);
});
}
addEvent(_isIE ? document.body : window, 'mouseup', stopChange);
// ------------------------------------------------------ //
// --------- Event listner's callback functions -------- //
// -------------------------------------------------------//
function stopChange(e, action) {
var mouseMoveAction = _mouseMoveAction;
if (_mouseMoveAction) { // why??? please test again...
// if (document.selection && _mouseMoveAction !== changeInputValue) {
// //ie -> prevent showing the accelerator menu
// document.selection.empty();
// }
window[cancelAnimationFrame](_renderTimer);
removeEvent(_isIE ? document.body : window, 'mousemove', _mouseMoveAction);
if (_delayState) { // hapens on inputs
_valueType = {type: 'alpha'};
renderAll();
}
// this is dirty... has to do with M|W|! button
if (typeof _mouseMoveAction === 'function' || typeof _mouseMoveAction === 'number') {
delete _options.webUnsave;
}
_delayState = 1;
_mouseMoveAction = undefined;
changeClass(_nodes.slds, 'do-drag', '');
changeClass(_nodes.panel, '(?:start-change|do-change)', '');
_nodes.resizer.style.cssText = '';
_nodes.memo_store.style.cssText = 'background-color: ' +
color2string(_colors.RND.rgb) + '; ' + getOpacityCSS(_colors.alpha);
_nodes.memo.className = _nodes.memo.className.replace(/\s+(?:dark|light)/, '') +
// (/dark/.test(_nodes.colorPicker.className) ? ' dark' : ' light');
(_colors['rgbaMix' + _bgTypes[_options.alphaBG]].luminance < 0.22 ? ' dark' : ' light');
// (_colors.rgbaMixCustom.luminance < 0.22 ? ' dark' : ' light')
_valueType = undefined;
resetCursors();
_nodes.panelCover.style.cssText = '';
if (_options.actionCallback) {
_options.actionCallback(e, _action || mouseMoveAction.name || action || 'external');
}
}
}
function changeXYValue(e) {
var event = e || window.event,
scale = _options.scale,
page = getPageXY(event),
x = (page.X - _targetOrigin.left) * (scale === 4 ? 2 : scale),
y = (page.Y - _targetOrigin.top) * scale,
mode = _options.mode;
_colors[mode.type][mode.x] = limitValue(x / 255, 0, 1);
_colors[mode.type][mode.y] = 1 - limitValue(y / 255, 0, 1);
convertColors();
return preventDefault(event);
}
function changeZValue(e) { // make this part of changeXYValue
var event = e || window.event,
page = getPageXY(event),
z = (page.Y - _targetOrigin.top) * _options.scale,
mode = _options.mode;
_colors[mode.type][mode.z] = 1 - limitValue(z / 255, 0, 1);
convertColors();
return preventDefault(event);
}
function changeOpacityValue(e) {
var event = e || window.event,
page = getPageXY(event);
_newData = true;
_colors.alpha = limitValue(Math.round(
(page.X - _targetOrigin.left) / _targetOrigin.width * 100), 0, 100
) / 100;
convertColors('alpha');
return preventDefault(event);
}
function changeInputValue(e) {
var event = e || window.event,
page = getPageXY(event),
delta = _startCoords.pageY - page.Y,
delayOffset = _options.delayOffset,
type = _valueType.type,
isAlpha = type === 'alpha',
ranges;
if (_delayState || Math.abs(delta) >= delayOffset) {
if (!_delayState) {
_delayState = (delta > 0 ? -delayOffset : delayOffset) +
(+_mainTarget.firstChild.data) * (isAlpha ? 100 : 1);
_startCoords.pageY += _delayState;
delta += _delayState;
_delayState = 1;
changeClass(_nodes.panel, 'start-change', 'do-change');
_nodes.panelCover.style.cssText = 'position:absolute;left:0;top:0;right:0;bottom:0';
// window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
document.activeElement.blur();
_renderTimer = window[requestAnimationFrame](renderAll);
}
if (type === 'cmyk' && _options.cmyOnly) {
type = 'cmy';
}
if (isAlpha) {
_newData = true;
_colors.alpha = limitValue(delta / 100, 0, 1);
} else {
ranges = _valueRanges[type][_valueType.z];
_colors[type][_valueType.z] = type === 'Lab' ? limitValue(delta, ranges[0], ranges[1]) :
limitValue(delta / ranges[1], 0, 1);
}
convertColors(isAlpha ? 'alpha' : type);
// event.returnValue is deprecated. Please use the standard event.preventDefault() instead.
// event.returnValue = false; // see: pauseEvent(event);
return preventDefault(event);
}
}
function keyControl(e) { // this is quite big for what it does...
var event = e || window.event,
keyCode = event.which || event.keyCode,
key = String.fromCharCode(keyCode),
elm = document.activeElement,
cln = elm.className.replace(_options.CSSPrefix, '').split('-'),
type = cln[0],
mode = cln[1],
isAlpha = type === 'alpha',
isHex = type === 'HEX',
arrowKey = {k40: -1, k38: 1, k34: -10, k33: 10}['k' + keyCode] / (isAlpha ? 100 : 1),
validKeys = {'HEX': /[0-9a-fA-F]/, 'Lab': /[\-0-9]/, 'alpha': /[\.0-9]/}[type] || /[0-9]/,
valueRange = _valueRanges[type][type] || _valueRanges[type][mode], // let op!
textNode = elm.firstChild, // chnge on TAB key
rangeData = caret(elm),
origValue = textNode.data, // do not change
value,
val = origValue === '0' && !isHex ? [] : origValue.split(''); // gefixt
if (/^(?:27|13)$/.test(keyCode)) { // ENTER || ESC
preventDefault(event);
elm.blur();
} else if (event.type === 'keydown') { // functional keys
if (arrowKey) { // arrow/page keys
value = limitValue(Math.round((+origValue + arrowKey) * 1e+6) / 1e+6, valueRange[0], valueRange[1]);
} else if (/^(?:8|46)$/.test(keyCode)) { // DELETE / BACKSPACE
if (!rangeData.range) {
rangeData.range++;
rangeData.start -= keyCode === 8 ? 1 : 0;
}
val.splice(rangeData.start, rangeData.range);
value = val.join('') || '0'; // never loose elm.firstChild
}
if (value !== undefined) { // prevent keypress
preventDefault(event, true);
}
} else if (event.type === 'keypress') {
if (!/^(?:37|39|8|46|9)$/.test(keyCode)) { // left, right,DEL, BACK, TAB for FF
preventDefault(event, true);
}
if (validKeys.test(key)) { // regular input
val.splice(rangeData.start, rangeData.range, key);
value = val.join('');
}
rangeData.start++;
}
if (keyCode === 13 && isHex) {
if (textNode.data.length % 3 === 0 || textNode.data === '0') { // textNode.data.length &&
return _colorPicker.setColor(textNode.data === '0' ? '000' : textNode.data, 'rgb', _colors.alpha, true);
} else {
preventDefault(event, true);
return elm.focus();
}
}
if (isHex && value !== undefined) {
value = /^0+/.test(value) ? value : parseInt(''+value, 16) || 0;
}
if (value !== undefined && value !== '' && +value >= valueRange[0] && +value <= valueRange[1]) {
if (isHex) {
value = value.toString(16).toUpperCase() || '0';
}
if (isAlpha) {
_colors[type] = +value;
} else if (!isHex) {
_colors[type][mode] = +value / (type === 'Lab' ? 1 : valueRange[1]);
}
convertColors(isAlpha ? 'alpha' : type);
preRenderAll(_colors);
_mouseMoveAction = true;
stopChange(e, event.type);
textNode.data = value; // if
caret(elm, Math.min(elm.firstChild.data.length, rangeData.start < 0 ? 0 : rangeData.start));
}
}
function buttonActions(e) {
var event = e || window.event,
target = event.target || event.srcElement,
targetClass = target.className,
parent = target.parentNode,
options = _options,
RGB = _colors.RND.rgb,
customBG, alphaBG,
mode = _options.mode,
newMode = '',
prefix = options.CSSPrefix,
isModeButton = /(?:hs|rgb)/.test(parent.className) && /^[HSBLRG]$/.test(
target.firstChild ? target.firstChild.data : ''
),
isDblClick = /dblc/.test(event.type),
buttonAction = ''; // think this over again....
if (isDblClick && !isModeButton) {
return;
} else if (targetClass.indexOf('-labl ' + prefix + 'labl') !== -1) { // HSB -> HSL; CMYK -> Lab buttons
changeClass(_nodes[targetClass.split('-')[0]], prefix + 'hide', '');
changeClass(_nodes[parent.className.split('-')[1]], prefix + 'hide');
} else if (targetClass.indexOf(prefix + 'butt') !== -1) { // BUTTONS
if (isModeButton) { // set render modes
if (isDblClick && _options.scale === 2) {
newMode = /hs/.test(mode.type) ? 'rgb' : /hide/.test(_nodes.hsl.className) ? 'hsv' : 'hsl';
newMode = newMode + '-' + newMode[mode.type.indexOf(mode.z)];
}
_colorPicker.setMode(newMode ? newMode : targetClass.replace('-butt', '').split(' ')[0]);
buttonAction = 'modeChange';
} else if (/^[rgb]/.test(targetClass)) { // no vertical slider rendering in RGB mode
newMode = targetClass.split('-')[1];
changeClass(_nodes.colorPicker, 'no-rgb-' + newMode,
(options['noRGB' + newMode] = !options['noRGB' + newMode]) ? undefined : '');
buttonAction = 'noRGB' + newMode;
// preRenderAll();
} else if (target === _nodes.alpha_labl) { // alpha button right (background of raster)
customBG = options.customBG;
alphaBG = options.alphaBG;
changeClass(_nodes.colorPicker, 'alpha-bg-' + alphaBG, 'alpha-bg-' +
(alphaBG = options.alphaBG = e.data || (alphaBG === 'w' ? (customBG ? 'c' : 'b') :
alphaBG === 'c' ? 'b' : 'w')));
target.firstChild.data = alphaBG.toUpperCase();
_nodes.ctrl.style.backgroundColor = _nodes.memo.style.backgroundColor =
alphaBG !== 'c' ? '' : 'rgb(' + Math.round(customBG.r * 255) + ', ' +
Math.round(customBG.g * 255) + ', ' +
Math.round(customBG.b * 255) + ')';
_nodes.raster.style.cssText = _nodes.raster_bg.previousSibling.style.cssText =
alphaBG !== 'c' ? '' : getOpacityCSS(customBG.luminance < 0.22 ? 0.5 : 0.4);
buttonAction = 'alphaBackground';
} else if (target === _nodes.alpha_butt) { // alpha button left (disable alpha rendering)
changeClass(_nodes.colorPicker, 'mute-alpha', (options.muteAlpha = !options.muteAlpha) ? undefined : '');
buttonAction = 'alphaState';
} else if (target === _nodes.HEX_butt) { // make it on/off
changeClass(_nodes.colorPicker, 'no-HEX', (options.HEXState = !options.HEXState) ? undefined : '');
buttonAction = 'HEXState';
} else if (target === _nodes.HEX_labl) { // web save state change
var isWebSave = _colors.saveColor === 'web save';
if (_colors.saveColor !== 'web smart' && !isWebSave) {
options.webUnsave = copyColor(RGB);
_colorPicker.setColor(_colors.webSmart, 'rgb');
} else if (!isWebSave) {
if (!options.webUnsave) {
options.webUnsave = copyColor(RGB);
}
_colorPicker.setColor(_colors.webSave, 'rgb');
} else {
_colorPicker.setColor(options.webUnsave, 'rgb');
}
buttonAction = 'webColorState';
} else if (/Lab-x-labl/.test(targetClass)) { //target === _nodes.cmyk_type) {
// switch between CMYK and CMY
changeClass(_nodes.colorPicker, 'cmy-only', (options.cmyOnly = !options.cmyOnly) ? undefined : '');
buttonAction = 'cmykState';
}
} else if (target === _nodes.bsav) { // SAVE
saveAsBackground();
buttonAction = 'saveAsBackground';
} else if (target === _nodes.bres) { // RESET
var tmpColor = copyColor(RGB),
tmpAlpha = _colors.alpha;
// a bit heavy but... doesn't matter here
// newCol, type, alpha, forceRender
_colorPicker.setColor(options.color);
saveAsBackground();
_colorPicker.setColor(tmpColor, 'rgb', tmpAlpha);
buttonAction = 'resetColor';
} else if (parent === _nodes.col1) { // COLOR left
// _colors.hsv.h = (_colors.hsv.h + 0.5) % 1; // not acurate
_colors.hsv.h -= (_colors.hsv.h > 0.5 ? 0.5 : -0.5);
convertColors('hsv');
buttonAction = 'shiftColor';
} else if (parent === _nodes.col2) { // COLOR right
_colorPicker.setColor(target.style.backgroundColor, 'rgb', _colors.background.alpha);
buttonAction = 'setSavedColor';
} else if (parent === _nodes.memo) { // MEMORIES // revisit...
var resetBlink = function() {
if (_nodes.memos.blinker) _nodes.memos.blinker.style.cssText = _nodes.memos.cssText;
},
doBlink = function(elm) {
_nodes.memos.blinker = elm;
elm.style.cssText = 'background-color:' + (_colors.RGBLuminance > 0.22 ? '#333' : '#DDD');
window.setTimeout(resetBlink, 200);
};
if (target === _nodes.memo_cursor) { // save color in memo
resetBlink();
_nodes.memos.blinker = undefined;
_nodes.testNode.style.cssText = _nodes.memo_store.style.cssText;
_nodes.memos.cssText = _nodes.testNode.style.cssText; // ...how browser sees css
for (var n = _nodes.memos.length - 1; n--; ) { // check if color already exists
if (_nodes.memos.cssText === _nodes.memos[n].style.cssText) {
doBlink(_nodes.memos[n]); // sets _nodes.memos.blinker
break;
}
}
if (!_nodes.memos.blinker) { // right shift colors
for (var n = _nodes.memos.length - 1; n--; ) {
_nodes.memos[n + 1].style.cssText = _nodes.memos[n].style.cssText;
}
_nodes.memos[0].style.cssText = _nodes.memo_store.style.cssText;
}
buttonAction = 'toMemery';
} else { // reset color from memo
resetBlink();
_colorPicker.setColor(target.style.backgroundColor, 'rgb', target.style.opacity || 1);
_nodes.memos.cssText = target.style.cssText;
doBlink(target);
// this is dirty... has to do with M|W|! button
_mouseMoveAction = 1;
buttonAction = 'fromMemory';
}
}
// think this over again, does this need to be like this??
if (buttonAction) {
preRenderAll(_colors);
_mouseMoveAction = _mouseMoveAction || true; // !!!! search for: // this is dirty...
stopChange(e, buttonAction);
}
}
function resizeApp(e, size) {
var event = e || window.event,
page = event ? getPageXY(event) : {},
isSize = size !== undefined,
x = isSize ? size : page.X - _targetOrigin.left + 8,
y = isSize ? size : page.Y - _targetOrigin.top + 8,
values = [' S XS XXS', ' S XS', ' S', ''],
sizes = _options.sizes, // from getUISizes();
currentSize = isSize ? size :
y < sizes.XXS[1] + 25 ? 0 :
x < sizes.XS[0] + 25 ? 1 :
x < sizes.S[0] + 25 || y < sizes.S[1] + 25 ? 2 : 3,
value = values[currentSize],
isXXS = false,
mode,
tmp = '';
if (_cashedVars.resizer !== value) {
isXXS = /XX/.test(value);
mode = _options.mode;
if (isXXS && (!/hs/.test(mode.type) || mode.z === 'h')) {
tmp = mode.type + '-' + mode.z;
_colorPicker.setMode(/hs/.test(mode.type) ? mode.type + '-s': 'hsv-s');
_options.mode.original = tmp;
} else if (mode.original) {
// setMode(mode) creates a new object so mode.original gets deleted automatically
_colorPicker.setMode(mode.original);
}
_nodes.colorPicker.className = _nodes.colorPicker.className.replace(/\s+(?:S|XS|XXS)/g, '') + value;
_options.scale = isXXS ? 4 : /S/.test(value) ? 2 : 1;
_options.currentSize = currentSize;
_cashedVars.resizer = value;
// fix this... from this point on inside if() ... convertColors();
_newData = true;
renderAll();
resetCursors();
}
_nodes.resizer.style.cssText = 'display: block;' +
'width: ' + (x > 10 ? x : 10) + 'px;' +
'height: ' + (y > 10 ? y : 10) + 'px;';
}
// ------------------------------------------------------ //
// --- Colors calculation and rendering related stuff --- //
// -------------------------------------------------------//
function setMode(mode) {
var ModeMatrix = {
rgb_r : {x: 'b', y: 'g'},
rgb_g : {x: 'b', y: 'r'},
rgb_b : {x: 'r', y: 'g'},
hsv_h : {x: 's', y: 'v'},
hsv_s : {x: 'h', y: 'v'},
hsv_v : {x: 'h', y: 's'},
hsl_h : {x: 's', y: 'l'},
hsl_s : {x: 'h', y: 'l'},
hsl_l : {x: 'h', y: 's'}
},
key = mode.replace('-', '_'),
regex = '\\b(?:rg|hs)\\w\\-\\w\\b'; // \\b\\w{3}\\-\\w\\b';
// changeClass(_nodes.colorPicker, '(?:.*?)$', mode);
// changeClass(_nodes.colorPicker, '\\b\\w{3}\\-\\w\\b', mode);
// changeClass(_nodes.slds, '\\b\\w{3}\\-\\w\\b', mode);
changeClass(_nodes.panel, regex, mode);
changeClass(_nodes.slds, regex, mode);
mode = mode.split('-');
return _options.mode = {
type: mode[0],
x: ModeMatrix[key].x,
y: ModeMatrix[key].y,
z: mode[1]
};
}
function initSliders() { // function name...
var regex = /\s+(?:hue-)*(?:dark|light)/g,
className = 'className'; // minification
_nodes.curl[className] = _nodes.curl[className].replace(regex, ''); // .....
_nodes.curr[className] = _nodes.curr[className].replace(regex, ''); // .....
_nodes.slds[className] = _nodes.slds[className].replace(regex, '');
// var sldrs = ['sldr_2', 'sldr_4', 'sldl_3'];
// for (var n = sldrs.length; n--; ) {
// _nodes[sldrs[n]][className] = _options.CSSPrefix + sldrs[n].replace('_', '-');
// }
_nodes.sldr_2[className] = _options.CSSPrefix + 'sldr-2';
_nodes.sldr_4[className] = _options.CSSPrefix + 'sldr-4';
_nodes.sldl_3[className] = _options.CSSPrefix + 'sldl-3';
for (var style in _nodes.styles) {
if (!style.indexOf('sld')) _nodes.styles[style].cssText = '';
}
_cashedVars = {};
}
function resetCursors() {
// _renderVars.isNoRGB = undefined;
_nodes.styles.curr.cssText = _nodes.styles.curl.cssText; // only coordinates
_nodes.curl.className = _options.CSSPrefix + 'curl' + (
_renderVars.noRGBZ ? ' ' + _options.CSSPrefix + 'curl-' +_renderVars.noRGBZ: '');
_nodes.curr.className = _options.CSSPrefix + 'curr ' + _options.CSSPrefix + 'curr-' +
(_options.mode.z === 'h' ? _renderVars.HUEContrast : _renderVars.noRGBZ ?
_renderVars.noRGBZ : _renderVars.RGBLuminance);
}
function convertColors(type) {
preRenderAll(_colorInstance.setColor(undefined, type || _options.mode.type));
_newData = true;
}
function saveAsBackground(refresh) {
_colorInstance.saveAsBackground();
_nodes.styles.col2.cssText = 'background-color: ' + color2string(_colors.background.RGB) + ';' +
getOpacityCSS(_colors.background.alpha);
if (refresh) {
preRenderAll(_colors);
// renderAll();
}
return (_colors);
}
function preRenderAll(colors) {
var renderVars = _renderVars,
bgType = _bgTypes[_options.alphaBG];
renderVars.hueDelta = Math.round(colors['rgbaMixBGMix' + bgType].hueDelta * 100);
// renderVars.RGBLuminanceDelta = Math.round(colors.RGBLuminanceDelta * 100);
renderVars.luminanceDelta = Math.round(colors['rgbaMixBGMix' + bgType].luminanceDelta * 100);
renderVars.RGBLuminance = colors.RGBLuminance > 0.22 ? 'light' : 'dark';
renderVars.HUEContrast = colors.HUELuminance > 0.22 ? 'light' : 'dark';
// renderVars.contrast = renderVars.RGBLuminanceDelta > renderVars.hueDelta ? 'contrast' : '';
renderVars.contrast = renderVars.luminanceDelta > renderVars.hueDelta ? 'contrast' : '';
renderVars.readabiltiy =
colors['rgbaMixBGMix' + bgType].WCAG2Ratio >= 7 ? 'green' :
colors['rgbaMixBGMix' + bgType].WCAG2Ratio >= 4.5 ? 'orange': '';
renderVars.noRGBZ = _options['no' + _options.mode.type.toUpperCase() + _options.mode.z] ?
(_options.mode.z === 'g' && colors.rgb.g < 0.59 || _options.mode.z === 'b' || _options.mode.z === 'r' ?
'dark' : 'light') : undefined;
}
function renderAll() { // maybe render alpha seperately...
if (_mouseMoveAction) {
// _renderTimer = window[requestAnimationFrame](renderAll);
if (!_newData) return (_renderTimer = window[requestAnimationFrame](renderAll));
_newData = false;
}
// console.time('renderAll');
var options = _options,
mode = options.mode,
scale = options.scale,
prefix = options.CSSPrefix,
colors = _colors,
nodes = _nodes,
CSS = nodes.styles,
textNodes = nodes.textNodes,
valueRanges = _valueRanges,
valueType = _valueType,
renderVars = _renderVars,
cashedVars = _cashedVars,
a = 0,
b = 0,
x = colors[mode.type][mode.x],
X = Math.round(x * 255 / (scale === 4 ? 2 : scale)),
y_ = colors[mode.type][mode.y],
y = 1 - y_,
Y = Math.round(y * 255 / scale),
z = 1 - colors[mode.type][mode.z],
Z = Math.round(z * 255 / scale),
coords = (1 === 1) ? [x, y_] : [0, 0], // (1 === 2) button label up
isRGB = mode.type === 'rgb',
isHue = mode.z === 'h',
isHSL = mode.type === 'hsl',
isHSL_S = isHSL && mode.z === 's',
moveXY = _mouseMoveAction === changeXYValue,
moveZ = _mouseMoveAction === changeZValue,
display, tmp, value, slider;
if (isRGB) {
if (coords[0] >= coords[1]) b = 1; else a = 1;
if (cashedVars.sliderSwap !== a) {
nodes.sldr_2.className = options.CSSPrefix + 'sldr-' + (3 - a);
cashedVars.sliderSwap = a;
}
}
if ((isRGB && !moveZ) || (isHue && !moveXY) || (!isHue && !moveZ)) {
CSS[isHue ? 'sldl_2' : 'sldr_2'][isRGB ? 'cssText' : 'backgroundColor'] =
isRGB ? getOpacityCSS((coords[a] - coords[b]) / (1 - (coords[b]) || 0)) : color2string(colors.hueRGB);
}
if (!isHue) {
if (!moveZ) CSS.sldr_4.cssText = getOpacityCSS(isRGB ? coords[b] : isHSL_S ? Math.abs(1 - y * 2) : y);
if (!moveXY) CSS.sldl_3.cssText = getOpacityCSS(isHSL && mode.z === 'l' ? Math.abs(1 - z * 2) : z);
if (isHSL) { // switch slider class name for black/white color half way through in HSL(S|L) mode(s)
slider = isHSL_S ? 'sldr_4' : 'sldl_3';
tmp = isHSL_S ? 'r-' : 'l-';
value = isHSL_S ? (y > 0.5 ? 4 : 3) : (z > 0.5 ? 3 : 4);
if (cashedVars[slider] !== value) {
nodes[slider].className = options.CSSPrefix + 'sld' + tmp + value;
cashedVars[slider] = value;
}
}
}
if (!moveZ) CSS.curm.cssText = 'left: ' + X + 'px; top: ' + Y + 'px;';
if (!moveXY) CSS.curl.top = Z + 'px';
if (valueType) CSS.curr.top = Z + 'px'; // && valueType.type !== mode.type
if ((valueType && valueType.type === 'alpha') || _mainTarget === nodes.opacity) {
CSS.opacity_slider.left = options.opacityPositionRelative ? (colors.alpha * (
(_targetOrigin.width || nodes.opacity.offsetWidth) -
(_targetOrigin.childWidth || nodes.opacity_slider.offsetWidth))) + 'px' :
(colors.alpha * 100) + '%';
}
CSS.col1.cssText = 'background-color: ' + color2string(colors.RND.rgb) + '; ' +
(options.muteAlpha ? '' : getOpacityCSS(colors.alpha));
CSS.opacity.backgroundColor = color2string(colors.RND.rgb);
CSS.cold.width = renderVars.hueDelta + '%';
CSS.cont.width = renderVars.luminanceDelta + '%';
for (display in textNodes) {
tmp = display.split('_');
if (options.cmyOnly) {
tmp[0] = tmp[0].replace('k', '');
}
value = tmp[1] ? colors.RND[tmp[0]][tmp[1]] : colors.RND[tmp[0]] || colors[tmp[0]];
if (cashedVars[display] !== value) {
cashedVars[display] = value;
textNodes[display].data = value > 359.5 && display !== 'HEX' ? 0 : value;
if (display !== 'HEX' && !options.noRangeBackground) {
value = colors[tmp[0]][tmp[1]] !== undefined ? colors[tmp[0]][tmp[1]] : colors[tmp[0]];
if (tmp[0] === 'Lab') {
value = (value - valueRanges[tmp[0]][tmp[1]][0]) /
(valueRanges[tmp[0]][tmp[1]][1] - valueRanges[tmp[0]][tmp[1]][0]);
}
CSS[display].backgroundPosition = Math.round((1 - value) * 100) + '% 0%';
}
}
}
// Lab out of gammut
tmp = colors._rgb ? [
colors._rgb.r !== colors.rgb.r,
colors._rgb.g !== colors.rgb.g,
colors._rgb.b !== colors.rgb.b
] : [];
if (tmp.join('') !== cashedVars.outOfGammut) {
nodes.rgb_r_labl.firstChild.data = tmp[0] ? '!' : ' ';
nodes.rgb_g_labl.firstChild.data = tmp[1] ? '!' : ' ';
nodes.rgb_b_labl.firstChild.data = tmp[2] ? '!' : ' ';
cashedVars.outOfGammut = tmp.join('');
}
if (renderVars.noRGBZ) {
if (cashedVars.noRGBZ !== renderVars.noRGBZ) {
nodes.curl.className = prefix + 'curl ' + prefix + 'curl-' + renderVars.noRGBZ;
if (!moveZ) {
nodes.curr.className = prefix + 'curr ' + prefix + 'curr-' + renderVars.noRGBZ;
}
cashedVars.noRGBZ = renderVars.noRGBZ;
}
}
if (cashedVars.HUEContrast !== renderVars.HUEContrast && mode.z === 'h') {
nodes.slds.className = nodes.slds.className.replace(/\s+hue-(?:dark|light)/, '') +
' hue-' + renderVars.HUEContrast;
if (!moveZ) {
nodes.curr.className = prefix + 'curr ' + prefix + 'curr-' + renderVars.HUEContrast;
}
cashedVars.HUEContrast = renderVars.HUEContrast;
} else if (cashedVars.RGBLuminance !== renderVars.RGBLuminance) { // test for no else
nodes.colorPicker.className = nodes.colorPicker.className.replace(/\s+(?:dark|light)/, '') +
' ' + renderVars.RGBLuminance;
if (!moveZ && mode.z !== 'h' && !renderVars.noRGBZ) {
nodes.curr.className = prefix + 'curr ' + prefix + 'curr-' + renderVars.RGBLuminance;
}
cashedVars.RGBLuminance = renderVars.RGBLuminance;
}
if (cashedVars.contrast !== renderVars.contrast || cashedVars.readabiltiy !== renderVars.readabiltiy) {
nodes.ctrl.className = nodes.ctrl.className.replace(' contrast', '').replace(/\s*(?:orange|green)/, '') +
(renderVars.contrast ? ' ' + renderVars.contrast : '') +
(renderVars.readabiltiy ? ' ' + renderVars.readabiltiy : '');
cashedVars.contrast = renderVars.contrast;
cashedVars.readabiltiy = renderVars.readabiltiy;
}
if (cashedVars.saveColor !== colors.saveColor) {
nodes.HEX_labl.firstChild.data = !colors.saveColor ? '!' : colors.saveColor === 'web save' ? 'W' : 'M';
cashedVars.saveColor = colors.saveColor;
}
if (options.renderCallback) {
options.renderCallback(colors, mode); // maybe more parameters
}
if (_mouseMoveAction) {
_renderTimer = window[requestAnimationFrame](renderAll);
}
// console.timeEnd('renderAll')
}
// ------------------------------------------------------ //
// ------------------ helper functions ------------------ //
// -------------------------------------------------------//
function copyColor(color) {
var newColor = {};
for (var n in color) {
newColor[n] = color[n];
}
return newColor;
}
// function color2string(color, type) {
// var out = [],
// n = 0;
// type = type || 'rgb';
// while (type.charAt(n)) { // IE7 // V8 type[n] ||
// out.push(color[type.charAt(n)]);
// n++;
// }
// return type + '(' + out.join(', ') + ')';
// }
function color2string(color, type) { // ~2 x faster on V8
var out = '',
t = (type || 'rgb').split(''),
n = t.length;
for ( ; n--; ) {
out = ', ' + color[t[n]] + out;
}
return (type || 'rgb') + '(' + out.substr(2) + ')';
}
function limitValue(value, min, max) {
// return Math.max(min, Math.min(max, value)); // faster??
return (value > max ? max : value < min ? min : value);
}
function getOpacityCSS(value) {
if (value === undefined) value = 1;
if (_doesOpacity) {
return 'opacity: ' + (Math.round(value * 10000000000) / 10000000000) + ';'; // value.toFixed(16) = 99% slower
// some speed test:
// return ['opacity: ', (Math.round(value * 1e+10) / 1e+10), ';'].join('');
} else {
return 'filter: alpha(opacity=' + Math.round(value * 100) + ');';
}
}
function preventDefault(e, skip) {
e.preventDefault ? e.preventDefault() : e.returnValue = false;
if (!skip) window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
return false;
}
function changeClass(elm, cln, newCln) {
return !elm ? false : elm.className = (newCln !== undefined ?
elm.className.replace(new RegExp('\\s+?' + cln, 'g'), newCln ? ' ' + newCln : '') :
elm.className + ' ' + cln);
}
function getOrigin(elm) {
var box = (elm.getBoundingClientRect) ? elm.getBoundingClientRect() : {top: 0, left: 0},
doc = elm && elm.ownerDocument,
body = doc.body,
win = doc.defaultView || doc.parentWindow || window,
docElem = doc.documentElement || body.parentNode,
clientTop = docElem.clientTop || body.clientTop || 0, // border on html or body or both
clientLeft = docElem.clientLeft || body.clientLeft || 0;
return {
left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
};
}
function getPageXY(e) {
return {
X: e.pageX || e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
Y: e.pageY || e.clientY + document.body.scrollTop + document.documentElement.scrollTop
};
}
function addEvent(obj, type, func) {
addEvent.cache = addEvent.cache || {
_get: function(obj, type, func, checkOnly) {
var cache = addEvent.cache[type] || [];
for (var n = cache.length; n--; ) {
if (obj === cache[n].obj && '' + func === '' + cache[n].func) {
func = cache[n].func;
if (!checkOnly) {
cache[n] = cache[n].obj = cache[n].func = null;
cache.splice(n, 1);
}
return func;
}
}
},
_set: function(obj, type, func) {
var cache = addEvent.cache[type] = addEvent.cache[type] || [];
if (addEvent.cache._get(obj, type, func, true)) {
return true;
} else {
cache.push({
func: func,
obj: obj
});
}
}
};
if (!func.name && addEvent.cache._set(obj, type, func) || typeof func !== 'function') {
return;
}
if (obj.addEventListener) obj.addEventListener(type, func, false);
else obj.attachEvent('on' + type, func);
}
function removeEvent(obj, type, func) {
if (typeof func !== 'function') return;
if (!func.name) {
func = addEvent.cache._get(obj, type, func) || func;
}
if (obj.removeEventListener) obj.removeEventListener(type, func, false);
else obj.detachEvent('on' + type, func);
}
function caret(target, pos) { // only for contenteditable
var out = {};
if (pos === undefined) { // get
if (window.getSelection) { // HTML5
target.focus();
var range1 = window.getSelection().getRangeAt(0),
range2 = range1.cloneRange();
range2.selectNodeContents(target);
range2.setEnd(range1.endContainer, range1.endOffset);
out = {
end: range2.toString().length,
range: range1.toString().length
};
} else { // IE < 9
target.focus();
var range1 = document.selection.createRange(),
range2 = document.body.createTextRange();
range2.moveToElementText(target);
range2.setEndPoint('EndToEnd', range1);
out = {
end: range2.text.length,
range: range1.text.length
};
}
out.start = out.end - out.range;
return out;
}
// set
if (pos == -1) pos = target['text']().length;
if (window.getSelection) { // HTML5
target.focus();
window.getSelection().collapse(target.firstChild, pos);
} else { // IE < 9
var range = document.body.createTextRange();
range.moveToElementText(target);
range.moveStart('character', pos);
range.collapse(true);
range.select();
}
return pos;
}
// ------------- requestAnimationFrame shim ------------- //
// ---------- quite optimized for minification ---------- //
for(var n = vendors.length; n-- && !window[requestAnimationFrame]; ) {
window[requestAnimationFrame] = window[vendors[n] + 'Request' + animationFrame];
window[cancelAnimationFrame] = window[vendors[n] + 'Cancel' + animationFrame] ||
window[vendors[n] + 'CancelRequest' + animationFrame];
}
window[requestAnimationFrame] = window[requestAnimationFrame] || function(callback) {
// this is good enough... and better than setTimeout
return window.setTimeout(callback, 1000 / _options.fps);
// return _renderTimer ? _renderTimer : window.setInterval(callback, 1000 / _options.fps);
};
window[cancelAnimationFrame] = window[cancelAnimationFrame] || function(id) {
// console.log('OFF-', id + '-' + _renderTimer)
window.clearTimeout(id);
return _renderTimer = null;
};
})(window);