jscolor.js 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844
  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.4
  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 uppercase the color code
  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.minS = 0; // min allowed saturation (0 - 100)
  785. this.maxS = 100; // max allowed saturation (0 - 100)
  786. this.minV = 0; // min allowed value (brightness) (0 - 100)
  787. this.maxV = 100; // max allowed value (brightness) (0 - 100)
  788. // Accessing the picked color
  789. //
  790. this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100]
  791. this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255]
  792. // Color Picker options
  793. //
  794. this.width = 181; // width of color palette (in px)
  795. this.height = 101; // height of color palette (in px)
  796. this.showOnClick = true; // whether to display the color picker when user clicks on its target element
  797. this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls
  798. this.position = 'bottom'; // left | right | top | bottom - position relative to the target element
  799. this.smartPosition = true; // automatically change picker position when there is not enough space for it
  800. this.sliderSize = 16; // px
  801. this.crossSize = 8; // px
  802. this.closable = false; // whether to display the Close button
  803. this.closeText = 'Close';
  804. this.buttonColor = '#000000'; // CSS color
  805. this.buttonHeight = 18; // px
  806. this.padding = 12; // px
  807. this.backgroundColor = '#FFFFFF'; // CSS color
  808. this.borderWidth = 1; // px
  809. this.borderColor = '#BBBBBB'; // CSS color
  810. this.borderRadius = 8; // px
  811. this.insetWidth = 1; // px
  812. this.insetColor = '#BBBBBB'; // CSS color
  813. this.shadow = true; // whether to display shadow
  814. this.shadowBlur = 15; // px
  815. this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color
  816. this.pointerColor = '#4C4C4C'; // px
  817. this.pointerBorderColor = '#FFFFFF'; // px
  818. this.pointerBorderWidth = 1; // px
  819. this.pointerThickness = 2; // px
  820. this.zIndex = 1000;
  821. this.container = null; // where to append the color picker (BODY element by default)
  822. for (var opt in options) {
  823. if (options.hasOwnProperty(opt)) {
  824. this[opt] = options[opt];
  825. }
  826. }
  827. this.hide = function () {
  828. if (isPickerOwner()) {
  829. detachPicker();
  830. }
  831. };
  832. this.show = function () {
  833. drawPicker();
  834. };
  835. this.redraw = function () {
  836. if (isPickerOwner()) {
  837. drawPicker();
  838. }
  839. };
  840. this.importColor = function () {
  841. if (!this.valueElement) {
  842. this.exportColor();
  843. } else {
  844. if (jsc.isElementType(this.valueElement, 'input')) {
  845. if (!this.refine) {
  846. if (!this.fromString(this.valueElement.value, jsc.leaveValue)) {
  847. if (this.styleElement) {
  848. this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage;
  849. this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor;
  850. this.styleElement.style.color = this.styleElement._jscOrigStyle.color;
  851. }
  852. this.exportColor(jsc.leaveValue | jsc.leaveStyle);
  853. }
  854. } else if (!this.required && /^\s*$/.test(this.valueElement.value)) {
  855. this.valueElement.value = '';
  856. if (this.styleElement) {
  857. this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage;
  858. this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor;
  859. this.styleElement.style.color = this.styleElement._jscOrigStyle.color;
  860. }
  861. this.exportColor(jsc.leaveValue | jsc.leaveStyle);
  862. } else if (this.fromString(this.valueElement.value)) {
  863. // managed to import color successfully from the value -> OK, don't do anything
  864. } else {
  865. this.exportColor();
  866. }
  867. } else {
  868. // not an input element -> doesn't have any value
  869. this.exportColor();
  870. }
  871. }
  872. };
  873. this.exportColor = function (flags) {
  874. if (!(flags & jsc.leaveValue) && this.valueElement) {
  875. var value = this.toString();
  876. if (this.uppercase) { value = value.toUpperCase(); }
  877. if (this.hash) { value = '#' + value; }
  878. if (jsc.isElementType(this.valueElement, 'input')) {
  879. this.valueElement.value = value;
  880. } else {
  881. this.valueElement.innerHTML = value;
  882. }
  883. }
  884. if (!(flags & jsc.leaveStyle)) {
  885. if (this.styleElement) {
  886. this.styleElement.style.backgroundImage = 'none';
  887. this.styleElement.style.backgroundColor = '#' + this.toString();
  888. this.styleElement.style.color = this.isLight() ? '#000' : '#FFF';
  889. }
  890. }
  891. if (!(flags & jsc.leavePad) && isPickerOwner()) {
  892. redrawPad();
  893. }
  894. if (!(flags & jsc.leaveSld) && isPickerOwner()) {
  895. redrawSld();
  896. }
  897. };
  898. // h: 0-360
  899. // s: 0-100
  900. // v: 0-100
  901. //
  902. this.fromHSV = function (h, s, v, flags) { // null = don't change
  903. if (h !== null) {
  904. if (isNaN(h)) { return false; }
  905. h = Math.max(0, Math.min(360, h));
  906. }
  907. if (s !== null) {
  908. if (isNaN(s)) { return false; }
  909. s = Math.max(0, Math.min(100, this.maxS, s), this.minS);
  910. }
  911. if (v !== null) {
  912. if (isNaN(v)) { return false; }
  913. v = Math.max(0, Math.min(100, this.maxV, v), this.minV);
  914. }
  915. this.rgb = HSV_RGB(
  916. h===null ? this.hsv[0] : (this.hsv[0]=h),
  917. s===null ? this.hsv[1] : (this.hsv[1]=s),
  918. v===null ? this.hsv[2] : (this.hsv[2]=v)
  919. );
  920. this.exportColor(flags);
  921. };
  922. // r: 0-255
  923. // g: 0-255
  924. // b: 0-255
  925. //
  926. this.fromRGB = function (r, g, b, flags) { // null = don't change
  927. if (r !== null) {
  928. if (isNaN(r)) { return false; }
  929. r = Math.max(0, Math.min(255, r));
  930. }
  931. if (g !== null) {
  932. if (isNaN(g)) { return false; }
  933. g = Math.max(0, Math.min(255, g));
  934. }
  935. if (b !== null) {
  936. if (isNaN(b)) { return false; }
  937. b = Math.max(0, Math.min(255, b));
  938. }
  939. var hsv = RGB_HSV(
  940. r===null ? this.rgb[0] : r,
  941. g===null ? this.rgb[1] : g,
  942. b===null ? this.rgb[2] : b
  943. );
  944. if (hsv[0] !== null) {
  945. this.hsv[0] = Math.max(0, Math.min(360, hsv[0]));
  946. }
  947. if (hsv[2] !== 0) {
  948. this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1]));
  949. }
  950. this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2]));
  951. // update RGB according to final HSV, as some values might be trimmed
  952. var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]);
  953. this.rgb[0] = rgb[0];
  954. this.rgb[1] = rgb[1];
  955. this.rgb[2] = rgb[2];
  956. this.exportColor(flags);
  957. };
  958. this.fromString = function (str, flags) {
  959. var m;
  960. if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) {
  961. // HEX notation
  962. //
  963. if (m[1].length === 6) {
  964. // 6-char notation
  965. this.fromRGB(
  966. parseInt(m[1].substr(0,2),16),
  967. parseInt(m[1].substr(2,2),16),
  968. parseInt(m[1].substr(4,2),16),
  969. flags
  970. );
  971. } else {
  972. // 3-char notation
  973. this.fromRGB(
  974. parseInt(m[1].charAt(0) + m[1].charAt(0),16),
  975. parseInt(m[1].charAt(1) + m[1].charAt(1),16),
  976. parseInt(m[1].charAt(2) + m[1].charAt(2),16),
  977. flags
  978. );
  979. }
  980. return true;
  981. } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) {
  982. var params = m[1].split(',');
  983. var re = /^\s*(\d*)(\.\d+)?\s*$/;
  984. var mR, mG, mB;
  985. if (
  986. params.length >= 3 &&
  987. (mR = params[0].match(re)) &&
  988. (mG = params[1].match(re)) &&
  989. (mB = params[2].match(re))
  990. ) {
  991. var r = parseFloat((mR[1] || '0') + (mR[2] || ''));
  992. var g = parseFloat((mG[1] || '0') + (mG[2] || ''));
  993. var b = parseFloat((mB[1] || '0') + (mB[2] || ''));
  994. this.fromRGB(r, g, b, flags);
  995. return true;
  996. }
  997. }
  998. return false;
  999. };
  1000. this.toString = function () {
  1001. return (
  1002. (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) +
  1003. (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) +
  1004. (0x100 | Math.round(this.rgb[2])).toString(16).substr(1)
  1005. );
  1006. };
  1007. this.toHEXString = function () {
  1008. return '#' + this.toString().toUpperCase();
  1009. };
  1010. this.toRGBString = function () {
  1011. return ('rgb(' +
  1012. Math.round(this.rgb[0]) + ',' +
  1013. Math.round(this.rgb[1]) + ',' +
  1014. Math.round(this.rgb[2]) + ')'
  1015. );
  1016. };
  1017. this.isLight = function () {
  1018. return (
  1019. 0.213 * this.rgb[0] +
  1020. 0.715 * this.rgb[1] +
  1021. 0.072 * this.rgb[2] >
  1022. 255 / 2
  1023. );
  1024. };
  1025. this._processParentElementsInDOM = function () {
  1026. if (this._linkedElementsProcessed) { return; }
  1027. this._linkedElementsProcessed = true;
  1028. var elm = this.targetElement;
  1029. do {
  1030. // If the target element or one of its parent nodes has fixed position,
  1031. // then use fixed positioning instead
  1032. //
  1033. // Note: In Firefox, getComputedStyle returns null in a hidden iframe,
  1034. // that's why we need to check if the returned style object is non-empty
  1035. var currStyle = jsc.getStyle(elm);
  1036. if (currStyle && currStyle.position.toLowerCase() === 'fixed') {
  1037. this.fixed = true;
  1038. }
  1039. if (elm !== this.targetElement) {
  1040. // Ensure to attach onParentScroll only once to each parent element
  1041. // (multiple targetElements can share the same parent nodes)
  1042. //
  1043. // Note: It's not just offsetParents that can be scrollable,
  1044. // that's why we loop through all parent nodes
  1045. if (!elm._jscEventsAttached) {
  1046. jsc.attachEvent(elm, 'scroll', jsc.onParentScroll);
  1047. elm._jscEventsAttached = true;
  1048. }
  1049. }
  1050. } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body'));
  1051. };
  1052. // r: 0-255
  1053. // g: 0-255
  1054. // b: 0-255
  1055. //
  1056. // returns: [ 0-360, 0-100, 0-100 ]
  1057. //
  1058. function RGB_HSV (r, g, b) {
  1059. r /= 255;
  1060. g /= 255;
  1061. b /= 255;
  1062. var n = Math.min(Math.min(r,g),b);
  1063. var v = Math.max(Math.max(r,g),b);
  1064. var m = v - n;
  1065. if (m === 0) { return [ null, 0, 100 * v ]; }
  1066. var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m);
  1067. return [
  1068. 60 * (h===6?0:h),
  1069. 100 * (m/v),
  1070. 100 * v
  1071. ];
  1072. }
  1073. // h: 0-360
  1074. // s: 0-100
  1075. // v: 0-100
  1076. //
  1077. // returns: [ 0-255, 0-255, 0-255 ]
  1078. //
  1079. function HSV_RGB (h, s, v) {
  1080. var u = 255 * (v / 100);
  1081. if (h === null) {
  1082. return [ u, u, u ];
  1083. }
  1084. h /= 60;
  1085. s /= 100;
  1086. var i = Math.floor(h);
  1087. var f = i%2 ? h-i : 1-(h-i);
  1088. var m = u * (1 - s);
  1089. var n = u * (1 - s * f);
  1090. switch (i) {
  1091. case 6:
  1092. case 0: return [u,n,m];
  1093. case 1: return [n,u,m];
  1094. case 2: return [m,u,n];
  1095. case 3: return [m,n,u];
  1096. case 4: return [n,m,u];
  1097. case 5: return [u,m,n];
  1098. }
  1099. }
  1100. function detachPicker () {
  1101. jsc.unsetClass(THIS.targetElement, THIS.activeClass);
  1102. jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap);
  1103. delete jsc.picker.owner;
  1104. }
  1105. function drawPicker () {
  1106. // At this point, when drawing the picker, we know what the parent elements are
  1107. // and we can do all related DOM operations, such as registering events on them
  1108. // or checking their positioning
  1109. THIS._processParentElementsInDOM();
  1110. if (!jsc.picker) {
  1111. jsc.picker = {
  1112. owner: null,
  1113. wrap : document.createElement('div'),
  1114. box : document.createElement('div'),
  1115. boxS : document.createElement('div'), // shadow area
  1116. boxB : document.createElement('div'), // border
  1117. pad : document.createElement('div'),
  1118. padB : document.createElement('div'), // border
  1119. padM : document.createElement('div'), // mouse/touch area
  1120. padPal : jsc.createPalette(),
  1121. cross : document.createElement('div'),
  1122. crossBY : document.createElement('div'), // border Y
  1123. crossBX : document.createElement('div'), // border X
  1124. crossLY : document.createElement('div'), // line Y
  1125. crossLX : document.createElement('div'), // line X
  1126. sld : document.createElement('div'),
  1127. sldB : document.createElement('div'), // border
  1128. sldM : document.createElement('div'), // mouse/touch area
  1129. sldGrad : jsc.createSliderGradient(),
  1130. sldPtrS : document.createElement('div'), // slider pointer spacer
  1131. sldPtrIB : document.createElement('div'), // slider pointer inner border
  1132. sldPtrMB : document.createElement('div'), // slider pointer middle border
  1133. sldPtrOB : document.createElement('div'), // slider pointer outer border
  1134. btn : document.createElement('div'),
  1135. btnT : document.createElement('span') // text
  1136. };
  1137. jsc.picker.pad.appendChild(jsc.picker.padPal.elm);
  1138. jsc.picker.padB.appendChild(jsc.picker.pad);
  1139. jsc.picker.cross.appendChild(jsc.picker.crossBY);
  1140. jsc.picker.cross.appendChild(jsc.picker.crossBX);
  1141. jsc.picker.cross.appendChild(jsc.picker.crossLY);
  1142. jsc.picker.cross.appendChild(jsc.picker.crossLX);
  1143. jsc.picker.padB.appendChild(jsc.picker.cross);
  1144. jsc.picker.box.appendChild(jsc.picker.padB);
  1145. jsc.picker.box.appendChild(jsc.picker.padM);
  1146. jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm);
  1147. jsc.picker.sldB.appendChild(jsc.picker.sld);
  1148. jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB);
  1149. jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB);
  1150. jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB);
  1151. jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS);
  1152. jsc.picker.box.appendChild(jsc.picker.sldB);
  1153. jsc.picker.box.appendChild(jsc.picker.sldM);
  1154. jsc.picker.btn.appendChild(jsc.picker.btnT);
  1155. jsc.picker.box.appendChild(jsc.picker.btn);
  1156. jsc.picker.boxB.appendChild(jsc.picker.box);
  1157. jsc.picker.wrap.appendChild(jsc.picker.boxS);
  1158. jsc.picker.wrap.appendChild(jsc.picker.boxB);
  1159. }
  1160. var p = jsc.picker;
  1161. var displaySlider = !!jsc.getSliderComponent(THIS);
  1162. var dims = jsc.getPickerDims(THIS);
  1163. var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize);
  1164. var padToSliderPadding = jsc.getPadToSliderPadding(THIS);
  1165. var borderRadius = Math.min(
  1166. THIS.borderRadius,
  1167. Math.round(THIS.padding * Math.PI)); // px
  1168. var padCursor = 'crosshair';
  1169. // wrap
  1170. p.wrap.style.clear = 'both';
  1171. p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px';
  1172. p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px';
  1173. p.wrap.style.zIndex = THIS.zIndex;
  1174. // picker
  1175. p.box.style.width = dims[0] + 'px';
  1176. p.box.style.height = dims[1] + 'px';
  1177. p.boxS.style.position = 'absolute';
  1178. p.boxS.style.left = '0';
  1179. p.boxS.style.top = '0';
  1180. p.boxS.style.width = '100%';
  1181. p.boxS.style.height = '100%';
  1182. jsc.setBorderRadius(p.boxS, borderRadius + 'px');
  1183. // picker border
  1184. p.boxB.style.position = 'relative';
  1185. p.boxB.style.border = THIS.borderWidth + 'px solid';
  1186. p.boxB.style.borderColor = THIS.borderColor;
  1187. p.boxB.style.background = THIS.backgroundColor;
  1188. jsc.setBorderRadius(p.boxB, borderRadius + 'px');
  1189. // IE hack:
  1190. // If the element is transparent, IE will trigger the event on the elements under it,
  1191. // e.g. on Canvas or on elements with border
  1192. p.padM.style.background =
  1193. p.sldM.style.background =
  1194. '#FFF';
  1195. jsc.setStyle(p.padM, 'opacity', '0');
  1196. jsc.setStyle(p.sldM, 'opacity', '0');
  1197. // pad
  1198. p.pad.style.position = 'relative';
  1199. p.pad.style.width = THIS.width + 'px';
  1200. p.pad.style.height = THIS.height + 'px';
  1201. // pad palettes (HSV and HVS)
  1202. p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS));
  1203. // pad border
  1204. p.padB.style.position = 'absolute';
  1205. p.padB.style.left = THIS.padding + 'px';
  1206. p.padB.style.top = THIS.padding + 'px';
  1207. p.padB.style.border = THIS.insetWidth + 'px solid';
  1208. p.padB.style.borderColor = THIS.insetColor;
  1209. // pad mouse area
  1210. p.padM._jscInstance = THIS;
  1211. p.padM._jscControlName = 'pad';
  1212. p.padM.style.position = 'absolute';
  1213. p.padM.style.left = '0';
  1214. p.padM.style.top = '0';
  1215. p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px';
  1216. p.padM.style.height = dims[1] + 'px';
  1217. p.padM.style.cursor = padCursor;
  1218. // pad cross
  1219. p.cross.style.position = 'absolute';
  1220. p.cross.style.left =
  1221. p.cross.style.top =
  1222. '0';
  1223. p.cross.style.width =
  1224. p.cross.style.height =
  1225. crossOuterSize + 'px';
  1226. // pad cross border Y and X
  1227. p.crossBY.style.position =
  1228. p.crossBX.style.position =
  1229. 'absolute';
  1230. p.crossBY.style.background =
  1231. p.crossBX.style.background =
  1232. THIS.pointerBorderColor;
  1233. p.crossBY.style.width =
  1234. p.crossBX.style.height =
  1235. (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px';
  1236. p.crossBY.style.height =
  1237. p.crossBX.style.width =
  1238. crossOuterSize + 'px';
  1239. p.crossBY.style.left =
  1240. p.crossBX.style.top =
  1241. (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px';
  1242. p.crossBY.style.top =
  1243. p.crossBX.style.left =
  1244. '0';
  1245. // pad cross line Y and X
  1246. p.crossLY.style.position =
  1247. p.crossLX.style.position =
  1248. 'absolute';
  1249. p.crossLY.style.background =
  1250. p.crossLX.style.background =
  1251. THIS.pointerColor;
  1252. p.crossLY.style.height =
  1253. p.crossLX.style.width =
  1254. (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px';
  1255. p.crossLY.style.width =
  1256. p.crossLX.style.height =
  1257. THIS.pointerThickness + 'px';
  1258. p.crossLY.style.left =
  1259. p.crossLX.style.top =
  1260. (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px';
  1261. p.crossLY.style.top =
  1262. p.crossLX.style.left =
  1263. THIS.pointerBorderWidth + 'px';
  1264. // slider
  1265. p.sld.style.overflow = 'hidden';
  1266. p.sld.style.width = THIS.sliderSize + 'px';
  1267. p.sld.style.height = THIS.height + 'px';
  1268. // slider gradient
  1269. p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000');
  1270. // slider border
  1271. p.sldB.style.display = displaySlider ? 'block' : 'none';
  1272. p.sldB.style.position = 'absolute';
  1273. p.sldB.style.right = THIS.padding + 'px';
  1274. p.sldB.style.top = THIS.padding + 'px';
  1275. p.sldB.style.border = THIS.insetWidth + 'px solid';
  1276. p.sldB.style.borderColor = THIS.insetColor;
  1277. // slider mouse area
  1278. p.sldM._jscInstance = THIS;
  1279. p.sldM._jscControlName = 'sld';
  1280. p.sldM.style.display = displaySlider ? 'block' : 'none';
  1281. p.sldM.style.position = 'absolute';
  1282. p.sldM.style.right = '0';
  1283. p.sldM.style.top = '0';
  1284. p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px';
  1285. p.sldM.style.height = dims[1] + 'px';
  1286. p.sldM.style.cursor = 'default';
  1287. // slider pointer inner and outer border
  1288. p.sldPtrIB.style.border =
  1289. p.sldPtrOB.style.border =
  1290. THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor;
  1291. // slider pointer outer border
  1292. p.sldPtrOB.style.position = 'absolute';
  1293. p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px';
  1294. p.sldPtrOB.style.top = '0';
  1295. // slider pointer middle border
  1296. p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor;
  1297. // slider pointer spacer
  1298. p.sldPtrS.style.width = THIS.sliderSize + 'px';
  1299. p.sldPtrS.style.height = sliderPtrSpace + 'px';
  1300. // the Close button
  1301. function setBtnBorder () {
  1302. var insetColors = THIS.insetColor.split(/\s+/);
  1303. var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1];
  1304. p.btn.style.borderColor = outsetColor;
  1305. }
  1306. p.btn.style.display = THIS.closable ? 'block' : 'none';
  1307. p.btn.style.position = 'absolute';
  1308. p.btn.style.left = THIS.padding + 'px';
  1309. p.btn.style.bottom = THIS.padding + 'px';
  1310. p.btn.style.padding = '0 15px';
  1311. p.btn.style.height = THIS.buttonHeight + 'px';
  1312. p.btn.style.border = THIS.insetWidth + 'px solid';
  1313. setBtnBorder();
  1314. p.btn.style.color = THIS.buttonColor;
  1315. p.btn.style.font = '12px sans-serif';
  1316. p.btn.style.textAlign = 'center';
  1317. try {
  1318. p.btn.style.cursor = 'pointer';
  1319. } catch(eOldIE) {
  1320. p.btn.style.cursor = 'hand';
  1321. }
  1322. p.btn.onmousedown = function () {
  1323. THIS.hide();
  1324. };
  1325. p.btnT.style.lineHeight = THIS.buttonHeight + 'px';
  1326. p.btnT.innerHTML = '';
  1327. p.btnT.appendChild(document.createTextNode(THIS.closeText));
  1328. // place pointers
  1329. redrawPad();
  1330. redrawSld();
  1331. // If we are changing the owner without first closing the picker,
  1332. // make sure to first deal with the old owner
  1333. if (jsc.picker.owner && jsc.picker.owner !== THIS) {
  1334. jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass);
  1335. }
  1336. // Set the new picker owner
  1337. jsc.picker.owner = THIS;
  1338. // The redrawPosition() method needs picker.owner to be set, that's why we call it here,
  1339. // after setting the owner
  1340. if (jsc.isElementType(container, 'body')) {
  1341. jsc.redrawPosition();
  1342. } else {
  1343. jsc._drawPosition(THIS, 0, 0, 'relative', false);
  1344. }
  1345. if (p.wrap.parentNode != container) {
  1346. container.appendChild(p.wrap);
  1347. }
  1348. jsc.setClass(THIS.targetElement, THIS.activeClass);
  1349. }
  1350. function redrawPad () {
  1351. // redraw the pad pointer
  1352. switch (jsc.getPadYComponent(THIS)) {
  1353. case 's': var yComponent = 1; break;
  1354. case 'v': var yComponent = 2; break;
  1355. }
  1356. var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1));
  1357. var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1));
  1358. var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize);
  1359. var ofs = -Math.floor(crossOuterSize / 2);
  1360. jsc.picker.cross.style.left = (x + ofs) + 'px';
  1361. jsc.picker.cross.style.top = (y + ofs) + 'px';
  1362. // redraw the slider
  1363. switch (jsc.getSliderComponent(THIS)) {
  1364. case 's':
  1365. var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]);
  1366. var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]);
  1367. var color1 = 'rgb(' +
  1368. Math.round(rgb1[0]) + ',' +
  1369. Math.round(rgb1[1]) + ',' +
  1370. Math.round(rgb1[2]) + ')';
  1371. var color2 = 'rgb(' +
  1372. Math.round(rgb2[0]) + ',' +
  1373. Math.round(rgb2[1]) + ',' +
  1374. Math.round(rgb2[2]) + ')';
  1375. jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2);
  1376. break;
  1377. case 'v':
  1378. var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100);
  1379. var color1 = 'rgb(' +
  1380. Math.round(rgb[0]) + ',' +
  1381. Math.round(rgb[1]) + ',' +
  1382. Math.round(rgb[2]) + ')';
  1383. var color2 = '#000';
  1384. jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2);
  1385. break;
  1386. }
  1387. }
  1388. function redrawSld () {
  1389. var sldComponent = jsc.getSliderComponent(THIS);
  1390. if (sldComponent) {
  1391. // redraw the slider pointer
  1392. switch (sldComponent) {
  1393. case 's': var yComponent = 1; break;
  1394. case 'v': var yComponent = 2; break;
  1395. }
  1396. var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1));
  1397. jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px';
  1398. }
  1399. }
  1400. function isPickerOwner () {
  1401. return jsc.picker && jsc.picker.owner === THIS;
  1402. }
  1403. function blurValue () {
  1404. THIS.importColor();
  1405. }
  1406. // Find the target element
  1407. if (typeof targetElement === 'string') {
  1408. var id = targetElement;
  1409. var elm = document.getElementById(id);
  1410. if (elm) {
  1411. this.targetElement = elm;
  1412. } else {
  1413. jsc.warn('Could not find target element with ID \'' + id + '\'');
  1414. }
  1415. } else if (targetElement) {
  1416. this.targetElement = targetElement;
  1417. } else {
  1418. jsc.warn('Invalid target element: \'' + targetElement + '\'');
  1419. }
  1420. if (this.targetElement._jscLinkedInstance) {
  1421. jsc.warn('Cannot link jscolor twice to the same element. Skipping.');
  1422. return;
  1423. }
  1424. this.targetElement._jscLinkedInstance = this;
  1425. // Find the value element
  1426. this.valueElement = jsc.fetchElement(this.valueElement);
  1427. // Find the style element
  1428. this.styleElement = jsc.fetchElement(this.styleElement);
  1429. var THIS = this;
  1430. var container =
  1431. this.container ?
  1432. jsc.fetchElement(this.container) :
  1433. document.getElementsByTagName('body')[0];
  1434. var sliderPtrSpace = 3; // px
  1435. // For BUTTON elements it's important to stop them from sending the form when clicked
  1436. // (e.g. in Safari)
  1437. if (jsc.isElementType(this.targetElement, 'button')) {
  1438. if (this.targetElement.onclick) {
  1439. var origCallback = this.targetElement.onclick;
  1440. this.targetElement.onclick = function (evt) {
  1441. origCallback.call(this, evt);
  1442. return false;
  1443. };
  1444. } else {
  1445. this.targetElement.onclick = function () { return false; };
  1446. }
  1447. }
  1448. /*
  1449. var elm = this.targetElement;
  1450. do {
  1451. // If the target element or one of its offsetParents has fixed position,
  1452. // then use fixed positioning instead
  1453. //
  1454. // Note: In Firefox, getComputedStyle returns null in a hidden iframe,
  1455. // that's why we need to check if the returned style object is non-empty
  1456. var currStyle = jsc.getStyle(elm);
  1457. if (currStyle && currStyle.position.toLowerCase() === 'fixed') {
  1458. this.fixed = true;
  1459. }
  1460. if (elm !== this.targetElement) {
  1461. // attach onParentScroll so that we can recompute the picker position
  1462. // when one of the offsetParents is scrolled
  1463. if (!elm._jscEventsAttached) {
  1464. jsc.attachEvent(elm, 'scroll', jsc.onParentScroll);
  1465. elm._jscEventsAttached = true;
  1466. }
  1467. }
  1468. } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body'));
  1469. */
  1470. // valueElement
  1471. if (this.valueElement) {
  1472. if (jsc.isElementType(this.valueElement, 'input')) {
  1473. var updateField = function () {
  1474. THIS.fromString(THIS.valueElement.value, jsc.leaveValue);
  1475. jsc.dispatchFineChange(THIS);
  1476. };
  1477. jsc.attachEvent(this.valueElement, 'keyup', updateField);
  1478. jsc.attachEvent(this.valueElement, 'input', updateField);
  1479. jsc.attachEvent(this.valueElement, 'blur', blurValue);
  1480. this.valueElement.setAttribute('autocomplete', 'off');
  1481. }
  1482. }
  1483. // styleElement
  1484. if (this.styleElement) {
  1485. this.styleElement._jscOrigStyle = {
  1486. backgroundImage : this.styleElement.style.backgroundImage,
  1487. backgroundColor : this.styleElement.style.backgroundColor,
  1488. color : this.styleElement.style.color
  1489. };
  1490. }
  1491. if (this.value) {
  1492. // Try to set the color from the .value option and if unsuccessful,
  1493. // export the current color
  1494. this.fromString(this.value) || this.exportColor();
  1495. } else {
  1496. this.importColor();
  1497. }
  1498. }
  1499. };
  1500. //================================
  1501. // Public properties and methods
  1502. //================================
  1503. // By default, search for all elements with class="jscolor" and install a color picker on them.
  1504. //
  1505. // You can change what class name will be looked for by setting the property jscolor.lookupClass
  1506. // anywhere in your HTML document. To completely disable the automatic lookup, set it to null.
  1507. //
  1508. jsc.jscolor.lookupClass = 'jscolor';
  1509. jsc.jscolor.installByClassName = function (className) {
  1510. var inputElms = document.getElementsByTagName('input');
  1511. var buttonElms = document.getElementsByTagName('button');
  1512. jsc.tryInstallOnElements(inputElms, className);
  1513. jsc.tryInstallOnElements(buttonElms, className);
  1514. };
  1515. jsc.register();
  1516. return jsc.jscolor;
  1517. })(); }