jscolor.js 78 KB

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