color-picker.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /* global wpColorPickerL10n */
  2. ( function( $, undef ) {
  3. var ColorPicker,
  4. _before = '<button type="button" class="button wp-color-result" aria-expanded="false"><span class="wp-color-result-text"></span></button>',
  5. _after = '<div class="wp-picker-holder" />',
  6. _wrap = '<div class="wp-picker-container" />',
  7. _button = '<input type="button" class="button button-small" />',
  8. _wrappingLabel = '<label></label>',
  9. _wrappingLabelText = '<span class="screen-reader-text"></span>';
  10. /**
  11. * @summary Creates a jQuery UI color picker.
  12. *
  13. * Creates a jQuery UI color picker that is used in the theme customizer.
  14. *
  15. * @since 3.5.0
  16. */
  17. ColorPicker = {
  18. options: {
  19. defaultColor: false,
  20. change: false,
  21. clear: false,
  22. hide: true,
  23. palettes: true,
  24. width: 255,
  25. mode: 'hsv',
  26. type: 'full',
  27. slider: 'horizontal'
  28. },
  29. /**
  30. * @summary Creates a color picker that only allows you to adjust the hue.
  31. *
  32. * @since 3.5.0
  33. *
  34. * @access private
  35. *
  36. * @returns {void}
  37. */
  38. _createHueOnly: function() {
  39. var self = this,
  40. el = self.element,
  41. color;
  42. el.hide();
  43. // Set the saturation to the maximum level.
  44. color = 'hsl(' + el.val() + ', 100, 50)';
  45. // Create an instance of the color picker, using the hsl mode.
  46. el.iris( {
  47. mode: 'hsl',
  48. type: 'hue',
  49. hide: false,
  50. color: color,
  51. /**
  52. * @summary Handles the onChange event if one has been defined in the options.
  53. *
  54. * @param {Event} event The event that's being called.
  55. * @param {HTMLElement} ui The HTMLElement containing the color picker.
  56. *
  57. * @returns {void}
  58. */
  59. change: function( event, ui ) {
  60. if ( $.isFunction( self.options.change ) ) {
  61. self.options.change.call( this, event, ui );
  62. }
  63. },
  64. width: self.options.width,
  65. slider: self.options.slider
  66. } );
  67. },
  68. /**
  69. * @summary Creates the color picker.
  70. *
  71. * Creates the color picker, sets default values, css classes and wraps it all in HTML.
  72. *
  73. * @since 3.5.0
  74. *
  75. * @access private
  76. *
  77. * @returns {void}
  78. */
  79. _create: function() {
  80. // Return early if Iris support is missing.
  81. if ( ! $.support.iris ) {
  82. return;
  83. }
  84. var self = this,
  85. el = self.element;
  86. // Override default options with options bound to the element.
  87. $.extend( self.options, el.data() );
  88. // Create a color picker which only allows adjustments to the hue.
  89. if ( self.options.type === 'hue' ) {
  90. return self._createHueOnly();
  91. }
  92. // Bind the close event.
  93. self.close = $.proxy( self.close, self );
  94. self.initialValue = el.val();
  95. // Add a CSS class to the input field.
  96. el.addClass( 'wp-color-picker' );
  97. /*
  98. * Check if there's already a wrapping label, e.g. in the Customizer.
  99. * If there's no label, add a default one to match the Customizer template.
  100. */
  101. if ( ! el.parent( 'label' ).length ) {
  102. // Wrap the input field in the default label.
  103. el.wrap( _wrappingLabel );
  104. // Insert the default label text.
  105. self.wrappingLabelText = $( _wrappingLabelText )
  106. .insertBefore( el )
  107. .text( wpColorPickerL10n.defaultLabel );
  108. }
  109. /*
  110. * At this point, either it's the standalone version or the Customizer
  111. * one, we have a wrapping label to use as hook in the DOM, let's store it.
  112. */
  113. self.wrappingLabel = el.parent();
  114. // Wrap the label in the main wrapper.
  115. self.wrappingLabel.wrap( _wrap );
  116. // Store a reference to the main wrapper.
  117. self.wrap = self.wrappingLabel.parent();
  118. // Set up the toggle button and insert it before the wrapping label.
  119. self.toggler = $( _before )
  120. .insertBefore( self.wrappingLabel )
  121. .css( { backgroundColor: self.initialValue } );
  122. // Set the toggle button span element text.
  123. self.toggler.find( '.wp-color-result-text' ).text( wpColorPickerL10n.pick );
  124. // Set up the Iris container and insert it after the wrapping label.
  125. self.pickerContainer = $( _after ).insertAfter( self.wrappingLabel );
  126. // Store a reference to the Clear/Default button.
  127. self.button = $( _button );
  128. // Set up the Clear/Default button.
  129. if ( self.options.defaultColor ) {
  130. self.button
  131. .addClass( 'wp-picker-default' )
  132. .val( wpColorPickerL10n.defaultString )
  133. .attr( 'aria-label', wpColorPickerL10n.defaultAriaLabel );
  134. } else {
  135. self.button
  136. .addClass( 'wp-picker-clear' )
  137. .val( wpColorPickerL10n.clear )
  138. .attr( 'aria-label', wpColorPickerL10n.clearAriaLabel );
  139. }
  140. // Wrap the wrapping label in its wrapper and append the Clear/Default button.
  141. self.wrappingLabel
  142. .wrap( '<span class="wp-picker-input-wrap hidden" />' )
  143. .after( self.button );
  144. /*
  145. * The input wrapper now contains the label+input+Clear/Default button.
  146. * Store a reference to the input wrapper: we'll use this to toggle
  147. * the controls visibility.
  148. */
  149. self.inputWrapper = el.closest( '.wp-picker-input-wrap' );
  150. el.iris( {
  151. target: self.pickerContainer,
  152. hide: self.options.hide,
  153. width: self.options.width,
  154. mode: self.options.mode,
  155. palettes: self.options.palettes,
  156. /**
  157. * @summary Handles the onChange event if one has been defined in the options.
  158. *
  159. * Handles the onChange event if one has been defined in the options and additionally
  160. * sets the background color for the toggler element.
  161. *
  162. * @since 3.5.0
  163. *
  164. * @param {Event} event The event that's being called.
  165. * @param {HTMLElement} ui The HTMLElement containing the color picker.
  166. *
  167. * @returns {void}
  168. */
  169. change: function( event, ui ) {
  170. self.toggler.css( { backgroundColor: ui.color.toString() } );
  171. if ( $.isFunction( self.options.change ) ) {
  172. self.options.change.call( this, event, ui );
  173. }
  174. }
  175. } );
  176. el.val( self.initialValue );
  177. self._addListeners();
  178. // Force the color picker to always be closed on initial load.
  179. if ( ! self.options.hide ) {
  180. self.toggler.click();
  181. }
  182. },
  183. /**
  184. * @summary Binds event listeners to the color picker.
  185. *
  186. * @since 3.5.0
  187. *
  188. * @access private
  189. *
  190. * @returns {void}
  191. */
  192. _addListeners: function() {
  193. var self = this;
  194. /**
  195. * @summary Prevent any clicks inside this widget from leaking to the top and closing it.
  196. *
  197. * @since 3.5.0
  198. *
  199. * @param {Event} event The event that's being called.
  200. *
  201. * @returs {void}
  202. */
  203. self.wrap.on( 'click.wpcolorpicker', function( event ) {
  204. event.stopPropagation();
  205. });
  206. /**
  207. * @summary Open or close the color picker depending on the class.
  208. *
  209. * @since 3.5
  210. */
  211. self.toggler.click( function(){
  212. if ( self.toggler.hasClass( 'wp-picker-open' ) ) {
  213. self.close();
  214. } else {
  215. self.open();
  216. }
  217. });
  218. /**
  219. * @summary Checks if value is empty when changing the color in the color picker.
  220. *
  221. * Checks if value is empty when changing the color in the color picker.
  222. * If so, the background color is cleared.
  223. *
  224. * @since 3.5.0
  225. *
  226. * @param {Event} event The event that's being called.
  227. *
  228. * @returns {void}
  229. */
  230. self.element.change( function( event ) {
  231. var me = $( this ),
  232. val = me.val();
  233. if ( val === '' || val === '#' ) {
  234. self.toggler.css( 'backgroundColor', '' );
  235. // Fire clear callback if we have one.
  236. if ( $.isFunction( self.options.clear ) ) {
  237. self.options.clear.call( this, event );
  238. }
  239. }
  240. });
  241. /**
  242. * @summary Enables the user to clear or revert the color in the color picker.
  243. *
  244. * Enables the user to either clear the color in the color picker or revert back to the default color.
  245. *
  246. * @since 3.5.0
  247. *
  248. * @param {Event} event The event that's being called.
  249. *
  250. * @returns {void}
  251. */
  252. self.button.click( function( event ) {
  253. var me = $( this );
  254. if ( me.hasClass( 'wp-picker-clear' ) ) {
  255. self.element.val( '' );
  256. self.toggler.css( 'backgroundColor', '' );
  257. if ( $.isFunction( self.options.clear ) ) {
  258. self.options.clear.call( this, event );
  259. }
  260. } else if ( me.hasClass( 'wp-picker-default' ) ) {
  261. self.element.val( self.options.defaultColor ).change();
  262. }
  263. });
  264. },
  265. /**
  266. * @summary Opens the color picker dialog.
  267. *
  268. * @since 3.5.0
  269. *
  270. * @returns {void}
  271. */
  272. open: function() {
  273. this.element.iris( 'toggle' );
  274. this.inputWrapper.removeClass( 'hidden' );
  275. this.wrap.addClass( 'wp-picker-active' );
  276. this.toggler
  277. .addClass( 'wp-picker-open' )
  278. .attr( 'aria-expanded', 'true' );
  279. $( 'body' ).trigger( 'click.wpcolorpicker' ).on( 'click.wpcolorpicker', this.close );
  280. },
  281. /**
  282. * @summary Closes the color picker dialog.
  283. *
  284. * @since 3.5.0
  285. *
  286. * @returns {void}
  287. */
  288. close: function() {
  289. this.element.iris( 'toggle' );
  290. this.inputWrapper.addClass( 'hidden' );
  291. this.wrap.removeClass( 'wp-picker-active' );
  292. this.toggler
  293. .removeClass( 'wp-picker-open' )
  294. .attr( 'aria-expanded', 'false' );
  295. $( 'body' ).off( 'click.wpcolorpicker', this.close );
  296. },
  297. /**
  298. * @summary Returns iris object or sets new color.
  299. *
  300. * Returns the iris object if no new color is provided. If a new color is provided, it sets the new color.
  301. *
  302. * @param newColor {string|*} The new color to use. Can be undefined.
  303. *
  304. * @since 3.5.0
  305. *
  306. * @returns {string} The element's color
  307. */
  308. color: function( newColor ) {
  309. if ( newColor === undef ) {
  310. return this.element.iris( 'option', 'color' );
  311. }
  312. this.element.iris( 'option', 'color', newColor );
  313. },
  314. /**
  315. * @summary Returns iris object or sets new default color.
  316. *
  317. * Returns the iris object if no new default color is provided.
  318. * If a new default color is provided, it sets the new default color.
  319. *
  320. * @param newDefaultColor {string|*} The new default color to use. Can be undefined.
  321. *
  322. * @since 3.5.0
  323. *
  324. * @returns {boolean|string} The element's color.
  325. */
  326. defaultColor: function( newDefaultColor ) {
  327. if ( newDefaultColor === undef ) {
  328. return this.options.defaultColor;
  329. }
  330. this.options.defaultColor = newDefaultColor;
  331. }
  332. };
  333. // Register the color picker as a widget.
  334. $.widget( 'wp.wpColorPicker', ColorPicker );
  335. }( jQuery ) );