jscolor.js 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855
  1. /**
  2. * jscolor - JavaScript Color Picker
  3. *
  4. * @link http://jscolor.com
  5. * @license For open source use: GPLv3
  6. * For commercial use: JSColor Commercial License
  7. * @author Jan Odvarko
  8. * @version 2.0.5
  9. *
  10. * See usage examples at http://jscolor.com/examples/
  11. */
  12. "use strict";
  13. if (!window.jscolor) { window.jscolor = (function () {
  14. var jsc = {
  15. register : function () {
  16. jsc.attachDOMReadyEvent(jsc.init);
  17. jsc.attachEvent(document, 'mousedown', jsc.onDocumentMouseDown);
  18. jsc.attachEvent(document, 'touchstart', jsc.onDocumentTouchStart);
  19. jsc.attachEvent(window, 'resize', jsc.onWindowResize);
  20. },
  21. init : function () {
  22. if (jsc.jscolor.lookupClass) {
  23. jsc.jscolor.installByClassName(jsc.jscolor.lookupClass);
  24. }
  25. },
  26. tryInstallOnElements : function (elms, className) {
  27. var matchClass = new RegExp('(^|\\s)(' + className + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i');
  28. for (var i = 0; i < elms.length; i += 1) {
  29. if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color') {
  30. if (jsc.isColorAttrSupported) {
  31. // skip inputs of type 'color' if supported by the browser
  32. continue;
  33. }
  34. }
  35. var m;
  36. if (!elms[i].jscolor && elms[i].className && (m = elms[i].className.match(matchClass))) {
  37. var targetElm = elms[i];
  38. var optsStr = null;
  39. var dataOptions = jsc.getDataAttr(targetElm, 'jscolor');
  40. if (dataOptions !== null) {
  41. optsStr = dataOptions;
  42. } else if (m[4]) {
  43. optsStr = m[4];
  44. }
  45. var opts = {};
  46. if (optsStr) {
  47. try {
  48. opts = (new Function ('return (' + optsStr + ')'))();
  49. } catch(eParseError) {
  50. jsc.warn('Error parsing jscolor options: ' + eParseError + ':\n' + optsStr);
  51. }
  52. }
  53. targetElm.jscolor = new jsc.jscolor(targetElm, opts);
  54. }
  55. }
  56. },
  57. isColorAttrSupported : (function () {
  58. var elm = document.createElement('input');
  59. if (elm.setAttribute) {
  60. elm.setAttribute('type', 'color');
  61. if (elm.type.toLowerCase() == 'color') {
  62. return true;
  63. }
  64. }
  65. return false;
  66. })(),
  67. isCanvasSupported : (function () {
  68. var elm = document.createElement('canvas');
  69. return !!(elm.getContext && elm.getContext('2d'));
  70. })(),
  71. fetchElement : function (mixed) {
  72. return typeof mixed === 'string' ? document.getElementById(mixed) : mixed;
  73. },
  74. isElementType : function (elm, type) {
  75. return elm.nodeName.toLowerCase() === type.toLowerCase();
  76. },
  77. getDataAttr : function (el, name) {
  78. var attrName = 'data-' + name;
  79. var attrValue = el.getAttribute(attrName);
  80. if (attrValue !== null) {
  81. return attrValue;
  82. }
  83. return null;
  84. },
  85. attachEvent : function (el, evnt, func) {
  86. if (el.addEventListener) {
  87. el.addEventListener(evnt, func, false);
  88. } else if (el.attachEvent) {
  89. el.attachEvent('on' + evnt, func);
  90. }
  91. },
  92. detachEvent : function (el, evnt, func) {
  93. if (el.removeEventListener) {
  94. el.removeEventListener(evnt, func, false);
  95. } else if (el.detachEvent) {
  96. el.detachEvent('on' + evnt, func);
  97. }
  98. },
  99. _attachedGroupEvents : {},
  100. attachGroupEvent : function (groupName, el, evnt, func) {
  101. if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) {
  102. jsc._attachedGroupEvents[groupName] = [];
  103. }
  104. jsc._attachedGroupEvents[groupName].push([el, evnt, func]);
  105. jsc.attachEvent(el, evnt, func);
  106. },
  107. detachGroupEvents : function (groupName) {
  108. if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) {
  109. for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) {
  110. var evt = jsc._attachedGroupEvents[groupName][i];
  111. jsc.detachEvent(evt[0], evt[1], evt[2]);
  112. }
  113. delete jsc._attachedGroupEvents[groupName];
  114. }
  115. },
  116. attachDOMReadyEvent : function (func) {
  117. var fired = false;
  118. var fireOnce = function () {
  119. if (!fired) {
  120. fired = true;
  121. func();
  122. }
  123. };
  124. if (document.readyState === 'complete') {
  125. setTimeout(fireOnce, 1); // async
  126. return;
  127. }
  128. if (document.addEventListener) {
  129. document.addEventListener('DOMContentLoaded', fireOnce, false);
  130. // Fallback
  131. window.addEventListener('load', fireOnce, false);
  132. } else if (document.attachEvent) {
  133. // IE
  134. document.attachEvent('onreadystatechange', function () {
  135. if (document.readyState === 'complete') {
  136. document.detachEvent('onreadystatechange', arguments.callee);
  137. fireOnce();
  138. }
  139. })
  140. // Fallback
  141. window.attachEvent('onload', fireOnce);
  142. // IE7/8
  143. if (document.documentElement.doScroll && window == window.top) {
  144. var tryScroll = function () {
  145. if (!document.body) { return; }
  146. try {
  147. document.documentElement.doScroll('left');
  148. fireOnce();
  149. } catch (e) {
  150. setTimeout(tryScroll, 1);
  151. }
  152. };
  153. tryScroll();
  154. }
  155. }
  156. },
  157. warn : function (msg) {
  158. if (window.console && window.console.warn) {
  159. window.console.warn(msg);
  160. }
  161. },
  162. preventDefault : function (e) {
  163. if (e.preventDefault) { e.preventDefault(); }
  164. e.returnValue = false;
  165. },
  166. captureTarget : function (target) {
  167. // IE
  168. if (target.setCapture) {
  169. jsc._capturedTarget = target;
  170. jsc._capturedTarget.setCapture();
  171. }
  172. },
  173. releaseTarget : function () {
  174. // IE
  175. if (jsc._capturedTarget) {
  176. jsc._capturedTarget.releaseCapture();
  177. jsc._capturedTarget = null;
  178. }
  179. },
  180. fireEvent : function (el, evnt) {
  181. if (!el) {
  182. return;
  183. }
  184. if (document.createEvent) {
  185. var ev = document.createEvent('HTMLEvents');
  186. ev.initEvent(evnt, true, true);
  187. el.dispatchEvent(ev);
  188. } else if (document.createEventObject) {
  189. var ev = document.createEventObject();
  190. el.fireEvent('on' + evnt, ev);
  191. } else if (el['on' + evnt]) { // alternatively use the traditional event model
  192. el['on' + evnt]();
  193. }
  194. },
  195. classNameToList : function (className) {
  196. return className.replace(/^\s+|\s+$/g, '').split(/\s+/);
  197. },
  198. // The className parameter (str) can only contain a single class name
  199. hasClass : function (elm, className) {
  200. if (!className) {
  201. return false;
  202. }
  203. return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' ');
  204. },
  205. // The className parameter (str) can contain multiple class names separated by whitespace
  206. setClass : function (elm, className) {
  207. var classList = jsc.classNameToList(className);
  208. for (var i = 0; i < classList.length; i += 1) {
  209. if (!jsc.hasClass(elm, classList[i])) {
  210. elm.className += (elm.className ? ' ' : '') + classList[i];
  211. }
  212. }
  213. },
  214. // The className parameter (str) can contain multiple class names separated by whitespace
  215. unsetClass : function (elm, className) {
  216. var classList = jsc.classNameToList(className);
  217. for (var i = 0; i < classList.length; i += 1) {
  218. var repl = new RegExp(
  219. '^\\s*' + classList[i] + '\\s*|' +
  220. '\\s*' + classList[i] + '\\s*$|' +
  221. '\\s+' + classList[i] + '(\\s+)',
  222. 'g'
  223. );
  224. elm.className = elm.className.replace(repl, '$1');
  225. }
  226. },
  227. getStyle : function (elm) {
  228. return window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle;
  229. },
  230. setStyle : (function () {
  231. var helper = document.createElement('div');
  232. var getSupportedProp = function (names) {
  233. for (var i = 0; i < names.length; i += 1) {
  234. if (names[i] in helper.style) {
  235. return names[i];
  236. }
  237. }
  238. };
  239. var props = {
  240. borderRadius: getSupportedProp(['borderRadius', 'MozBorderRadius', 'webkitBorderRadius']),
  241. boxShadow: getSupportedProp(['boxShadow', 'MozBoxShadow', 'webkitBoxShadow'])
  242. };
  243. return function (elm, prop, value) {
  244. switch (prop.toLowerCase()) {
  245. case 'opacity':
  246. var alphaOpacity = Math.round(parseFloat(value) * 100);
  247. elm.style.opacity = value;
  248. elm.style.filter = 'alpha(opacity=' + alphaOpacity + ')';
  249. break;
  250. default:
  251. elm.style[props[prop]] = value;
  252. break;
  253. }
  254. };
  255. })(),
  256. setBorderRadius : function (elm, value) {
  257. jsc.setStyle(elm, 'borderRadius', value || '0');
  258. },
  259. setBoxShadow : function (elm, value) {
  260. jsc.setStyle(elm, 'boxShadow', value || 'none');
  261. },
  262. getElementPos : function (e, relativeToViewport) {
  263. var x=0, y=0;
  264. var rect = e.getBoundingClientRect();
  265. x = rect.left;
  266. y = rect.top;
  267. if (!relativeToViewport) {
  268. var viewPos = jsc.getViewPos();
  269. x += viewPos[0];
  270. y += viewPos[1];
  271. }
  272. return [x, y];
  273. },
  274. getElementSize : function (e) {
  275. return [e.offsetWidth, e.offsetHeight];
  276. },
  277. // get pointer's X/Y coordinates relative to viewport
  278. getAbsPointerPos : function (e) {
  279. if (!e) { e = window.event; }
  280. var x = 0, y = 0;
  281. if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) {
  282. // touch devices
  283. x = e.changedTouches[0].clientX;
  284. y = e.changedTouches[0].clientY;
  285. } else if (typeof e.clientX === 'number') {
  286. x = e.clientX;
  287. y = e.clientY;
  288. }
  289. return { x: x, y: y };
  290. },
  291. // get pointer's X/Y coordinates relative to target element
  292. getRelPointerPos : function (e) {
  293. if (!e) { e = window.event; }
  294. var target = e.target || e.srcElement;
  295. var targetRect = target.getBoundingClientRect();
  296. var x = 0, y = 0;
  297. var clientX = 0, clientY = 0;
  298. if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) {
  299. // touch devices
  300. clientX = e.changedTouches[0].clientX;
  301. clientY = e.changedTouches[0].clientY;
  302. } else if (typeof e.clientX === 'number') {
  303. clientX = e.clientX;
  304. clientY = e.clientY;
  305. }
  306. x = clientX - targetRect.left;
  307. y = clientY - targetRect.top;
  308. return { x: x, y: y };
  309. },
  310. getViewPos : function () {
  311. var doc = document.documentElement;
  312. return [
  313. (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0),
  314. (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)
  315. ];
  316. },
  317. getViewSize : function () {
  318. var doc = document.documentElement;
  319. return [
  320. (window.innerWidth || doc.clientWidth),
  321. (window.innerHeight || doc.clientHeight),
  322. ];
  323. },
  324. redrawPosition : function () {
  325. if (jsc.picker && jsc.picker.owner) {
  326. var thisObj = jsc.picker.owner;
  327. var tp, vp;
  328. if (thisObj.fixed) {
  329. // Fixed elements are positioned relative to viewport,
  330. // therefore we can ignore the scroll offset
  331. tp = jsc.getElementPos(thisObj.targetElement, true); // target pos
  332. vp = [0, 0]; // view pos
  333. } else {
  334. tp = jsc.getElementPos(thisObj.targetElement); // target pos
  335. vp = jsc.getViewPos(); // view pos
  336. }
  337. var ts = jsc.getElementSize(thisObj.targetElement); // target size
  338. var vs = jsc.getViewSize(); // view size
  339. var ps = jsc.getPickerOuterDims(thisObj); // picker size
  340. var a, b, c;
  341. switch (thisObj.position.toLowerCase()) {
  342. case 'left': a=1; b=0; c=-1; break;
  343. case 'right':a=1; b=0; c=1; break;
  344. case 'top': a=0; b=1; c=-1; break;
  345. default: a=0; b=1; c=1; break;
  346. }
  347. var l = (ts[b]+ps[b])/2;
  348. // compute picker position
  349. if (!thisObj.smartPosition) {
  350. var pp = [
  351. tp[a],
  352. tp[b]+ts[b]-l+l*c
  353. ];
  354. } else {
  355. var pp = [
  356. -vp[a]+tp[a]+ps[a] > vs[a] ?
  357. (-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]) :
  358. tp[a],
  359. -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ?
  360. (-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) :
  361. (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c)
  362. ];
  363. }
  364. var x = pp[a];
  365. var y = pp[b];
  366. var positionValue = thisObj.fixed ? 'fixed' : 'absolute';
  367. var contractShadow =
  368. (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) &&
  369. (pp[1] + ps[1] < tp[1] + ts[1]);
  370. jsc._drawPosition(thisObj, x, y, positionValue, contractShadow);
  371. }
  372. },
  373. _drawPosition : function (thisObj, x, y, positionValue, contractShadow) {
  374. var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px
  375. jsc.picker.wrap.style.position = positionValue;
  376. jsc.picker.wrap.style.left = x + 'px';
  377. jsc.picker.wrap.style.top = y + 'px';
  378. jsc.setBoxShadow(
  379. jsc.picker.boxS,
  380. thisObj.shadow ?
  381. new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) :
  382. null);
  383. },
  384. getPickerDims : function (thisObj) {
  385. var displaySlider = !!jsc.getSliderComponent(thisObj);
  386. var dims = [
  387. 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.width +
  388. (displaySlider ? 2 * thisObj.insetWidth + jsc.getPadToSliderPadding(thisObj) + thisObj.sliderSize : 0),
  389. 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.height +
  390. (thisObj.closable ? 2 * thisObj.insetWidth + thisObj.padding + thisObj.buttonHeight : 0)
  391. ];
  392. return dims;
  393. },
  394. getPickerOuterDims : function (thisObj) {
  395. var dims = jsc.getPickerDims(thisObj);
  396. return [
  397. dims[0] + 2 * thisObj.borderWidth,
  398. dims[1] + 2 * thisObj.borderWidth
  399. ];
  400. },
  401. getPadToSliderPadding : function (thisObj) {
  402. return Math.max(thisObj.padding, 1.5 * (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness));
  403. },
  404. getPadYComponent : function (thisObj) {
  405. switch (thisObj.mode.charAt(1).toLowerCase()) {
  406. case 'v': return 'v'; break;
  407. }
  408. return 's';
  409. },
  410. getSliderComponent : function (thisObj) {
  411. if (thisObj.mode.length > 2) {
  412. switch (thisObj.mode.charAt(2).toLowerCase()) {
  413. case 's': return 's'; break;
  414. case 'v': return 'v'; break;
  415. }
  416. }
  417. return null;
  418. },
  419. onDocumentMouseDown : function (e) {
  420. if (!e) { e = window.event; }
  421. var target = e.target || e.srcElement;
  422. if (target._jscLinkedInstance) {
  423. if (target._jscLinkedInstance.showOnClick) {
  424. target._jscLinkedInstance.show();
  425. }
  426. } else if (target._jscControlName) {
  427. jsc.onControlPointerStart(e, target, target._jscControlName, 'mouse');
  428. } else {
  429. // Mouse is outside the picker controls -> hide the color picker!
  430. if (jsc.picker && jsc.picker.owner) {
  431. jsc.picker.owner.hide();
  432. }
  433. }
  434. },
  435. onDocumentTouchStart : function (e) {
  436. if (!e) { e = window.event; }
  437. var target = e.target || e.srcElement;
  438. if (target._jscLinkedInstance) {
  439. if (target._jscLinkedInstance.showOnClick) {
  440. target._jscLinkedInstance.show();
  441. }
  442. } else if (target._jscControlName) {
  443. jsc.onControlPointerStart(e, target, target._jscControlName, 'touch');
  444. } else {
  445. if (jsc.picker && jsc.picker.owner) {
  446. jsc.picker.owner.hide();
  447. }
  448. }
  449. },
  450. onWindowResize : function (e) {
  451. jsc.redrawPosition();
  452. },
  453. onParentScroll : function (e) {
  454. // hide the picker when one of the parent elements is scrolled
  455. if (jsc.picker && jsc.picker.owner) {
  456. jsc.picker.owner.hide();
  457. }
  458. },
  459. _pointerMoveEvent : {
  460. mouse: 'mousemove',
  461. touch: 'touchmove'
  462. },
  463. _pointerEndEvent : {
  464. mouse: 'mouseup',
  465. touch: 'touchend'
  466. },
  467. _pointerOrigin : null,
  468. _capturedTarget : null,
  469. onControlPointerStart : function (e, target, controlName, pointerType) {
  470. var thisObj = target._jscInstance;
  471. jsc.preventDefault(e);
  472. jsc.captureTarget(target);
  473. var registerDragEvents = function (doc, offset) {
  474. jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType],
  475. jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset));
  476. jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType],
  477. jsc.onDocumentPointerEnd(e, target, controlName, pointerType));
  478. };
  479. registerDragEvents(document, [0, 0]);
  480. if (window.parent && window.frameElement) {
  481. var rect = window.frameElement.getBoundingClientRect();
  482. var ofs = [-rect.left, -rect.top];
  483. registerDragEvents(window.parent.window.document, ofs);
  484. }
  485. var abs = jsc.getAbsPointerPos(e);
  486. var rel = jsc.getRelPointerPos(e);
  487. jsc._pointerOrigin = {
  488. x: abs.x - rel.x,
  489. y: abs.y - rel.y
  490. };
  491. switch (controlName) {
  492. case 'pad':
  493. // if the slider is at the bottom, move it up
  494. switch (jsc.getSliderComponent(thisObj)) {
  495. case 's': if (thisObj.hsv[1] === 0) { thisObj.fromHSV(null, 100, null); }; break;
  496. case 'v': if (thisObj.hsv[2] === 0) { thisObj.fromHSV(null, null, 100); }; break;
  497. }
  498. jsc.setPad(thisObj, e, 0, 0);
  499. break;
  500. case 'sld':
  501. jsc.setSld(thisObj, e, 0);
  502. break;
  503. }
  504. jsc.dispatchFineChange(thisObj);
  505. },
  506. onDocumentPointerMove : function (e, target, controlName, pointerType, offset) {
  507. return function (e) {
  508. var thisObj = target._jscInstance;
  509. switch (controlName) {
  510. case 'pad':
  511. if (!e) { e = window.event; }
  512. jsc.setPad(thisObj, e, offset[0], offset[1]);
  513. jsc.dispatchFineChange(thisObj);
  514. break;
  515. case 'sld':
  516. if (!e) { e = window.event; }
  517. jsc.setSld(thisObj, e, offset[1]);
  518. jsc.dispatchFineChange(thisObj);
  519. break;
  520. }
  521. }
  522. },
  523. onDocumentPointerEnd : function (e, target, controlName, pointerType) {
  524. return function (e) {
  525. var thisObj = target._jscInstance;
  526. jsc.detachGroupEvents('drag');
  527. jsc.releaseTarget();
  528. // Always dispatch changes after detaching outstanding mouse handlers,
  529. // in case some user interaction will occur in user's onchange callback
  530. // that would intrude with current mouse events
  531. jsc.dispatchChange(thisObj);
  532. };
  533. },
  534. dispatchChange : function (thisObj) {
  535. if (thisObj.valueElement) {
  536. if (jsc.isElementType(thisObj.valueElement, 'input')) {
  537. jsc.fireEvent(thisObj.valueElement, 'change');
  538. }
  539. }
  540. },
  541. dispatchFineChange : function (thisObj) {
  542. if (thisObj.onFineChange) {
  543. var callback;
  544. if (typeof thisObj.onFineChange === 'string') {
  545. callback = new Function (thisObj.onFineChange);
  546. } else {
  547. callback = thisObj.onFineChange;
  548. }
  549. callback.call(thisObj);
  550. }
  551. },
  552. setPad : function (thisObj, e, ofsX, ofsY) {
  553. var pointerAbs = jsc.getAbsPointerPos(e);
  554. var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.insetWidth;
  555. var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth;
  556. var xVal = x * (360 / (thisObj.width - 1));
  557. var yVal = 100 - (y * (100 / (thisObj.height - 1)));
  558. switch (jsc.getPadYComponent(thisObj)) {
  559. case 's': thisObj.fromHSV(xVal, yVal, null, jsc.leaveSld); break;
  560. case 'v': thisObj.fromHSV(xVal, null, yVal, jsc.leaveSld); break;
  561. }
  562. },
  563. setSld : function (thisObj, e, ofsY) {
  564. var pointerAbs = jsc.getAbsPointerPos(e);
  565. var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth;
  566. var yVal = 100 - (y * (100 / (thisObj.height - 1)));
  567. switch (jsc.getSliderComponent(thisObj)) {
  568. case 's': thisObj.fromHSV(null, yVal, null, jsc.leavePad); break;
  569. case 'v': thisObj.fromHSV(null, null, yVal, jsc.leavePad); break;
  570. }
  571. },
  572. _vmlNS : 'jsc_vml_',
  573. _vmlCSS : 'jsc_vml_css_',
  574. _vmlReady : false,
  575. initVML : function () {
  576. if (!jsc._vmlReady) {
  577. // init VML namespace
  578. var doc = document;
  579. if (!doc.namespaces[jsc._vmlNS]) {
  580. doc.namespaces.add(jsc._vmlNS, 'urn:schemas-microsoft-com:vml');
  581. }
  582. if (!doc.styleSheets[jsc._vmlCSS]) {
  583. var tags = ['shape', 'shapetype', 'group', 'background', 'path', 'formulas', 'handles', 'fill', 'stroke', 'shadow', 'textbox', 'textpath', 'imagedata', 'line', 'polyline', 'curve', 'rect', 'roundrect', 'oval', 'arc', 'image'];
  584. var ss = doc.createStyleSheet();
  585. ss.owningElement.id = jsc._vmlCSS;
  586. for (var i = 0; i < tags.length; i += 1) {
  587. ss.addRule(jsc._vmlNS + '\\:' + tags[i], 'behavior:url(#default#VML);');
  588. }
  589. }
  590. jsc._vmlReady = true;
  591. }
  592. },
  593. createPalette : function () {
  594. var paletteObj = {
  595. elm: null,
  596. draw: null
  597. };
  598. if (jsc.isCanvasSupported) {
  599. // Canvas implementation for modern browsers
  600. var canvas = document.createElement('canvas');
  601. var ctx = canvas.getContext('2d');
  602. var drawFunc = function (width, height, type) {
  603. canvas.width = width;
  604. canvas.height = height;
  605. ctx.clearRect(0, 0, canvas.width, canvas.height);
  606. var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0);
  607. hGrad.addColorStop(0 / 6, '#F00');
  608. hGrad.addColorStop(1 / 6, '#FF0');
  609. hGrad.addColorStop(2 / 6, '#0F0');
  610. hGrad.addColorStop(3 / 6, '#0FF');
  611. hGrad.addColorStop(4 / 6, '#00F');
  612. hGrad.addColorStop(5 / 6, '#F0F');
  613. hGrad.addColorStop(6 / 6, '#F00');
  614. ctx.fillStyle = hGrad;
  615. ctx.fillRect(0, 0, canvas.width, canvas.height);
  616. var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height);
  617. switch (type.toLowerCase()) {
  618. case 's':
  619. vGrad.addColorStop(0, 'rgba(255,255,255,0)');
  620. vGrad.addColorStop(1, 'rgba(255,255,255,1)');
  621. break;
  622. case 'v':
  623. vGrad.addColorStop(0, 'rgba(0,0,0,0)');
  624. vGrad.addColorStop(1, 'rgba(0,0,0,1)');
  625. break;
  626. }
  627. ctx.fillStyle = vGrad;
  628. ctx.fillRect(0, 0, canvas.width, canvas.height);
  629. };
  630. paletteObj.elm = canvas;
  631. paletteObj.draw = drawFunc;
  632. } else {
  633. // VML fallback for IE 7 and 8
  634. jsc.initVML();
  635. var vmlContainer = document.createElement('div');
  636. vmlContainer.style.position = 'relative';
  637. vmlContainer.style.overflow = 'hidden';
  638. var hGrad = document.createElement(jsc._vmlNS + ':fill');
  639. hGrad.type = 'gradient';
  640. hGrad.method = 'linear';
  641. hGrad.angle = '90';
  642. hGrad.colors = '16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0'
  643. var hRect = document.createElement(jsc._vmlNS + ':rect');
  644. hRect.style.position = 'absolute';
  645. hRect.style.left = -1 + 'px';
  646. hRect.style.top = -1 + 'px';
  647. hRect.stroked = false;
  648. hRect.appendChild(hGrad);
  649. vmlContainer.appendChild(hRect);
  650. var vGrad = document.createElement(jsc._vmlNS + ':fill');
  651. vGrad.type = 'gradient';
  652. vGrad.method = 'linear';
  653. vGrad.angle = '180';
  654. vGrad.opacity = '0';
  655. var vRect = document.createElement(jsc._vmlNS + ':rect');
  656. vRect.style.position = 'absolute';
  657. vRect.style.left = -1 + 'px';
  658. vRect.style.top = -1 + 'px';
  659. vRect.stroked = false;
  660. vRect.appendChild(vGrad);
  661. vmlContainer.appendChild(vRect);
  662. var drawFunc = function (width, height, type) {
  663. vmlContainer.style.width = width + 'px';
  664. vmlContainer.style.height = height + 'px';
  665. hRect.style.width =
  666. vRect.style.width =
  667. (width + 1) + 'px';
  668. hRect.style.height =
  669. vRect.style.height =
  670. (height + 1) + 'px';
  671. // Colors must be specified during every redraw, otherwise IE won't display
  672. // a full gradient during a subsequential redraw
  673. hGrad.color = '#F00';
  674. hGrad.color2 = '#F00';
  675. switch (type.toLowerCase()) {
  676. case 's':
  677. vGrad.color = vGrad.color2 = '#FFF';
  678. break;
  679. case 'v':
  680. vGrad.color = vGrad.color2 = '#000';
  681. break;
  682. }
  683. };
  684. paletteObj.elm = vmlContainer;
  685. paletteObj.draw = drawFunc;
  686. }
  687. return paletteObj;
  688. },
  689. createSliderGradient : function () {
  690. var sliderObj = {
  691. elm: null,
  692. draw: null
  693. };
  694. if (jsc.isCanvasSupported) {
  695. // Canvas implementation for modern browsers
  696. var canvas = document.createElement('canvas');
  697. var ctx = canvas.getContext('2d');
  698. var drawFunc = function (width, height, color1, color2) {
  699. canvas.width = width;
  700. canvas.height = height;
  701. ctx.clearRect(0, 0, canvas.width, canvas.height);
  702. var grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
  703. grad.addColorStop(0, color1);
  704. grad.addColorStop(1, color2);
  705. ctx.fillStyle = grad;
  706. ctx.fillRect(0, 0, canvas.width, canvas.height);
  707. };
  708. sliderObj.elm = canvas;
  709. sliderObj.draw = drawFunc;
  710. } else {
  711. // VML fallback for IE 7 and 8
  712. jsc.initVML();
  713. var vmlContainer = document.createElement('div');
  714. vmlContainer.style.position = 'relative';
  715. vmlContainer.style.overflow = 'hidden';
  716. var grad = document.createElement(jsc._vmlNS + ':fill');
  717. grad.type = 'gradient';
  718. grad.method = 'linear';
  719. grad.angle = '180';
  720. var rect = document.createElement(jsc._vmlNS + ':rect');
  721. rect.style.position = 'absolute';
  722. rect.style.left = -1 + 'px';
  723. rect.style.top = -1 + 'px';
  724. rect.stroked = false;
  725. rect.appendChild(grad);
  726. vmlContainer.appendChild(rect);
  727. var drawFunc = function (width, height, color1, color2) {
  728. vmlContainer.style.width = width + 'px';
  729. vmlContainer.style.height = height + 'px';
  730. rect.style.width = (width + 1) + 'px';
  731. rect.style.height = (height + 1) + 'px';
  732. grad.color = color1;
  733. grad.color2 = color2;
  734. };
  735. sliderObj.elm = vmlContainer;
  736. sliderObj.draw = drawFunc;
  737. }
  738. return sliderObj;
  739. },
  740. leaveValue : 1<<0,
  741. leaveStyle : 1<<1,
  742. leavePad : 1<<2,
  743. leaveSld : 1<<3,
  744. BoxShadow : (function () {
  745. var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) {
  746. this.hShadow = hShadow;
  747. this.vShadow = vShadow;
  748. this.blur = blur;
  749. this.spread = spread;
  750. this.color = color;
  751. this.inset = !!inset;
  752. };
  753. BoxShadow.prototype.toString = function () {
  754. var vals = [
  755. Math.round(this.hShadow) + 'px',
  756. Math.round(this.vShadow) + 'px',
  757. Math.round(this.blur) + 'px',
  758. Math.round(this.spread) + 'px',
  759. this.color
  760. ];
  761. if (this.inset) {
  762. vals.push('inset');
  763. }
  764. return vals.join(' ');
  765. };
  766. return BoxShadow;
  767. })(),
  768. //
  769. // Usage:
  770. // var myColor = new jscolor(<targetElement> [, <options>])
  771. //
  772. jscolor : function (targetElement, options) {
  773. // General options
  774. //
  775. this.value = null; // initial HEX color. To change it later, use methods fromString(), fromHSV() and fromRGB()
  776. this.valueElement = targetElement; // element that will be used to display and input the color code
  777. this.styleElement = targetElement; // element that will preview the picked color using CSS backgroundColor
  778. this.required = true; // whether the associated text <input> can be left empty
  779. this.refine = true; // whether to refine the entered color code (e.g. uppercase it and remove whitespace)
  780. this.hash = false; // whether to prefix the HEX color code with # symbol
  781. this.uppercase = true; // whether to show the color code in upper case
  782. this.onFineChange = null; // called instantly every time the color changes (value can be either a function or a string with javascript code)
  783. this.activeClass = 'jscolor-active'; // class to be set to the target element when a picker window is open on it
  784. this.overwriteImportant = false; // whether to overwrite colors of styleElement using !important
  785. this.minS = 0; // min allowed saturation (0 - 100)
  786. this.maxS = 100; // max allowed saturation (0 - 100)
  787. this.minV = 0; // min allowed value (brightness) (0 - 100)
  788. this.maxV = 100; // max allowed value (brightness) (0 - 100)
  789. // Accessing the picked color
  790. //
  791. this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100]
  792. this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255]
  793. // Color Picker options
  794. //
  795. this.width = 181; // width of color palette (in px)
  796. this.height = 101; // height of color palette (in px)
  797. this.showOnClick = true; // whether to display the color picker when user clicks on its target element
  798. this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls
  799. this.position = 'bottom'; // left | right | top | bottom - position relative to the target element
  800. this.smartPosition = true; // automatically change picker position when there is not enough space for it
  801. this.sliderSize = 16; // px
  802. this.crossSize = 8; // px
  803. this.closable = false; // whether to display the Close button
  804. this.closeText = 'Close';
  805. this.buttonColor = '#000000'; // CSS color
  806. this.buttonHeight = 18; // px
  807. this.padding = 12; // px
  808. this.backgroundColor = '#FFFFFF'; // CSS color
  809. this.borderWidth = 1; // px
  810. this.borderColor = '#BBBBBB'; // CSS color
  811. this.borderRadius = 8; // px
  812. this.insetWidth = 1; // px
  813. this.insetColor = '#BBBBBB'; // CSS color
  814. this.shadow = true; // whether to display shadow
  815. this.shadowBlur = 15; // px
  816. this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color
  817. this.pointerColor = '#4C4C4C'; // px
  818. this.pointerBorderColor = '#FFFFFF'; // px
  819. this.pointerBorderWidth = 1; // px
  820. this.pointerThickness = 2; // px
  821. this.zIndex = 1000;
  822. this.container = null; // where to append the color picker (BODY element by default)
  823. for (var opt in options) {
  824. if (options.hasOwnProperty(opt)) {
  825. this[opt] = options[opt];
  826. }
  827. }
  828. this.hide = function () {
  829. if (isPickerOwner()) {
  830. detachPicker();
  831. }
  832. };
  833. this.show = function () {
  834. drawPicker();
  835. };
  836. this.redraw = function () {
  837. if (isPickerOwner()) {
  838. drawPicker();
  839. }
  840. };
  841. this.importColor = function () {
  842. if (!this.valueElement) {
  843. this.exportColor();
  844. } else {
  845. if (jsc.isElementType(this.valueElement, 'input')) {
  846. if (!this.refine) {
  847. if (!this.fromString(this.valueElement.value, jsc.leaveValue)) {
  848. if (this.styleElement) {
  849. this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage;
  850. this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor;
  851. this.styleElement.style.color = this.styleElement._jscOrigStyle.color;
  852. }
  853. this.exportColor(jsc.leaveValue | jsc.leaveStyle);
  854. }
  855. } else if (!this.required && /^\s*$/.test(this.valueElement.value)) {
  856. this.valueElement.value = '';
  857. if (this.styleElement) {
  858. this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage;
  859. this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor;
  860. this.styleElement.style.color = this.styleElement._jscOrigStyle.color;
  861. }
  862. this.exportColor(jsc.leaveValue | jsc.leaveStyle);
  863. } else if (this.fromString(this.valueElement.value)) {
  864. // managed to import color successfully from the value -> OK, don't do anything
  865. } else {
  866. this.exportColor();
  867. }
  868. } else {
  869. // not an input element -> doesn't have any value
  870. this.exportColor();
  871. }
  872. }
  873. };
  874. this.exportColor = function (flags) {
  875. if (!(flags & jsc.leaveValue) && this.valueElement) {
  876. var value = this.toString();
  877. if (this.uppercase) { value = value.toUpperCase(); }
  878. if (this.hash) { value = '#' + value; }
  879. if (jsc.isElementType(this.valueElement, 'input')) {
  880. this.valueElement.value = value;
  881. } else {
  882. this.valueElement.innerHTML = value;
  883. }
  884. }
  885. if (!(flags & jsc.leaveStyle)) {
  886. if (this.styleElement) {
  887. var bgColor = '#' + this.toString();
  888. var fgColor = this.isLight() ? '#000' : '#FFF';
  889. this.styleElement.style.backgroundImage = 'none';
  890. this.styleElement.style.backgroundColor = bgColor;
  891. this.styleElement.style.color = fgColor;
  892. if (this.overwriteImportant) {
  893. this.styleElement.setAttribute('style',
  894. 'background: ' + bgColor + ' !important; ' +
  895. 'color: ' + fgColor + ' !important;'
  896. );
  897. }
  898. }
  899. }
  900. if (!(flags & jsc.leavePad) && isPickerOwner()) {
  901. redrawPad();
  902. }
  903. if (!(flags & jsc.leaveSld) && isPickerOwner()) {
  904. redrawSld();
  905. }
  906. };
  907. // h: 0-360
  908. // s: 0-100
  909. // v: 0-100
  910. //
  911. this.fromHSV = function (h, s, v, flags) { // null = don't change
  912. if (h !== null) {
  913. if (isNaN(h)) { return false; }
  914. h = Math.max(0, Math.min(360, h));
  915. }
  916. if (s !== null) {
  917. if (isNaN(s)) { return false; }
  918. s = Math.max(0, Math.min(100, this.maxS, s), this.minS);
  919. }
  920. if (v !== null) {
  921. if (isNaN(v)) { return false; }
  922. v = Math.max(0, Math.min(100, this.maxV, v), this.minV);
  923. }
  924. this.rgb = HSV_RGB(
  925. h===null ? this.hsv[0] : (this.hsv[0]=h),
  926. s===null ? this.hsv[1] : (this.hsv[1]=s),
  927. v===null ? this.hsv[2] : (this.hsv[2]=v)
  928. );
  929. this.exportColor(flags);
  930. };
  931. // r: 0-255
  932. // g: 0-255
  933. // b: 0-255
  934. //
  935. this.fromRGB = function (r, g, b, flags) { // null = don't change
  936. if (r !== null) {
  937. if (isNaN(r)) { return false; }
  938. r = Math.max(0, Math.min(255, r));
  939. }
  940. if (g !== null) {
  941. if (isNaN(g)) { return false; }
  942. g = Math.max(0, Math.min(255, g));
  943. }
  944. if (b !== null) {
  945. if (isNaN(b)) { return false; }
  946. b = Math.max(0, Math.min(255, b));
  947. }
  948. var hsv = RGB_HSV(
  949. r===null ? this.rgb[0] : r,
  950. g===null ? this.rgb[1] : g,
  951. b===null ? this.rgb[2] : b
  952. );
  953. if (hsv[0] !== null) {
  954. this.hsv[0] = Math.max(0, Math.min(360, hsv[0]));
  955. }
  956. if (hsv[2] !== 0) {
  957. this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1]));
  958. }
  959. this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2]));
  960. // update RGB according to final HSV, as some values might be trimmed
  961. var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]);
  962. this.rgb[0] = rgb[0];
  963. this.rgb[1] = rgb[1];
  964. this.rgb[2] = rgb[2];
  965. this.exportColor(flags);
  966. };
  967. this.fromString = function (str, flags) {
  968. var m;
  969. if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) {
  970. // HEX notation
  971. //
  972. if (m[1].length === 6) {
  973. // 6-char notation
  974. this.fromRGB(
  975. parseInt(m[1].substr(0,2),16),
  976. parseInt(m[1].substr(2,2),16),
  977. parseInt(m[1].substr(4,2),16),
  978. flags
  979. );
  980. } else {
  981. // 3-char notation
  982. this.fromRGB(
  983. parseInt(m[1].charAt(0) + m[1].charAt(0),16),
  984. parseInt(m[1].charAt(1) + m[1].charAt(1),16),
  985. parseInt(m[1].charAt(2) + m[1].charAt(2),16),
  986. flags
  987. );
  988. }
  989. return true;
  990. } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) {
  991. var params = m[1].split(',');
  992. var re = /^\s*(\d*)(\.\d+)?\s*$/;
  993. var mR, mG, mB;
  994. if (
  995. params.length >= 3 &&
  996. (mR = params[0].match(re)) &&
  997. (mG = params[1].match(re)) &&
  998. (mB = params[2].match(re))
  999. ) {
  1000. var r = parseFloat((mR[1] || '0') + (mR[2] || ''));
  1001. var g = parseFloat((mG[1] || '0') + (mG[2] || ''));
  1002. var b = parseFloat((mB[1] || '0') + (mB[2] || ''));
  1003. this.fromRGB(r, g, b, flags);
  1004. return true;
  1005. }
  1006. }
  1007. return false;
  1008. };
  1009. this.toString = function () {
  1010. return (
  1011. (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) +
  1012. (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) +
  1013. (0x100 | Math.round(this.rgb[2])).toString(16).substr(1)
  1014. );
  1015. };
  1016. this.toHEXString = function () {
  1017. return '#' + this.toString().toUpperCase();
  1018. };
  1019. this.toRGBString = function () {
  1020. return ('rgb(' +
  1021. Math.round(this.rgb[0]) + ',' +
  1022. Math.round(this.rgb[1]) + ',' +
  1023. Math.round(this.rgb[2]) + ')'
  1024. );
  1025. };
  1026. this.isLight = function () {
  1027. return (
  1028. 0.213 * this.rgb[0] +
  1029. 0.715 * this.rgb[1] +
  1030. 0.072 * this.rgb[2] >
  1031. 255 / 2
  1032. );
  1033. };
  1034. this._processParentElementsInDOM = function () {
  1035. if (this._linkedElementsProcessed) { return; }
  1036. this._linkedElementsProcessed = true;
  1037. var elm = this.targetElement;
  1038. do {
  1039. // If the target element or one of its parent nodes has fixed position,
  1040. // then use fixed positioning instead
  1041. //
  1042. // Note: In Firefox, getComputedStyle returns null in a hidden iframe,
  1043. // that's why we need to check if the returned style object is non-empty
  1044. var currStyle = jsc.getStyle(elm);
  1045. if (currStyle && currStyle.position.toLowerCase() === 'fixed') {
  1046. this.fixed = true;
  1047. }
  1048. if (elm !== this.targetElement) {
  1049. // Ensure to attach onParentScroll only once to each parent element
  1050. // (multiple targetElements can share the same parent nodes)
  1051. //
  1052. // Note: It's not just offsetParents that can be scrollable,
  1053. // that's why we loop through all parent nodes
  1054. if (!elm._jscEventsAttached) {
  1055. jsc.attachEvent(elm, 'scroll', jsc.onParentScroll);
  1056. elm._jscEventsAttached = true;
  1057. }
  1058. }
  1059. } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body'));
  1060. };
  1061. // r: 0-255
  1062. // g: 0-255
  1063. // b: 0-255
  1064. //
  1065. // returns: [ 0-360, 0-100, 0-100 ]
  1066. //
  1067. function RGB_HSV (r, g, b) {
  1068. r /= 255;
  1069. g /= 255;
  1070. b /= 255;
  1071. var n = Math.min(Math.min(r,g),b);
  1072. var v = Math.max(Math.max(r,g),b);
  1073. var m = v - n;
  1074. if (m === 0) { return [ null, 0, 100 * v ]; }
  1075. var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m);
  1076. return [
  1077. 60 * (h===6?0:h),
  1078. 100 * (m/v),
  1079. 100 * v
  1080. ];
  1081. }
  1082. // h: 0-360
  1083. // s: 0-100
  1084. // v: 0-100
  1085. //
  1086. // returns: [ 0-255, 0-255, 0-255 ]
  1087. //
  1088. function HSV_RGB (h, s, v) {
  1089. var u = 255 * (v / 100);
  1090. if (h === null) {
  1091. return [ u, u, u ];
  1092. }
  1093. h /= 60;
  1094. s /= 100;
  1095. var i = Math.floor(h);
  1096. var f = i%2 ? h-i : 1-(h-i);
  1097. var m = u * (1 - s);
  1098. var n = u * (1 - s * f);
  1099. switch (i) {
  1100. case 6:
  1101. case 0: return [u,n,m];
  1102. case 1: return [n,u,m];
  1103. case 2: return [m,u,n];
  1104. case 3: return [m,n,u];
  1105. case 4: return [n,m,u];
  1106. case 5: return [u,m,n];
  1107. }
  1108. }
  1109. function detachPicker () {
  1110. jsc.unsetClass(THIS.targetElement, THIS.activeClass);
  1111. jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap);
  1112. delete jsc.picker.owner;
  1113. }
  1114. function drawPicker () {
  1115. // At this point, when drawing the picker, we know what the parent elements are
  1116. // and we can do all related DOM operations, such as registering events on them
  1117. // or checking their positioning
  1118. THIS._processParentElementsInDOM();
  1119. if (!jsc.picker) {
  1120. jsc.picker = {
  1121. owner: null,
  1122. wrap : document.createElement('div'),
  1123. box : document.createElement('div'),
  1124. boxS : document.createElement('div'), // shadow area
  1125. boxB : document.createElement('div'), // border
  1126. pad : document.createElement('div'),
  1127. padB : document.createElement('div'), // border
  1128. padM : document.createElement('div'), // mouse/touch area
  1129. padPal : jsc.createPalette(),
  1130. cross : document.createElement('div'),
  1131. crossBY : document.createElement('div'), // border Y
  1132. crossBX : document.createElement('div'), // border X
  1133. crossLY : document.createElement('div'), // line Y
  1134. crossLX : document.createElement('div'), // line X
  1135. sld : document.createElement('div'),
  1136. sldB : document.createElement('div'), // border
  1137. sldM : document.createElement('div'), // mouse/touch area
  1138. sldGrad : jsc.createSliderGradient(),
  1139. sldPtrS : document.createElement('div'), // slider pointer spacer
  1140. sldPtrIB : document.createElement('div'), // slider pointer inner border
  1141. sldPtrMB : document.createElement('div'), // slider pointer middle border
  1142. sldPtrOB : document.createElement('div'), // slider pointer outer border
  1143. btn : document.createElement('div'),
  1144. btnT : document.createElement('span') // text
  1145. };
  1146. jsc.picker.pad.appendChild(jsc.picker.padPal.elm);
  1147. jsc.picker.padB.appendChild(jsc.picker.pad);
  1148. jsc.picker.cross.appendChild(jsc.picker.crossBY);
  1149. jsc.picker.cross.appendChild(jsc.picker.crossBX);
  1150. jsc.picker.cross.appendChild(jsc.picker.crossLY);
  1151. jsc.picker.cross.appendChild(jsc.picker.crossLX);
  1152. jsc.picker.padB.appendChild(jsc.picker.cross);
  1153. jsc.picker.box.appendChild(jsc.picker.padB);
  1154. jsc.picker.box.appendChild(jsc.picker.padM);
  1155. jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm);
  1156. jsc.picker.sldB.appendChild(jsc.picker.sld);
  1157. jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB);
  1158. jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB);
  1159. jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB);
  1160. jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS);
  1161. jsc.picker.box.appendChild(jsc.picker.sldB);
  1162. jsc.picker.box.appendChild(jsc.picker.sldM);
  1163. jsc.picker.btn.appendChild(jsc.picker.btnT);
  1164. jsc.picker.box.appendChild(jsc.picker.btn);
  1165. jsc.picker.boxB.appendChild(jsc.picker.box);
  1166. jsc.picker.wrap.appendChild(jsc.picker.boxS);
  1167. jsc.picker.wrap.appendChild(jsc.picker.boxB);
  1168. }
  1169. var p = jsc.picker;
  1170. var displaySlider = !!jsc.getSliderComponent(THIS);
  1171. var dims = jsc.getPickerDims(THIS);
  1172. var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize);
  1173. var padToSliderPadding = jsc.getPadToSliderPadding(THIS);
  1174. var borderRadius = Math.min(
  1175. THIS.borderRadius,
  1176. Math.round(THIS.padding * Math.PI)); // px
  1177. var padCursor = 'crosshair';
  1178. // wrap
  1179. p.wrap.style.clear = 'both';
  1180. p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px';
  1181. p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px';
  1182. p.wrap.style.zIndex = THIS.zIndex;
  1183. // picker
  1184. p.box.style.width = dims[0] + 'px';
  1185. p.box.style.height = dims[1] + 'px';
  1186. p.boxS.style.position = 'absolute';
  1187. p.boxS.style.left = '0';
  1188. p.boxS.style.top = '0';
  1189. p.boxS.style.width = '100%';
  1190. p.boxS.style.height = '100%';
  1191. jsc.setBorderRadius(p.boxS, borderRadius + 'px');
  1192. // picker border
  1193. p.boxB.style.position = 'relative';
  1194. p.boxB.style.border = THIS.borderWidth + 'px solid';
  1195. p.boxB.style.borderColor = THIS.borderColor;
  1196. p.boxB.style.background = THIS.backgroundColor;
  1197. jsc.setBorderRadius(p.boxB, borderRadius + 'px');
  1198. // IE hack:
  1199. // If the element is transparent, IE will trigger the event on the elements under it,
  1200. // e.g. on Canvas or on elements with border
  1201. p.padM.style.background =
  1202. p.sldM.style.background =
  1203. '#FFF';
  1204. jsc.setStyle(p.padM, 'opacity', '0');
  1205. jsc.setStyle(p.sldM, 'opacity', '0');
  1206. // pad
  1207. p.pad.style.position = 'relative';
  1208. p.pad.style.width = THIS.width + 'px';
  1209. p.pad.style.height = THIS.height + 'px';
  1210. // pad palettes (HSV and HVS)
  1211. p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS));
  1212. // pad border
  1213. p.padB.style.position = 'absolute';
  1214. p.padB.style.left = THIS.padding + 'px';
  1215. p.padB.style.top = THIS.padding + 'px';
  1216. p.padB.style.border = THIS.insetWidth + 'px solid';
  1217. p.padB.style.borderColor = THIS.insetColor;
  1218. // pad mouse area
  1219. p.padM._jscInstance = THIS;
  1220. p.padM._jscControlName = 'pad';
  1221. p.padM.style.position = 'absolute';
  1222. p.padM.style.left = '0';
  1223. p.padM.style.top = '0';
  1224. p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px';
  1225. p.padM.style.height = dims[1] + 'px';
  1226. p.padM.style.cursor = padCursor;
  1227. // pad cross
  1228. p.cross.style.position = 'absolute';
  1229. p.cross.style.left =
  1230. p.cross.style.top =
  1231. '0';
  1232. p.cross.style.width =
  1233. p.cross.style.height =
  1234. crossOuterSize + 'px';
  1235. // pad cross border Y and X
  1236. p.crossBY.style.position =
  1237. p.crossBX.style.position =
  1238. 'absolute';
  1239. p.crossBY.style.background =
  1240. p.crossBX.style.background =
  1241. THIS.pointerBorderColor;
  1242. p.crossBY.style.width =
  1243. p.crossBX.style.height =
  1244. (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px';
  1245. p.crossBY.style.height =
  1246. p.crossBX.style.width =
  1247. crossOuterSize + 'px';
  1248. p.crossBY.style.left =
  1249. p.crossBX.style.top =
  1250. (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px';
  1251. p.crossBY.style.top =
  1252. p.crossBX.style.left =
  1253. '0';
  1254. // pad cross line Y and X
  1255. p.crossLY.style.position =
  1256. p.crossLX.style.position =
  1257. 'absolute';
  1258. p.crossLY.style.background =
  1259. p.crossLX.style.background =
  1260. THIS.pointerColor;
  1261. p.crossLY.style.height =
  1262. p.crossLX.style.width =
  1263. (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px';
  1264. p.crossLY.style.width =
  1265. p.crossLX.style.height =
  1266. THIS.pointerThickness + 'px';
  1267. p.crossLY.style.left =
  1268. p.crossLX.style.top =
  1269. (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px';
  1270. p.crossLY.style.top =
  1271. p.crossLX.style.left =
  1272. THIS.pointerBorderWidth + 'px';
  1273. // slider
  1274. p.sld.style.overflow = 'hidden';
  1275. p.sld.style.width = THIS.sliderSize + 'px';
  1276. p.sld.style.height = THIS.height + 'px';
  1277. // slider gradient
  1278. p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000');
  1279. // slider border
  1280. p.sldB.style.display = displaySlider ? 'block' : 'none';
  1281. p.sldB.style.position = 'absolute';
  1282. p.sldB.style.right = THIS.padding + 'px';
  1283. p.sldB.style.top = THIS.padding + 'px';
  1284. p.sldB.style.border = THIS.insetWidth + 'px solid';
  1285. p.sldB.style.borderColor = THIS.insetColor;
  1286. // slider mouse area
  1287. p.sldM._jscInstance = THIS;
  1288. p.sldM._jscControlName = 'sld';
  1289. p.sldM.style.display = displaySlider ? 'block' : 'none';
  1290. p.sldM.style.position = 'absolute';
  1291. p.sldM.style.right = '0';
  1292. p.sldM.style.top = '0';
  1293. p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px';
  1294. p.sldM.style.height = dims[1] + 'px';
  1295. p.sldM.style.cursor = 'default';
  1296. // slider pointer inner and outer border
  1297. p.sldPtrIB.style.border =
  1298. p.sldPtrOB.style.border =
  1299. THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor;
  1300. // slider pointer outer border
  1301. p.sldPtrOB.style.position = 'absolute';
  1302. p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px';
  1303. p.sldPtrOB.style.top = '0';
  1304. // slider pointer middle border
  1305. p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor;
  1306. // slider pointer spacer
  1307. p.sldPtrS.style.width = THIS.sliderSize + 'px';
  1308. p.sldPtrS.style.height = sliderPtrSpace + 'px';
  1309. // the Close button
  1310. function setBtnBorder () {
  1311. var insetColors = THIS.insetColor.split(/\s+/);
  1312. var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1];
  1313. p.btn.style.borderColor = outsetColor;
  1314. }
  1315. p.btn.style.display = THIS.closable ? 'block' : 'none';
  1316. p.btn.style.position = 'absolute';
  1317. p.btn.style.left = THIS.padding + 'px';
  1318. p.btn.style.bottom = THIS.padding + 'px';
  1319. p.btn.style.padding = '0 15px';
  1320. p.btn.style.height = THIS.buttonHeight + 'px';
  1321. p.btn.style.border = THIS.insetWidth + 'px solid';
  1322. setBtnBorder();
  1323. p.btn.style.color = THIS.buttonColor;
  1324. p.btn.style.font = '12px sans-serif';
  1325. p.btn.style.textAlign = 'center';
  1326. try {
  1327. p.btn.style.cursor = 'pointer';
  1328. } catch(eOldIE) {
  1329. p.btn.style.cursor = 'hand';
  1330. }
  1331. p.btn.onmousedown = function () {
  1332. THIS.hide();
  1333. };
  1334. p.btnT.style.lineHeight = THIS.buttonHeight + 'px';
  1335. p.btnT.innerHTML = '';
  1336. p.btnT.appendChild(document.createTextNode(THIS.closeText));
  1337. // place pointers
  1338. redrawPad();
  1339. redrawSld();
  1340. // If we are changing the owner without first closing the picker,
  1341. // make sure to first deal with the old owner
  1342. if (jsc.picker.owner && jsc.picker.owner !== THIS) {
  1343. jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass);
  1344. }
  1345. // Set the new picker owner
  1346. jsc.picker.owner = THIS;
  1347. // The redrawPosition() method needs picker.owner to be set, that's why we call it here,
  1348. // after setting the owner
  1349. if (jsc.isElementType(container, 'body')) {
  1350. jsc.redrawPosition();
  1351. } else {
  1352. jsc._drawPosition(THIS, 0, 0, 'relative', false);
  1353. }
  1354. if (p.wrap.parentNode != container) {
  1355. container.appendChild(p.wrap);
  1356. }
  1357. jsc.setClass(THIS.targetElement, THIS.activeClass);
  1358. }
  1359. function redrawPad () {
  1360. // redraw the pad pointer
  1361. switch (jsc.getPadYComponent(THIS)) {
  1362. case 's': var yComponent = 1; break;
  1363. case 'v': var yComponent = 2; break;
  1364. }
  1365. var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1));
  1366. var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1));
  1367. var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize);
  1368. var ofs = -Math.floor(crossOuterSize / 2);
  1369. jsc.picker.cross.style.left = (x + ofs) + 'px';
  1370. jsc.picker.cross.style.top = (y + ofs) + 'px';
  1371. // redraw the slider
  1372. switch (jsc.getSliderComponent(THIS)) {
  1373. case 's':
  1374. var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]);
  1375. var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]);
  1376. var color1 = 'rgb(' +
  1377. Math.round(rgb1[0]) + ',' +
  1378. Math.round(rgb1[1]) + ',' +
  1379. Math.round(rgb1[2]) + ')';
  1380. var color2 = 'rgb(' +
  1381. Math.round(rgb2[0]) + ',' +
  1382. Math.round(rgb2[1]) + ',' +
  1383. Math.round(rgb2[2]) + ')';
  1384. jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2);
  1385. break;
  1386. case 'v':
  1387. var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100);
  1388. var color1 = 'rgb(' +
  1389. Math.round(rgb[0]) + ',' +
  1390. Math.round(rgb[1]) + ',' +
  1391. Math.round(rgb[2]) + ')';
  1392. var color2 = '#000';
  1393. jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2);
  1394. break;
  1395. }
  1396. }
  1397. function redrawSld () {
  1398. var sldComponent = jsc.getSliderComponent(THIS);
  1399. if (sldComponent) {
  1400. // redraw the slider pointer
  1401. switch (sldComponent) {
  1402. case 's': var yComponent = 1; break;
  1403. case 'v': var yComponent = 2; break;
  1404. }
  1405. var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1));
  1406. jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px';
  1407. }
  1408. }
  1409. function isPickerOwner () {
  1410. return jsc.picker && jsc.picker.owner === THIS;
  1411. }
  1412. function blurValue () {
  1413. THIS.importColor();
  1414. }
  1415. // Find the target element
  1416. if (typeof targetElement === 'string') {
  1417. var id = targetElement;
  1418. var elm = document.getElementById(id);
  1419. if (elm) {
  1420. this.targetElement = elm;
  1421. } else {
  1422. jsc.warn('Could not find target element with ID \'' + id + '\'');
  1423. }
  1424. } else if (targetElement) {
  1425. this.targetElement = targetElement;
  1426. } else {
  1427. jsc.warn('Invalid target element: \'' + targetElement + '\'');
  1428. }
  1429. if (this.targetElement._jscLinkedInstance) {
  1430. jsc.warn('Cannot link jscolor twice to the same element. Skipping.');
  1431. return;
  1432. }
  1433. this.targetElement._jscLinkedInstance = this;
  1434. // Find the value element
  1435. this.valueElement = jsc.fetchElement(this.valueElement);
  1436. // Find the style element
  1437. this.styleElement = jsc.fetchElement(this.styleElement);
  1438. var THIS = this;
  1439. var container =
  1440. this.container ?
  1441. jsc.fetchElement(this.container) :
  1442. document.getElementsByTagName('body')[0];
  1443. var sliderPtrSpace = 3; // px
  1444. // For BUTTON elements it's important to stop them from sending the form when clicked
  1445. // (e.g. in Safari)
  1446. if (jsc.isElementType(this.targetElement, 'button')) {
  1447. if (this.targetElement.onclick) {
  1448. var origCallback = this.targetElement.onclick;
  1449. this.targetElement.onclick = function (evt) {
  1450. origCallback.call(this, evt);
  1451. return false;
  1452. };
  1453. } else {
  1454. this.targetElement.onclick = function () { return false; };
  1455. }
  1456. }
  1457. /*
  1458. var elm = this.targetElement;
  1459. do {
  1460. // If the target element or one of its offsetParents has fixed position,
  1461. // then use fixed positioning instead
  1462. //
  1463. // Note: In Firefox, getComputedStyle returns null in a hidden iframe,
  1464. // that's why we need to check if the returned style object is non-empty
  1465. var currStyle = jsc.getStyle(elm);
  1466. if (currStyle && currStyle.position.toLowerCase() === 'fixed') {
  1467. this.fixed = true;
  1468. }
  1469. if (elm !== this.targetElement) {
  1470. // attach onParentScroll so that we can recompute the picker position
  1471. // when one of the offsetParents is scrolled
  1472. if (!elm._jscEventsAttached) {
  1473. jsc.attachEvent(elm, 'scroll', jsc.onParentScroll);
  1474. elm._jscEventsAttached = true;
  1475. }
  1476. }
  1477. } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body'));
  1478. */
  1479. // valueElement
  1480. if (this.valueElement) {
  1481. if (jsc.isElementType(this.valueElement, 'input')) {
  1482. var updateField = function () {
  1483. THIS.fromString(THIS.valueElement.value, jsc.leaveValue);
  1484. jsc.dispatchFineChange(THIS);
  1485. };
  1486. jsc.attachEvent(this.valueElement, 'keyup', updateField);
  1487. jsc.attachEvent(this.valueElement, 'input', updateField);
  1488. jsc.attachEvent(this.valueElement, 'blur', blurValue);
  1489. this.valueElement.setAttribute('autocomplete', 'off');
  1490. }
  1491. }
  1492. // styleElement
  1493. if (this.styleElement) {
  1494. this.styleElement._jscOrigStyle = {
  1495. backgroundImage : this.styleElement.style.backgroundImage,
  1496. backgroundColor : this.styleElement.style.backgroundColor,
  1497. color : this.styleElement.style.color
  1498. };
  1499. }
  1500. if (this.value) {
  1501. // Try to set the color from the .value option and if unsuccessful,
  1502. // export the current color
  1503. this.fromString(this.value) || this.exportColor();
  1504. } else {
  1505. this.importColor();
  1506. }
  1507. }
  1508. };
  1509. //================================
  1510. // Public properties and methods
  1511. //================================
  1512. // By default, search for all elements with class="jscolor" and install a color picker on them.
  1513. //
  1514. // You can change what class name will be looked for by setting the property jscolor.lookupClass
  1515. // anywhere in your HTML document. To completely disable the automatic lookup, set it to null.
  1516. //
  1517. jsc.jscolor.lookupClass = 'jscolor';
  1518. jsc.jscolor.installByClassName = function (className) {
  1519. var inputElms = document.getElementsByTagName('input');
  1520. var buttonElms = document.getElementsByTagName('button');
  1521. jsc.tryInstallOnElements(inputElms, className);
  1522. jsc.tryInstallOnElements(buttonElms, className);
  1523. };
  1524. jsc.register();
  1525. return jsc.jscolor;
  1526. })(); }