Colorpicker.js

  1. 'use strict';
  2. import Extension from './Extension';
  3. import defaults from './options';
  4. import coreExtensions from 'extensions';
  5. import $ from 'jquery';
  6. import SliderHandler from './SliderHandler';
  7. import PopupHandler from './PopupHandler';
  8. import InputHandler from './InputHandler';
  9. import ColorHandler from './ColorHandler';
  10. import PickerHandler from './PickerHandler';
  11. import AddonHandler from './AddonHandler';
  12. import ColorItem from './ColorItem';
  13. let colorPickerIdCounter = 0;
  14. let root = (typeof self !== 'undefined' ? self : this); // window
  15. /**
  16. * Colorpicker widget class
  17. */
  18. class Colorpicker {
  19. /**
  20. * Color class
  21. *
  22. * @static
  23. * @type {Color}
  24. */
  25. static get Color() {
  26. return ColorItem;
  27. }
  28. /**
  29. * Extension class
  30. *
  31. * @static
  32. * @type {Extension}
  33. */
  34. static get Extension() {
  35. return Extension;
  36. }
  37. /**
  38. * Internal color object
  39. *
  40. * @type {Color|null}
  41. */
  42. get color() {
  43. return this.colorHandler.color;
  44. }
  45. /**
  46. * Internal color format
  47. *
  48. * @type {String|null}
  49. */
  50. get format() {
  51. return this.colorHandler.format;
  52. }
  53. /**
  54. * Getter of the picker element
  55. *
  56. * @returns {jQuery|HTMLElement}
  57. */
  58. get picker() {
  59. return this.pickerHandler.picker;
  60. }
  61. /**
  62. * @fires Colorpicker#colorpickerCreate
  63. * @param {Object|String} element
  64. * @param {Object} options
  65. * @constructor
  66. */
  67. constructor(element, options) {
  68. colorPickerIdCounter += 1;
  69. /**
  70. * The colorpicker instance number
  71. * @type {number}
  72. */
  73. this.id = colorPickerIdCounter;
  74. /**
  75. * Latest colorpicker event
  76. *
  77. * @type {{name: String, e: *}}
  78. */
  79. this.lastEvent = {
  80. alias: null,
  81. e: null
  82. };
  83. /**
  84. * The element that the colorpicker is bound to
  85. *
  86. * @type {*|jQuery}
  87. */
  88. this.element = $(element)
  89. .addClass('colorpicker-element')
  90. .attr('data-colorpicker-id', this.id);
  91. /**
  92. * @type {defaults}
  93. */
  94. this.options = $.extend(true, {}, defaults, options, this.element.data());
  95. /**
  96. * @type {boolean}
  97. * @private
  98. */
  99. this.disabled = false;
  100. /**
  101. * Extensions added to this instance
  102. *
  103. * @type {Extension[]}
  104. */
  105. this.extensions = [];
  106. /**
  107. * The element where the
  108. * @type {*|jQuery}
  109. */
  110. this.container = (
  111. this.options.container === true ||
  112. (this.options.container !== true && this.options.inline === true)
  113. ) ? this.element : this.options.container;
  114. this.container = (this.container !== false) ? $(this.container) : false;
  115. /**
  116. * @type {InputHandler}
  117. */
  118. this.inputHandler = new InputHandler(this);
  119. /**
  120. * @type {ColorHandler}
  121. */
  122. this.colorHandler = new ColorHandler(this);
  123. /**
  124. * @type {SliderHandler}
  125. */
  126. this.sliderHandler = new SliderHandler(this);
  127. /**
  128. * @type {PopupHandler}
  129. */
  130. this.popupHandler = new PopupHandler(this, root);
  131. /**
  132. * @type {PickerHandler}
  133. */
  134. this.pickerHandler = new PickerHandler(this);
  135. /**
  136. * @type {AddonHandler}
  137. */
  138. this.addonHandler = new AddonHandler(this);
  139. this.init();
  140. // Emit a create event
  141. $($.proxy(function () {
  142. /**
  143. * (Colorpicker) When the Colorpicker instance has been created and the DOM is ready.
  144. *
  145. * @event Colorpicker#colorpickerCreate
  146. */
  147. this.trigger('colorpickerCreate');
  148. }, this));
  149. }
  150. /**
  151. * Initializes the plugin
  152. * @private
  153. */
  154. init() {
  155. // Init addon
  156. this.addonHandler.bind();
  157. // Init input
  158. this.inputHandler.bind();
  159. // Init extensions (before initializing the color)
  160. this.initExtensions();
  161. // Init color
  162. this.colorHandler.bind();
  163. // Init picker
  164. this.pickerHandler.bind();
  165. // Init sliders and popup
  166. this.sliderHandler.bind();
  167. this.popupHandler.bind();
  168. // Inject into the DOM (this may make it visible)
  169. this.pickerHandler.attach();
  170. // Update all components
  171. this.update();
  172. if (this.inputHandler.isDisabled()) {
  173. this.disable();
  174. }
  175. }
  176. /**
  177. * Initializes the plugin extensions
  178. * @private
  179. */
  180. initExtensions() {
  181. if (!Array.isArray(this.options.extensions)) {
  182. this.options.extensions = [];
  183. }
  184. if (this.options.debug) {
  185. this.options.extensions.push({name: 'debugger'});
  186. }
  187. // Register and instantiate extensions
  188. this.options.extensions.forEach((ext) => {
  189. this.registerExtension(Colorpicker.extensions[ext.name.toLowerCase()], ext.options || {});
  190. });
  191. }
  192. /**
  193. * Creates and registers the given extension
  194. *
  195. * @param {Extension} ExtensionClass The extension class to instantiate
  196. * @param {Object} [config] Extension configuration
  197. * @returns {Extension}
  198. */
  199. registerExtension(ExtensionClass, config = {}) {
  200. let ext = new ExtensionClass(this, config);
  201. this.extensions.push(ext);
  202. return ext;
  203. }
  204. /**
  205. * Destroys the current instance
  206. *
  207. * @fires Colorpicker#colorpickerDestroy
  208. */
  209. destroy() {
  210. let color = this.color;
  211. this.sliderHandler.unbind();
  212. this.inputHandler.unbind();
  213. this.popupHandler.unbind();
  214. this.colorHandler.unbind();
  215. this.addonHandler.unbind();
  216. this.pickerHandler.unbind();
  217. this.element
  218. .removeClass('colorpicker-element')
  219. .removeData('colorpicker')
  220. .removeData('color')
  221. .off('.colorpicker');
  222. /**
  223. * (Colorpicker) When the instance is destroyed with all events unbound.
  224. *
  225. * @event Colorpicker#colorpickerDestroy
  226. */
  227. this.trigger('colorpickerDestroy', color);
  228. }
  229. /**
  230. * Shows the colorpicker widget if hidden.
  231. * If the colorpicker is disabled this call will be ignored.
  232. *
  233. * @fires Colorpicker#colorpickerShow
  234. * @param {Event} [e]
  235. */
  236. show(e) {
  237. this.popupHandler.show(e);
  238. }
  239. /**
  240. * Hides the colorpicker widget.
  241. *
  242. * @fires Colorpicker#colorpickerHide
  243. * @param {Event} [e]
  244. */
  245. hide(e) {
  246. this.popupHandler.hide(e);
  247. }
  248. /**
  249. * Toggles the colorpicker between visible and hidden.
  250. *
  251. * @fires Colorpicker#colorpickerShow
  252. * @fires Colorpicker#colorpickerHide
  253. * @param {Event} [e]
  254. */
  255. toggle(e) {
  256. this.popupHandler.toggle(e);
  257. }
  258. /**
  259. * Returns the current color value as string
  260. *
  261. * @param {String|*} [defaultValue]
  262. * @returns {String|*}
  263. */
  264. getValue(defaultValue = null) {
  265. let val = this.colorHandler.color;
  266. val = (val instanceof ColorItem) ? val : defaultValue;
  267. if (val instanceof ColorItem) {
  268. return val.string(this.format);
  269. }
  270. return val;
  271. }
  272. /**
  273. * Sets the color manually
  274. *
  275. * @fires Colorpicker#colorpickerChange
  276. * @param {String|Color} val
  277. */
  278. setValue(val) {
  279. if (this.isDisabled()) {
  280. return;
  281. }
  282. let ch = this.colorHandler;
  283. if (
  284. (ch.hasColor() && !!val && ch.color.equals(val)) ||
  285. (!ch.hasColor() && !val)
  286. ) {
  287. // same color or still empty
  288. return;
  289. }
  290. ch.color = val ? ch.createColor(val, this.options.autoInputFallback, this.options.autoHexInputFallback) : null;
  291. /**
  292. * (Colorpicker) When the color is set programmatically with setValue().
  293. *
  294. * @event Colorpicker#colorpickerChange
  295. */
  296. this.trigger('colorpickerChange', ch.color, val);
  297. // force update if color has changed to empty
  298. this.update();
  299. }
  300. /**
  301. * Updates the UI and the input color according to the internal color.
  302. *
  303. * @fires Colorpicker#colorpickerUpdate
  304. */
  305. update() {
  306. if (this.colorHandler.hasColor()) {
  307. this.inputHandler.update();
  308. } else {
  309. this.colorHandler.assureColor();
  310. }
  311. this.addonHandler.update();
  312. this.pickerHandler.update();
  313. /**
  314. * (Colorpicker) Fired when the widget is updated.
  315. *
  316. * @event Colorpicker#colorpickerUpdate
  317. */
  318. this.trigger('colorpickerUpdate');
  319. }
  320. /**
  321. * Enables the widget and the input if any
  322. *
  323. * @fires Colorpicker#colorpickerEnable
  324. * @returns {boolean}
  325. */
  326. enable() {
  327. this.inputHandler.enable();
  328. this.disabled = false;
  329. this.picker.removeClass('colorpicker-disabled');
  330. /**
  331. * (Colorpicker) When the widget has been enabled.
  332. *
  333. * @event Colorpicker#colorpickerEnable
  334. */
  335. this.trigger('colorpickerEnable');
  336. return true;
  337. }
  338. /**
  339. * Disables the widget and the input if any
  340. *
  341. * @fires Colorpicker#colorpickerDisable
  342. * @returns {boolean}
  343. */
  344. disable() {
  345. this.inputHandler.disable();
  346. this.disabled = true;
  347. this.picker.addClass('colorpicker-disabled');
  348. /**
  349. * (Colorpicker) When the widget has been disabled.
  350. *
  351. * @event Colorpicker#colorpickerDisable
  352. */
  353. this.trigger('colorpickerDisable');
  354. return true;
  355. }
  356. /**
  357. * Returns true if this instance is enabled
  358. * @returns {boolean}
  359. */
  360. isEnabled() {
  361. return !this.isDisabled();
  362. }
  363. /**
  364. * Returns true if this instance is disabled
  365. * @returns {boolean}
  366. */
  367. isDisabled() {
  368. return this.disabled === true;
  369. }
  370. /**
  371. * Triggers a Colorpicker event.
  372. *
  373. * @param eventName
  374. * @param color
  375. * @param value
  376. */
  377. trigger(eventName, color = null, value = null) {
  378. this.element.trigger({
  379. type: eventName,
  380. colorpicker: this,
  381. color: color ? color : this.color,
  382. value: value ? value : this.getValue()
  383. });
  384. }
  385. }
  386. /**
  387. * Colorpicker extension classes, indexed by extension name
  388. *
  389. * @static
  390. * @type {Object} a map between the extension name and its class
  391. */
  392. Colorpicker.extensions = coreExtensions;
  393. export default Colorpicker;