colpick.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /*
  2. colpick Color Picker / colpick.com
  3. Copyright 2013 Jose Vargas. Licensed under GPL license.
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. Based on Stefan Petre's Color Picker www.eyecon.ro, dual licensed under the MIT and GPL licenses
  15. */
  16. (function ($) {
  17. var colpick = function () {
  18. var
  19. tpl = '<div class="colpick"><div class="colpick_color"><div><div></div></div></div><div class="colpick_hue"><div></div></div><div class="colpick_new_color"></div><div class="colpick_current_color"></div><div class="colpick_hex_field"><div>#</div><input type="text" maxlength="6" size="6" /></div><div class="colpick_rgb_r colpick_field"><div>R</div><input type="text" maxlength="3" size="3" /><span></span></div><div class="colpick_rgb_g colpick_field"><div>G</div><input type="text" maxlength="3" size="3" /><span></span></div><div class="colpick_rgb_b colpick_field"><div>B</div><input type="text" maxlength="3" size="3" /><span></span></div><div class="colpick_hsb_h colpick_field"><div>H</div><input type="text" maxlength="3" size="3" /><span></span></div><div class="colpick_hsb_s colpick_field"><div>S</div><input type="text" maxlength="3" size="3" /><span></span></div><div class="colpick_hsb_b colpick_field"><div>B</div><input type="text" maxlength="3" size="3" /><span></span></div><div class="colpick_submit"></div></div>',
  20. defaults = {
  21. showEvent: 'click',
  22. onShow: function () {},
  23. onBeforeShow: function(){},
  24. onHide: function () {},
  25. onChange: function () {},
  26. onSubmit: function () {},
  27. colorScheme: 'light',
  28. color: '11ff00',
  29. livePreview: true,
  30. flat: false,
  31. layout: 'full',
  32. submit: 1,
  33. submitText: 'OK',
  34. height: 150
  35. },
  36. //Fill the inputs of the plugin
  37. fillRGBFields = function (hsb, cal) {
  38. var rgb = HSBToRGB(hsb);
  39. $(cal).data('colpick').fields
  40. .eq(1).val(rgb.r).end()
  41. .eq(2).val(rgb.g).end()
  42. .eq(3).val(rgb.b).end();
  43. },
  44. fillHSBFields = function (hsb, cal) {
  45. $(cal).data('colpick').fields
  46. .eq(4).val(Math.round(hsb.h*10)/10).end()
  47. .eq(5).val(Math.round(hsb.s*10)/10).end()
  48. .eq(6).val(Math.round(hsb.b*10)/10).end();
  49. },
  50. fillHexFields = function (hsb, cal) {
  51. $(cal).data('colpick').fields.eq(0).val(HSBToHex(hsb));
  52. },
  53. //Set the round selector position
  54. setSelector = function (hsb, cal) {
  55. $(cal).data('colpick').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100}));
  56. $(cal).data('colpick').selectorIndic.css({
  57. left: parseInt($(cal).data('colpick').height * hsb.s/100, 10),
  58. top: parseInt($(cal).data('colpick').height * (100-hsb.b)/100, 10)
  59. });
  60. },
  61. //Set the hue selector position
  62. setHue = function (hsb, cal) {
  63. $(cal).data('colpick').hue.css('top', parseInt($(cal).data('colpick').height - $(cal).data('colpick').height * hsb.h/360, 10));
  64. },
  65. //Set current and new colors
  66. setCurrentColor = function (hsb, cal) {
  67. $(cal).data('colpick').currentColor.css('backgroundColor', '#' + HSBToHex(hsb));
  68. },
  69. setNewColor = function (hsb, cal) {
  70. $(cal).data('colpick').newColor.css('backgroundColor', '#' + HSBToHex(hsb));
  71. },
  72. //Function called when the new color is changed
  73. change = function (ev) {
  74. var cal = $(this).parent().parent(), col;
  75. if (this.parentNode.className.indexOf('_hex') > 0) {
  76. cal.data('colpick').color = col = HexToHSB(fixHex(this.value));
  77. } else if (this.parentNode.className.indexOf('_hsb') > 0) {
  78. cal.data('colpick').color = col = fixHSB({
  79. h: parseInt(cal.data('colpick').fields.eq(4).val(), 10),
  80. s: parseInt(cal.data('colpick').fields.eq(5).val(), 10),
  81. b: parseInt(cal.data('colpick').fields.eq(6).val(), 10)
  82. });
  83. } else {
  84. cal.data('colpick').color = col = RGBToHSB(fixRGB({
  85. r: parseInt(cal.data('colpick').fields.eq(1).val(), 10),
  86. g: parseInt(cal.data('colpick').fields.eq(2).val(), 10),
  87. b: parseInt(cal.data('colpick').fields.eq(3).val(), 10)
  88. }));
  89. }
  90. if (ev) {
  91. fillRGBFields(col, cal.get(0));
  92. fillHexFields(col, cal.get(0));
  93. fillHSBFields(col, cal.get(0));
  94. }
  95. setSelector(col, cal.get(0));
  96. setHue(col, cal.get(0));
  97. setNewColor(col, cal.get(0));
  98. cal.data('colpick').onChange.apply(cal.parent(), [col, HSBToHex(col), HSBToRGB(col)]);
  99. },
  100. //Change style on blur and focus of inputs
  101. blur = function (ev) {
  102. $(this).parent().removeClass('colpick_focus');
  103. },
  104. focus = function () {
  105. $(this).parent().parent().data('colpick').fields.parent().removeClass('colpick_focus');
  106. $(this).parent().addClass('colpick_focus');
  107. },
  108. //Increment/decrement arrows next to the fields
  109. downIncrement = function (ev) {
  110. var field = $(this).parent().find('input').focus();
  111. var current = {
  112. el: $(this).parent().addClass('colpick_slider'),
  113. max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255),
  114. y: ev.pageY,
  115. field: field,
  116. val: parseInt(field.val(), 10),
  117. preview: $(this).parent().parent().data('colpick').livePreview
  118. };
  119. $(document).mouseup(current, upIncrement);
  120. $(document).mousemove(current, moveIncrement);
  121. },
  122. moveIncrement = function (ev) {
  123. ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val - ev.pageY + ev.data.y, 10))));
  124. if (ev.data.preview) {
  125. change.apply(ev.data.field.get(0), [true]);
  126. }
  127. return false;
  128. },
  129. upIncrement = function (ev) {
  130. change.apply(ev.data.field.get(0), [true]);
  131. ev.data.el.removeClass('colpick_slider').find('input').focus();
  132. $(document).off('mouseup', upIncrement);
  133. $(document).off('mousemove', moveIncrement);
  134. return false;
  135. },
  136. //Hue slider functions
  137. downHue = function (ev) {
  138. var current = {
  139. cal: $(this).parent(),
  140. y: $(this).offset().top
  141. };
  142. current.preview = current.cal.data('colpick').livePreview;
  143. $(document).mouseup(current, upHue);
  144. $(document).mousemove(current, moveHue);
  145. change.apply(
  146. current.cal.data('colpick')
  147. .fields.eq(4).val(parseInt(360*(current.cal.data('colpick').height - (ev.pageY - current.y))/current.cal.data('colpick').height, 10))
  148. .get(0),
  149. [current.preview]
  150. );
  151. },
  152. moveHue = function (ev) {
  153. change.apply(
  154. ev.data.cal.data('colpick')
  155. .fields.eq(4).val(parseInt(360*(ev.data.cal.data('colpick').height - Math.max(0,Math.min(ev.data.cal.data('colpick').height,(ev.pageY - ev.data.y))))/ev.data.cal.data('colpick').height, 10))
  156. .get(0),
  157. [ev.data.preview]
  158. );
  159. return false;
  160. },
  161. upHue = function (ev) {
  162. fillRGBFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
  163. fillHexFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
  164. $(document).off('mouseup', upHue);
  165. $(document).off('mousemove', moveHue);
  166. return false;
  167. },
  168. //Color selector functions
  169. downSelector = function (ev) {
  170. ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
  171. //Get colpick and position of the selector
  172. var current = {
  173. cal: $(this).parent(),
  174. pos: $(this).offset()
  175. };
  176. current.preview = current.cal.data('colpick').livePreview;
  177. $(document).mouseup(current, upSelector);
  178. $(document).mousemove(current, moveSelector);
  179. change.apply(
  180. current.cal.data('colpick').fields
  181. .eq(6).val(parseInt(100*(current.cal.data('colpick').height - (ev.pageY - current.pos.top))/current.cal.data('colpick').height, 10)).end()
  182. .eq(5).val(parseInt(100*(ev.pageX - current.pos.left)/current.cal.data('colpick').height, 10))
  183. .get(0),
  184. [current.preview]
  185. );
  186. },
  187. moveSelector = function (ev) {
  188. change.apply(
  189. ev.data.cal.data('colpick').fields
  190. .eq(6).val(parseInt(100*(ev.data.cal.data('colpick').height - Math.max(0,Math.min(ev.data.cal.data('colpick').height,(ev.pageY - ev.data.pos.top))))/ev.data.cal.data('colpick').height, 10)).end()
  191. .eq(5).val(parseInt(100*(Math.max(0,Math.min(ev.data.cal.data('colpick').height,(ev.pageX - ev.data.pos.left))))/ev.data.cal.data('colpick').height, 10))
  192. .get(0),
  193. [ev.data.preview]
  194. );
  195. return false;
  196. },
  197. upSelector = function (ev) {
  198. fillRGBFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
  199. fillHexFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
  200. $(document).off('mouseup', upSelector);
  201. $(document).off('mousemove', moveSelector);
  202. return false;
  203. },
  204. //Submit button
  205. clickSubmit = function (ev) {
  206. var cal = $(this).parent();
  207. var col = cal.data('colpick').color;
  208. cal.data('colpick').origColor = col;
  209. setCurrentColor(col, cal.get(0));
  210. cal.data('colpick').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colpick').el);
  211. },
  212. //Show/hide the color picker
  213. show = function (ev) {
  214. var cal = $('#' + $(this).data('colpickId'));
  215. cal.data('colpick').onBeforeShow.apply(this, [cal.get(0)]);
  216. var pos = $(this).offset();
  217. var top = pos.top + this.offsetHeight;
  218. var left = pos.left;
  219. var viewPort = getViewport();
  220. if (left + 346 > viewPort.l + viewPort.w) {
  221. left -= 346;
  222. }
  223. cal.css({left: left + 'px', top: top + 'px'});
  224. if (cal.data('colpick').onShow.apply(this, [cal.get(0)]) != false) {
  225. cal.show();
  226. }
  227. //Hide when user clicks outside
  228. $('html').mousedown({cal:cal}, hide);
  229. cal.mousedown(function(ev){ev.stopPropagation();})
  230. return false;
  231. },
  232. hide = function (ev) {
  233. if (ev.data.cal.data('colpick').onHide.apply(this, [ev.data.cal.get(0)]) != false) {
  234. ev.data.cal.hide();
  235. }
  236. $('html').off('mousedown', hide);
  237. return false;
  238. },
  239. getViewport = function () {
  240. var m = document.compatMode == 'CSS1Compat';
  241. return {
  242. l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft),
  243. w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth),
  244. };
  245. },
  246. //Fix the values if the user enters a negative or high value
  247. fixHSB = function (hsb) {
  248. return {
  249. h: Math.min(360, Math.max(0, hsb.h)),
  250. s: Math.min(100, Math.max(0, hsb.s)),
  251. b: Math.min(100, Math.max(0, hsb.b))
  252. };
  253. },
  254. fixRGB = function (rgb) {
  255. return {
  256. r: Math.min(255, Math.max(0, rgb.r)),
  257. g: Math.min(255, Math.max(0, rgb.g)),
  258. b: Math.min(255, Math.max(0, rgb.b))
  259. };
  260. },
  261. fixHex = function (hex) {
  262. var len = 6 - hex.length;
  263. if (len > 0) {
  264. var o = [];
  265. for (var i=0; i<len; i++) {
  266. o.push('0');
  267. }
  268. o.push(hex);
  269. hex = o.join('');
  270. }
  271. return hex;
  272. },
  273. //Color space convertion
  274. HexToRGB = function (hex) {
  275. var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
  276. return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)};
  277. },
  278. HexToHSB = function (hex) {
  279. return RGBToHSB(HexToRGB(hex));
  280. },
  281. RGBToHSB = function (rgb) {
  282. var hsb = {
  283. h: 0,
  284. s: 0,
  285. b: 0
  286. };
  287. var min = Math.min(rgb.r, rgb.g, rgb.b);
  288. var max = Math.max(rgb.r, rgb.g, rgb.b);
  289. var delta = max - min;
  290. hsb.b = max;
  291. if (max != 0) {
  292. }
  293. hsb.s = max != 0 ? 255 * delta / max : 0;
  294. if (hsb.s != 0) {
  295. if (rgb.r == max) {
  296. hsb.h = (rgb.g - rgb.b) / delta;
  297. } else if (rgb.g == max) {
  298. hsb.h = 2 + (rgb.b - rgb.r) / delta;
  299. } else {
  300. hsb.h = 4 + (rgb.r - rgb.g) / delta;
  301. }
  302. } else {
  303. hsb.h = -1;
  304. }
  305. hsb.h *= 60;
  306. if (hsb.h < 0) {
  307. hsb.h += 360;
  308. }
  309. hsb.s *= 100/255;
  310. hsb.b *= 100/255;
  311. return hsb;
  312. },
  313. HSBToRGB = function (hsb) {
  314. var rgb = {};
  315. var h = Math.round(hsb.h);
  316. var s = Math.round(hsb.s*255/100);
  317. var v = Math.round(hsb.b*255/100);
  318. if(s == 0) {
  319. rgb.r = rgb.g = rgb.b = v;
  320. } else {
  321. var t1 = v;
  322. var t2 = (255-s)*v/255;
  323. var t3 = (t1-t2)*(h%60)/60;
  324. if(h==360) h = 0;
  325. if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3}
  326. else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3}
  327. else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3}
  328. else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3}
  329. else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3}
  330. else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3}
  331. else {rgb.r=0; rgb.g=0; rgb.b=0}
  332. }
  333. return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)};
  334. },
  335. RGBToHex = function (rgb) {
  336. var hex = [
  337. rgb.r.toString(16),
  338. rgb.g.toString(16),
  339. rgb.b.toString(16)
  340. ];
  341. $.each(hex, function (nr, val) {
  342. if (val.length == 1) {
  343. hex[nr] = '0' + val;
  344. }
  345. });
  346. return hex.join('');
  347. },
  348. HSBToHex = function (hsb) {
  349. return RGBToHex(HSBToRGB(hsb));
  350. },
  351. restoreOriginal = function () {
  352. var cal = $(this).parent();
  353. var col = cal.data('colpick').origColor;
  354. cal.data('colpick').color = col;
  355. fillRGBFields(col, cal.get(0));
  356. fillHexFields(col, cal.get(0));
  357. fillHSBFields(col, cal.get(0));
  358. setSelector(col, cal.get(0));
  359. setHue(col, cal.get(0));
  360. setNewColor(col, cal.get(0));
  361. };
  362. return {
  363. init: function (opt) {
  364. opt = $.extend({}, defaults, opt||{});
  365. //Set color
  366. if (typeof opt.color == 'string') {
  367. opt.color = HexToHSB(opt.color);
  368. } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) {
  369. opt.color = RGBToHSB(opt.color);
  370. } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) {
  371. opt.color = fixHSB(opt.color);
  372. } else {
  373. return this;
  374. }
  375. //For each selected DOM element
  376. return this.each(function () {
  377. //If the element does not have an ID
  378. if (!$(this).data('colpickId')) {
  379. var options = $.extend({}, opt);
  380. options.origColor = opt.color;
  381. //Generate and assign a random ID
  382. var id = 'collorpicker_' + parseInt(Math.random() * 1000);
  383. $(this).data('colpickId', id);
  384. //Set the tpl's ID and get the HTML
  385. var cal = $(tpl).attr('id', id);
  386. //Add class according to layout
  387. cal.addClass('colpick_'+options.layout+(options.submit?'':' colpick_'+options.layout+'_ns'));
  388. //Add class if the color scheme is not dark(default)
  389. if(options.colorScheme != 'light') {
  390. cal.addClass('colpick_'+options.colorScheme);
  391. }
  392. //Setup submit button
  393. cal.find('div.colpick_submit').html(options.submitText).click(clickSubmit);
  394. //Setup input fields
  395. options.fields = cal.find('input').change(change).blur(blur).focus(focus);
  396. cal.find('span').mousedown(downIncrement).end().find('>div.colpick_current_color').click(restoreOriginal);
  397. //Setup hue selector
  398. options.selector = cal.find('div.colpick_color').mousedown(downSelector);
  399. options.selectorIndic = options.selector.find('div div');
  400. //Store parts of the plugin
  401. options.el = this;
  402. options.hue = cal.find('div.colpick_hue div');
  403. cal.find('div.colpick_hue').mousedown(downHue);
  404. options.newColor = cal.find('div.colpick_new_color');
  405. options.currentColor = cal.find('div.colpick_current_color');
  406. //Store options and fill with default color
  407. cal.data('colpick', options);
  408. fillRGBFields(options.color, cal.get(0));
  409. fillHSBFields(options.color, cal.get(0));
  410. fillHexFields(options.color, cal.get(0));
  411. setHue(options.color, cal.get(0));
  412. setSelector(options.color, cal.get(0));
  413. setCurrentColor(options.color, cal.get(0));
  414. setNewColor(options.color, cal.get(0));
  415. //Append to the body if flat=false, else show in place
  416. if (options.flat) {
  417. cal.appendTo(this).show();
  418. cal.css({
  419. position: 'relative',
  420. display: 'block'
  421. });
  422. } else {
  423. cal.appendTo(document.body);
  424. $(this).on(options.showEvent, show);
  425. cal.css({
  426. position:'absolute'
  427. });
  428. }
  429. }
  430. });
  431. },
  432. //colpickShow
  433. showPicker: function() {
  434. return this.each( function () {
  435. if ($(this).data('colpickId')) {
  436. show.apply(this);
  437. }
  438. });
  439. },
  440. //colpickHide
  441. hidePicker: function() {
  442. return this.each( function () {
  443. if ($(this).data('colpickId')) {
  444. $('#' + $(this).data('colpickId')).hide();
  445. }
  446. });
  447. },
  448. //colpickSetColor
  449. setColor: function(col) {
  450. if (typeof col == 'string') {
  451. col = HexToHSB(col);
  452. } else if (col.r != undefined && col.g != undefined && col.b != undefined) {
  453. col = RGBToHSB(col);
  454. } else if (col.h != undefined && col.s != undefined && col.b != undefined) {
  455. col = fixHSB(col);
  456. } else {
  457. return this;
  458. }
  459. return this.each(function(){
  460. if ($(this).data('colpickId')) {
  461. var cal = $('#' + $(this).data('colpickId'));
  462. cal.data('colpick').color = col;
  463. cal.data('colpick').origColor = col;
  464. fillRGBFields(col, cal.get(0));
  465. fillHSBFields(col, cal.get(0));
  466. fillHexFields(col, cal.get(0));
  467. setHue(col, cal.get(0));
  468. setSelector(col, cal.get(0));
  469. setCurrentColor(col, cal.get(0));
  470. setNewColor(col, cal.get(0));
  471. }
  472. });
  473. }
  474. };
  475. }();
  476. $.fn.extend({
  477. colpick: colpick.init,
  478. colpickHide: colpick.hidePicker,
  479. colpickShow: colpick.showPicker,
  480. colpickSetColor: colpick.setColor
  481. });
  482. })(jQuery)