fl-color-picker.js 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162
  1. /**
  2. * Custom color picker for Beaver Builder based on Iris by Matt Wiebe.
  3. *
  4. * Iris Color Picker - v1.0.7 - 2014-11-28
  5. * https://github.com/Automattic/Iris
  6. * Copyright (c) 2014 Matt Wiebe; Licensed GPLv2
  7. */
  8. /**
  9. * Global variable for referencing the color picker.
  10. */
  11. var FLBuilderColorPicker;
  12. (function( $, undef ) {
  13. var FLBuilderColorPresets = [],
  14. UA = navigator.userAgent.toLowerCase(),
  15. isIE = navigator.appName === 'Microsoft Internet Explorer',
  16. IEVersion = isIE ? parseFloat( UA.match( /msie ([0-9]{1,}[\.0-9]{0,})/ )[1] ) : 0,
  17. nonGradientIE = ( isIE && IEVersion < 10 ),
  18. gradientType = false,
  19. vendorPrefixes = [ '-moz-', '-webkit-', '-o-', '-ms-' ];
  20. /**
  21. * Run some tests to check if the current browser supports CSS3 gradients.
  22. * Sets gradientType accordingly.
  23. *
  24. * @since 1.6.4
  25. * @method testGradientType
  26. */
  27. function testGradientType() {
  28. var el, base,
  29. bgImageString = 'backgroundImage';
  30. // check current browser is an IE version that doesn't support CSS3 Gradients
  31. if ( nonGradientIE ) {
  32. // if yes, set gradientType to filter
  33. gradientType = 'filter';
  34. }
  35. else {
  36. // if no, runs a quick test to check if the browser supports modern gradient syntax
  37. el = $( '<div id="iris-gradtest" />' );
  38. base = 'linear-gradient(top,#fff,#000)';
  39. $.each( vendorPrefixes, function( i, val ){
  40. el.css( bgImageString, val + base );
  41. if ( el.css( bgImageString ).match( 'gradient' ) ) {
  42. gradientType = i;
  43. return false;
  44. }
  45. });
  46. // check for legacy webkit gradient syntax
  47. if ( gradientType === false ) {
  48. el.css( 'background', '-webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#000))' );
  49. if ( el.css( this.bgImageString ).match( 'gradient' ) ) {
  50. gradientType = 'webkit';
  51. }
  52. }
  53. el.remove();
  54. }
  55. }
  56. /**
  57. * Only for CSS3 gradients. oldIE will use a separate function.
  58. *
  59. * Accepts as many color stops as necessary from 2nd arg on, or 2nd
  60. * arg can be an array of color stops
  61. *
  62. * @since 1.6.4
  63. * @method createGradient
  64. * @param {String} origin Gradient origin - top or left, defaults to left.
  65. * @return {String} Appropriate CSS3 gradient string for use in
  66. */
  67. function createGradient( origin, stops ) {
  68. origin = ( origin === 'top' ) ? 'top' : 'left';
  69. stops = $.isArray( stops ) ? stops : Array.prototype.slice.call( arguments, 1 );
  70. if ( gradientType === 'webkit' ) {
  71. return legacyWebkitGradient( origin, stops );
  72. }
  73. else {
  74. return vendorPrefixes[ gradientType ] + 'linear-gradient(' + origin + ', ' + stops.join(', ') + ')';
  75. }
  76. }
  77. /**
  78. * Gradients for stupid IE.
  79. *
  80. * @since 1.6.4
  81. * @method stupidIEGradient
  82. * @param {String} origin
  83. * @return {Array} stops
  84. */
  85. function stupidIEGradient( origin, stops ) {
  86. var type, self, lastIndex, filter, startPosProp, endPosProp, dimensionProp, template, html;
  87. origin = ( origin === 'top' ) ? 'top' : 'left';
  88. stops = $.isArray( stops ) ? stops : Array.prototype.slice.call( arguments, 1 );
  89. // 8 hex: AARRGGBB
  90. // GradientType: 0 vertical, 1 horizontal
  91. type = ( origin === 'top' ) ? 0 : 1;
  92. self = $( this );
  93. lastIndex = stops.length - 1;
  94. filter = 'filter';
  95. startPosProp = ( type === 1 ) ? 'left' : 'top';
  96. endPosProp = ( type === 1 ) ? 'right' : 'bottom';
  97. dimensionProp = ( type === 1 ) ? 'height' : 'width';
  98. template = '<div class="iris-ie-gradient-shim" style="position:absolute;' + dimensionProp + ':100%;' + startPosProp + ':%start%;' + endPosProp + ':%end%;' + filter + ':%filter%;" data-color:"%color%"></div>';
  99. html = '';
  100. // need a positioning context
  101. if ( self.css('position') === 'static' ) {
  102. self.css( {position: 'relative' } );
  103. }
  104. stops = fillColorStops( stops );
  105. $.each(stops, function( i, startColor ) {
  106. var endColor, endStop, filterVal;
  107. // we want two at a time. if we're on the last pair, bail.
  108. if ( i === lastIndex ) {
  109. return false;
  110. }
  111. endColor = stops[ i + 1 ];
  112. //if our pairs are at the same color stop, moving along.
  113. if ( startColor.stop === endColor.stop ) {
  114. return;
  115. }
  116. endStop = 100 - parseFloat( endColor.stop ) + '%';
  117. startColor.octoHex = new FLBuilderColor( startColor.color ).toIEOctoHex();
  118. endColor.octoHex = new FLBuilderColor( endColor.color ).toIEOctoHex();
  119. filterVal = 'progid:DXImageTransform.Microsoft.Gradient(GradientType=' + type + ', StartColorStr=\'' + startColor.octoHex + '\', EndColorStr=\'' + endColor.octoHex + '\')';
  120. html += template.replace( '%start%', startColor.stop ).replace( '%end%', endStop ).replace( '%filter%', filterVal );
  121. });
  122. self.find( '.iris-ie-gradient-shim' ).remove();
  123. $( html ).prependTo( self );
  124. }
  125. /**
  126. * Builds CSS syntax for legacy webkit browsers.
  127. *
  128. * @see fillColorStops
  129. * @since 1.6.4
  130. * @method legacyWebkitGradient
  131. * @param {String} origin Where the gradient starts.
  132. * @param {Array} colorList
  133. * @return {String} The correct CSS gradient syntax.
  134. */
  135. function legacyWebkitGradient( origin, colorList ) {
  136. var stops = [];
  137. origin = ( origin === 'top' ) ? '0% 0%,0% 100%,' : '0% 100%,100% 100%,';
  138. colorList = fillColorStops( colorList );
  139. $.each( colorList, function( i, val ){
  140. stops.push( 'color-stop(' + ( parseFloat( val.stop ) / 100 ) + ', ' + val.color + ')' );
  141. });
  142. return '-webkit-gradient(linear,' + origin + stops.join(',') + ')';
  143. }
  144. /**
  145. * @since 1.6.4
  146. * @method fillColorStops
  147. * @param {Array} colorList
  148. * @return {Array}
  149. */
  150. function fillColorStops( colorList ) {
  151. var colors = [],
  152. percs = [],
  153. newColorList = [],
  154. lastIndex = colorList.length - 1;
  155. $.each( colorList, function( index, val ) {
  156. var color = val,
  157. perc = false,
  158. match = val.match( /1?[0-9]{1,2}%$/ );
  159. if ( match ) {
  160. color = val.replace( /\s?1?[0-9]{1,2}%$/, '' );
  161. perc = match.shift();
  162. }
  163. colors.push( color );
  164. percs.push( perc );
  165. });
  166. // back fill first and last
  167. if ( percs[0] === false ) {
  168. percs[0] = '0%';
  169. }
  170. if ( percs[lastIndex] === false ) {
  171. percs[lastIndex] = '100%';
  172. }
  173. percs = backFillColorStops( percs );
  174. $.each( percs, function( i ){
  175. newColorList[i] = { color: colors[i], stop: percs[i] };
  176. });
  177. return newColorList;
  178. }
  179. /**
  180. * @since 1.6.4
  181. * @method backFillColorStops
  182. * @param {Array} stops
  183. * @return {Array}
  184. */
  185. function backFillColorStops( stops ) {
  186. var first = 0,
  187. last = stops.length - 1,
  188. i = 0,
  189. foundFirst = false,
  190. incr,
  191. steps,
  192. step,
  193. firstVal;
  194. if ( stops.length <= 2 || $.inArray( false, stops ) < 0 ) {
  195. return stops;
  196. }
  197. while ( i < stops.length - 1 ) {
  198. if ( ! foundFirst && stops[i] === false ) {
  199. first = i - 1;
  200. foundFirst = true;
  201. } else if ( foundFirst && stops[i] !== false ) {
  202. last = i;
  203. i = stops.length;
  204. }
  205. i++;
  206. }
  207. steps = last - first;
  208. firstVal = parseInt( stops[first].replace('%'), 10 );
  209. incr = ( parseFloat( stops[last].replace('%') ) - firstVal ) / steps;
  210. i = first + 1;
  211. step = 1;
  212. while ( i < last ) {
  213. stops[i] = ( firstVal + ( step * incr ) ) + '%';
  214. step++;
  215. i++;
  216. }
  217. return backFillColorStops( stops );
  218. }
  219. /**
  220. * @since 1.8.4
  221. * @method flBuilderParseColorValue
  222. * @return {Array}
  223. */
  224. flBuilderParseColorValue = function( val ) {
  225. var value = val.replace(/\s+/g, ''),
  226. alpha = ( value.indexOf('rgba') !== -1 ) ? parseFloat( value.replace(/^.*,(.+)\)/, '$1') * 100 ) : 100,
  227. rgba = ( alpha < 100 ) ? true : false;
  228. return { value: value, alpha: alpha, rgba: rgba };
  229. }
  230. /**
  231. * @since 1.6.4
  232. * @method $.fn.flBuilderColorPickerGradient
  233. * @return {Array}
  234. */
  235. $.fn.flBuilderColorPickerGradient = function() {
  236. var args = arguments;
  237. return this.each( function() {
  238. // this'll be oldishIE
  239. if ( nonGradientIE ) {
  240. stupidIEGradient.apply( this, args );
  241. } else {
  242. // new hotness
  243. $( this ).css( 'backgroundImage', createGradient.apply( this, args ) );
  244. }
  245. });
  246. };
  247. /**
  248. * @since 1.6.4
  249. * @method $.fn.flBuilderColorPickerRaninbowGradient
  250. * @return {Array}
  251. */
  252. $.fn.flBuilderColorPickerRaninbowGradient = function( origin, args ) {
  253. var opts, template, i, steps;
  254. origin = origin || 'top';
  255. opts = $.extend( {}, { s: 100, l: 50 }, args );
  256. template = 'hsl(%h%,' + opts.s + '%,' + opts.l + '%)';
  257. i = 0;
  258. steps = [];
  259. while ( i <= 360 ) {
  260. steps.push( template.replace('%h%', i) );
  261. i += 30;
  262. }
  263. return this.each(function() {
  264. $(this).flBuilderColorPickerGradient( origin, steps );
  265. });
  266. };
  267. /**
  268. * Main color picker class for Beaver Builder's custom implementation.
  269. *
  270. * @class FLBuilderColorPicker
  271. * @param {Object} settings
  272. * @since 1.6.4
  273. */
  274. FLBuilderColorPicker = function( settings )
  275. {
  276. this._html = '<div class="fl-color-picker-ui"><div class="iris-picker"><div class="iris-picker-inner"><div class="iris-square"><a class="iris-square-value" href="javascript:void(0);"><span class="iris-square-handle ui-slider-handle"></span></a><div class="iris-square-inner iris-square-horiz"></div><div class="iris-square-inner iris-square-vert"></div></div><div class="iris-slider iris-strip"><div class="iris-slider-offset"></div></div></div></div></div>';
  277. // default settings
  278. var defaults = {
  279. elements : null,
  280. color : '',
  281. mode : 'hsl',
  282. controls : {
  283. horiz : 's', // horizontal defaults to saturation
  284. vert : 'l', // vertical defaults to lightness
  285. strip : 'h' // right strip defaults to hue
  286. },
  287. target : false, // a DOM element / jQuery selector that the element will be appended within. Only used when called on an input.
  288. width : 200, // the width of the collection of UI elements
  289. presets: [],
  290. labels : {
  291. colorPresets : 'Color Presets',
  292. colorPicker : 'Color Picker',
  293. placeholder : 'Paste color here...',
  294. removePresetConfirm : 'Are you sure?',
  295. noneColorSelected : 'None color selected.',
  296. alreadySaved : '%s is already a saved preset.',
  297. noPresets : 'Add a color preset first.',
  298. presetAdded : '%s added to presets!',
  299. }
  300. };
  301. // setting plugin options
  302. this.options = $.extend( {}, defaults, settings );
  303. // Bail for IE <= 7
  304. if ( nonGradientIE === false || nonGradientIE === true && IEVersion > 7 ) {
  305. // initialize the color picker single instance
  306. this._init();
  307. }
  308. };
  309. /**
  310. * Prototype for new instances.
  311. *
  312. * @since 1.6.4
  313. * @property {Object} prototype
  314. */
  315. FLBuilderColorPicker.prototype = {
  316. /**
  317. * Initial markup for the color picker.
  318. *
  319. * @since 1.6.4
  320. * @property {String} _html
  321. */
  322. _html : '',
  323. /**
  324. * Current set color for the color picker.
  325. *
  326. * @since 1.6.4
  327. * @property {String} _color
  328. */
  329. _color : '',
  330. /**
  331. * A reference to the current picker setting element.
  332. *
  333. * @since 1.6.4
  334. * @property {Object} _currentElement
  335. */
  336. _currentElement : '',
  337. /**
  338. * Whether the picker has been initialized or not.
  339. *
  340. * @since 1.6.4
  341. * @property {Boolean} _inited
  342. */
  343. _inited : false,
  344. /**
  345. * Defaults for the HSL controls.
  346. *
  347. * @since 1.6.4
  348. * @property {Object} _defaultHSLControls
  349. */
  350. _defaultHSLControls : {
  351. horiz : 's',
  352. vert : 'l',
  353. strip : 'h'
  354. },
  355. /**
  356. * Defaults for the HSV controls.
  357. *
  358. * @since 1.6.4
  359. * @property {Object} _defaultHSVControls
  360. */
  361. _defaultHSVControls : {
  362. horiz : 'h',
  363. vert : 'v',
  364. strip : 's'
  365. },
  366. /**
  367. * @since 1.6.4
  368. * @property {Object} _scale
  369. */
  370. _scale : {
  371. h: 360,
  372. s: 100,
  373. l: 100,
  374. v: 100
  375. },
  376. /**
  377. * Initializes this instance.
  378. *
  379. * @since 1.6.4
  380. * @method _init
  381. */
  382. _init: function(){
  383. var self = this,
  384. el = $( self.options.elements );
  385. this._color = new FLBuilderColor( '#ff0000' ).setHSpace( self.options.mode );
  386. // Set picker color presets
  387. FLBuilderColorPresets = this.options.presets;
  388. if ( gradientType === false ) {
  389. testGradientType();
  390. }
  391. // appends color picker markup to the body
  392. // check if there's already a color picker instance
  393. if( $('html').hasClass( 'fl-color-picker-init' ) ){
  394. self.picker = $( '.fl-color-picker-ui' );
  395. } else {
  396. self.picker = $( this._html ).appendTo( 'body' );
  397. }
  398. // Browsers / Versions
  399. // Feature detection doesn't work for these, and $.browser is deprecated
  400. if ( isIE ) {
  401. if ( IEVersion === 9 ) {
  402. self.picker.addClass( 'iris-ie-9' );
  403. } else if ( IEVersion <= 8 ) {
  404. self.picker.addClass( 'iris-ie-lt9' );
  405. }
  406. } else if ( UA.indexOf('compatible') < 0 && UA.indexOf('khtml') < 0 && UA.match( /mozilla/ ) ) {
  407. self.picker.addClass( 'iris-mozilla' );
  408. }
  409. // prep 'em for re-use
  410. self.controls = {
  411. square : self.picker.find( '.iris-square' ),
  412. squareDrag : self.picker.find( '.iris-square-value' ),
  413. horiz : self.picker.find( '.iris-square-horiz' ),
  414. vert : self.picker.find( '.iris-square-vert' ),
  415. strip : self.picker.find( '.iris-strip' ),
  416. stripSlider : self.picker.find( '.iris-strip .iris-slider-offset' )
  417. };
  418. // small sanity check - if we chose hsv, change default controls away from hsl
  419. if ( self.options.mode === 'hsv' && self._has('l', self.options.controls) ) {
  420. self.options.controls = self._defaultHSVControls;
  421. } else if ( self.options.mode === 'hsl' && self._has('v', self.options.controls) ) {
  422. self.options.controls = self._defaultHSLControls;
  423. }
  424. // store it. HSL gets squirrely
  425. self.hue = self._color.h();
  426. this._setTemplates();
  427. // COLOR PRESETS UI -------------------------------------//
  428. // cache reference to the picker wrapper
  429. this._ui = $( '.fl-color-picker-ui' );
  430. this._iris = $( '.iris-picker' );
  431. this._wrapper = $( 'body' );
  432. if( !$('html').hasClass( 'fl-color-picker-init' ) ){
  433. this._ui
  434. .prepend( this._hexHtml )
  435. .append( this._presetsHtml );
  436. }
  437. self.element = this._ui.find( '.fl-color-picker-input' );
  438. self._initControls();
  439. self.active = 'external';
  440. //self._dimensions();
  441. self._change();
  442. // binds listeners to all color picker instances
  443. self._addInputListeners( self.element );
  444. // build the presets UI
  445. this._buildUI();
  446. // adds needed markup and bind functions to all color fields
  447. this._prepareColorFields();
  448. // bind picker control events
  449. this._pickerControls();
  450. // bind presets control events
  451. this._presetsControls();
  452. // adds opacity/alpha support
  453. this._buildAlphaUI();
  454. // now we know that the picker is already added to the body
  455. $('html').addClass( 'fl-color-picker-init' );
  456. },
  457. /**
  458. * @since 1.6.4
  459. * @method _prepareColorFields
  460. */
  461. _prepareColorFields: function(){
  462. var self = this;
  463. // append presets initial html and trigger that toggles the picker
  464. $('.fl-color-picker-value').each( function(){
  465. var $this = $( this ),
  466. $colorValue = $this.val(),
  467. $colorTrigger = $this.parent().find( '.fl-color-picker-color' ),
  468. $parsedValue = flBuilderParseColorValue( $colorValue ),
  469. $bgColor = '';
  470. if( $colorValue ){
  471. // set initial color, check for alpha support
  472. if ( $colorTrigger.hasClass('fl-color-picker-alpha-enabled') && $parsedValue.rgba ) {
  473. $bgColor = $this.val().toString();
  474. }
  475. else if ( !$colorTrigger.hasClass('fl-color-picker-alpha-enabled') && $parsedValue.rgba ) {
  476. var $newColorValue = $colorValue.replace('rgba', 'rgb')
  477. $newColorValue = $newColorValue.substr(0, $newColorValue.lastIndexOf(",")) + ')';
  478. self._color._alpha = 1;
  479. $bgColor = $newColorValue;
  480. $this.val($newColorValue);
  481. }
  482. else {
  483. $bgColor = '#' + $this.val().toString();
  484. }
  485. $colorTrigger.css({ backgroundColor: $bgColor });
  486. }
  487. });
  488. },
  489. /**
  490. * Sets templates to build the color picker markup.
  491. *
  492. * @since 1.6.4
  493. * @method _setTemplates
  494. */
  495. _setTemplates: function(){
  496. this._alphaHtml = '<div class="fl-alpha-wrap">' +
  497. '<div class="fl-alpha-slider"></div>' +
  498. '<div class="fl-alpha-slider-offset"></div>' +
  499. '<div class="fl-alpha-text"></div>' +
  500. '</div>';
  501. this._presetsHtml = '<div class="fl-color-picker-presets">' +
  502. '<div class="fl-color-picker-presets-toggle">' +
  503. '<div class="fl-color-picker-presets-open-label fl-color-picker-active">' + this.options.labels.colorPresets + ' <span class="fl-color-picker-icon-arrow-up"></span></div>' +
  504. '<div class="fl-color-picker-presets-close-label">' + this.options.labels.colorPicker + ' <span class="fl-color-picker-icon-arrow-down"></span></div>' +
  505. '</div>' +
  506. '<ul class="fl-color-picker-presets-list"></ul>' +
  507. '</div>';
  508. this._hexHtml = '<input type="text" class="fl-color-picker-input" maxlength="7" placeholder="' + this.options.labels.placeholder + '">' +
  509. '<div class="fl-color-picker-preset-add"></div>';
  510. this._presetsTpl = '<li class="fl-color-picker-preset"><span class="fl-color-picker-preset-color"></span> <span class="fl-color-picker-preset-label"></span> <span class="fl-color-picker-preset-remove fl-color-picker-icon-remove"></span></li>';
  511. this._noPresetsTpl = '<li class="fl-color-picker-no-preset"><span class="fl-color-picker-preset-label">' + this.options.labels.noPresets + '</span></li>';
  512. },
  513. /**
  514. * @since 1.6.4
  515. * @method _has
  516. * @param {String} needle
  517. * @param {Object} haystack
  518. * @return {Boolean}
  519. */
  520. _has: function( needle, haystack ) {
  521. var ret = false;
  522. $.each( haystack, function( i, v ){
  523. if ( needle === v ) {
  524. ret = true;
  525. // exit the loop
  526. return false;
  527. }
  528. });
  529. return ret;
  530. },
  531. /**
  532. * Builds the UI for color presets
  533. *
  534. * @see _addPresetView
  535. * @since 1.6.4
  536. * @method _buildUI
  537. */
  538. _buildUI: function(){
  539. var self = this;
  540. self._presetsList = this._ui.find( '.fl-color-picker-presets-list' );
  541. self._presetsList.html('');
  542. if( this.options.presets.length > 0 ){
  543. $.each( this.options.presets, function( index, val ) {
  544. self._addPresetView( val );
  545. });
  546. } else {
  547. self._presetsList.append( this._noPresetsTpl );
  548. }
  549. },
  550. /**
  551. * Helper function to build a view for each color preset.
  552. *
  553. * @since 1.6.4
  554. * @param {string} val the respective hex code for the color preset.
  555. * @return void
  556. */
  557. _addPresetView: function( val ){
  558. var hasEmpty = this._presetsList.find( '.fl-color-picker-no-preset' );
  559. if( hasEmpty.length > 0 ){
  560. hasEmpty.remove();
  561. }
  562. var tpl = $( this._presetsTpl ),
  563. color = FLBuilderColor( val );
  564. tpl
  565. .attr( 'data-color', val )
  566. .find( '.fl-color-picker-preset-color' )
  567. .css({ backgroundColor: color.toString() })
  568. .end()
  569. .find( '.fl-color-picker-preset-label' )
  570. .html( color.toString() );
  571. this._presetsList.append( tpl );
  572. },
  573. /**
  574. * Shows a visual feedback when a color is added as a preset.
  575. *
  576. * @since 1.6.4
  577. * @method _addPresetFeedback
  578. */
  579. _addPresetFeedback: function(){
  580. this._ui.append( '<div class="fl-color-picker-added"><div class="fl-color-picker-added-text"><div class="fl-color-picker-icon-check"></div> "' + this.options.labels.presetAdded.replace( '%s', this._color.toString() ) + '"</div></div>' );
  581. this._ui
  582. .find( '.fl-color-picker-added' )
  583. .hide()
  584. .fadeIn( 200 )
  585. .delay( 2000 )
  586. .fadeOut( 200, function(){
  587. $(this).remove();
  588. } );
  589. },
  590. /**
  591. * Sets some triggers for positioning logic of the picker and color reset.
  592. *
  593. * @since 1.6.4
  594. * @method _pickerControls
  595. */
  596. _pickerControls: function(){
  597. var self = this;
  598. // logic for picker positioning
  599. this._wrapper
  600. .on( 'click', '.fl-color-picker-color', function(){
  601. var $this = $(this);
  602. self._currentElement = $this.parent().find('.fl-color-picker-value');
  603. self._ui.position({
  604. my: 'left top',
  605. at: 'left bottom',
  606. of: $this,
  607. collision: 'flip',
  608. using: function( position, feedback ){
  609. self._togglePicker( position );
  610. }
  611. })
  612. } )
  613. .on( 'click', '.fl-color-picker-clear', function(){
  614. var $this = $(this);
  615. self._currentElement = $this.parent().find('.fl-color-picker-value');
  616. $this
  617. .prev( '.fl-color-picker-color' )
  618. .css({ backgroundColor: 'transparent' })
  619. .addClass('fl-color-picker-empty');
  620. self._setColor( '' );
  621. self.element.val( '' );
  622. self._currentElement
  623. .val( '' )
  624. .trigger( 'change' );
  625. } );
  626. // logic to hide picker when the user clicks outside it
  627. $( document ).on( 'mousedown', function( event ) {
  628. if ( 0 === $( event.target ).closest( '.fl-color-picker-ui' ).length ) {
  629. $( '.fl-color-picker-ui.fl-color-picker-active' ).removeClass( 'fl-color-picker-active' );
  630. }
  631. });
  632. },
  633. /**
  634. * Logic for color presets UI.
  635. *
  636. * @see _addPreset
  637. * @see _removePreset
  638. * @since 1.6.4
  639. * @method _presetsControls
  640. */
  641. _presetsControls: function(){
  642. var self = this,
  643. addPreset = self._ui.find( '.fl-color-picker-preset-add' ),
  644. presets = self._ui.find( '.fl-color-picker-presets' ),
  645. presetsOpenLabel = presets.find( '.fl-color-picker-presets-open-label' ),
  646. presetsCloseLabel = presets.find( '.fl-color-picker-presets-close-label' ),
  647. presetsList = presets.find( '.fl-color-picker-presets-list' );
  648. // add preset
  649. addPreset
  650. .off( 'click' )
  651. .on( 'click', function(){
  652. self._addPreset( self.element.val() );
  653. } );
  654. // presets toggle
  655. presetsList
  656. .css({ height: ( self.element.innerHeight() + self._iris.innerHeight() + 14 ) + 'px' })
  657. .hide();
  658. presets
  659. .off( 'click' )
  660. .on( 'click', '.fl-color-picker-presets-toggle', function(){
  661. presetsOpenLabel.toggleClass('fl-color-picker-active');
  662. presetsCloseLabel.toggleClass('fl-color-picker-active');
  663. presetsList.slideToggle( 500 );
  664. } )
  665. // set preset as current color
  666. .on( 'click', '.fl-color-picker-preset', function( e ){
  667. var currentColor = new FLBuilderColor( $( this ).data( 'color' ).toString() );
  668. self._setColor( currentColor );
  669. self._currentElement
  670. .parent()
  671. .find( '.fl-color-picker-color' )
  672. .css({ backgroundColor: currentColor.toString() })
  673. .removeClass('fl-color-picker-empty');
  674. presetsOpenLabel.toggleClass('fl-color-picker-active');
  675. presetsCloseLabel.toggleClass('fl-color-picker-active');
  676. presetsList.slideToggle( 500 );
  677. })
  678. // removes a preset
  679. .on( 'click', '.fl-color-picker-preset-remove', function( e ){
  680. e.stopPropagation();
  681. self._removePreset( $( this ).parent().data( 'color' ) );
  682. });
  683. },
  684. /**
  685. * Removes a color preset from the array of presets and from the UI.
  686. *
  687. * @since 1.6.4
  688. * @method _removePreset
  689. * @param {String} preset The respective hex value of the preset.
  690. */
  691. _removePreset: function( preset ){
  692. if( confirm( this.options.labels.removePresetConfirm ) ){
  693. var color = preset.toString(),
  694. index = FLBuilderColorPresets.indexOf( color );
  695. if( index > -1 ){
  696. FLBuilderColorPresets.splice( index, 1 );
  697. this.options.presets = FLBuilderColorPresets;
  698. this._presetsList
  699. .find('.fl-color-picker-preset[data-color="'+ color +'"]' )
  700. .slideUp( function(){
  701. $( this ).remove();
  702. });
  703. }
  704. if( FLBuilderColorPresets.length < 1 ){
  705. this._presetsList.append( this._noPresetsTpl );
  706. }
  707. // CALLBACK FOR PRESET REMOVED
  708. $(this).trigger( 'presetRemoved', { presets: FLBuilderColorPresets } );
  709. }
  710. },
  711. /**
  712. * Logic to add a color preset to the array of presets, and to the UI.
  713. *
  714. * @see _addPresetView
  715. * @see _addPresetFeedback
  716. * @method _addPreset
  717. * @param {String} preset The respective hex value of the preset.
  718. * @since 1.6.4
  719. */
  720. _addPreset: function( preset ){
  721. var color = preset.toString().replace( /^#/, '' );
  722. // check if color is empty
  723. if( color === '' ){
  724. alert( this.options.labels.noneColorSelected );
  725. // check if the color is already added
  726. } else if( FLBuilderColorPresets.indexOf( color ) > -1 ){
  727. alert( this.options.labels.alreadySaved.replace( '%s', '#' + color ) );
  728. // add color to presets, fires visual feedback and triggers an event
  729. } else {
  730. this._addPresetView( color );
  731. this._addPresetFeedback();
  732. FLBuilderColorPresets.push( color );
  733. this.options.presets = FLBuilderColorPresets;
  734. // CALLBACK FOR COLOR ADDED
  735. $(this).trigger( 'presetAdded', { presets: FLBuilderColorPresets } );
  736. }
  737. },
  738. /**
  739. * Logic for positioning of the color picker.
  740. *
  741. * @since 1.6.4
  742. * @method _togglePicker
  743. * @param {Object} position An object containing x and y location for positioning.
  744. */
  745. _togglePicker: function( position ){
  746. var self = this;
  747. // logic for correct order of things
  748. if( this._ui.hasClass( 'fl-color-picker-active' ) ){
  749. // if the picker is open, hides first, then changes the position
  750. this._ui.removeClass( 'fl-color-picker-active' );
  751. if( position ){
  752. setTimeout( function(){
  753. self._ui.css( position );
  754. self._ui.addClass( 'fl-color-picker-active' );
  755. self._setColor( self._currentElement.val() );
  756. }, 200 );
  757. }
  758. } else {
  759. if( position ){
  760. self._ui.css( position );
  761. }
  762. // if the picker is closed, changes position first, then shows it
  763. setTimeout( function(){
  764. self._ui.addClass( 'fl-color-picker-active' );
  765. self._setColor( self._currentElement.val() );
  766. }, 200 );
  767. }
  768. },
  769. /**
  770. * @since 1.6.4
  771. * @method _paint
  772. */
  773. _paint: function() {
  774. var self = this;
  775. self._paintDimension( 'right', 'strip' );
  776. self._paintDimension( 'top', 'vert' );
  777. self._paintDimension( 'left', 'horiz' );
  778. },
  779. /**
  780. * @since 1.6.4
  781. * @method _paintDimension
  782. * @param {String} origin
  783. * @param {String} control
  784. */
  785. _paintDimension: function( origin, control ) {
  786. var self = this,
  787. c = self._color,
  788. mode = self.options.mode,
  789. color = self._getHSpaceColor(),
  790. target = self.controls[ control ],
  791. controlOpts = self.options.controls,
  792. stops;
  793. // don't paint the active control
  794. if ( control === self.active || ( self.active === 'square' && control !== 'strip' ) ) {
  795. return;
  796. }
  797. switch ( controlOpts[ control ] ) {
  798. case 'h':
  799. if ( mode === 'hsv' ) {
  800. color = c.clone();
  801. switch ( control ) {
  802. case 'horiz':
  803. color[controlOpts.vert](100);
  804. break;
  805. case 'vert':
  806. color[controlOpts.horiz](100);
  807. break;
  808. case 'strip':
  809. color.setHSpace('hsl');
  810. break;
  811. }
  812. stops = color.toHsl();
  813. } else {
  814. if ( control === 'strip' ) {
  815. stops = { s: color.s, l: color.l };
  816. } else {
  817. stops = { s: 100, l: color.l };
  818. }
  819. }
  820. target.flBuilderColorPickerRaninbowGradient( origin, stops );
  821. break;
  822. case 's':
  823. if ( mode === 'hsv' ) {
  824. if ( control === 'vert' ) {
  825. stops = [ c.clone().a(0).s(0).toCSS('rgba'), c.clone().a(1).s(0).toCSS('rgba') ];
  826. } else if ( control === 'strip' ) {
  827. stops = [ c.clone().s(100).toCSS('hsl'), c.clone().s(0).toCSS('hsl') ];
  828. } else if ( control === 'horiz' ) {
  829. stops = [ '#fff', 'hsl(' + color.h + ',100%,50%)' ];
  830. }
  831. } else { // implicit mode === 'hsl'
  832. if ( control === 'vert' && self.options.controls.horiz === 'h' ) {
  833. stops = ['hsla(0, 0%, ' + color.l + '%, 0)', 'hsla(0, 0%, ' + color.l + '%, 1)'];
  834. } else {
  835. stops = ['hsl('+ color.h +',0%,50%)', 'hsl(' + color.h + ',100%,50%)'];
  836. }
  837. }
  838. target.flBuilderColorPickerGradient( origin, stops );
  839. break;
  840. case 'l':
  841. if ( control === 'strip' ) {
  842. stops = ['hsl(' + color.h + ',100%,100%)', 'hsl(' + color.h + ', ' + color.s + '%,50%)', 'hsl('+ color.h +',100%,0%)'];
  843. } else {
  844. stops = ['#fff', 'rgba(255,255,255,0) 50%', 'rgba(0,0,0,0) 50%', 'rgba(0,0,0,1)'];
  845. }
  846. target.flBuilderColorPickerGradient( origin, stops );
  847. break;
  848. case 'v':
  849. if ( control === 'strip' ) {
  850. stops = [ c.clone().v(100).toCSS(), c.clone().v(0).toCSS() ];
  851. } else {
  852. stops = ['rgba(0,0,0,0)', '#000'];
  853. }
  854. target.flBuilderColorPickerGradient( origin, stops );
  855. break;
  856. default:
  857. break;
  858. }
  859. },
  860. /**
  861. * @since 1.6.4
  862. * @method _getHSpaceColor
  863. * @return {Boolean}
  864. */
  865. _getHSpaceColor: function() {
  866. return ( this.options.mode === 'hsv' ) ? this._color.toHsv() : this._color.toHsl();
  867. },
  868. /**
  869. * @since 1.6.4
  870. * @method _dimensions
  871. * @return {Boolean}
  872. */
  873. _dimensions: function( reset ) {
  874. // whatever size
  875. var self = this,
  876. opts = self.options,
  877. controls = self.controls,
  878. square = controls.square,
  879. strip = self.picker.find( '.iris-strip' ),
  880. squareWidth = '77.5%',
  881. stripWidth = '12%',
  882. totalPadding = 20,
  883. innerWidth = opts.border ? opts.width - totalPadding : opts.width,
  884. controlsHeight;
  885. //paletteCount = $.isArray( opts.palettes ) ? opts.palettes.length : self._palettes.length,
  886. //paletteMargin, paletteWidth, paletteContainerWidth;
  887. if ( reset ) {
  888. square.css( 'width', '' );
  889. strip.css( 'width', '' );
  890. self.picker.css( {width: '', height: ''} );
  891. }
  892. squareWidth = innerWidth * ( parseFloat( squareWidth ) / 100 );
  893. stripWidth = innerWidth * ( parseFloat( stripWidth ) / 100 );
  894. controlsHeight = opts.border ? squareWidth + totalPadding : squareWidth;
  895. square.width( squareWidth ).height( squareWidth );
  896. strip.height( squareWidth ).width( stripWidth );
  897. self.picker.css( { width: opts.width, height: controlsHeight } );
  898. if ( ! opts.palettes ) {
  899. return self.picker.css( 'paddingBottom', '' );
  900. }
  901. // single margin at 2%
  902. /*paletteMargin = squareWidth * 2 / 100;
  903. paletteContainerWidth = squareWidth - ( ( paletteCount - 1 ) * paletteMargin );
  904. paletteWidth = paletteContainerWidth / paletteCount;
  905. self.picker.find('.iris-palette').each( function( i ) {
  906. var margin = i === 0 ? 0 : paletteMargin;
  907. $( this ).css({
  908. width: paletteWidth,
  909. height: paletteWidth,
  910. marginLeft: margin
  911. });
  912. });
  913. self.picker.css( 'paddingBottom', paletteWidth + paletteMargin );
  914. strip.height( paletteWidth + paletteMargin + squareWidth );*/
  915. },
  916. /**
  917. * Logic to listen to events from the main color input and to bind it to the current color field.
  918. *
  919. * @see _setColor
  920. * @since 1.6.4
  921. * @method _addInputListeners
  922. * @param {Object} input
  923. */
  924. _addInputListeners: function( input ) {
  925. var self = this,
  926. debounceTimeout = 100,
  927. callback = function( event ){
  928. var color = new FLBuilderColor( input.val() ),
  929. val = input.val().replace( /^#/, '' ),
  930. isPickerEmpty = self._currentElement.hasClass( 'fl-color-picker-empty' );
  931. input.removeClass( 'iris-error' );
  932. // we gave a bad color
  933. if ( color.error ) {
  934. // don't error on an empty input - we want those allowed
  935. if ( val !== '' ) {
  936. input.addClass( 'iris-error' );
  937. }
  938. } else {
  939. if ( color.toString() !== self._color.toString() || ( '' !== self._color.toString() && isPickerEmpty ) ) {
  940. if( event.type === 'keyup' ){
  941. if( val.match( /^[0-9a-fA-F]{3}$/ ) )
  942. return;
  943. self._setColor( val );
  944. self._currentElement
  945. .parent()
  946. .find( '.fl-color-picker-color' )
  947. .css({ backgroundColor: FLBuilderColor( val ).toString() })
  948. .removeClass( 'fl-color-picker-empty' );
  949. self._currentElement
  950. .val( val )
  951. .trigger( 'change' );
  952. } else if( event.type === 'paste' ){
  953. val = event.originalEvent.clipboardData.getData( 'text' ).replace( /^#/, '' );
  954. hex = FLBuilderColor( val ).toString();
  955. self._setColor( val );
  956. input.val( hex );
  957. self._currentElement
  958. .parent()
  959. .find( '.fl-color-picker-color' )
  960. .css({ backgroundColor: hex })
  961. .removeClass( 'fl-color-picker-empty' );
  962. self._currentElement
  963. .val( val )
  964. .trigger( 'change' );
  965. return false;
  966. }
  967. }
  968. }
  969. };
  970. input.on( 'change', callback ).on( 'keyup', self._debounce( callback, debounceTimeout ) );
  971. },
  972. /**
  973. * @since 1.6.4
  974. * @method _initControls
  975. */
  976. _initControls: function() {
  977. var self = this,
  978. controls = self.controls,
  979. square = controls.square,
  980. controlOpts = self.options.controls,
  981. stripScale = self._scale[controlOpts.strip];
  982. controls.stripSlider.slider({
  983. orientation: 'horizontal',
  984. max: stripScale,
  985. slide: function( event, ui ) {
  986. self.active = 'strip';
  987. // "reverse" for hue.
  988. if ( controlOpts.strip === 'h' ) {
  989. ui.value = stripScale - ui.value;
  990. }
  991. self._color[controlOpts.strip]( ui.value );
  992. self._change.apply( self, arguments );
  993. }
  994. });
  995. controls.squareDrag.draggable({
  996. containment: controls.square.find( '.iris-square-inner' ),
  997. zIndex: 1000,
  998. cursor: 'move',
  999. drag: function( event, ui ) {
  1000. self._squareDrag( event, ui );
  1001. },
  1002. start: function() {
  1003. square.addClass( 'iris-dragging' );
  1004. $(this).addClass( 'ui-state-focus' );
  1005. },
  1006. stop: function() {
  1007. square.removeClass( 'iris-dragging' );
  1008. $(this).removeClass( 'ui-state-focus' );
  1009. }
  1010. }).on( 'mousedown mouseup', function( event ) {
  1011. var focusClass = 'ui-state-focus';
  1012. event.preventDefault();
  1013. if (event.type === 'mousedown' ) {
  1014. self.picker.find( '.' + focusClass ).removeClass( focusClass ).blur();
  1015. $(this).addClass( focusClass ).focus();
  1016. } else {
  1017. $(this).removeClass( focusClass );
  1018. }
  1019. }).on( 'keydown', function( event ) {
  1020. var container = controls.square,
  1021. draggable = controls.squareDrag,
  1022. position = draggable.position(),
  1023. distance = 2; // Distance in pixels the draggable should be moved: 1 "stop"
  1024. // make alt key go "10"
  1025. if ( event.altKey ) {
  1026. distance *= 10;
  1027. }
  1028. // Reposition if one of the directional keys is pressed
  1029. switch ( event.keyCode ) {
  1030. case 37: position.left -= distance; break; // Left
  1031. case 38: position.top -= distance; break; // Up
  1032. case 39: position.left += distance; break; // Right
  1033. case 40: position.top += distance; break; // Down
  1034. default: return true; // Exit and bubble
  1035. }
  1036. // Keep draggable within container
  1037. position.left = Math.max( 0, Math.min( position.left, container.width() ) );
  1038. position.top = Math.max( 0, Math.min( position.top, container.height() ) );
  1039. draggable.css(position);
  1040. self._squareDrag( event, { position: position });
  1041. event.preventDefault();
  1042. });
  1043. // allow clicking on the square to move there and keep dragging
  1044. square.mousedown( function( event ) {
  1045. var squareOffset, pos;
  1046. // only left click
  1047. if ( event.which !== 1 ) {
  1048. return;
  1049. }
  1050. // prevent bubbling from the handle: no infinite loops
  1051. if ( ! $( event.target ).is( 'div' ) ) {
  1052. return;
  1053. }
  1054. squareOffset = self.controls.square.offset();
  1055. pos = {
  1056. top: event.pageY - squareOffset.top,
  1057. left: event.pageX - squareOffset.left
  1058. };
  1059. event.preventDefault();
  1060. self._squareDrag( event, { position: pos } );
  1061. event.target = self.controls.squareDrag.get(0);
  1062. self.controls.squareDrag.css( pos ).trigger( event );
  1063. });
  1064. },
  1065. /**
  1066. * @since 1.6.4
  1067. * @method _squareDrag
  1068. * @param {Object} event
  1069. * @param {Object} ui
  1070. */
  1071. _squareDrag: function( event, ui ) {
  1072. var self = this,
  1073. controlOpts = self.options.controls,
  1074. dimensions = self._squareDimensions(),
  1075. vertVal = Math.round( ( dimensions.h - ui.position.top ) / dimensions.h * self._scale[controlOpts.vert] ),
  1076. horizVal = self._scale[controlOpts.horiz] - Math.round( ( dimensions.w - ui.position.left ) / dimensions.w * self._scale[controlOpts.horiz] );
  1077. self._color[controlOpts.horiz]( horizVal )[controlOpts.vert]( vertVal );
  1078. self.active = 'square';
  1079. self._change.apply( self, arguments );
  1080. },
  1081. /**
  1082. * @since 1.6.4
  1083. * @method _setColor
  1084. * @param {String} value
  1085. */
  1086. _setColor: function( value ) {
  1087. var self = this,
  1088. oldValue = self.options.color,
  1089. defaultColor = '#ff0000',
  1090. doDimensions = false,
  1091. hexLessColor,
  1092. newColor,
  1093. method;
  1094. // Set default if value is empty.
  1095. if ( '' === value ) {
  1096. value = defaultColor;
  1097. self.default = true;
  1098. }
  1099. else {
  1100. self.default = false;
  1101. }
  1102. // ensure the new value is set. We can reset to oldValue if some check wasn't met.
  1103. self.options.color = value;
  1104. // cast to string in case we have a number
  1105. value = '' + value;
  1106. hexLessColor = value.replace( /^#/, '' );
  1107. newColor = new FLBuilderColor( value ).setHSpace( self.options.mode );
  1108. if ( newColor.error ) {
  1109. self.options.color = oldValue;
  1110. } else {
  1111. self._color = newColor;
  1112. self.options.color = self._color.toString();
  1113. self.active = 'external';
  1114. self._change();
  1115. }
  1116. },
  1117. /**
  1118. * @since 1.6.4
  1119. * @method _squareDimensions
  1120. * @param {Boolean} forceRefresh
  1121. * @return {Object}
  1122. */
  1123. _squareDimensions: function( forceRefresh ) {
  1124. var square = this.controls.square,
  1125. dimensions,
  1126. control;
  1127. if ( forceRefresh !== undef && square.data('dimensions') ) {
  1128. return square.data('dimensions');
  1129. }
  1130. control = this.controls.squareDrag;
  1131. dimensions = {
  1132. w: square.width(),
  1133. h: square.height()
  1134. };
  1135. square.data( 'dimensions', dimensions );
  1136. return dimensions;
  1137. },
  1138. /**
  1139. * @since 1.6.4
  1140. * @method _isNonHueControl
  1141. * @param {String} active
  1142. * @param {String} type
  1143. * @return {Boolean}
  1144. */
  1145. _isNonHueControl: function( active, type ) {
  1146. if ( active === 'square' && this.options.controls.strip === 'h' ) {
  1147. return true;
  1148. } else if ( type === 'external' || ( type === 'h' && active === 'strip' ) ) {
  1149. return false;
  1150. }
  1151. return true;
  1152. },
  1153. /**
  1154. * @since 1.6.4
  1155. * @method _change
  1156. */
  1157. _change: function() {
  1158. var self = this,
  1159. controls = self.controls,
  1160. color = self._getHSpaceColor(),
  1161. actions = [ 'square', 'strip' ],
  1162. controlOpts = self.options.controls,
  1163. type = controlOpts[self.active] || 'external',
  1164. oldHue = self.hue;
  1165. if ( self.active === 'strip' ) {
  1166. // take no action on any of the square sliders if we adjusted the strip
  1167. actions = [];
  1168. } else if ( self.active !== 'external' ) {
  1169. // for non-strip, non-external, strip should never change
  1170. actions.pop(); // conveniently the last item
  1171. }
  1172. $.each( actions, function(index, item) {
  1173. var value, dimensions, cssObj;
  1174. if ( item !== self.active ) {
  1175. switch ( item ) {
  1176. case 'strip':
  1177. // reverse for hue
  1178. value = ( controlOpts.strip === 'h' ) ? self._scale[controlOpts.strip] - color[controlOpts.strip] : color[controlOpts.strip];
  1179. controls.stripSlider.slider( 'value', value );
  1180. break;
  1181. case 'square':
  1182. dimensions = self._squareDimensions();
  1183. cssObj = {
  1184. left: color[controlOpts.horiz] / self._scale[controlOpts.horiz] * dimensions.w,
  1185. top: dimensions.h - ( color[controlOpts.vert] / self._scale[controlOpts.vert] * dimensions.h )
  1186. };
  1187. self.controls.squareDrag.css( cssObj );
  1188. break;
  1189. }
  1190. }
  1191. });
  1192. // Ensure that we don't change hue if we triggered a hue reset
  1193. if ( color.h !== oldHue && self._isNonHueControl( self.active, type ) ) {
  1194. self._color.h(oldHue);
  1195. }
  1196. // store hue for repeating above check next time
  1197. self.hue = self._color.h();
  1198. self.options.color = self._color.toString();
  1199. if ( self.element.is( ':input' ) && ! self._color.error ) {
  1200. self.element.removeClass( 'iris-error' );
  1201. if ( self.element.val() !== self._color.toString() ) {
  1202. self.element.val( self._color.toString() );
  1203. if( this._currentElement ){
  1204. // Check if picker is not a default or an empty.
  1205. if ( ! self.default || (self.default && 'external' !== self.active ) ) {
  1206. this._currentElement
  1207. .val( self._color.toString().replace( /^#/, '' ) )
  1208. .parent()
  1209. .find( '.fl-color-picker-color' )
  1210. .css({ backgroundColor: self._color.toString() })
  1211. .removeClass( 'fl-color-picker-empty' );
  1212. }
  1213. else {
  1214. this._currentElement.val( '' );
  1215. }
  1216. self._wrapper.find('.fl-alpha-slider-offset').css('background-color', self._color.toString());
  1217. this._currentElement.trigger( 'change' );
  1218. }
  1219. }
  1220. }
  1221. self._paint();
  1222. self._inited = true;
  1223. self.active = false;
  1224. },
  1225. /**
  1226. * Taken from underscore.js _.debounce method
  1227. *
  1228. * @since 1.6.4
  1229. * @method _debounce
  1230. */
  1231. _debounce: function( func, wait, immediate ) {
  1232. var timeout, result;
  1233. return function() {
  1234. var context = this,
  1235. args = arguments,
  1236. later,
  1237. callNow;
  1238. later = function() {
  1239. timeout = null;
  1240. if ( ! immediate) {
  1241. result = func.apply( context, args );
  1242. }
  1243. };
  1244. callNow = immediate && !timeout;
  1245. clearTimeout( timeout );
  1246. timeout = setTimeout( later, wait );
  1247. if ( callNow ) {
  1248. result = func.apply( context, args );
  1249. }
  1250. return result;
  1251. };
  1252. },
  1253. /**
  1254. * Show an alpha UI when it is enabled
  1255. *
  1256. * @since 1.8.5
  1257. * @method _buildAlphaUI
  1258. */
  1259. _buildAlphaUI: function() {
  1260. var self = this;
  1261. self._wrapper.on( 'click', '.fl-color-picker-color', function(){
  1262. var $this = $(this),
  1263. $currentColor = self._currentElement.val();
  1264. if ( $this.hasClass('fl-color-picker-alpha-enabled') ) {
  1265. // Add alpha if not exists
  1266. if (self._ui.find('.fl-alpha-wrap').length <= 0) {
  1267. $(self._alphaHtml).insertAfter( self._iris );
  1268. }
  1269. self.picker.addClass('fl-color-alpha-enabled');
  1270. self._pickerAlphaControls();
  1271. }
  1272. else {
  1273. self._ui.find('.fl-alpha-wrap').remove();
  1274. }
  1275. });
  1276. },
  1277. /**
  1278. * Enable the opacity/alpha to color picker
  1279. * Credits to https://github.com/Codestar/codestar-wp-color-picker
  1280. *
  1281. * @since 1.8.5
  1282. * @method _pickerAlphaControls
  1283. */
  1284. _pickerAlphaControls: function() {
  1285. var self = this,
  1286. el = self._currentElement,
  1287. picker = flBuilderParseColorValue( el.val() ),
  1288. floatValue = parseFloat( picker.alpha / 100 ),
  1289. wrapper = self._wrapper,
  1290. container = self._ui,
  1291. alphaWrap = container.find('.fl-alpha-wrap'),
  1292. alphaSlider = alphaWrap.find('.fl-alpha-slider'),
  1293. alphaText = alphaWrap.find('.fl-alpha-text'),
  1294. alphaOffset = alphaWrap.find('.fl-alpha-slider-offset');
  1295. alphaHandle = alphaWrap.find('.ui-slider-handle');
  1296. // Set alpha text value
  1297. alphaText.text(floatValue < 1 ? floatValue : '');
  1298. // alpha slider
  1299. alphaSlider.slider({
  1300. orientation: "vertical",
  1301. // slider: slide
  1302. slide: function( event, ui ) {
  1303. var slideValue = parseFloat( ui.value / 100 );
  1304. // update iris data alpha && color option && alpha text
  1305. self._color._alpha = slideValue;
  1306. alphaText.text( ( slideValue < 1 ? slideValue : '' ) );
  1307. self._change.apply( self, arguments );
  1308. },
  1309. // slider: create
  1310. create: function() {
  1311. // Initializes alpha values
  1312. alphaOffset.css({ backgroundColor: picker.value });
  1313. // Clear alpha values
  1314. wrapper.on('click', '.fl-color-picker-clear', function() {
  1315. self._color._alpha = 1;
  1316. alphaText.text('');
  1317. alphaSlider.slider('value', 100).trigger('slide');
  1318. });
  1319. },
  1320. // slider: options
  1321. value: picker.alpha,
  1322. step: 1,
  1323. min: 1,
  1324. max: 100
  1325. });
  1326. },
  1327. };
  1328. }( jQuery ));
  1329. /**
  1330. * Color.js is included for Iris support.
  1331. *
  1332. * Color.js - v0.9.11 - 2013-08-09
  1333. * https://github.com/Automattic/Color.js
  1334. * Copyright (c) 2013 Matt Wiebe; Licensed GPLv2
  1335. */
  1336. (function(global, undef) {
  1337. var Color = function( color, type ) {
  1338. if ( ! ( this instanceof Color ) )
  1339. return new Color( color, type );
  1340. return this._init( color, type );
  1341. };
  1342. Color.fn = Color.prototype = {
  1343. _color: 0,
  1344. _alpha: 1,
  1345. error: false,
  1346. // for preserving hue/sat in fromHsl().toHsl() flows
  1347. _hsl: { h: 0, s: 0, l: 0 },
  1348. // for preserving hue/sat in fromHsv().toHsv() flows
  1349. _hsv: { h: 0, s: 0, v: 0 },
  1350. // for setting hsl or hsv space - needed for .h() & .s() functions to function properly
  1351. _hSpace: 'hsl',
  1352. _init: function( color ) {
  1353. var func = 'noop';
  1354. switch ( typeof color ) {
  1355. case 'object':
  1356. // alpha?
  1357. if ( color.a !== undef )
  1358. this.a( color.a );
  1359. func = ( color.r !== undef ) ? 'fromRgb' :
  1360. ( color.l !== undef ) ? 'fromHsl' :
  1361. ( color.v !== undef ) ? 'fromHsv' : func;
  1362. return this[func]( color );
  1363. case 'string':
  1364. return this.fromCSS( color );
  1365. case 'number':
  1366. return this.fromInt( parseInt( color, 10 ) );
  1367. }
  1368. return this;
  1369. },
  1370. _error: function() {
  1371. this.error = true;
  1372. return this;
  1373. },
  1374. clone: function() {
  1375. var newColor = new Color( this.toInt() ),
  1376. copy = ['_alpha', '_hSpace', '_hsl', '_hsv', 'error'];
  1377. for ( var i = copy.length - 1; i >= 0; i-- ) {
  1378. newColor[ copy[i] ] = this[ copy[i] ];
  1379. }
  1380. return newColor;
  1381. },
  1382. setHSpace: function( space ) {
  1383. this._hSpace = ( space === 'hsv' ) ? space : 'hsl';
  1384. return this;
  1385. },
  1386. noop: function() {
  1387. return this;
  1388. },
  1389. fromCSS: function( color ) {
  1390. var list,
  1391. leadingRE = /^(rgb|hs(l|v))a?\(/;
  1392. this.error = false;
  1393. // whitespace and semicolon trim
  1394. color = color.replace(/^\s+/, '').replace(/\s+$/, '').replace(/;$/, '');
  1395. if ( color.match(leadingRE) && color.match(/\)$/) ) {
  1396. list = color.replace(/(\s|%)/g, '').replace(leadingRE, '').replace(/,?\);?$/, '').split(',');
  1397. if ( list.length < 3 )
  1398. return this._error();
  1399. if ( list.length === 4 ) {
  1400. this.a( parseFloat( list.pop() ) );
  1401. // error state has been set to true in .a() if we passed NaN
  1402. if ( this.error )
  1403. return this;
  1404. }
  1405. for (var i = list.length - 1; i >= 0; i--) {
  1406. list[i] = parseInt(list[i], 10);
  1407. if ( isNaN( list[i] ) )
  1408. return this._error();
  1409. }
  1410. if ( color.match(/^rgb/) ) {
  1411. return this.fromRgb( {
  1412. r: list[0],
  1413. g: list[1],
  1414. b: list[2]
  1415. } );
  1416. } else if ( color.match(/^hsv/) ) {
  1417. return this.fromHsv( {
  1418. h: list[0],
  1419. s: list[1],
  1420. v: list[2]
  1421. } );
  1422. } else {
  1423. return this.fromHsl( {
  1424. h: list[0],
  1425. s: list[1],
  1426. l: list[2]
  1427. } );
  1428. }
  1429. } else {
  1430. // must be hex amirite?
  1431. return this.fromHex( color );
  1432. }
  1433. },
  1434. fromRgb: function( rgb, preserve ) {
  1435. if ( typeof rgb !== 'object' || rgb.r === undef || rgb.g === undef || rgb.b === undef )
  1436. return this._error();
  1437. this.error = false;
  1438. return this.fromInt( parseInt( ( rgb.r << 16 ) + ( rgb.g << 8 ) + rgb.b, 10 ), preserve );
  1439. },
  1440. fromHex: function( color ) {
  1441. color = color.replace(/^#/, '').replace(/^0x/, '');
  1442. if ( color.length === 3 ) {
  1443. color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2];
  1444. }
  1445. // rough error checking - this is where things go squirrely the most
  1446. this.error = ! /^[0-9A-F]{6}$/i.test( color );
  1447. return this.fromInt( parseInt( color, 16 ) );
  1448. },
  1449. fromHsl: function( hsl ) {
  1450. var r, g, b, q, p, h, s, l;
  1451. if ( typeof hsl !== 'object' || hsl.h === undef || hsl.s === undef || hsl.l === undef )
  1452. return this._error();
  1453. this._hsl = hsl; // store it
  1454. this._hSpace = 'hsl'; // implicit
  1455. h = hsl.h / 360; s = hsl.s / 100; l = hsl.l / 100;
  1456. if ( s === 0 ) {
  1457. r = g = b = l; // achromatic
  1458. }
  1459. else {
  1460. q = l < 0.5 ? l * ( 1 + s ) : l + s - l * s;
  1461. p = 2 * l - q;
  1462. r = this.hue2rgb( p, q, h + 1/3 );
  1463. g = this.hue2rgb( p, q, h );
  1464. b = this.hue2rgb( p, q, h - 1/3 );
  1465. }
  1466. return this.fromRgb( {
  1467. r: r * 255,
  1468. g: g * 255,
  1469. b: b * 255
  1470. }, true ); // true preserves hue/sat
  1471. },
  1472. fromHsv: function( hsv ) {
  1473. var h, s, v, r, g, b, i, f, p, q, t;
  1474. if ( typeof hsv !== 'object' || hsv.h === undef || hsv.s === undef || hsv.v === undef )
  1475. return this._error();
  1476. this._hsv = hsv; // store it
  1477. this._hSpace = 'hsv'; // implicit
  1478. h = hsv.h / 360; s = hsv.s / 100; v = hsv.v / 100;
  1479. i = Math.floor( h * 6 );
  1480. f = h * 6 - i;
  1481. p = v * ( 1 - s );
  1482. q = v * ( 1 - f * s );
  1483. t = v * ( 1 - ( 1 - f ) * s );
  1484. switch( i % 6 ) {
  1485. case 0:
  1486. r = v; g = t; b = p;
  1487. break;
  1488. case 1:
  1489. r = q; g = v; b = p;
  1490. break;
  1491. case 2:
  1492. r = p; g = v; b = t;
  1493. break;
  1494. case 3:
  1495. r = p; g = q; b = v;
  1496. break;
  1497. case 4:
  1498. r = t; g = p; b = v;
  1499. break;
  1500. case 5:
  1501. r = v; g = p; b = q;
  1502. break;
  1503. }
  1504. return this.fromRgb( {
  1505. r: r * 255,
  1506. g: g * 255,
  1507. b: b * 255
  1508. }, true ); // true preserves hue/sat
  1509. },
  1510. // everything comes down to fromInt
  1511. fromInt: function( color, preserve ) {
  1512. this._color = parseInt( color, 10 );
  1513. if ( isNaN( this._color ) )
  1514. this._color = 0;
  1515. // let's coerce things
  1516. if ( this._color > 16777215 )
  1517. this._color = 16777215;
  1518. else if ( this._color < 0 )
  1519. this._color = 0;
  1520. // let's not do weird things
  1521. if ( preserve === undef ) {
  1522. this._hsv.h = this._hsv.s = this._hsl.h = this._hsl.s = 0;
  1523. }
  1524. // EVENT GOES HERE
  1525. return this;
  1526. },
  1527. hue2rgb: function( p, q, t ) {
  1528. if ( t < 0 ) {
  1529. t += 1;
  1530. }
  1531. if ( t > 1 ) {
  1532. t -= 1;
  1533. }
  1534. if ( t < 1/6 ) {
  1535. return p + ( q - p ) * 6 * t;
  1536. }
  1537. if ( t < 1/2 ) {
  1538. return q;
  1539. }
  1540. if ( t < 2/3 ) {
  1541. return p + ( q - p ) * ( 2/3 - t ) * 6;
  1542. }
  1543. return p;
  1544. },
  1545. toString: function() {
  1546. if ( this._alpha < 1 ) {
  1547. return this.toCSS('rgba', this._alpha).replace(/\s+/g, '');
  1548. }
  1549. var hex = parseInt( this._color, 10 ).toString( 16 );
  1550. if ( this.error )
  1551. return '';
  1552. // maybe left pad it
  1553. if ( hex.length < 6 ) {
  1554. for (var i = 6 - hex.length - 1; i >= 0; i--) {
  1555. hex = '0' + hex;
  1556. }
  1557. }
  1558. return '#' + hex;
  1559. },
  1560. toCSS: function( type, alpha ) {
  1561. type = type || 'hex';
  1562. alpha = parseFloat( alpha || this._alpha );
  1563. switch ( type ) {
  1564. case 'rgb':
  1565. case 'rgba':
  1566. var rgb = this.toRgb();
  1567. if ( alpha < 1 ) {
  1568. return "rgba( " + rgb.r + ", " + rgb.g + ", " + rgb.b + ", " + alpha + " )";
  1569. }
  1570. else {
  1571. return "rgb( " + rgb.r + ", " + rgb.g + ", " + rgb.b + " )";
  1572. }
  1573. break;
  1574. case 'hsl':
  1575. case 'hsla':
  1576. var hsl = this.toHsl();
  1577. if ( alpha < 1 ) {
  1578. return "hsla( " + hsl.h + ", " + hsl.s + "%, " + hsl.l + "%, " + alpha + " )";
  1579. }
  1580. else {
  1581. return "hsl( " + hsl.h + ", " + hsl.s + "%, " + hsl.l + "% )";
  1582. }
  1583. break;
  1584. default:
  1585. return this.toString();
  1586. }
  1587. },
  1588. toRgb: function() {
  1589. return {
  1590. r: 255 & ( this._color >> 16 ),
  1591. g: 255 & ( this._color >> 8 ),
  1592. b: 255 & ( this._color )
  1593. };
  1594. },
  1595. toHsl: function() {
  1596. var rgb = this.toRgb();
  1597. var r = rgb.r / 255, g = rgb.g / 255, b = rgb.b / 255;
  1598. var max = Math.max( r, g, b ), min = Math.min( r, g, b );
  1599. var h, s, l = ( max + min ) / 2;
  1600. if ( max === min ) {
  1601. h = s = 0; // achromatic
  1602. } else {
  1603. var d = max - min;
  1604. s = l > 0.5 ? d / ( 2 - max - min ) : d / ( max + min );
  1605. switch ( max ) {
  1606. case r: h = ( g - b ) / d + ( g < b ? 6 : 0 );
  1607. break;
  1608. case g: h = ( b - r ) / d + 2;
  1609. break;
  1610. case b: h = ( r - g ) / d + 4;
  1611. break;
  1612. }
  1613. h /= 6;
  1614. }
  1615. // maintain hue & sat if we've been manipulating things in the HSL space.
  1616. h = Math.round( h * 360 );
  1617. if ( h === 0 && this._hsl.h !== h ) {
  1618. h = this._hsl.h;
  1619. }
  1620. s = Math.round( s * 100 );
  1621. if ( s === 0 && this._hsl.s ) {
  1622. s = this._hsl.s;
  1623. }
  1624. return {
  1625. h: h,
  1626. s: s,
  1627. l: Math.round( l * 100 )
  1628. };
  1629. },
  1630. toHsv: function() {
  1631. var rgb = this.toRgb();
  1632. var r = rgb.r / 255, g = rgb.g / 255, b = rgb.b / 255;
  1633. var max = Math.max( r, g, b ), min = Math.min( r, g, b );
  1634. var h, s, v = max;
  1635. var d = max - min;
  1636. s = max === 0 ? 0 : d / max;
  1637. if ( max === min ) {
  1638. h = s = 0; // achromatic
  1639. } else {
  1640. switch( max ){
  1641. case r:
  1642. h = ( g - b ) / d + ( g < b ? 6 : 0 );
  1643. break;
  1644. case g:
  1645. h = ( b - r ) / d + 2;
  1646. break;
  1647. case b:
  1648. h = ( r - g ) / d + 4;
  1649. break;
  1650. }
  1651. h /= 6;
  1652. }
  1653. // maintain hue & sat if we've been manipulating things in the HSV space.
  1654. h = Math.round( h * 360 );
  1655. if ( h === 0 && this._hsv.h !== h ) {
  1656. h = this._hsv.h;
  1657. }
  1658. s = Math.round( s * 100 );
  1659. if ( s === 0 && this._hsv.s ) {
  1660. s = this._hsv.s;
  1661. }
  1662. return {
  1663. h: h,
  1664. s: s,
  1665. v: Math.round( v * 100 )
  1666. };
  1667. },
  1668. toInt: function() {
  1669. return this._color;
  1670. },
  1671. toIEOctoHex: function() {
  1672. // AARRBBGG
  1673. var hex = this.toString();
  1674. var AA = parseInt( 255 * this._alpha, 10 ).toString(16);
  1675. if ( AA.length === 1 ) {
  1676. AA = '0' + AA;
  1677. }
  1678. return '#' + AA + hex.replace(/^#/, '' );
  1679. },
  1680. toLuminosity: function() {
  1681. var rgb = this.toRgb();
  1682. return 0.2126 * Math.pow( rgb.r / 255, 2.2 ) + 0.7152 * Math.pow( rgb.g / 255, 2.2 ) + 0.0722 * Math.pow( rgb.b / 255, 2.2);
  1683. },
  1684. getDistanceLuminosityFrom: function( color ) {
  1685. if ( ! ( color instanceof Color ) ) {
  1686. throw 'getDistanceLuminosityFrom requires a Color object';
  1687. }
  1688. var lum1 = this.toLuminosity();
  1689. var lum2 = color.toLuminosity();
  1690. if ( lum1 > lum2 ) {
  1691. return ( lum1 + 0.05 ) / ( lum2 + 0.05 );
  1692. }
  1693. else {
  1694. return ( lum2 + 0.05 ) / ( lum1 + 0.05 );
  1695. }
  1696. },
  1697. getMaxContrastColor: function() {
  1698. var lum = this.toLuminosity();
  1699. var hex = ( lum >= 0.5 ) ? '000000' : 'ffffff';
  1700. return new Color( hex );
  1701. },
  1702. getReadableContrastingColor: function( bgColor, minContrast ) {
  1703. if ( ! ( bgColor instanceof Color ) ) {
  1704. return this;
  1705. }
  1706. // you shouldn't use less than 5, but you might want to.
  1707. var targetContrast = ( minContrast === undef ) ? 5 : minContrast;
  1708. // working things
  1709. var contrast = bgColor.getDistanceLuminosityFrom( this );
  1710. var maxContrastColor = bgColor.getMaxContrastColor();
  1711. var maxContrast = maxContrastColor.getDistanceLuminosityFrom( bgColor );
  1712. // if current max contrast is less than the target contrast, we had wishful thinking.
  1713. // still, go max
  1714. if ( maxContrast <= targetContrast ) {
  1715. return maxContrastColor;
  1716. }
  1717. // or, we might already have sufficient contrast
  1718. else if ( contrast >= targetContrast ) {
  1719. return this;
  1720. }
  1721. var incr = ( 0 === maxContrastColor.toInt() ) ? -1 : 1;
  1722. while ( contrast < targetContrast ) {
  1723. this.l( incr, true ); // 2nd arg turns this into an incrementer
  1724. contrast = this.getDistanceLuminosityFrom( bgColor );
  1725. // infininite loop prevention: you never know.
  1726. if ( this._color === 0 || this._color === 16777215 ) {
  1727. break;
  1728. }
  1729. }
  1730. return this;
  1731. },
  1732. a: function( val ) {
  1733. if ( val === undef )
  1734. return this._alpha;
  1735. var a = parseFloat( val );
  1736. if ( isNaN( a ) )
  1737. return this._error();
  1738. this._alpha = a;
  1739. return this;
  1740. },
  1741. // TRANSFORMS
  1742. darken: function( amount ) {
  1743. amount = amount || 5;
  1744. return this.l( - amount, true );
  1745. },
  1746. lighten: function( amount ) {
  1747. amount = amount || 5;
  1748. return this.l( amount, true );
  1749. },
  1750. saturate: function( amount ) {
  1751. amount = amount || 15;
  1752. return this.s( amount, true );
  1753. },
  1754. desaturate: function( amount ) {
  1755. amount = amount || 15;
  1756. return this.s( - amount, true );
  1757. },
  1758. toGrayscale: function() {
  1759. return this.setHSpace('hsl').s( 0 );
  1760. },
  1761. getComplement: function() {
  1762. return this.h( 180, true );
  1763. },
  1764. getSplitComplement: function( step ) {
  1765. step = step || 1;
  1766. var incr = 180 + ( step * 30 );
  1767. return this.h( incr, true );
  1768. },
  1769. getAnalog: function( step ) {
  1770. step = step || 1;
  1771. var incr = step * 30;
  1772. return this.h( incr, true );
  1773. },
  1774. getTetrad: function( step ) {
  1775. step = step || 1;
  1776. var incr = step * 60;
  1777. return this.h( incr, true );
  1778. },
  1779. getTriad: function( step ) {
  1780. step = step || 1;
  1781. var incr = step * 120;
  1782. return this.h( incr, true );
  1783. },
  1784. _partial: function( key ) {
  1785. var prop = shortProps[key];
  1786. return function( val, incr ) {
  1787. var color = this._spaceFunc('to', prop.space);
  1788. // GETTER
  1789. if ( val === undef )
  1790. return color[key];
  1791. // INCREMENT
  1792. if ( incr === true )
  1793. val = color[key] + val;
  1794. // MOD & RANGE
  1795. if ( prop.mod )
  1796. val = val % prop.mod;
  1797. if ( prop.range )
  1798. val = ( val < prop.range[0] ) ? prop.range[0] : ( val > prop.range[1] ) ? prop.range[1] : val;
  1799. // NEW VALUE
  1800. color[key] = val;
  1801. return this._spaceFunc('from', prop.space, color);
  1802. };
  1803. },
  1804. _spaceFunc: function( dir, s, val ) {
  1805. var space = s || this._hSpace,
  1806. funcName = dir + space.charAt(0).toUpperCase() + space.substr(1);
  1807. return this[funcName](val);
  1808. }
  1809. };
  1810. var shortProps = {
  1811. h: {
  1812. mod: 360
  1813. },
  1814. s: {
  1815. range: [0,100]
  1816. },
  1817. l: {
  1818. space: 'hsl',
  1819. range: [0,100]
  1820. },
  1821. v: {
  1822. space: 'hsv',
  1823. range: [0,100]
  1824. },
  1825. r: {
  1826. space: 'rgb',
  1827. range: [0,255]
  1828. },
  1829. g: {
  1830. space: 'rgb',
  1831. range: [0,255]
  1832. },
  1833. b: {
  1834. space: 'rgb',
  1835. range: [0,255]
  1836. }
  1837. };
  1838. for ( var key in shortProps ) {
  1839. if ( shortProps.hasOwnProperty( key ) )
  1840. Color.fn[key] = Color.fn._partial(key);
  1841. }
  1842. // play nicely with Node + browser
  1843. if ( typeof exports === 'object' ) {
  1844. module.exports = Color;
  1845. } else {
  1846. global.FLBuilderColor = Color;
  1847. global.Color = Color; // Add for backwards compatibility.
  1848. }
  1849. }(this));