--- /dev/null
+/**\r
+ * jscolor - JavaScript Color Picker\r
+ *\r
+ * @link http://jscolor.com\r
+ * @license For open source use: GPLv3\r
+ * For commercial use: JSColor Commercial License\r
+ * @author Jan Odvarko\r
+ * @version 2.0.5\r
+ *\r
+ * See usage examples at http://jscolor.com/examples/\r
+ */\r
+\r
+\r
+"use strict";\r
+\r
+\r
+if (!window.jscolor) { window.jscolor = (function () {\r
+\r
+\r
+var jsc = {\r
+\r
+\r
+ register : function () {\r
+ jsc.attachDOMReadyEvent(jsc.init);\r
+ jsc.attachEvent(document, 'mousedown', jsc.onDocumentMouseDown);\r
+ jsc.attachEvent(document, 'touchstart', jsc.onDocumentTouchStart);\r
+ jsc.attachEvent(window, 'resize', jsc.onWindowResize);\r
+ },\r
+\r
+\r
+ init : function () {\r
+ if (jsc.jscolor.lookupClass) {\r
+ jsc.jscolor.installByClassName(jsc.jscolor.lookupClass);\r
+ }\r
+ },\r
+\r
+\r
+ tryInstallOnElements : function (elms, className) {\r
+ var matchClass = new RegExp('(^|\\s)(' + className + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i');\r
+\r
+ for (var i = 0; i < elms.length; i += 1) {\r
+ if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color') {\r
+ if (jsc.isColorAttrSupported) {\r
+ // skip inputs of type 'color' if supported by the browser\r
+ continue;\r
+ }\r
+ }\r
+ var m;\r
+ if (!elms[i].jscolor && elms[i].className && (m = elms[i].className.match(matchClass))) {\r
+ var targetElm = elms[i];\r
+ var optsStr = null;\r
+\r
+ var dataOptions = jsc.getDataAttr(targetElm, 'jscolor');\r
+ if (dataOptions !== null) {\r
+ optsStr = dataOptions;\r
+ } else if (m[4]) {\r
+ optsStr = m[4];\r
+ }\r
+\r
+ var opts = {};\r
+ if (optsStr) {\r
+ try {\r
+ opts = (new Function ('return (' + optsStr + ')'))();\r
+ } catch(eParseError) {\r
+ jsc.warn('Error parsing jscolor options: ' + eParseError + ':\n' + optsStr);\r
+ }\r
+ }\r
+ targetElm.jscolor = new jsc.jscolor(targetElm, opts);\r
+ }\r
+ }\r
+ },\r
+\r
+\r
+ isColorAttrSupported : (function () {\r
+ var elm = document.createElement('input');\r
+ if (elm.setAttribute) {\r
+ elm.setAttribute('type', 'color');\r
+ if (elm.type.toLowerCase() == 'color') {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ })(),\r
+\r
+\r
+ isCanvasSupported : (function () {\r
+ var elm = document.createElement('canvas');\r
+ return !!(elm.getContext && elm.getContext('2d'));\r
+ })(),\r
+\r
+\r
+ fetchElement : function (mixed) {\r
+ return typeof mixed === 'string' ? document.getElementById(mixed) : mixed;\r
+ },\r
+\r
+\r
+ isElementType : function (elm, type) {\r
+ return elm.nodeName.toLowerCase() === type.toLowerCase();\r
+ },\r
+\r
+\r
+ getDataAttr : function (el, name) {\r
+ var attrName = 'data-' + name;\r
+ var attrValue = el.getAttribute(attrName);\r
+ if (attrValue !== null) {\r
+ return attrValue;\r
+ }\r
+ return null;\r
+ },\r
+\r
+\r
+ attachEvent : function (el, evnt, func) {\r
+ if (el.addEventListener) {\r
+ el.addEventListener(evnt, func, false);\r
+ } else if (el.attachEvent) {\r
+ el.attachEvent('on' + evnt, func);\r
+ }\r
+ },\r
+\r
+\r
+ detachEvent : function (el, evnt, func) {\r
+ if (el.removeEventListener) {\r
+ el.removeEventListener(evnt, func, false);\r
+ } else if (el.detachEvent) {\r
+ el.detachEvent('on' + evnt, func);\r
+ }\r
+ },\r
+\r
+\r
+ _attachedGroupEvents : {},\r
+\r
+\r
+ attachGroupEvent : function (groupName, el, evnt, func) {\r
+ if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) {\r
+ jsc._attachedGroupEvents[groupName] = [];\r
+ }\r
+ jsc._attachedGroupEvents[groupName].push([el, evnt, func]);\r
+ jsc.attachEvent(el, evnt, func);\r
+ },\r
+\r
+\r
+ detachGroupEvents : function (groupName) {\r
+ if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) {\r
+ for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) {\r
+ var evt = jsc._attachedGroupEvents[groupName][i];\r
+ jsc.detachEvent(evt[0], evt[1], evt[2]);\r
+ }\r
+ delete jsc._attachedGroupEvents[groupName];\r
+ }\r
+ },\r
+\r
+\r
+ attachDOMReadyEvent : function (func) {\r
+ var fired = false;\r
+ var fireOnce = function () {\r
+ if (!fired) {\r
+ fired = true;\r
+ func();\r
+ }\r
+ };\r
+\r
+ if (document.readyState === 'complete') {\r
+ setTimeout(fireOnce, 1); // async\r
+ return;\r
+ }\r
+\r
+ if (document.addEventListener) {\r
+ document.addEventListener('DOMContentLoaded', fireOnce, false);\r
+\r
+ // Fallback\r
+ window.addEventListener('load', fireOnce, false);\r
+\r
+ } else if (document.attachEvent) {\r
+ // IE\r
+ document.attachEvent('onreadystatechange', function () {\r
+ if (document.readyState === 'complete') {\r
+ document.detachEvent('onreadystatechange', arguments.callee);\r
+ fireOnce();\r
+ }\r
+ })\r
+\r
+ // Fallback\r
+ window.attachEvent('onload', fireOnce);\r
+\r
+ // IE7/8\r
+ if (document.documentElement.doScroll && window == window.top) {\r
+ var tryScroll = function () {\r
+ if (!document.body) { return; }\r
+ try {\r
+ document.documentElement.doScroll('left');\r
+ fireOnce();\r
+ } catch (e) {\r
+ setTimeout(tryScroll, 1);\r
+ }\r
+ };\r
+ tryScroll();\r
+ }\r
+ }\r
+ },\r
+\r
+\r
+ warn : function (msg) {\r
+ if (window.console && window.console.warn) {\r
+ window.console.warn(msg);\r
+ }\r
+ },\r
+\r
+\r
+ preventDefault : function (e) {\r
+ if (e.preventDefault) { e.preventDefault(); }\r
+ e.returnValue = false;\r
+ },\r
+\r
+\r
+ captureTarget : function (target) {\r
+ // IE\r
+ if (target.setCapture) {\r
+ jsc._capturedTarget = target;\r
+ jsc._capturedTarget.setCapture();\r
+ }\r
+ },\r
+\r
+\r
+ releaseTarget : function () {\r
+ // IE\r
+ if (jsc._capturedTarget) {\r
+ jsc._capturedTarget.releaseCapture();\r
+ jsc._capturedTarget = null;\r
+ }\r
+ },\r
+\r
+\r
+ fireEvent : function (el, evnt) {\r
+ if (!el) {\r
+ return;\r
+ }\r
+ if (document.createEvent) {\r
+ var ev = document.createEvent('HTMLEvents');\r
+ ev.initEvent(evnt, true, true);\r
+ el.dispatchEvent(ev);\r
+ } else if (document.createEventObject) {\r
+ var ev = document.createEventObject();\r
+ el.fireEvent('on' + evnt, ev);\r
+ } else if (el['on' + evnt]) { // alternatively use the traditional event model\r
+ el['on' + evnt]();\r
+ }\r
+ },\r
+\r
+\r
+ classNameToList : function (className) {\r
+ return className.replace(/^\s+|\s+$/g, '').split(/\s+/);\r
+ },\r
+\r
+\r
+ // The className parameter (str) can only contain a single class name\r
+ hasClass : function (elm, className) {\r
+ if (!className) {\r
+ return false;\r
+ }\r
+ return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' ');\r
+ },\r
+\r
+\r
+ // The className parameter (str) can contain multiple class names separated by whitespace\r
+ setClass : function (elm, className) {\r
+ var classList = jsc.classNameToList(className);\r
+ for (var i = 0; i < classList.length; i += 1) {\r
+ if (!jsc.hasClass(elm, classList[i])) {\r
+ elm.className += (elm.className ? ' ' : '') + classList[i];\r
+ }\r
+ }\r
+ },\r
+\r
+\r
+ // The className parameter (str) can contain multiple class names separated by whitespace\r
+ unsetClass : function (elm, className) {\r
+ var classList = jsc.classNameToList(className);\r
+ for (var i = 0; i < classList.length; i += 1) {\r
+ var repl = new RegExp(\r
+ '^\\s*' + classList[i] + '\\s*|' +\r
+ '\\s*' + classList[i] + '\\s*$|' +\r
+ '\\s+' + classList[i] + '(\\s+)',\r
+ 'g'\r
+ );\r
+ elm.className = elm.className.replace(repl, '$1');\r
+ }\r
+ },\r
+\r
+\r
+ getStyle : function (elm) {\r
+ return window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle;\r
+ },\r
+\r
+\r
+ setStyle : (function () {\r
+ var helper = document.createElement('div');\r
+ var getSupportedProp = function (names) {\r
+ for (var i = 0; i < names.length; i += 1) {\r
+ if (names[i] in helper.style) {\r
+ return names[i];\r
+ }\r
+ }\r
+ };\r
+ var props = {\r
+ borderRadius: getSupportedProp(['borderRadius', 'MozBorderRadius', 'webkitBorderRadius']),\r
+ boxShadow: getSupportedProp(['boxShadow', 'MozBoxShadow', 'webkitBoxShadow'])\r
+ };\r
+ return function (elm, prop, value) {\r
+ switch (prop.toLowerCase()) {\r
+ case 'opacity':\r
+ var alphaOpacity = Math.round(parseFloat(value) * 100);\r
+ elm.style.opacity = value;\r
+ elm.style.filter = 'alpha(opacity=' + alphaOpacity + ')';\r
+ break;\r
+ default:\r
+ elm.style[props[prop]] = value;\r
+ break;\r
+ }\r
+ };\r
+ })(),\r
+\r
+\r
+ setBorderRadius : function (elm, value) {\r
+ jsc.setStyle(elm, 'borderRadius', value || '0');\r
+ },\r
+\r
+\r
+ setBoxShadow : function (elm, value) {\r
+ jsc.setStyle(elm, 'boxShadow', value || 'none');\r
+ },\r
+\r
+\r
+ getElementPos : function (e, relativeToViewport) {\r
+ var x=0, y=0;\r
+ var rect = e.getBoundingClientRect();\r
+ x = rect.left;\r
+ y = rect.top;\r
+ if (!relativeToViewport) {\r
+ var viewPos = jsc.getViewPos();\r
+ x += viewPos[0];\r
+ y += viewPos[1];\r
+ }\r
+ return [x, y];\r
+ },\r
+\r
+\r
+ getElementSize : function (e) {\r
+ return [e.offsetWidth, e.offsetHeight];\r
+ },\r
+\r
+\r
+ // get pointer's X/Y coordinates relative to viewport\r
+ getAbsPointerPos : function (e) {\r
+ if (!e) { e = window.event; }\r
+ var x = 0, y = 0;\r
+ if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) {\r
+ // touch devices\r
+ x = e.changedTouches[0].clientX;\r
+ y = e.changedTouches[0].clientY;\r
+ } else if (typeof e.clientX === 'number') {\r
+ x = e.clientX;\r
+ y = e.clientY;\r
+ }\r
+ return { x: x, y: y };\r
+ },\r
+\r
+\r
+ // get pointer's X/Y coordinates relative to target element\r
+ getRelPointerPos : function (e) {\r
+ if (!e) { e = window.event; }\r
+ var target = e.target || e.srcElement;\r
+ var targetRect = target.getBoundingClientRect();\r
+\r
+ var x = 0, y = 0;\r
+\r
+ var clientX = 0, clientY = 0;\r
+ if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) {\r
+ // touch devices\r
+ clientX = e.changedTouches[0].clientX;\r
+ clientY = e.changedTouches[0].clientY;\r
+ } else if (typeof e.clientX === 'number') {\r
+ clientX = e.clientX;\r
+ clientY = e.clientY;\r
+ }\r
+\r
+ x = clientX - targetRect.left;\r
+ y = clientY - targetRect.top;\r
+ return { x: x, y: y };\r
+ },\r
+\r
+\r
+ getViewPos : function () {\r
+ var doc = document.documentElement;\r
+ return [\r
+ (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0),\r
+ (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)\r
+ ];\r
+ },\r
+\r
+\r
+ getViewSize : function () {\r
+ var doc = document.documentElement;\r
+ return [\r
+ (window.innerWidth || doc.clientWidth),\r
+ (window.innerHeight || doc.clientHeight),\r
+ ];\r
+ },\r
+\r
+\r
+ redrawPosition : function () {\r
+\r
+ if (jsc.picker && jsc.picker.owner) {\r
+ var thisObj = jsc.picker.owner;\r
+\r
+ var tp, vp;\r
+\r
+ if (thisObj.fixed) {\r
+ // Fixed elements are positioned relative to viewport,\r
+ // therefore we can ignore the scroll offset\r
+ tp = jsc.getElementPos(thisObj.targetElement, true); // target pos\r
+ vp = [0, 0]; // view pos\r
+ } else {\r
+ tp = jsc.getElementPos(thisObj.targetElement); // target pos\r
+ vp = jsc.getViewPos(); // view pos\r
+ }\r
+\r
+ var ts = jsc.getElementSize(thisObj.targetElement); // target size\r
+ var vs = jsc.getViewSize(); // view size\r
+ var ps = jsc.getPickerOuterDims(thisObj); // picker size\r
+ var a, b, c;\r
+ switch (thisObj.position.toLowerCase()) {\r
+ case 'left': a=1; b=0; c=-1; break;\r
+ case 'right':a=1; b=0; c=1; break;\r
+ case 'top': a=0; b=1; c=-1; break;\r
+ default: a=0; b=1; c=1; break;\r
+ }\r
+ var l = (ts[b]+ps[b])/2;\r
+\r
+ // compute picker position\r
+ if (!thisObj.smartPosition) {\r
+ var pp = [\r
+ tp[a],\r
+ tp[b]+ts[b]-l+l*c\r
+ ];\r
+ } else {\r
+ var pp = [\r
+ -vp[a]+tp[a]+ps[a] > vs[a] ?\r
+ (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) :\r
+ tp[a],\r
+ -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ?\r
+ (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) :\r
+ (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c)\r
+ ];\r
+ }\r
+\r
+ var x = pp[a];\r
+ var y = pp[b];\r
+ var positionValue = thisObj.fixed ? 'fixed' : 'absolute';\r
+ var contractShadow =\r
+ (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) &&\r
+ (pp[1] + ps[1] < tp[1] + ts[1]);\r
+\r
+ jsc._drawPosition(thisObj, x, y, positionValue, contractShadow);\r
+ }\r
+ },\r
+\r
+\r
+ _drawPosition : function (thisObj, x, y, positionValue, contractShadow) {\r
+ var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px\r
+\r
+ jsc.picker.wrap.style.position = positionValue;\r
+ jsc.picker.wrap.style.left = x + 'px';\r
+ jsc.picker.wrap.style.top = y + 'px';\r
+\r
+ jsc.setBoxShadow(\r
+ jsc.picker.boxS,\r
+ thisObj.shadow ?\r
+ new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) :\r
+ null);\r
+ },\r
+\r
+\r
+ getPickerDims : function (thisObj) {\r
+ var displaySlider = !!jsc.getSliderComponent(thisObj);\r
+ var dims = [\r
+ 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.width +\r
+ (displaySlider ? 2 * thisObj.insetWidth + jsc.getPadToSliderPadding(thisObj) + thisObj.sliderSize : 0),\r
+ 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.height +\r
+ (thisObj.closable ? 2 * thisObj.insetWidth + thisObj.padding + thisObj.buttonHeight : 0)\r
+ ];\r
+ return dims;\r
+ },\r
+\r
+\r
+ getPickerOuterDims : function (thisObj) {\r
+ var dims = jsc.getPickerDims(thisObj);\r
+ return [\r
+ dims[0] + 2 * thisObj.borderWidth,\r
+ dims[1] + 2 * thisObj.borderWidth\r
+ ];\r
+ },\r
+\r
+\r
+ getPadToSliderPadding : function (thisObj) {\r
+ return Math.max(thisObj.padding, 1.5 * (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness));\r
+ },\r
+\r
+\r
+ getPadYComponent : function (thisObj) {\r
+ switch (thisObj.mode.charAt(1).toLowerCase()) {\r
+ case 'v': return 'v'; break;\r
+ }\r
+ return 's';\r
+ },\r
+\r
+\r
+ getSliderComponent : function (thisObj) {\r
+ if (thisObj.mode.length > 2) {\r
+ switch (thisObj.mode.charAt(2).toLowerCase()) {\r
+ case 's': return 's'; break;\r
+ case 'v': return 'v'; break;\r
+ }\r
+ }\r
+ return null;\r
+ },\r
+\r
+\r
+ onDocumentMouseDown : function (e) {\r
+ if (!e) { e = window.event; }\r
+ var target = e.target || e.srcElement;\r
+\r
+ if (target._jscLinkedInstance) {\r
+ if (target._jscLinkedInstance.showOnClick) {\r
+ target._jscLinkedInstance.show();\r
+ }\r
+ } else if (target._jscControlName) {\r
+ jsc.onControlPointerStart(e, target, target._jscControlName, 'mouse');\r
+ } else {\r
+ // Mouse is outside the picker controls -> hide the color picker!\r
+ if (jsc.picker && jsc.picker.owner) {\r
+ jsc.picker.owner.hide();\r
+ }\r
+ }\r
+ },\r
+\r
+\r
+ onDocumentTouchStart : function (e) {\r
+ if (!e) { e = window.event; }\r
+ var target = e.target || e.srcElement;\r
+\r
+ if (target._jscLinkedInstance) {\r
+ if (target._jscLinkedInstance.showOnClick) {\r
+ target._jscLinkedInstance.show();\r
+ }\r
+ } else if (target._jscControlName) {\r
+ jsc.onControlPointerStart(e, target, target._jscControlName, 'touch');\r
+ } else {\r
+ if (jsc.picker && jsc.picker.owner) {\r
+ jsc.picker.owner.hide();\r
+ }\r
+ }\r
+ },\r
+\r
+\r
+ onWindowResize : function (e) {\r
+ jsc.redrawPosition();\r
+ },\r
+\r
+\r
+ onParentScroll : function (e) {\r
+ // hide the picker when one of the parent elements is scrolled\r
+ if (jsc.picker && jsc.picker.owner) {\r
+ jsc.picker.owner.hide();\r
+ }\r
+ },\r
+\r
+\r
+ _pointerMoveEvent : {\r
+ mouse: 'mousemove',\r
+ touch: 'touchmove'\r
+ },\r
+ _pointerEndEvent : {\r
+ mouse: 'mouseup',\r
+ touch: 'touchend'\r
+ },\r
+\r
+\r
+ _pointerOrigin : null,\r
+ _capturedTarget : null,\r
+\r
+\r
+ onControlPointerStart : function (e, target, controlName, pointerType) {\r
+ var thisObj = target._jscInstance;\r
+\r
+ jsc.preventDefault(e);\r
+ jsc.captureTarget(target);\r
+\r
+ var registerDragEvents = function (doc, offset) {\r
+ jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType],\r
+ jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset));\r
+ jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType],\r
+ jsc.onDocumentPointerEnd(e, target, controlName, pointerType));\r
+ };\r
+\r
+ registerDragEvents(document, [0, 0]);\r
+\r
+ if (window.parent && window.frameElement) {\r
+ var rect = window.frameElement.getBoundingClientRect();\r
+ var ofs = [-rect.left, -rect.top];\r
+ registerDragEvents(window.parent.window.document, ofs);\r
+ }\r
+\r
+ var abs = jsc.getAbsPointerPos(e);\r
+ var rel = jsc.getRelPointerPos(e);\r
+ jsc._pointerOrigin = {\r
+ x: abs.x - rel.x,\r
+ y: abs.y - rel.y\r
+ };\r
+\r
+ switch (controlName) {\r
+ case 'pad':\r
+ // if the slider is at the bottom, move it up\r
+ switch (jsc.getSliderComponent(thisObj)) {\r
+ case 's': if (thisObj.hsv[1] === 0) { thisObj.fromHSV(null, 100, null); }; break;\r
+ case 'v': if (thisObj.hsv[2] === 0) { thisObj.fromHSV(null, null, 100); }; break;\r
+ }\r
+ jsc.setPad(thisObj, e, 0, 0);\r
+ break;\r
+\r
+ case 'sld':\r
+ jsc.setSld(thisObj, e, 0);\r
+ break;\r
+ }\r
+\r
+ jsc.dispatchFineChange(thisObj);\r
+ },\r
+\r
+\r
+ onDocumentPointerMove : function (e, target, controlName, pointerType, offset) {\r
+ return function (e) {\r
+ var thisObj = target._jscInstance;\r
+ switch (controlName) {\r
+ case 'pad':\r
+ if (!e) { e = window.event; }\r
+ jsc.setPad(thisObj, e, offset[0], offset[1]);\r
+ jsc.dispatchFineChange(thisObj);\r
+ break;\r
+\r
+ case 'sld':\r
+ if (!e) { e = window.event; }\r
+ jsc.setSld(thisObj, e, offset[1]);\r
+ jsc.dispatchFineChange(thisObj);\r
+ break;\r
+ }\r
+ }\r
+ },\r
+\r
+\r
+ onDocumentPointerEnd : function (e, target, controlName, pointerType) {\r
+ return function (e) {\r
+ var thisObj = target._jscInstance;\r
+ jsc.detachGroupEvents('drag');\r
+ jsc.releaseTarget();\r
+ // Always dispatch changes after detaching outstanding mouse handlers,\r
+ // in case some user interaction will occur in user's onchange callback\r
+ // that would intrude with current mouse events\r
+ jsc.dispatchChange(thisObj);\r
+ };\r
+ },\r
+\r
+\r
+ dispatchChange : function (thisObj) {\r
+ if (thisObj.valueElement) {\r
+ if (jsc.isElementType(thisObj.valueElement, 'input')) {\r
+ jsc.fireEvent(thisObj.valueElement, 'change');\r
+ }\r
+ }\r
+ },\r
+\r
+\r
+ dispatchFineChange : function (thisObj) {\r
+ if (thisObj.onFineChange) {\r
+ var callback;\r
+ if (typeof thisObj.onFineChange === 'string') {\r
+ callback = new Function (thisObj.onFineChange);\r
+ } else {\r
+ callback = thisObj.onFineChange;\r
+ }\r
+ callback.call(thisObj);\r
+ }\r
+ },\r
+\r
+\r
+ setPad : function (thisObj, e, ofsX, ofsY) {\r
+ var pointerAbs = jsc.getAbsPointerPos(e);\r
+ var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.insetWidth;\r
+ var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth;\r
+\r
+ var xVal = x * (360 / (thisObj.width - 1));\r
+ var yVal = 100 - (y * (100 / (thisObj.height - 1)));\r
+\r
+ switch (jsc.getPadYComponent(thisObj)) {\r
+ case 's': thisObj.fromHSV(xVal, yVal, null, jsc.leaveSld); break;\r
+ case 'v': thisObj.fromHSV(xVal, null, yVal, jsc.leaveSld); break;\r
+ }\r
+ },\r
+\r
+\r
+ setSld : function (thisObj, e, ofsY) {\r
+ var pointerAbs = jsc.getAbsPointerPos(e);\r
+ var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth;\r
+\r
+ var yVal = 100 - (y * (100 / (thisObj.height - 1)));\r
+\r
+ switch (jsc.getSliderComponent(thisObj)) {\r
+ case 's': thisObj.fromHSV(null, yVal, null, jsc.leavePad); break;\r
+ case 'v': thisObj.fromHSV(null, null, yVal, jsc.leavePad); break;\r
+ }\r
+ },\r
+\r
+\r
+ _vmlNS : 'jsc_vml_',\r
+ _vmlCSS : 'jsc_vml_css_',\r
+ _vmlReady : false,\r
+\r
+\r
+ initVML : function () {\r
+ if (!jsc._vmlReady) {\r
+ // init VML namespace\r
+ var doc = document;\r
+ if (!doc.namespaces[jsc._vmlNS]) {\r
+ doc.namespaces.add(jsc._vmlNS, 'urn:schemas-microsoft-com:vml');\r
+ }\r
+ if (!doc.styleSheets[jsc._vmlCSS]) {\r
+ var tags = ['shape', 'shapetype', 'group', 'background', 'path', 'formulas', 'handles', 'fill', 'stroke', 'shadow', 'textbox', 'textpath', 'imagedata', 'line', 'polyline', 'curve', 'rect', 'roundrect', 'oval', 'arc', 'image'];\r
+ var ss = doc.createStyleSheet();\r
+ ss.owningElement.id = jsc._vmlCSS;\r
+ for (var i = 0; i < tags.length; i += 1) {\r
+ ss.addRule(jsc._vmlNS + '\\:' + tags[i], 'behavior:url(#default#VML);');\r
+ }\r
+ }\r
+ jsc._vmlReady = true;\r
+ }\r
+ },\r
+\r
+\r
+ createPalette : function () {\r
+\r
+ var paletteObj = {\r
+ elm: null,\r
+ draw: null\r
+ };\r
+\r
+ if (jsc.isCanvasSupported) {\r
+ // Canvas implementation for modern browsers\r
+\r
+ var canvas = document.createElement('canvas');\r
+ var ctx = canvas.getContext('2d');\r
+\r
+ var drawFunc = function (width, height, type) {\r
+ canvas.width = width;\r
+ canvas.height = height;\r
+\r
+ ctx.clearRect(0, 0, canvas.width, canvas.height);\r
+\r
+ var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0);\r
+ hGrad.addColorStop(0 / 6, '#F00');\r
+ hGrad.addColorStop(1 / 6, '#FF0');\r
+ hGrad.addColorStop(2 / 6, '#0F0');\r
+ hGrad.addColorStop(3 / 6, '#0FF');\r
+ hGrad.addColorStop(4 / 6, '#00F');\r
+ hGrad.addColorStop(5 / 6, '#F0F');\r
+ hGrad.addColorStop(6 / 6, '#F00');\r
+\r
+ ctx.fillStyle = hGrad;\r
+ ctx.fillRect(0, 0, canvas.width, canvas.height);\r
+\r
+ var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height);\r
+ switch (type.toLowerCase()) {\r
+ case 's':\r
+ vGrad.addColorStop(0, 'rgba(255,255,255,0)');\r
+ vGrad.addColorStop(1, 'rgba(255,255,255,1)');\r
+ break;\r
+ case 'v':\r
+ vGrad.addColorStop(0, 'rgba(0,0,0,0)');\r
+ vGrad.addColorStop(1, 'rgba(0,0,0,1)');\r
+ break;\r
+ }\r
+ ctx.fillStyle = vGrad;\r
+ ctx.fillRect(0, 0, canvas.width, canvas.height);\r
+ };\r
+\r
+ paletteObj.elm = canvas;\r
+ paletteObj.draw = drawFunc;\r
+\r
+ } else {\r
+ // VML fallback for IE 7 and 8\r
+\r
+ jsc.initVML();\r
+\r
+ var vmlContainer = document.createElement('div');\r
+ vmlContainer.style.position = 'relative';\r
+ vmlContainer.style.overflow = 'hidden';\r
+\r
+ var hGrad = document.createElement(jsc._vmlNS + ':fill');\r
+ hGrad.type = 'gradient';\r
+ hGrad.method = 'linear';\r
+ hGrad.angle = '90';\r
+ hGrad.colors = '16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0'\r
+\r
+ var hRect = document.createElement(jsc._vmlNS + ':rect');\r
+ hRect.style.position = 'absolute';\r
+ hRect.style.left = -1 + 'px';\r
+ hRect.style.top = -1 + 'px';\r
+ hRect.stroked = false;\r
+ hRect.appendChild(hGrad);\r
+ vmlContainer.appendChild(hRect);\r
+\r
+ var vGrad = document.createElement(jsc._vmlNS + ':fill');\r
+ vGrad.type = 'gradient';\r
+ vGrad.method = 'linear';\r
+ vGrad.angle = '180';\r
+ vGrad.opacity = '0';\r
+\r
+ var vRect = document.createElement(jsc._vmlNS + ':rect');\r
+ vRect.style.position = 'absolute';\r
+ vRect.style.left = -1 + 'px';\r
+ vRect.style.top = -1 + 'px';\r
+ vRect.stroked = false;\r
+ vRect.appendChild(vGrad);\r
+ vmlContainer.appendChild(vRect);\r
+\r
+ var drawFunc = function (width, height, type) {\r
+ vmlContainer.style.width = width + 'px';\r
+ vmlContainer.style.height = height + 'px';\r
+\r
+ hRect.style.width =\r
+ vRect.style.width =\r
+ (width + 1) + 'px';\r
+ hRect.style.height =\r
+ vRect.style.height =\r
+ (height + 1) + 'px';\r
+\r
+ // Colors must be specified during every redraw, otherwise IE won't display\r
+ // a full gradient during a subsequential redraw\r
+ hGrad.color = '#F00';\r
+ hGrad.color2 = '#F00';\r
+\r
+ switch (type.toLowerCase()) {\r
+ case 's':\r
+ vGrad.color = vGrad.color2 = '#FFF';\r
+ break;\r
+ case 'v':\r
+ vGrad.color = vGrad.color2 = '#000';\r
+ break;\r
+ }\r
+ };\r
+ \r
+ paletteObj.elm = vmlContainer;\r
+ paletteObj.draw = drawFunc;\r
+ }\r
+\r
+ return paletteObj;\r
+ },\r
+\r
+\r
+ createSliderGradient : function () {\r
+\r
+ var sliderObj = {\r
+ elm: null,\r
+ draw: null\r
+ };\r
+\r
+ if (jsc.isCanvasSupported) {\r
+ // Canvas implementation for modern browsers\r
+\r
+ var canvas = document.createElement('canvas');\r
+ var ctx = canvas.getContext('2d');\r
+\r
+ var drawFunc = function (width, height, color1, color2) {\r
+ canvas.width = width;\r
+ canvas.height = height;\r
+\r
+ ctx.clearRect(0, 0, canvas.width, canvas.height);\r
+\r
+ var grad = ctx.createLinearGradient(0, 0, 0, canvas.height);\r
+ grad.addColorStop(0, color1);\r
+ grad.addColorStop(1, color2);\r
+\r
+ ctx.fillStyle = grad;\r
+ ctx.fillRect(0, 0, canvas.width, canvas.height);\r
+ };\r
+\r
+ sliderObj.elm = canvas;\r
+ sliderObj.draw = drawFunc;\r
+\r
+ } else {\r
+ // VML fallback for IE 7 and 8\r
+\r
+ jsc.initVML();\r
+\r
+ var vmlContainer = document.createElement('div');\r
+ vmlContainer.style.position = 'relative';\r
+ vmlContainer.style.overflow = 'hidden';\r
+\r
+ var grad = document.createElement(jsc._vmlNS + ':fill');\r
+ grad.type = 'gradient';\r
+ grad.method = 'linear';\r
+ grad.angle = '180';\r
+\r
+ var rect = document.createElement(jsc._vmlNS + ':rect');\r
+ rect.style.position = 'absolute';\r
+ rect.style.left = -1 + 'px';\r
+ rect.style.top = -1 + 'px';\r
+ rect.stroked = false;\r
+ rect.appendChild(grad);\r
+ vmlContainer.appendChild(rect);\r
+\r
+ var drawFunc = function (width, height, color1, color2) {\r
+ vmlContainer.style.width = width + 'px';\r
+ vmlContainer.style.height = height + 'px';\r
+\r
+ rect.style.width = (width + 1) + 'px';\r
+ rect.style.height = (height + 1) + 'px';\r
+\r
+ grad.color = color1;\r
+ grad.color2 = color2;\r
+ };\r
+ \r
+ sliderObj.elm = vmlContainer;\r
+ sliderObj.draw = drawFunc;\r
+ }\r
+\r
+ return sliderObj;\r
+ },\r
+\r
+\r
+ leaveValue : 1<<0,\r
+ leaveStyle : 1<<1,\r
+ leavePad : 1<<2,\r
+ leaveSld : 1<<3,\r
+\r
+\r
+ BoxShadow : (function () {\r
+ var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) {\r
+ this.hShadow = hShadow;\r
+ this.vShadow = vShadow;\r
+ this.blur = blur;\r
+ this.spread = spread;\r
+ this.color = color;\r
+ this.inset = !!inset;\r
+ };\r
+\r
+ BoxShadow.prototype.toString = function () {\r
+ var vals = [\r
+ Math.round(this.hShadow) + 'px',\r
+ Math.round(this.vShadow) + 'px',\r
+ Math.round(this.blur) + 'px',\r
+ Math.round(this.spread) + 'px',\r
+ this.color\r
+ ];\r
+ if (this.inset) {\r
+ vals.push('inset');\r
+ }\r
+ return vals.join(' ');\r
+ };\r
+\r
+ return BoxShadow;\r
+ })(),\r
+\r
+\r
+ //\r
+ // Usage:\r
+ // var myColor = new jscolor(<targetElement> [, <options>])\r
+ //\r
+\r
+ jscolor : function (targetElement, options) {\r
+\r
+ // General options\r
+ //\r
+ this.value = null; // initial HEX color. To change it later, use methods fromString(), fromHSV() and fromRGB()\r
+ this.valueElement = targetElement; // element that will be used to display and input the color code\r
+ this.styleElement = targetElement; // element that will preview the picked color using CSS backgroundColor\r
+ this.required = true; // whether the associated text <input> can be left empty\r
+ this.refine = true; // whether to refine the entered color code (e.g. uppercase it and remove whitespace)\r
+ this.hash = false; // whether to prefix the HEX color code with # symbol\r
+ this.uppercase = true; // whether to show the color code in upper case\r
+ this.onFineChange = null; // called instantly every time the color changes (value can be either a function or a string with javascript code)\r
+ this.activeClass = 'jscolor-active'; // class to be set to the target element when a picker window is open on it\r
+ this.overwriteImportant = false; // whether to overwrite colors of styleElement using !important\r
+ this.minS = 0; // min allowed saturation (0 - 100)\r
+ this.maxS = 100; // max allowed saturation (0 - 100)\r
+ this.minV = 0; // min allowed value (brightness) (0 - 100)\r
+ this.maxV = 100; // max allowed value (brightness) (0 - 100)\r
+\r
+ // Accessing the picked color\r
+ //\r
+ this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100]\r
+ this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255]\r
+\r
+ // Color Picker options\r
+ //\r
+ this.width = 181; // width of color palette (in px)\r
+ this.height = 101; // height of color palette (in px)\r
+ this.showOnClick = true; // whether to display the color picker when user clicks on its target element\r
+ this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls\r
+ this.position = 'bottom'; // left | right | top | bottom - position relative to the target element\r
+ this.smartPosition = true; // automatically change picker position when there is not enough space for it\r
+ this.sliderSize = 16; // px\r
+ this.crossSize = 8; // px\r
+ this.closable = false; // whether to display the Close button\r
+ this.closeText = 'Close';\r
+ this.buttonColor = '#000000'; // CSS color\r
+ this.buttonHeight = 18; // px\r
+ this.padding = 12; // px\r
+ this.backgroundColor = '#FFFFFF'; // CSS color\r
+ this.borderWidth = 1; // px\r
+ this.borderColor = '#BBBBBB'; // CSS color\r
+ this.borderRadius = 8; // px\r
+ this.insetWidth = 1; // px\r
+ this.insetColor = '#BBBBBB'; // CSS color\r
+ this.shadow = true; // whether to display shadow\r
+ this.shadowBlur = 15; // px\r
+ this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color\r
+ this.pointerColor = '#4C4C4C'; // px\r
+ this.pointerBorderColor = '#FFFFFF'; // px\r
+ this.pointerBorderWidth = 1; // px\r
+ this.pointerThickness = 2; // px\r
+ this.zIndex = 1000;\r
+ this.container = null; // where to append the color picker (BODY element by default)\r
+\r
+\r
+ for (var opt in options) {\r
+ if (options.hasOwnProperty(opt)) {\r
+ this[opt] = options[opt];\r
+ }\r
+ }\r
+\r
+\r
+ this.hide = function () {\r
+ if (isPickerOwner()) {\r
+ detachPicker();\r
+ }\r
+ };\r
+\r
+\r
+ this.show = function () {\r
+ drawPicker();\r
+ };\r
+\r
+\r
+ this.redraw = function () {\r
+ if (isPickerOwner()) {\r
+ drawPicker();\r
+ }\r
+ };\r
+\r
+\r
+ this.importColor = function () {\r
+ if (!this.valueElement) {\r
+ this.exportColor();\r
+ } else {\r
+ if (jsc.isElementType(this.valueElement, 'input')) {\r
+ if (!this.refine) {\r
+ if (!this.fromString(this.valueElement.value, jsc.leaveValue)) {\r
+ if (this.styleElement) {\r
+ this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage;\r
+ this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor;\r
+ this.styleElement.style.color = this.styleElement._jscOrigStyle.color;\r
+ }\r
+ this.exportColor(jsc.leaveValue | jsc.leaveStyle);\r
+ }\r
+ } else if (!this.required && /^\s*$/.test(this.valueElement.value)) {\r
+ this.valueElement.value = '';\r
+ if (this.styleElement) {\r
+ this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage;\r
+ this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor;\r
+ this.styleElement.style.color = this.styleElement._jscOrigStyle.color;\r
+ }\r
+ this.exportColor(jsc.leaveValue | jsc.leaveStyle);\r
+\r
+ } else if (this.fromString(this.valueElement.value)) {\r
+ // managed to import color successfully from the value -> OK, don't do anything\r
+ } else {\r
+ this.exportColor();\r
+ }\r
+ } else {\r
+ // not an input element -> doesn't have any value\r
+ this.exportColor();\r
+ }\r
+ }\r
+ };\r
+\r
+\r
+ this.exportColor = function (flags) {\r
+ if (!(flags & jsc.leaveValue) && this.valueElement) {\r
+ var value = this.toString();\r
+ if (this.uppercase) { value = value.toUpperCase(); }\r
+ if (this.hash) { value = '#' + value; }\r
+\r
+ if (jsc.isElementType(this.valueElement, 'input')) {\r
+ this.valueElement.value = value;\r
+ } else {\r
+ this.valueElement.innerHTML = value;\r
+ }\r
+ }\r
+ if (!(flags & jsc.leaveStyle)) {\r
+ if (this.styleElement) {\r
+ var bgColor = '#' + this.toString();\r
+ var fgColor = this.isLight() ? '#000' : '#FFF';\r
+\r
+ this.styleElement.style.backgroundImage = 'none';\r
+ this.styleElement.style.backgroundColor = bgColor;\r
+ this.styleElement.style.color = fgColor;\r
+\r
+ if (this.overwriteImportant) {\r
+ this.styleElement.setAttribute('style',\r
+ 'background: ' + bgColor + ' !important; ' +\r
+ 'color: ' + fgColor + ' !important;'\r
+ );\r
+ }\r
+ }\r
+ }\r
+ if (!(flags & jsc.leavePad) && isPickerOwner()) {\r
+ redrawPad();\r
+ }\r
+ if (!(flags & jsc.leaveSld) && isPickerOwner()) {\r
+ redrawSld();\r
+ }\r
+ };\r
+\r
+\r
+ // h: 0-360\r
+ // s: 0-100\r
+ // v: 0-100\r
+ //\r
+ this.fromHSV = function (h, s, v, flags) { // null = don't change\r
+ if (h !== null) {\r
+ if (isNaN(h)) { return false; }\r
+ h = Math.max(0, Math.min(360, h));\r
+ }\r
+ if (s !== null) {\r
+ if (isNaN(s)) { return false; }\r
+ s = Math.max(0, Math.min(100, this.maxS, s), this.minS);\r
+ }\r
+ if (v !== null) {\r
+ if (isNaN(v)) { return false; }\r
+ v = Math.max(0, Math.min(100, this.maxV, v), this.minV);\r
+ }\r
+\r
+ this.rgb = HSV_RGB(\r
+ h===null ? this.hsv[0] : (this.hsv[0]=h),\r
+ s===null ? this.hsv[1] : (this.hsv[1]=s),\r
+ v===null ? this.hsv[2] : (this.hsv[2]=v)\r
+ );\r
+\r
+ this.exportColor(flags);\r
+ };\r
+\r
+\r
+ // r: 0-255\r
+ // g: 0-255\r
+ // b: 0-255\r
+ //\r
+ this.fromRGB = function (r, g, b, flags) { // null = don't change\r
+ if (r !== null) {\r
+ if (isNaN(r)) { return false; }\r
+ r = Math.max(0, Math.min(255, r));\r
+ }\r
+ if (g !== null) {\r
+ if (isNaN(g)) { return false; }\r
+ g = Math.max(0, Math.min(255, g));\r
+ }\r
+ if (b !== null) {\r
+ if (isNaN(b)) { return false; }\r
+ b = Math.max(0, Math.min(255, b));\r
+ }\r
+\r
+ var hsv = RGB_HSV(\r
+ r===null ? this.rgb[0] : r,\r
+ g===null ? this.rgb[1] : g,\r
+ b===null ? this.rgb[2] : b\r
+ );\r
+ if (hsv[0] !== null) {\r
+ this.hsv[0] = Math.max(0, Math.min(360, hsv[0]));\r
+ }\r
+ if (hsv[2] !== 0) {\r
+ this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1]));\r
+ }\r
+ this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2]));\r
+\r
+ // update RGB according to final HSV, as some values might be trimmed\r
+ var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]);\r
+ this.rgb[0] = rgb[0];\r
+ this.rgb[1] = rgb[1];\r
+ this.rgb[2] = rgb[2];\r
+\r
+ this.exportColor(flags);\r
+ };\r
+\r
+\r
+ this.fromString = function (str, flags) {\r
+ var m;\r
+ if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) {\r
+ // HEX notation\r
+ //\r
+\r
+ if (m[1].length === 6) {\r
+ // 6-char notation\r
+ this.fromRGB(\r
+ parseInt(m[1].substr(0,2),16),\r
+ parseInt(m[1].substr(2,2),16),\r
+ parseInt(m[1].substr(4,2),16),\r
+ flags\r
+ );\r
+ } else {\r
+ // 3-char notation\r
+ this.fromRGB(\r
+ parseInt(m[1].charAt(0) + m[1].charAt(0),16),\r
+ parseInt(m[1].charAt(1) + m[1].charAt(1),16),\r
+ parseInt(m[1].charAt(2) + m[1].charAt(2),16),\r
+ flags\r
+ );\r
+ }\r
+ return true;\r
+\r
+ } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) {\r
+ var params = m[1].split(',');\r
+ var re = /^\s*(\d*)(\.\d+)?\s*$/;\r
+ var mR, mG, mB;\r
+ if (\r
+ params.length >= 3 &&\r
+ (mR = params[0].match(re)) &&\r
+ (mG = params[1].match(re)) &&\r
+ (mB = params[2].match(re))\r
+ ) {\r
+ var r = parseFloat((mR[1] || '0') + (mR[2] || ''));\r
+ var g = parseFloat((mG[1] || '0') + (mG[2] || ''));\r
+ var b = parseFloat((mB[1] || '0') + (mB[2] || ''));\r
+ this.fromRGB(r, g, b, flags);\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ };\r
+\r
+\r
+ this.toString = function () {\r
+ return (\r
+ (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) +\r
+ (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) +\r
+ (0x100 | Math.round(this.rgb[2])).toString(16).substr(1)\r
+ );\r
+ };\r
+\r
+\r
+ this.toHEXString = function () {\r
+ return '#' + this.toString().toUpperCase();\r
+ };\r
+\r
+\r
+ this.toRGBString = function () {\r
+ return ('rgb(' +\r
+ Math.round(this.rgb[0]) + ',' +\r
+ Math.round(this.rgb[1]) + ',' +\r
+ Math.round(this.rgb[2]) + ')'\r
+ );\r
+ };\r
+\r
+\r
+ this.isLight = function () {\r
+ return (\r
+ 0.213 * this.rgb[0] +\r
+ 0.715 * this.rgb[1] +\r
+ 0.072 * this.rgb[2] >\r
+ 255 / 2\r
+ );\r
+ };\r
+\r
+\r
+ this._processParentElementsInDOM = function () {\r
+ if (this._linkedElementsProcessed) { return; }\r
+ this._linkedElementsProcessed = true;\r
+\r
+ var elm = this.targetElement;\r
+ do {\r
+ // If the target element or one of its parent nodes has fixed position,\r
+ // then use fixed positioning instead\r
+ //\r
+ // Note: In Firefox, getComputedStyle returns null in a hidden iframe,\r
+ // that's why we need to check if the returned style object is non-empty\r
+ var currStyle = jsc.getStyle(elm);\r
+ if (currStyle && currStyle.position.toLowerCase() === 'fixed') {\r
+ this.fixed = true;\r
+ }\r
+\r
+ if (elm !== this.targetElement) {\r
+ // Ensure to attach onParentScroll only once to each parent element\r
+ // (multiple targetElements can share the same parent nodes)\r
+ //\r
+ // Note: It's not just offsetParents that can be scrollable,\r
+ // that's why we loop through all parent nodes\r
+ if (!elm._jscEventsAttached) {\r
+ jsc.attachEvent(elm, 'scroll', jsc.onParentScroll);\r
+ elm._jscEventsAttached = true;\r
+ }\r
+ }\r
+ } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body'));\r
+ };\r
+\r
+\r
+ // r: 0-255\r
+ // g: 0-255\r
+ // b: 0-255\r
+ //\r
+ // returns: [ 0-360, 0-100, 0-100 ]\r
+ //\r
+ function RGB_HSV (r, g, b) {\r
+ r /= 255;\r
+ g /= 255;\r
+ b /= 255;\r
+ var n = Math.min(Math.min(r,g),b);\r
+ var v = Math.max(Math.max(r,g),b);\r
+ var m = v - n;\r
+ if (m === 0) { return [ null, 0, 100 * v ]; }\r
+ var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m);\r
+ return [\r
+ 60 * (h===6?0:h),\r
+ 100 * (m/v),\r
+ 100 * v\r
+ ];\r
+ }\r
+\r
+\r
+ // h: 0-360\r
+ // s: 0-100\r
+ // v: 0-100\r
+ //\r
+ // returns: [ 0-255, 0-255, 0-255 ]\r
+ //\r
+ function HSV_RGB (h, s, v) {\r
+ var u = 255 * (v / 100);\r
+\r
+ if (h === null) {\r
+ return [ u, u, u ];\r
+ }\r
+\r
+ h /= 60;\r
+ s /= 100;\r
+\r
+ var i = Math.floor(h);\r
+ var f = i%2 ? h-i : 1-(h-i);\r
+ var m = u * (1 - s);\r
+ var n = u * (1 - s * f);\r
+ switch (i) {\r
+ case 6:\r
+ case 0: return [u,n,m];\r
+ case 1: return [n,u,m];\r
+ case 2: return [m,u,n];\r
+ case 3: return [m,n,u];\r
+ case 4: return [n,m,u];\r
+ case 5: return [u,m,n];\r
+ }\r
+ }\r
+\r
+\r
+ function detachPicker () {\r
+ jsc.unsetClass(THIS.targetElement, THIS.activeClass);\r
+ jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap);\r
+ delete jsc.picker.owner;\r
+ }\r
+\r
+\r
+ function drawPicker () {\r
+\r
+ // At this point, when drawing the picker, we know what the parent elements are\r
+ // and we can do all related DOM operations, such as registering events on them\r
+ // or checking their positioning\r
+ THIS._processParentElementsInDOM();\r
+\r
+ if (!jsc.picker) {\r
+ jsc.picker = {\r
+ owner: null,\r
+ wrap : document.createElement('div'),\r
+ box : document.createElement('div'),\r
+ boxS : document.createElement('div'), // shadow area\r
+ boxB : document.createElement('div'), // border\r
+ pad : document.createElement('div'),\r
+ padB : document.createElement('div'), // border\r
+ padM : document.createElement('div'), // mouse/touch area\r
+ padPal : jsc.createPalette(),\r
+ cross : document.createElement('div'),\r
+ crossBY : document.createElement('div'), // border Y\r
+ crossBX : document.createElement('div'), // border X\r
+ crossLY : document.createElement('div'), // line Y\r
+ crossLX : document.createElement('div'), // line X\r
+ sld : document.createElement('div'),\r
+ sldB : document.createElement('div'), // border\r
+ sldM : document.createElement('div'), // mouse/touch area\r
+ sldGrad : jsc.createSliderGradient(),\r
+ sldPtrS : document.createElement('div'), // slider pointer spacer\r
+ sldPtrIB : document.createElement('div'), // slider pointer inner border\r
+ sldPtrMB : document.createElement('div'), // slider pointer middle border\r
+ sldPtrOB : document.createElement('div'), // slider pointer outer border\r
+ btn : document.createElement('div'),\r
+ btnT : document.createElement('span') // text\r
+ };\r
+\r
+ jsc.picker.pad.appendChild(jsc.picker.padPal.elm);\r
+ jsc.picker.padB.appendChild(jsc.picker.pad);\r
+ jsc.picker.cross.appendChild(jsc.picker.crossBY);\r
+ jsc.picker.cross.appendChild(jsc.picker.crossBX);\r
+ jsc.picker.cross.appendChild(jsc.picker.crossLY);\r
+ jsc.picker.cross.appendChild(jsc.picker.crossLX);\r
+ jsc.picker.padB.appendChild(jsc.picker.cross);\r
+ jsc.picker.box.appendChild(jsc.picker.padB);\r
+ jsc.picker.box.appendChild(jsc.picker.padM);\r
+\r
+ jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm);\r
+ jsc.picker.sldB.appendChild(jsc.picker.sld);\r
+ jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB);\r
+ jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB);\r
+ jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB);\r
+ jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS);\r
+ jsc.picker.box.appendChild(jsc.picker.sldB);\r
+ jsc.picker.box.appendChild(jsc.picker.sldM);\r
+\r
+ jsc.picker.btn.appendChild(jsc.picker.btnT);\r
+ jsc.picker.box.appendChild(jsc.picker.btn);\r
+\r
+ jsc.picker.boxB.appendChild(jsc.picker.box);\r
+ jsc.picker.wrap.appendChild(jsc.picker.boxS);\r
+ jsc.picker.wrap.appendChild(jsc.picker.boxB);\r
+ }\r
+\r
+ var p = jsc.picker;\r
+\r
+ var displaySlider = !!jsc.getSliderComponent(THIS);\r
+ var dims = jsc.getPickerDims(THIS);\r
+ var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize);\r
+ var padToSliderPadding = jsc.getPadToSliderPadding(THIS);\r
+ var borderRadius = Math.min(\r
+ THIS.borderRadius,\r
+ Math.round(THIS.padding * Math.PI)); // px\r
+ var padCursor = 'crosshair';\r
+\r
+ // wrap\r
+ p.wrap.style.clear = 'both';\r
+ p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px';\r
+ p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px';\r
+ p.wrap.style.zIndex = THIS.zIndex;\r
+\r
+ // picker\r
+ p.box.style.width = dims[0] + 'px';\r
+ p.box.style.height = dims[1] + 'px';\r
+\r
+ p.boxS.style.position = 'absolute';\r
+ p.boxS.style.left = '0';\r
+ p.boxS.style.top = '0';\r
+ p.boxS.style.width = '100%';\r
+ p.boxS.style.height = '100%';\r
+ jsc.setBorderRadius(p.boxS, borderRadius + 'px');\r
+\r
+ // picker border\r
+ p.boxB.style.position = 'relative';\r
+ p.boxB.style.border = THIS.borderWidth + 'px solid';\r
+ p.boxB.style.borderColor = THIS.borderColor;\r
+ p.boxB.style.background = THIS.backgroundColor;\r
+ jsc.setBorderRadius(p.boxB, borderRadius + 'px');\r
+\r
+ // IE hack:\r
+ // If the element is transparent, IE will trigger the event on the elements under it,\r
+ // e.g. on Canvas or on elements with border\r
+ p.padM.style.background =\r
+ p.sldM.style.background =\r
+ '#FFF';\r
+ jsc.setStyle(p.padM, 'opacity', '0');\r
+ jsc.setStyle(p.sldM, 'opacity', '0');\r
+\r
+ // pad\r
+ p.pad.style.position = 'relative';\r
+ p.pad.style.width = THIS.width + 'px';\r
+ p.pad.style.height = THIS.height + 'px';\r
+\r
+ // pad palettes (HSV and HVS)\r
+ p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS));\r
+\r
+ // pad border\r
+ p.padB.style.position = 'absolute';\r
+ p.padB.style.left = THIS.padding + 'px';\r
+ p.padB.style.top = THIS.padding + 'px';\r
+ p.padB.style.border = THIS.insetWidth + 'px solid';\r
+ p.padB.style.borderColor = THIS.insetColor;\r
+\r
+ // pad mouse area\r
+ p.padM._jscInstance = THIS;\r
+ p.padM._jscControlName = 'pad';\r
+ p.padM.style.position = 'absolute';\r
+ p.padM.style.left = '0';\r
+ p.padM.style.top = '0';\r
+ p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px';\r
+ p.padM.style.height = dims[1] + 'px';\r
+ p.padM.style.cursor = padCursor;\r
+\r
+ // pad cross\r
+ p.cross.style.position = 'absolute';\r
+ p.cross.style.left =\r
+ p.cross.style.top =\r
+ '0';\r
+ p.cross.style.width =\r
+ p.cross.style.height =\r
+ crossOuterSize + 'px';\r
+\r
+ // pad cross border Y and X\r
+ p.crossBY.style.position =\r
+ p.crossBX.style.position =\r
+ 'absolute';\r
+ p.crossBY.style.background =\r
+ p.crossBX.style.background =\r
+ THIS.pointerBorderColor;\r
+ p.crossBY.style.width =\r
+ p.crossBX.style.height =\r
+ (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px';\r
+ p.crossBY.style.height =\r
+ p.crossBX.style.width =\r
+ crossOuterSize + 'px';\r
+ p.crossBY.style.left =\r
+ p.crossBX.style.top =\r
+ (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px';\r
+ p.crossBY.style.top =\r
+ p.crossBX.style.left =\r
+ '0';\r
+\r
+ // pad cross line Y and X\r
+ p.crossLY.style.position =\r
+ p.crossLX.style.position =\r
+ 'absolute';\r
+ p.crossLY.style.background =\r
+ p.crossLX.style.background =\r
+ THIS.pointerColor;\r
+ p.crossLY.style.height =\r
+ p.crossLX.style.width =\r
+ (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px';\r
+ p.crossLY.style.width =\r
+ p.crossLX.style.height =\r
+ THIS.pointerThickness + 'px';\r
+ p.crossLY.style.left =\r
+ p.crossLX.style.top =\r
+ (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px';\r
+ p.crossLY.style.top =\r
+ p.crossLX.style.left =\r
+ THIS.pointerBorderWidth + 'px';\r
+\r
+ // slider\r
+ p.sld.style.overflow = 'hidden';\r
+ p.sld.style.width = THIS.sliderSize + 'px';\r
+ p.sld.style.height = THIS.height + 'px';\r
+\r
+ // slider gradient\r
+ p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000');\r
+\r
+ // slider border\r
+ p.sldB.style.display = displaySlider ? 'block' : 'none';\r
+ p.sldB.style.position = 'absolute';\r
+ p.sldB.style.right = THIS.padding + 'px';\r
+ p.sldB.style.top = THIS.padding + 'px';\r
+ p.sldB.style.border = THIS.insetWidth + 'px solid';\r
+ p.sldB.style.borderColor = THIS.insetColor;\r
+\r
+ // slider mouse area\r
+ p.sldM._jscInstance = THIS;\r
+ p.sldM._jscControlName = 'sld';\r
+ p.sldM.style.display = displaySlider ? 'block' : 'none';\r
+ p.sldM.style.position = 'absolute';\r
+ p.sldM.style.right = '0';\r
+ p.sldM.style.top = '0';\r
+ p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px';\r
+ p.sldM.style.height = dims[1] + 'px';\r
+ p.sldM.style.cursor = 'default';\r
+\r
+ // slider pointer inner and outer border\r
+ p.sldPtrIB.style.border =\r
+ p.sldPtrOB.style.border =\r
+ THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor;\r
+\r
+ // slider pointer outer border\r
+ p.sldPtrOB.style.position = 'absolute';\r
+ p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px';\r
+ p.sldPtrOB.style.top = '0';\r
+\r
+ // slider pointer middle border\r
+ p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor;\r
+\r
+ // slider pointer spacer\r
+ p.sldPtrS.style.width = THIS.sliderSize + 'px';\r
+ p.sldPtrS.style.height = sliderPtrSpace + 'px';\r
+\r
+ // the Close button\r
+ function setBtnBorder () {\r
+ var insetColors = THIS.insetColor.split(/\s+/);\r
+ var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1];\r
+ p.btn.style.borderColor = outsetColor;\r
+ }\r
+ p.btn.style.display = THIS.closable ? 'block' : 'none';\r
+ p.btn.style.position = 'absolute';\r
+ p.btn.style.left = THIS.padding + 'px';\r
+ p.btn.style.bottom = THIS.padding + 'px';\r
+ p.btn.style.padding = '0 15px';\r
+ p.btn.style.height = THIS.buttonHeight + 'px';\r
+ p.btn.style.border = THIS.insetWidth + 'px solid';\r
+ setBtnBorder();\r
+ p.btn.style.color = THIS.buttonColor;\r
+ p.btn.style.font = '12px sans-serif';\r
+ p.btn.style.textAlign = 'center';\r
+ try {\r
+ p.btn.style.cursor = 'pointer';\r
+ } catch(eOldIE) {\r
+ p.btn.style.cursor = 'hand';\r
+ }\r
+ p.btn.onmousedown = function () {\r
+ THIS.hide();\r
+ };\r
+ p.btnT.style.lineHeight = THIS.buttonHeight + 'px';\r
+ p.btnT.innerHTML = '';\r
+ p.btnT.appendChild(document.createTextNode(THIS.closeText));\r
+\r
+ // place pointers\r
+ redrawPad();\r
+ redrawSld();\r
+\r
+ // If we are changing the owner without first closing the picker,\r
+ // make sure to first deal with the old owner\r
+ if (jsc.picker.owner && jsc.picker.owner !== THIS) {\r
+ jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass);\r
+ }\r
+\r
+ // Set the new picker owner\r
+ jsc.picker.owner = THIS;\r
+\r
+ // The redrawPosition() method needs picker.owner to be set, that's why we call it here,\r
+ // after setting the owner\r
+ if (jsc.isElementType(container, 'body')) {\r
+ jsc.redrawPosition();\r
+ } else {\r
+ jsc._drawPosition(THIS, 0, 0, 'relative', false);\r
+ }\r
+\r
+ if (p.wrap.parentNode != container) {\r
+ container.appendChild(p.wrap);\r
+ }\r
+\r
+ jsc.setClass(THIS.targetElement, THIS.activeClass);\r
+ }\r
+\r
+\r
+ function redrawPad () {\r
+ // redraw the pad pointer\r
+ switch (jsc.getPadYComponent(THIS)) {\r
+ case 's': var yComponent = 1; break;\r
+ case 'v': var yComponent = 2; break;\r
+ }\r
+ var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1));\r
+ var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1));\r
+ var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize);\r
+ var ofs = -Math.floor(crossOuterSize / 2);\r
+ jsc.picker.cross.style.left = (x + ofs) + 'px';\r
+ jsc.picker.cross.style.top = (y + ofs) + 'px';\r
+\r
+ // redraw the slider\r
+ switch (jsc.getSliderComponent(THIS)) {\r
+ case 's':\r
+ var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]);\r
+ var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]);\r
+ var color1 = 'rgb(' +\r
+ Math.round(rgb1[0]) + ',' +\r
+ Math.round(rgb1[1]) + ',' +\r
+ Math.round(rgb1[2]) + ')';\r
+ var color2 = 'rgb(' +\r
+ Math.round(rgb2[0]) + ',' +\r
+ Math.round(rgb2[1]) + ',' +\r
+ Math.round(rgb2[2]) + ')';\r
+ jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2);\r
+ break;\r
+ case 'v':\r
+ var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100);\r
+ var color1 = 'rgb(' +\r
+ Math.round(rgb[0]) + ',' +\r
+ Math.round(rgb[1]) + ',' +\r
+ Math.round(rgb[2]) + ')';\r
+ var color2 = '#000';\r
+ jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2);\r
+ break;\r
+ }\r
+ }\r
+\r
+\r
+ function redrawSld () {\r
+ var sldComponent = jsc.getSliderComponent(THIS);\r
+ if (sldComponent) {\r
+ // redraw the slider pointer\r
+ switch (sldComponent) {\r
+ case 's': var yComponent = 1; break;\r
+ case 'v': var yComponent = 2; break;\r
+ }\r
+ var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1));\r
+ jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px';\r
+ }\r
+ }\r
+\r
+\r
+ function isPickerOwner () {\r
+ return jsc.picker && jsc.picker.owner === THIS;\r
+ }\r
+\r
+\r
+ function blurValue () {\r
+ THIS.importColor();\r
+ }\r
+\r
+\r
+ // Find the target element\r
+ if (typeof targetElement === 'string') {\r
+ var id = targetElement;\r
+ var elm = document.getElementById(id);\r
+ if (elm) {\r
+ this.targetElement = elm;\r
+ } else {\r
+ jsc.warn('Could not find target element with ID \'' + id + '\'');\r
+ }\r
+ } else if (targetElement) {\r
+ this.targetElement = targetElement;\r
+ } else {\r
+ jsc.warn('Invalid target element: \'' + targetElement + '\'');\r
+ }\r
+\r
+ if (this.targetElement._jscLinkedInstance) {\r
+ jsc.warn('Cannot link jscolor twice to the same element. Skipping.');\r
+ return;\r
+ }\r
+ this.targetElement._jscLinkedInstance = this;\r
+\r
+ // Find the value element\r
+ this.valueElement = jsc.fetchElement(this.valueElement);\r
+ // Find the style element\r
+ this.styleElement = jsc.fetchElement(this.styleElement);\r
+\r
+ var THIS = this;\r
+ var container =\r
+ this.container ?\r
+ jsc.fetchElement(this.container) :\r
+ document.getElementsByTagName('body')[0];\r
+ var sliderPtrSpace = 3; // px\r
+\r
+ // For BUTTON elements it's important to stop them from sending the form when clicked\r
+ // (e.g. in Safari)\r
+ if (jsc.isElementType(this.targetElement, 'button')) {\r
+ if (this.targetElement.onclick) {\r
+ var origCallback = this.targetElement.onclick;\r
+ this.targetElement.onclick = function (evt) {\r
+ origCallback.call(this, evt);\r
+ return false;\r
+ };\r
+ } else {\r
+ this.targetElement.onclick = function () { return false; };\r
+ }\r
+ }\r
+\r
+ /*\r
+ var elm = this.targetElement;\r
+ do {\r
+ // If the target element or one of its offsetParents has fixed position,\r
+ // then use fixed positioning instead\r
+ //\r
+ // Note: In Firefox, getComputedStyle returns null in a hidden iframe,\r
+ // that's why we need to check if the returned style object is non-empty\r
+ var currStyle = jsc.getStyle(elm);\r
+ if (currStyle && currStyle.position.toLowerCase() === 'fixed') {\r
+ this.fixed = true;\r
+ }\r
+\r
+ if (elm !== this.targetElement) {\r
+ // attach onParentScroll so that we can recompute the picker position\r
+ // when one of the offsetParents is scrolled\r
+ if (!elm._jscEventsAttached) {\r
+ jsc.attachEvent(elm, 'scroll', jsc.onParentScroll);\r
+ elm._jscEventsAttached = true;\r
+ }\r
+ }\r
+ } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body'));\r
+ */\r
+\r
+ // valueElement\r
+ if (this.valueElement) {\r
+ if (jsc.isElementType(this.valueElement, 'input')) {\r
+ var updateField = function () {\r
+ THIS.fromString(THIS.valueElement.value, jsc.leaveValue);\r
+ jsc.dispatchFineChange(THIS);\r
+ };\r
+ jsc.attachEvent(this.valueElement, 'keyup', updateField);\r
+ jsc.attachEvent(this.valueElement, 'input', updateField);\r
+ jsc.attachEvent(this.valueElement, 'blur', blurValue);\r
+ this.valueElement.setAttribute('autocomplete', 'off');\r
+ }\r
+ }\r
+\r
+ // styleElement\r
+ if (this.styleElement) {\r
+ this.styleElement._jscOrigStyle = {\r
+ backgroundImage : this.styleElement.style.backgroundImage,\r
+ backgroundColor : this.styleElement.style.backgroundColor,\r
+ color : this.styleElement.style.color\r
+ };\r
+ }\r
+\r
+ if (this.value) {\r
+ // Try to set the color from the .value option and if unsuccessful,\r
+ // export the current color\r
+ this.fromString(this.value) || this.exportColor();\r
+ } else {\r
+ this.importColor();\r
+ }\r
+ }\r
+\r
+};\r
+\r
+\r
+//================================\r
+// Public properties and methods\r
+//================================\r
+\r
+\r
+// By default, search for all elements with class="jscolor" and install a color picker on them.\r
+//\r
+// You can change what class name will be looked for by setting the property jscolor.lookupClass\r
+// anywhere in your HTML document. To completely disable the automatic lookup, set it to null.\r
+//\r
+jsc.jscolor.lookupClass = 'jscolor';\r
+\r
+\r
+jsc.jscolor.installByClassName = function (className) {\r
+ var inputElms = document.getElementsByTagName('input');\r
+ var buttonElms = document.getElementsByTagName('button');\r
+\r
+ jsc.tryInstallOnElements(inputElms, className);\r
+ jsc.tryInstallOnElements(buttonElms, className);\r
+};\r
+\r
+\r
+jsc.register();\r
+\r
+\r
+return jsc.jscolor;\r
+\r
+\r
+})(); }\r