spectrum.js 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109
  1. // Spectrum Colorpicker v1.3.4
  2. // https://github.com/bgrins/spectrum
  3. // Author: Brian Grinstead
  4. // License: MIT
  5. (function (window, $, undefined) {
  6. "use strict";
  7. var defaultOpts = {
  8. // Callbacks
  9. beforeShow: noop,
  10. move: noop,
  11. change: noop,
  12. show: noop,
  13. hide: noop,
  14. // Options
  15. color: false,
  16. flat: false,
  17. showInput: false,
  18. allowEmpty: false,
  19. showButtons: true,
  20. clickoutFiresChange: false,
  21. showInitial: false,
  22. showPalette: false,
  23. showPaletteOnly: false,
  24. showSelectionPalette: true,
  25. localStorageKey: false,
  26. appendTo: "body",
  27. maxSelectionSize: 7,
  28. cancelText: "cancel",
  29. chooseText: "choose",
  30. clearText: "Clear Color Selection",
  31. noColorSelectedText: "No Color Selected",
  32. preferredFormat: false,
  33. className: "", // Deprecated - use containerClassName and replacerClassName instead.
  34. containerClassName: "",
  35. replacerClassName: "",
  36. showAlpha: false,
  37. theme: "sp-light",
  38. palette: [["#ffffff", "#000000", "#ff0000", "#ff8000", "#ffff00", "#008000", "#0000ff", "#4b0082", "#9400d3"]],
  39. selectionPalette: [],
  40. disabled: false
  41. },
  42. spectrums = [],
  43. IE = !!/msie/i.exec( window.navigator.userAgent ),
  44. rgbaSupport = (function() {
  45. function contains( str, substr ) {
  46. return !!~('' + str).indexOf(substr);
  47. }
  48. var elem = document.createElement('div');
  49. var style = elem.style;
  50. style.cssText = 'background-color:rgba(0,0,0,.5)';
  51. return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');
  52. })(),
  53. inputTypeColorSupport = (function() {
  54. var colorInput = $("<input type='color' value='!' />")[0];
  55. return colorInput.type === "color" && colorInput.value !== "!";
  56. })(),
  57. replaceInput = [
  58. "<div class='sp-replacer'>",
  59. "<div class='sp-preview'><div class='sp-preview-inner'></div></div>",
  60. "<div class='sp-dd'>&#9660;</div>",
  61. "</div>"
  62. ].join(''),
  63. markup = (function () {
  64. // IE does not support gradients with multiple stops, so we need to simulate
  65. // that for the rainbow slider with 8 divs that each have a single gradient
  66. var gradientFix = "";
  67. if (IE) {
  68. for (var i = 1; i <= 6; i++) {
  69. gradientFix += "<div class='sp-" + i + "'></div>";
  70. }
  71. }
  72. return [
  73. "<div class='sp-container sp-hidden'>",
  74. "<div class='sp-palette-container'>",
  75. "<div class='sp-palette sp-thumb sp-cf'></div>",
  76. "</div>",
  77. "<div class='sp-picker-container'>",
  78. "<div class='sp-top sp-cf'>",
  79. "<div class='sp-fill'></div>",
  80. "<div class='sp-top-inner'>",
  81. "<div class='sp-color'>",
  82. "<div class='sp-sat'>",
  83. "<div class='sp-val'>",
  84. "<div class='sp-dragger'></div>",
  85. "</div>",
  86. "</div>",
  87. "</div>",
  88. "<div class='sp-clear sp-clear-display'>",
  89. "</div>",
  90. "<div class='sp-hue'>",
  91. "<div class='sp-slider'></div>",
  92. gradientFix,
  93. "</div>",
  94. "</div>",
  95. "<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle'></div></div></div>",
  96. "</div>",
  97. "<div class='sp-input-container sp-cf'>",
  98. "<input class='sp-input' type='text' spellcheck='false' />",
  99. "</div>",
  100. "<div class='sp-initial sp-thumb sp-cf'></div>",
  101. "<div class='sp-button-container sp-cf'>",
  102. "<a class='sp-cancel' href='#'></a>",
  103. "<button type='button' class='sp-choose button-secondary'></button>",
  104. "</div>",
  105. "</div>",
  106. "</div>"
  107. ].join("");
  108. })();
  109. function paletteTemplate (p, color, className, opts) {
  110. var html = [];
  111. for (var i = 0; i < p.length; i++) {
  112. var current = p[i];
  113. if(current) {
  114. var tiny = tinycolor(current);
  115. var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light";
  116. c += (tinycolor.equals(color, current)) ? " sp-thumb-active" : "";
  117. var formattedString = tiny.toString(opts.preferredFormat || "rgb");
  118. var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter();
  119. html.push('<span title="' + formattedString + '" data-color="' + tiny.toRgbString() + '" class="' + c + '"><span class="sp-thumb-inner" style="' + swatchStyle + ';" /></span>');
  120. } else {
  121. var cls = 'sp-clear-display';
  122. html.push($('<div />')
  123. .append($('<span data-color="" style="background-color:transparent;" class="' + cls + '"></span>')
  124. .attr('title', opts.noColorSelectedText)
  125. )
  126. .html()
  127. );
  128. }
  129. }
  130. return "<div class='sp-cf " + className + "'>" + html.join('') + "</div>";
  131. }
  132. function hideAll() {
  133. for (var i = 0; i < spectrums.length; i++) {
  134. if (spectrums[i]) {
  135. spectrums[i].hide();
  136. }
  137. }
  138. }
  139. function instanceOptions(o, callbackContext) {
  140. var opts = $.extend({}, defaultOpts, o);
  141. opts.callbacks = {
  142. 'move': bind(opts.move, callbackContext),
  143. 'change': bind(opts.change, callbackContext),
  144. 'show': bind(opts.show, callbackContext),
  145. 'hide': bind(opts.hide, callbackContext),
  146. 'beforeShow': bind(opts.beforeShow, callbackContext)
  147. };
  148. return opts;
  149. }
  150. function spectrum(element, o) {
  151. var opts = instanceOptions(o, element),
  152. flat = opts.flat,
  153. showSelectionPalette = opts.showSelectionPalette,
  154. localStorageKey = opts.localStorageKey,
  155. theme = opts.theme,
  156. callbacks = opts.callbacks,
  157. resize = throttle(reflow, 10),
  158. visible = false,
  159. dragWidth = 0,
  160. dragHeight = 0,
  161. dragHelperHeight = 0,
  162. slideHeight = 0,
  163. slideWidth = 0,
  164. alphaWidth = 0,
  165. alphaSlideHelperWidth = 0,
  166. slideHelperHeight = 0,
  167. currentHue = 0,
  168. currentSaturation = 0,
  169. currentValue = 0,
  170. currentAlpha = 1,
  171. palette = [],
  172. paletteArray = [],
  173. paletteLookup = {},
  174. selectionPalette = opts.selectionPalette.slice(0),
  175. maxSelectionSize = opts.maxSelectionSize,
  176. draggingClass = "sp-dragging",
  177. shiftMovementDirection = null;
  178. var doc = element.ownerDocument,
  179. body = doc.body,
  180. boundElement = $(element),
  181. disabled = false,
  182. container = $(markup, doc).addClass(theme),
  183. dragger = container.find(".sp-color"),
  184. dragHelper = container.find(".sp-dragger"),
  185. slider = container.find(".sp-hue"),
  186. slideHelper = container.find(".sp-slider"),
  187. alphaSliderInner = container.find(".sp-alpha-inner"),
  188. alphaSlider = container.find(".sp-alpha"),
  189. alphaSlideHelper = container.find(".sp-alpha-handle"),
  190. textInput = container.find(".sp-input"),
  191. paletteContainer = container.find(".sp-palette"),
  192. initialColorContainer = container.find(".sp-initial"),
  193. cancelButton = container.find(".sp-cancel"),
  194. clearButton = container.find(".sp-clear"),
  195. chooseButton = container.find(".sp-choose"),
  196. isInput = boundElement.is("input"),
  197. isInputTypeColor = isInput && inputTypeColorSupport && boundElement.attr("type") === "color",
  198. shouldReplace = isInput && !flat,
  199. replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]),
  200. offsetElement = (shouldReplace) ? replacer : boundElement,
  201. previewElement = replacer.find(".sp-preview-inner"),
  202. initialColor = opts.color || (isInput && boundElement.val()),
  203. colorOnShow = false,
  204. preferredFormat = opts.preferredFormat,
  205. currentPreferredFormat = preferredFormat,
  206. clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange,
  207. isEmpty = !initialColor,
  208. allowEmpty = opts.allowEmpty && !isInputTypeColor;
  209. function applyOptions() {
  210. if (opts.showPaletteOnly) {
  211. opts.showPalette = true;
  212. }
  213. if (opts.palette) {
  214. palette = opts.palette.slice(0);
  215. paletteArray = $.isArray(palette[0]) ? palette : [palette];
  216. paletteLookup = {};
  217. for (var i = 0; i < paletteArray.length; i++) {
  218. for (var j = 0; j < paletteArray[i].length; j++) {
  219. var rgb = tinycolor(paletteArray[i][j]).toRgbString();
  220. paletteLookup[rgb] = true;
  221. }
  222. }
  223. }
  224. container.toggleClass("sp-flat", flat);
  225. container.toggleClass("sp-input-disabled", !opts.showInput);
  226. container.toggleClass("sp-alpha-enabled", opts.showAlpha);
  227. container.toggleClass("sp-clear-enabled", allowEmpty);
  228. container.toggleClass("sp-buttons-disabled", !opts.showButtons);
  229. container.toggleClass("sp-palette-disabled", !opts.showPalette);
  230. container.toggleClass("sp-palette-only", opts.showPaletteOnly);
  231. container.toggleClass("sp-initial-disabled", !opts.showInitial);
  232. container.addClass(opts.className).addClass(opts.containerClassName);
  233. reflow();
  234. }
  235. function initialize() {
  236. if (IE) {
  237. container.find("*:not(input)").attr("unselectable", "on");
  238. }
  239. applyOptions();
  240. if (shouldReplace) {
  241. boundElement.after(replacer).hide();
  242. }
  243. if (!allowEmpty) {
  244. clearButton.hide();
  245. }
  246. if (flat) {
  247. boundElement.after(container).hide();
  248. }
  249. else {
  250. var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo);
  251. if (appendTo.length !== 1) {
  252. appendTo = $("body");
  253. }
  254. appendTo.append(container);
  255. }
  256. updateSelectionPaletteFromStorage();
  257. offsetElement.bind("click.spectrum touchstart.spectrum", function (e) {
  258. if (!disabled) {
  259. toggle();
  260. }
  261. e.stopPropagation();
  262. if (!$(e.target).is("input")) {
  263. e.preventDefault();
  264. }
  265. });
  266. if(boundElement.is(":disabled") || (opts.disabled === true)) {
  267. disable();
  268. }
  269. // Prevent clicks from bubbling up to document. This would cause it to be hidden.
  270. container.click(stopPropagation);
  271. // Handle user typed input
  272. textInput.change(setFromTextInput);
  273. textInput.bind("paste", function () {
  274. setTimeout(setFromTextInput, 1);
  275. });
  276. textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } });
  277. cancelButton.text(opts.cancelText);
  278. cancelButton.bind("click.spectrum", function (e) {
  279. e.stopPropagation();
  280. e.preventDefault();
  281. hide("cancel");
  282. });
  283. clearButton.attr("title", opts.clearText);
  284. clearButton.bind("click.spectrum", function (e) {
  285. e.stopPropagation();
  286. e.preventDefault();
  287. isEmpty = true;
  288. move();
  289. if(flat) {
  290. //for the flat style, this is a change event
  291. updateOriginalInput(true);
  292. }
  293. });
  294. chooseButton.text(opts.chooseText);
  295. chooseButton.bind("click.spectrum", function (e) {
  296. e.stopPropagation();
  297. e.preventDefault();
  298. if (isValid()) {
  299. updateOriginalInput(true);
  300. hide();
  301. }
  302. });
  303. draggable(alphaSlider, function (dragX, dragY, e) {
  304. currentAlpha = (dragX / alphaWidth);
  305. isEmpty = false;
  306. if (e.shiftKey) {
  307. currentAlpha = Math.round(currentAlpha * 10) / 10;
  308. }
  309. move();
  310. }, dragStart, dragStop);
  311. draggable(slider, function (dragX, dragY) {
  312. currentHue = parseFloat(dragY / slideHeight);
  313. isEmpty = false;
  314. if (!opts.showAlpha) {
  315. currentAlpha = 1;
  316. }
  317. move();
  318. }, dragStart, dragStop);
  319. draggable(dragger, function (dragX, dragY, e) {
  320. // shift+drag should snap the movement to either the x or y axis.
  321. if (!e.shiftKey) {
  322. shiftMovementDirection = null;
  323. }
  324. else if (!shiftMovementDirection) {
  325. var oldDragX = currentSaturation * dragWidth;
  326. var oldDragY = dragHeight - (currentValue * dragHeight);
  327. var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY);
  328. shiftMovementDirection = furtherFromX ? "x" : "y";
  329. }
  330. var setSaturation = !shiftMovementDirection || shiftMovementDirection === "x";
  331. var setValue = !shiftMovementDirection || shiftMovementDirection === "y";
  332. if (setSaturation) {
  333. currentSaturation = parseFloat(dragX / dragWidth);
  334. }
  335. if (setValue) {
  336. currentValue = parseFloat((dragHeight - dragY) / dragHeight);
  337. }
  338. isEmpty = false;
  339. if (!opts.showAlpha) {
  340. currentAlpha = 1;
  341. }
  342. move();
  343. }, dragStart, dragStop);
  344. if (!!initialColor) {
  345. set(initialColor);
  346. // In case color was black - update the preview UI and set the format
  347. // since the set function will not run (default color is black).
  348. updateUI();
  349. currentPreferredFormat = preferredFormat || tinycolor(initialColor).format;
  350. addColorToSelectionPalette(initialColor);
  351. }
  352. else {
  353. updateUI();
  354. }
  355. if (flat) {
  356. show();
  357. }
  358. function paletteElementClick(e) {
  359. if (e.data && e.data.ignore) {
  360. set($(e.target).closest(".sp-thumb-el").data("color"));
  361. move();
  362. }
  363. else {
  364. set($(e.target).closest(".sp-thumb-el").data("color"));
  365. move();
  366. updateOriginalInput(true);
  367. hide();
  368. }
  369. return false;
  370. }
  371. var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum";
  372. paletteContainer.delegate(".sp-thumb-el", paletteEvent, paletteElementClick);
  373. initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, paletteElementClick);
  374. }
  375. function updateSelectionPaletteFromStorage() {
  376. if (localStorageKey && window.localStorage) {
  377. // Migrate old palettes over to new format. May want to remove this eventually.
  378. try {
  379. var oldPalette = window.localStorage[localStorageKey].split(",#");
  380. if (oldPalette.length > 1) {
  381. delete window.localStorage[localStorageKey];
  382. $.each(oldPalette, function(i, c) {
  383. addColorToSelectionPalette(c);
  384. });
  385. }
  386. }
  387. catch(e) { }
  388. try {
  389. selectionPalette = window.localStorage[localStorageKey].split(";");
  390. }
  391. catch (e) { }
  392. }
  393. }
  394. function addColorToSelectionPalette(color) {
  395. if (showSelectionPalette) {
  396. var rgb = tinycolor(color).toRgbString();
  397. if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) {
  398. selectionPalette.push(rgb);
  399. while(selectionPalette.length > maxSelectionSize) {
  400. selectionPalette.shift();
  401. }
  402. }
  403. if (localStorageKey && window.localStorage) {
  404. try {
  405. window.localStorage[localStorageKey] = selectionPalette.join(";");
  406. }
  407. catch(e) { }
  408. }
  409. }
  410. }
  411. function getUniqueSelectionPalette() {
  412. var unique = [];
  413. if (opts.showPalette) {
  414. for (var i = 0; i < selectionPalette.length; i++) {
  415. var rgb = tinycolor(selectionPalette[i]).toRgbString();
  416. if (!paletteLookup[rgb]) {
  417. unique.push(selectionPalette[i]);
  418. }
  419. }
  420. }
  421. return unique.reverse().slice(0, opts.maxSelectionSize);
  422. }
  423. function drawPalette() {
  424. var currentColor = get();
  425. var html = $.map(paletteArray, function (palette, i) {
  426. return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i, opts);
  427. });
  428. updateSelectionPaletteFromStorage();
  429. if (selectionPalette) {
  430. html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection", opts));
  431. }
  432. paletteContainer.html(html.join(""));
  433. }
  434. function drawInitial() {
  435. if (opts.showInitial) {
  436. var initial = colorOnShow;
  437. var current = get();
  438. initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial", opts));
  439. }
  440. }
  441. function dragStart() {
  442. if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) {
  443. reflow();
  444. }
  445. container.addClass(draggingClass);
  446. shiftMovementDirection = null;
  447. boundElement.trigger('dragstart.spectrum', [ get() ]);
  448. }
  449. function dragStop() {
  450. container.removeClass(draggingClass);
  451. boundElement.trigger('dragstop.spectrum', [ get() ]);
  452. }
  453. function setFromTextInput() {
  454. var value = textInput.val();
  455. if ((value === null || value === "") && allowEmpty) {
  456. set(null);
  457. updateOriginalInput(true);
  458. }
  459. else {
  460. var tiny = tinycolor(value);
  461. if (tiny.isValid()) {
  462. set(tiny);
  463. updateOriginalInput(true);
  464. }
  465. else {
  466. textInput.addClass("sp-validation-error");
  467. }
  468. }
  469. }
  470. function toggle() {
  471. if (visible) {
  472. hide();
  473. }
  474. else {
  475. show();
  476. }
  477. }
  478. function show() {
  479. var event = $.Event('beforeShow.spectrum');
  480. if (visible) {
  481. reflow();
  482. return;
  483. }
  484. boundElement.trigger(event, [ get() ]);
  485. if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) {
  486. return;
  487. }
  488. hideAll();
  489. visible = true;
  490. $(doc).bind("click.spectrum", hide);
  491. $(window).bind("resize.spectrum", resize);
  492. replacer.addClass("sp-active");
  493. container.removeClass("sp-hidden");
  494. reflow();
  495. updateUI();
  496. colorOnShow = get();
  497. drawInitial();
  498. callbacks.show(colorOnShow);
  499. boundElement.trigger('show.spectrum', [ colorOnShow ]);
  500. }
  501. function hide(e) {
  502. // Return on right click
  503. if (e && e.type == "click" && e.button == 2) { return; }
  504. // Return if hiding is unnecessary
  505. if (!visible || flat) { return; }
  506. visible = false;
  507. $(doc).unbind("click.spectrum", hide);
  508. $(window).unbind("resize.spectrum", resize);
  509. replacer.removeClass("sp-active");
  510. container.addClass("sp-hidden");
  511. var colorHasChanged = !tinycolor.equals(get(), colorOnShow);
  512. if (colorHasChanged) {
  513. if (clickoutFiresChange && e !== "cancel") {
  514. updateOriginalInput(true);
  515. }
  516. else {
  517. revert();
  518. }
  519. }
  520. callbacks.hide(get());
  521. boundElement.trigger('hide.spectrum', [ get() ]);
  522. }
  523. function revert() {
  524. set(colorOnShow, true);
  525. }
  526. function set(color, ignoreFormatChange) {
  527. if (tinycolor.equals(color, get())) {
  528. // Update UI just in case a validation error needs
  529. // to be cleared.
  530. updateUI();
  531. return;
  532. }
  533. var newColor, newHsv;
  534. if (!color && allowEmpty) {
  535. isEmpty = true;
  536. } else {
  537. isEmpty = false;
  538. newColor = tinycolor(color);
  539. newHsv = newColor.toHsv();
  540. currentHue = (newHsv.h % 360) / 360;
  541. currentSaturation = newHsv.s;
  542. currentValue = newHsv.v;
  543. currentAlpha = newHsv.a;
  544. }
  545. updateUI();
  546. if (newColor && newColor.isValid() && !ignoreFormatChange) {
  547. currentPreferredFormat = preferredFormat || newColor.getFormat();
  548. }
  549. }
  550. function get(opts) {
  551. opts = opts || { };
  552. if (allowEmpty && isEmpty) {
  553. return null;
  554. }
  555. return tinycolor.fromRatio({
  556. h: currentHue,
  557. s: currentSaturation,
  558. v: currentValue,
  559. a: Math.round(currentAlpha * 100) / 100
  560. }, { format: opts.format || currentPreferredFormat });
  561. }
  562. function isValid() {
  563. return !textInput.hasClass("sp-validation-error");
  564. }
  565. function move() {
  566. updateUI();
  567. callbacks.move(get());
  568. boundElement.trigger('move.spectrum', [ get() ]);
  569. }
  570. function updateUI() {
  571. textInput.removeClass("sp-validation-error");
  572. updateHelperLocations();
  573. // Update dragger background color (gradients take care of saturation and value).
  574. var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 });
  575. dragger.css("background-color", flatColor.toHexString());
  576. // Get a format that alpha will be included in (hex and names ignore alpha)
  577. var format = currentPreferredFormat;
  578. if (currentAlpha < 1 && !(currentAlpha === 0 && format === "name")) {
  579. if (format === "hex" || format === "hex3" || format === "hex6" || format === "name") {
  580. format = "rgb";
  581. }
  582. }
  583. var realColor = get({ format: format }),
  584. displayColor = '';
  585. //reset background info for preview element
  586. previewElement.removeClass("sp-clear-display");
  587. previewElement.css('background-color', 'transparent');
  588. if (!realColor && allowEmpty) {
  589. // Update the replaced elements background with icon indicating no color selection
  590. previewElement.addClass("sp-clear-display");
  591. }
  592. else {
  593. var realHex = realColor.toHexString(),
  594. realRgb = realColor.toRgbString();
  595. // Update the replaced elements background color (with actual selected color)
  596. if (rgbaSupport || realColor.alpha === 1) {
  597. previewElement.css("background-color", realRgb);
  598. }
  599. else {
  600. previewElement.css("background-color", "transparent");
  601. previewElement.css("filter", realColor.toFilter());
  602. }
  603. if (opts.showAlpha) {
  604. var rgb = realColor.toRgb();
  605. rgb.a = 0;
  606. var realAlpha = tinycolor(rgb).toRgbString();
  607. var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")";
  608. if (IE) {
  609. alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex));
  610. }
  611. else {
  612. alphaSliderInner.css("background", "-webkit-" + gradient);
  613. alphaSliderInner.css("background", "-moz-" + gradient);
  614. alphaSliderInner.css("background", "-ms-" + gradient);
  615. // Use current syntax gradient on unprefixed property.
  616. alphaSliderInner.css("background",
  617. "linear-gradient(to right, " + realAlpha + ", " + realHex + ")");
  618. }
  619. }
  620. displayColor = realColor.toString(format);
  621. }
  622. // Update the text entry input as it changes happen
  623. if (opts.showInput) {
  624. textInput.val(displayColor);
  625. }
  626. if (opts.showPalette) {
  627. drawPalette();
  628. }
  629. drawInitial();
  630. }
  631. function updateHelperLocations() {
  632. var s = currentSaturation;
  633. var v = currentValue;
  634. if(allowEmpty && isEmpty) {
  635. //if selected color is empty, hide the helpers
  636. alphaSlideHelper.hide();
  637. slideHelper.hide();
  638. dragHelper.hide();
  639. }
  640. else {
  641. //make sure helpers are visible
  642. alphaSlideHelper.show();
  643. slideHelper.show();
  644. dragHelper.show();
  645. // Where to show the little circle in that displays your current selected color
  646. var dragX = s * dragWidth;
  647. var dragY = dragHeight - (v * dragHeight);
  648. dragX = Math.max(
  649. -dragHelperHeight,
  650. Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight)
  651. );
  652. dragY = Math.max(
  653. -dragHelperHeight,
  654. Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight)
  655. );
  656. dragHelper.css({
  657. "top": dragY + "px",
  658. "left": dragX + "px"
  659. });
  660. var alphaX = currentAlpha * alphaWidth;
  661. alphaSlideHelper.css({
  662. "left": (alphaX - (alphaSlideHelperWidth / 2)) + "px"
  663. });
  664. // Where to show the bar that displays your current selected hue
  665. var slideY = (currentHue) * slideHeight;
  666. slideHelper.css({
  667. "top": (slideY - slideHelperHeight) + "px"
  668. });
  669. }
  670. }
  671. function updateOriginalInput(fireCallback) {
  672. var color = get(),
  673. displayColor = '',
  674. hasChanged = !tinycolor.equals(color, colorOnShow);
  675. if (color) {
  676. displayColor = color.toString(currentPreferredFormat);
  677. // Update the selection palette with the current color
  678. addColorToSelectionPalette(color);
  679. }
  680. if (isInput) {
  681. boundElement.val(displayColor);
  682. }
  683. colorOnShow = color;
  684. if (fireCallback && hasChanged) {
  685. callbacks.change(color);
  686. boundElement.trigger('change', [ color ]);
  687. }
  688. }
  689. function reflow() {
  690. dragWidth = dragger.width();
  691. dragHeight = dragger.height();
  692. dragHelperHeight = dragHelper.height();
  693. slideWidth = slider.width();
  694. slideHeight = slider.height();
  695. slideHelperHeight = slideHelper.height();
  696. alphaWidth = alphaSlider.width();
  697. alphaSlideHelperWidth = alphaSlideHelper.width();
  698. if (!flat) {
  699. container.css("position", "absolute");
  700. container.offset(getOffset(container, offsetElement));
  701. }
  702. updateHelperLocations();
  703. if (opts.showPalette) {
  704. drawPalette();
  705. }
  706. boundElement.trigger('reflow.spectrum');
  707. }
  708. function destroy() {
  709. boundElement.show();
  710. offsetElement.unbind("click.spectrum touchstart.spectrum");
  711. container.remove();
  712. replacer.remove();
  713. spectrums[spect.id] = null;
  714. }
  715. function option(optionName, optionValue) {
  716. if (optionName === undefined) {
  717. return $.extend({}, opts);
  718. }
  719. if (optionValue === undefined) {
  720. return opts[optionName];
  721. }
  722. opts[optionName] = optionValue;
  723. applyOptions();
  724. }
  725. function enable() {
  726. disabled = false;
  727. boundElement.attr("disabled", false);
  728. offsetElement.removeClass("sp-disabled");
  729. }
  730. function disable() {
  731. hide();
  732. disabled = true;
  733. boundElement.attr("disabled", true);
  734. offsetElement.addClass("sp-disabled");
  735. }
  736. initialize();
  737. var spect = {
  738. show: show,
  739. hide: hide,
  740. toggle: toggle,
  741. reflow: reflow,
  742. option: option,
  743. enable: enable,
  744. disable: disable,
  745. set: function (c) {
  746. set(c);
  747. updateOriginalInput();
  748. },
  749. get: get,
  750. destroy: destroy,
  751. container: container
  752. };
  753. spect.id = spectrums.push(spect) - 1;
  754. return spect;
  755. }
  756. /**
  757. * checkOffset - get the offset below/above and left/right element depending on screen position
  758. * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js
  759. */
  760. function getOffset(picker, input) {
  761. var extraY = 0;
  762. var dpWidth = picker.outerWidth();
  763. var dpHeight = picker.outerHeight();
  764. var inputHeight = input.outerHeight();
  765. var doc = picker[0].ownerDocument;
  766. var docElem = doc.documentElement;
  767. var viewWidth = docElem.clientWidth + $(doc).scrollLeft();
  768. var viewHeight = docElem.clientHeight + $(doc).scrollTop();
  769. var offset = input.offset();
  770. offset.top += inputHeight;
  771. offset.left -=
  772. Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
  773. Math.abs(offset.left + dpWidth - viewWidth) : 0);
  774. offset.top -=
  775. Math.min(offset.top, ((offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
  776. Math.abs(dpHeight + inputHeight - extraY) : extraY));
  777. return offset;
  778. }
  779. /**
  780. * noop - do nothing
  781. */
  782. function noop() {
  783. }
  784. /**
  785. * stopPropagation - makes the code only doing this a little easier to read in line
  786. */
  787. function stopPropagation(e) {
  788. e.stopPropagation();
  789. }
  790. /**
  791. * Create a function bound to a given object
  792. * Thanks to underscore.js
  793. */
  794. function bind(func, obj) {
  795. var slice = Array.prototype.slice;
  796. var args = slice.call(arguments, 2);
  797. return function () {
  798. return func.apply(obj, args.concat(slice.call(arguments)));
  799. };
  800. }
  801. /**
  802. * Lightweight drag helper. Handles containment within the element, so that
  803. * when dragging, the x is within [0,element.width] and y is within [0,element.height]
  804. */
  805. function draggable(element, onmove, onstart, onstop) {
  806. onmove = onmove || function () { };
  807. onstart = onstart || function () { };
  808. onstop = onstop || function () { };
  809. var doc = element.ownerDocument || document;
  810. var dragging = false;
  811. var offset = {};
  812. var maxHeight = 0;
  813. var maxWidth = 0;
  814. var hasTouch = ('ontouchstart' in window);
  815. var duringDragEvents = {};
  816. duringDragEvents["selectstart"] = prevent;
  817. duringDragEvents["dragstart"] = prevent;
  818. duringDragEvents["touchmove mousemove"] = move;
  819. duringDragEvents["touchend mouseup"] = stop;
  820. function prevent(e) {
  821. if (e.stopPropagation) {
  822. e.stopPropagation();
  823. }
  824. if (e.preventDefault) {
  825. e.preventDefault();
  826. }
  827. e.returnValue = false;
  828. }
  829. function move(e) {
  830. if (dragging) {
  831. // Mouseup happened outside of window
  832. if (IE && document.documentMode < 9 && !e.button) {
  833. return stop();
  834. }
  835. var touches = e.originalEvent.touches;
  836. var pageX = touches ? touches[0].pageX : e.pageX;
  837. var pageY = touches ? touches[0].pageY : e.pageY;
  838. var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
  839. var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
  840. if (hasTouch) {
  841. // Stop scrolling in iOS
  842. prevent(e);
  843. }
  844. onmove.apply(element, [dragX, dragY, e]);
  845. }
  846. }
  847. function start(e) {
  848. var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);
  849. var touches = e.originalEvent.touches;
  850. if (!rightclick && !dragging) {
  851. if (onstart.apply(element, arguments) !== false) {
  852. dragging = true;
  853. maxHeight = $(element).height();
  854. maxWidth = $(element).width();
  855. offset = $(element).offset();
  856. $(doc).bind(duringDragEvents);
  857. $(doc.body).addClass("sp-dragging");
  858. if (!hasTouch) {
  859. move(e);
  860. }
  861. prevent(e);
  862. }
  863. }
  864. }
  865. function stop() {
  866. if (dragging) {
  867. $(doc).unbind(duringDragEvents);
  868. $(doc.body).removeClass("sp-dragging");
  869. onstop.apply(element, arguments);
  870. }
  871. dragging = false;
  872. }
  873. $(element).bind("touchstart mousedown", start);
  874. }
  875. function throttle(func, wait, debounce) {
  876. var timeout;
  877. return function () {
  878. var context = this, args = arguments;
  879. var throttler = function () {
  880. timeout = null;
  881. func.apply(context, args);
  882. };
  883. if (debounce) clearTimeout(timeout);
  884. if (debounce || !timeout) timeout = setTimeout(throttler, wait);
  885. };
  886. }
  887. /**
  888. * Define a jQuery plugin
  889. */
  890. var dataID = "spectrum.id";
  891. $.fn.spectrum = function (opts, extra) {
  892. if (typeof opts == "string") {
  893. var returnValue = this;
  894. var args = Array.prototype.slice.call( arguments, 1 );
  895. this.each(function () {
  896. var spect = spectrums[$(this).data(dataID)];
  897. if (spect) {
  898. var method = spect[opts];
  899. if (!method) {
  900. throw new Error( "Spectrum: no such method: '" + opts + "'" );
  901. }
  902. if (opts == "get") {
  903. returnValue = spect.get();
  904. }
  905. else if (opts == "container") {
  906. returnValue = spect.container;
  907. }
  908. else if (opts == "option") {
  909. returnValue = spect.option.apply(spect, args);
  910. }
  911. else if (opts == "destroy") {
  912. spect.destroy();
  913. $(this).removeData(dataID);
  914. }
  915. else {
  916. method.apply(spect, args);
  917. }
  918. }
  919. });
  920. return returnValue;
  921. }
  922. // Initializing a new instance of spectrum
  923. return this.spectrum("destroy").each(function () {
  924. var options = $.extend({}, opts, $(this).data());
  925. var spect = spectrum(this, options);
  926. $(this).data(dataID, spect.id);
  927. });
  928. };
  929. $.fn.spectrum.load = true;
  930. $.fn.spectrum.loadOpts = {};
  931. $.fn.spectrum.draggable = draggable;
  932. $.fn.spectrum.defaults = defaultOpts;
  933. $.spectrum = { };
  934. $.spectrum.localization = { };
  935. $.spectrum.palettes = { };
  936. $.fn.spectrum.processNativeColorInputs = function () {
  937. if (!inputTypeColorSupport) {
  938. $("input[type=color]").spectrum({
  939. preferredFormat: "hex6"
  940. });
  941. }
  942. };
  943. // TinyColor v0.10.0
  944. // https://github.com/bgrins/TinyColor
  945. // 2013-08-10, Brian Grinstead, MIT License
  946. (function() {
  947. var trimLeft = /^[\s,#]+/,
  948. trimRight = /\s+$/,
  949. tinyCounter = 0,
  950. math = Math,
  951. mathRound = math.round,
  952. mathMin = math.min,
  953. mathMax = math.max,
  954. mathRandom = math.random;
  955. var tinycolor = function tinycolor (color, opts) {
  956. color = (color) ? color : '';
  957. opts = opts || { };
  958. // If input is already a tinycolor, return itself
  959. if (color instanceof tinycolor) {
  960. return color;
  961. }
  962. // If we are called as a function, call using new instead
  963. if (!(this instanceof tinycolor)) {
  964. return new tinycolor(color, opts);
  965. }
  966. var rgb = inputToRGB(color);
  967. this._r = rgb.r,
  968. this._g = rgb.g,
  969. this._b = rgb.b,
  970. this._a = rgb.a,
  971. this._roundA = mathRound(100*this._a) / 100,
  972. this._format = opts.format || rgb.format;
  973. this._gradientType = opts.gradientType;
  974. // Don't let the range of [0,255] come back in [0,1].
  975. // Potentially lose a little bit of precision here, but will fix issues where
  976. // .5 gets interpreted as half of the total, instead of half of 1
  977. // If it was supposed to be 128, this was already taken care of by `inputToRgb`
  978. if (this._r < 1) { this._r = mathRound(this._r); }
  979. if (this._g < 1) { this._g = mathRound(this._g); }
  980. if (this._b < 1) { this._b = mathRound(this._b); }
  981. this._ok = rgb.ok;
  982. this._tc_id = tinyCounter++;
  983. };
  984. tinycolor.prototype = {
  985. isValid: function() {
  986. return this._ok;
  987. },
  988. getFormat: function() {
  989. return this._format;
  990. },
  991. getAlpha: function() {
  992. return this._a;
  993. },
  994. setAlpha: function(value) {
  995. this._a = boundAlpha(value);
  996. this._roundA = mathRound(100*this._a) / 100;
  997. },
  998. toHsv: function() {
  999. var hsv = rgbToHsv(this._r, this._g, this._b);
  1000. return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
  1001. },
  1002. toHsvString: function() {
  1003. var hsv = rgbToHsv(this._r, this._g, this._b);
  1004. var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
  1005. return (this._a == 1) ?
  1006. "hsv(" + h + ", " + s + "%, " + v + "%)" :
  1007. "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
  1008. },
  1009. toHsl: function() {
  1010. var hsl = rgbToHsl(this._r, this._g, this._b);
  1011. return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
  1012. },
  1013. toHslString: function() {
  1014. var hsl = rgbToHsl(this._r, this._g, this._b);
  1015. var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
  1016. return (this._a == 1) ?
  1017. "hsl(" + h + ", " + s + "%, " + l + "%)" :
  1018. "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
  1019. },
  1020. toHex: function(allow3Char) {
  1021. return rgbToHex(this._r, this._g, this._b, allow3Char);
  1022. },
  1023. toHexString: function(allow3Char) {
  1024. return '#' + this.toHex(allow3Char);
  1025. },
  1026. toHex8: function() {
  1027. return rgbaToHex(this._r, this._g, this._b, this._a);
  1028. },
  1029. toHex8String: function() {
  1030. return '#' + this.toHex8();
  1031. },
  1032. toRgb: function() {
  1033. return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
  1034. },
  1035. toRgbString: function() {
  1036. return (this._a == 1) ?
  1037. "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
  1038. "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
  1039. },
  1040. toPercentageRgb: function() {
  1041. return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
  1042. },
  1043. toPercentageRgbString: function() {
  1044. return (this._a == 1) ?
  1045. "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
  1046. "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
  1047. },
  1048. toName: function() {
  1049. if (this._a === 0) {
  1050. return "transparent";
  1051. }
  1052. if (this._a < 1) {
  1053. return false;
  1054. }
  1055. return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
  1056. },
  1057. toFilter: function(secondColor) {
  1058. var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);
  1059. var secondHex8String = hex8String;
  1060. var gradientType = this._gradientType ? "GradientType = 1, " : "";
  1061. if (secondColor) {
  1062. var s = tinycolor(secondColor);
  1063. secondHex8String = s.toHex8String();
  1064. }
  1065. return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
  1066. },
  1067. toString: function(format) {
  1068. var formatSet = !!format;
  1069. format = format || this._format;
  1070. var formattedString = false;
  1071. var hasAlpha = this._a < 1 && this._a >= 0;
  1072. var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");
  1073. if (needsAlphaFormat) {
  1074. // Special case for "transparent", all other non-alpha formats
  1075. // will return rgba when there is transparency.
  1076. if (format === "name" && this._a === 0) {
  1077. return this.toName();
  1078. }
  1079. return this.toRgbString();
  1080. }
  1081. if (format === "rgb") {
  1082. formattedString = this.toRgbString();
  1083. }
  1084. if (format === "prgb") {
  1085. formattedString = this.toPercentageRgbString();
  1086. }
  1087. if (format === "hex" || format === "hex6") {
  1088. formattedString = this.toHexString();
  1089. }
  1090. if (format === "hex3") {
  1091. formattedString = this.toHexString(true);
  1092. }
  1093. if (format === "hex8") {
  1094. formattedString = this.toHex8String();
  1095. }
  1096. if (format === "name") {
  1097. formattedString = this.toName();
  1098. }
  1099. if (format === "hsl") {
  1100. formattedString = this.toHslString();
  1101. }
  1102. if (format === "hsv") {
  1103. formattedString = this.toHsvString();
  1104. }
  1105. return formattedString || this.toHexString();
  1106. }
  1107. };
  1108. // If input is an object, force 1 into "1.0" to handle ratios properly
  1109. // String input requires "1.0" as input, so 1 will be treated as 1
  1110. tinycolor.fromRatio = function(color, opts) {
  1111. if (typeof color == "object") {
  1112. var newColor = {};
  1113. for (var i in color) {
  1114. if (color.hasOwnProperty(i)) {
  1115. if (i === "a") {
  1116. newColor[i] = color[i];
  1117. }
  1118. else {
  1119. newColor[i] = convertToPercentage(color[i]);
  1120. }
  1121. }
  1122. }
  1123. color = newColor;
  1124. }
  1125. return tinycolor(color, opts);
  1126. };
  1127. // Given a string or object, convert that input to RGB
  1128. // Possible string inputs:
  1129. //
  1130. // "red"
  1131. // "#f00" or "f00"
  1132. // "#ff0000" or "ff0000"
  1133. // "#ff000000" or "ff000000"
  1134. // "rgb 255 0 0" or "rgb (255, 0, 0)"
  1135. // "rgb 1.0 0 0" or "rgb (1, 0, 0)"
  1136. // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
  1137. // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
  1138. // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
  1139. // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
  1140. // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
  1141. //
  1142. function inputToRGB(color) {
  1143. var rgb = { r: 0, g: 0, b: 0 };
  1144. var a = 1;
  1145. var ok = false;
  1146. var format = false;
  1147. if (typeof color == "string") {
  1148. color = stringInputToObject(color);
  1149. }
  1150. if (typeof color == "object") {
  1151. if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
  1152. rgb = rgbToRgb(color.r, color.g, color.b);
  1153. ok = true;
  1154. format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
  1155. }
  1156. else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
  1157. color.s = convertToPercentage(color.s);
  1158. color.v = convertToPercentage(color.v);
  1159. rgb = hsvToRgb(color.h, color.s, color.v);
  1160. ok = true;
  1161. format = "hsv";
  1162. }
  1163. else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
  1164. color.s = convertToPercentage(color.s);
  1165. color.l = convertToPercentage(color.l);
  1166. rgb = hslToRgb(color.h, color.s, color.l);
  1167. ok = true;
  1168. format = "hsl";
  1169. }
  1170. if (color.hasOwnProperty("a")) {
  1171. a = color.a;
  1172. }
  1173. }
  1174. a = boundAlpha(a);
  1175. return {
  1176. ok: ok,
  1177. format: color.format || format,
  1178. r: mathMin(255, mathMax(rgb.r, 0)),
  1179. g: mathMin(255, mathMax(rgb.g, 0)),
  1180. b: mathMin(255, mathMax(rgb.b, 0)),
  1181. a: a
  1182. };
  1183. }
  1184. // Conversion Functions
  1185. // --------------------
  1186. // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
  1187. // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
  1188. // `rgbToRgb`
  1189. // Handle bounds / percentage checking to conform to CSS color spec
  1190. // <http://www.w3.org/TR/css3-color/>
  1191. // *Assumes:* r, g, b in [0, 255] or [0, 1]
  1192. // *Returns:* { r, g, b } in [0, 255]
  1193. function rgbToRgb(r, g, b){
  1194. return {
  1195. r: bound01(r, 255) * 255,
  1196. g: bound01(g, 255) * 255,
  1197. b: bound01(b, 255) * 255
  1198. };
  1199. }
  1200. // `rgbToHsl`
  1201. // Converts an RGB color value to HSL.
  1202. // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
  1203. // *Returns:* { h, s, l } in [0,1]
  1204. function rgbToHsl(r, g, b) {
  1205. r = bound01(r, 255);
  1206. g = bound01(g, 255);
  1207. b = bound01(b, 255);
  1208. var max = mathMax(r, g, b), min = mathMin(r, g, b);
  1209. var h, s, l = (max + min) / 2;
  1210. if(max == min) {
  1211. h = s = 0; // achromatic
  1212. }
  1213. else {
  1214. var d = max - min;
  1215. s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
  1216. switch(max) {
  1217. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  1218. case g: h = (b - r) / d + 2; break;
  1219. case b: h = (r - g) / d + 4; break;
  1220. }
  1221. h /= 6;
  1222. }
  1223. return { h: h, s: s, l: l };
  1224. }
  1225. // `hslToRgb`
  1226. // Converts an HSL color value to RGB.
  1227. // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
  1228. // *Returns:* { r, g, b } in the set [0, 255]
  1229. function hslToRgb(h, s, l) {
  1230. var r, g, b;
  1231. h = bound01(h, 360);
  1232. s = bound01(s, 100);
  1233. l = bound01(l, 100);
  1234. function hue2rgb(p, q, t) {
  1235. if(t < 0) t += 1;
  1236. if(t > 1) t -= 1;
  1237. if(t < 1/6) return p + (q - p) * 6 * t;
  1238. if(t < 1/2) return q;
  1239. if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
  1240. return p;
  1241. }
  1242. if(s === 0) {
  1243. r = g = b = l; // achromatic
  1244. }
  1245. else {
  1246. var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  1247. var p = 2 * l - q;
  1248. r = hue2rgb(p, q, h + 1/3);
  1249. g = hue2rgb(p, q, h);
  1250. b = hue2rgb(p, q, h - 1/3);
  1251. }
  1252. return { r: r * 255, g: g * 255, b: b * 255 };
  1253. }
  1254. // `rgbToHsv`
  1255. // Converts an RGB color value to HSV
  1256. // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
  1257. // *Returns:* { h, s, v } in [0,1]
  1258. function rgbToHsv(r, g, b) {
  1259. r = bound01(r, 255);
  1260. g = bound01(g, 255);
  1261. b = bound01(b, 255);
  1262. var max = mathMax(r, g, b), min = mathMin(r, g, b);
  1263. var h, s, v = max;
  1264. var d = max - min;
  1265. s = max === 0 ? 0 : d / max;
  1266. if(max == min) {
  1267. h = 0; // achromatic
  1268. }
  1269. else {
  1270. switch(max) {
  1271. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  1272. case g: h = (b - r) / d + 2; break;
  1273. case b: h = (r - g) / d + 4; break;
  1274. }
  1275. h /= 6;
  1276. }
  1277. return { h: h, s: s, v: v };
  1278. }
  1279. // `hsvToRgb`
  1280. // Converts an HSV color value to RGB.
  1281. // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
  1282. // *Returns:* { r, g, b } in the set [0, 255]
  1283. function hsvToRgb(h, s, v) {
  1284. h = bound01(h, 360) * 6;
  1285. s = bound01(s, 100);
  1286. v = bound01(v, 100);
  1287. var i = math.floor(h),
  1288. f = h - i,
  1289. p = v * (1 - s),
  1290. q = v * (1 - f * s),
  1291. t = v * (1 - (1 - f) * s),
  1292. mod = i % 6,
  1293. r = [v, q, p, p, t, v][mod],
  1294. g = [t, v, v, q, p, p][mod],
  1295. b = [p, p, t, v, v, q][mod];
  1296. return { r: r * 255, g: g * 255, b: b * 255 };
  1297. }
  1298. // `rgbToHex`
  1299. // Converts an RGB color to hex
  1300. // Assumes r, g, and b are contained in the set [0, 255]
  1301. // Returns a 3 or 6 character hex
  1302. function rgbToHex(r, g, b, allow3Char) {
  1303. var hex = [
  1304. pad2(mathRound(r).toString(16)),
  1305. pad2(mathRound(g).toString(16)),
  1306. pad2(mathRound(b).toString(16))
  1307. ];
  1308. // Return a 3 character hex if possible
  1309. if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
  1310. return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
  1311. }
  1312. return hex.join("");
  1313. }
  1314. // `rgbaToHex`
  1315. // Converts an RGBA color plus alpha transparency to hex
  1316. // Assumes r, g, b and a are contained in the set [0, 255]
  1317. // Returns an 8 character hex
  1318. function rgbaToHex(r, g, b, a) {
  1319. var hex = [
  1320. pad2(convertDecimalToHex(a)),
  1321. pad2(mathRound(r).toString(16)),
  1322. pad2(mathRound(g).toString(16)),
  1323. pad2(mathRound(b).toString(16))
  1324. ];
  1325. return hex.join("");
  1326. }
  1327. // `equals`
  1328. // Can be called with any tinycolor input
  1329. tinycolor.equals = function (color1, color2) {
  1330. if (!color1 || !color2) { return false; }
  1331. return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
  1332. };
  1333. tinycolor.random = function() {
  1334. return tinycolor.fromRatio({
  1335. r: mathRandom(),
  1336. g: mathRandom(),
  1337. b: mathRandom()
  1338. });
  1339. };
  1340. // Modification Functions
  1341. // ----------------------
  1342. // Thanks to less.js for some of the basics here
  1343. // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
  1344. tinycolor.desaturate = function (color, amount) {
  1345. amount = (amount === 0) ? 0 : (amount || 10);
  1346. var hsl = tinycolor(color).toHsl();
  1347. hsl.s -= amount / 100;
  1348. hsl.s = clamp01(hsl.s);
  1349. return tinycolor(hsl);
  1350. };
  1351. tinycolor.saturate = function (color, amount) {
  1352. amount = (amount === 0) ? 0 : (amount || 10);
  1353. var hsl = tinycolor(color).toHsl();
  1354. hsl.s += amount / 100;
  1355. hsl.s = clamp01(hsl.s);
  1356. return tinycolor(hsl);
  1357. };
  1358. tinycolor.greyscale = function(color) {
  1359. return tinycolor.desaturate(color, 100);
  1360. };
  1361. tinycolor.lighten = function(color, amount) {
  1362. amount = (amount === 0) ? 0 : (amount || 10);
  1363. var hsl = tinycolor(color).toHsl();
  1364. hsl.l += amount / 100;
  1365. hsl.l = clamp01(hsl.l);
  1366. return tinycolor(hsl);
  1367. };
  1368. tinycolor.brighten = function(color, amount) {
  1369. amount = (amount === 0) ? 0 : (amount || 10);
  1370. var rgb = tinycolor(color).toRgb();
  1371. rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
  1372. rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
  1373. rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
  1374. return tinycolor(rgb);
  1375. };
  1376. tinycolor.darken = function (color, amount) {
  1377. amount = (amount === 0) ? 0 : (amount || 10);
  1378. var hsl = tinycolor(color).toHsl();
  1379. hsl.l -= amount / 100;
  1380. hsl.l = clamp01(hsl.l);
  1381. return tinycolor(hsl);
  1382. };
  1383. tinycolor.complement = function(color) {
  1384. var hsl = tinycolor(color).toHsl();
  1385. hsl.h = (hsl.h + 180) % 360;
  1386. return tinycolor(hsl);
  1387. };
  1388. // Combination Functions
  1389. // ---------------------
  1390. // Thanks to jQuery xColor for some of the ideas behind these
  1391. // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
  1392. tinycolor.triad = function(color) {
  1393. var hsl = tinycolor(color).toHsl();
  1394. var h = hsl.h;
  1395. return [
  1396. tinycolor(color),
  1397. tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
  1398. tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
  1399. ];
  1400. };
  1401. tinycolor.tetrad = function(color) {
  1402. var hsl = tinycolor(color).toHsl();
  1403. var h = hsl.h;
  1404. return [
  1405. tinycolor(color),
  1406. tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
  1407. tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
  1408. tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
  1409. ];
  1410. };
  1411. tinycolor.splitcomplement = function(color) {
  1412. var hsl = tinycolor(color).toHsl();
  1413. var h = hsl.h;
  1414. return [
  1415. tinycolor(color),
  1416. tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
  1417. tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
  1418. ];
  1419. };
  1420. tinycolor.analogous = function(color, results, slices) {
  1421. results = results || 6;
  1422. slices = slices || 30;
  1423. var hsl = tinycolor(color).toHsl();
  1424. var part = 360 / slices;
  1425. var ret = [tinycolor(color)];
  1426. for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
  1427. hsl.h = (hsl.h + part) % 360;
  1428. ret.push(tinycolor(hsl));
  1429. }
  1430. return ret;
  1431. };
  1432. tinycolor.monochromatic = function(color, results) {
  1433. results = results || 6;
  1434. var hsv = tinycolor(color).toHsv();
  1435. var h = hsv.h, s = hsv.s, v = hsv.v;
  1436. var ret = [];
  1437. var modification = 1 / results;
  1438. while (results--) {
  1439. ret.push(tinycolor({ h: h, s: s, v: v}));
  1440. v = (v + modification) % 1;
  1441. }
  1442. return ret;
  1443. };
  1444. // Readability Functions
  1445. // ---------------------
  1446. // <http://www.w3.org/TR/AERT#color-contrast>
  1447. // `readability`
  1448. // Analyze the 2 colors and returns an object with the following properties:
  1449. // `brightness`: difference in brightness between the two colors
  1450. // `color`: difference in color/hue between the two colors
  1451. tinycolor.readability = function(color1, color2) {
  1452. var a = tinycolor(color1).toRgb();
  1453. var b = tinycolor(color2).toRgb();
  1454. var brightnessA = (a.r * 299 + a.g * 587 + a.b * 114) / 1000;
  1455. var brightnessB = (b.r * 299 + b.g * 587 + b.b * 114) / 1000;
  1456. var colorDiff = (
  1457. Math.max(a.r, b.r) - Math.min(a.r, b.r) +
  1458. Math.max(a.g, b.g) - Math.min(a.g, b.g) +
  1459. Math.max(a.b, b.b) - Math.min(a.b, b.b)
  1460. );
  1461. return {
  1462. brightness: Math.abs(brightnessA - brightnessB),
  1463. color: colorDiff
  1464. };
  1465. };
  1466. // `readable`
  1467. // http://www.w3.org/TR/AERT#color-contrast
  1468. // Ensure that foreground and background color combinations provide sufficient contrast.
  1469. // *Example*
  1470. // tinycolor.readable("#000", "#111") => false
  1471. tinycolor.readable = function(color1, color2) {
  1472. var readability = tinycolor.readability(color1, color2);
  1473. return readability.brightness > 125 && readability.color > 500;
  1474. };
  1475. // `mostReadable`
  1476. // Given a base color and a list of possible foreground or background
  1477. // colors for that base, returns the most readable color.
  1478. // *Example*
  1479. // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000"
  1480. tinycolor.mostReadable = function(baseColor, colorList) {
  1481. var bestColor = null;
  1482. var bestScore = 0;
  1483. var bestIsReadable = false;
  1484. for (var i=0; i < colorList.length; i++) {
  1485. // We normalize both around the "acceptable" breaking point,
  1486. // but rank brightness constrast higher than hue.
  1487. var readability = tinycolor.readability(baseColor, colorList[i]);
  1488. var readable = readability.brightness > 125 && readability.color > 500;
  1489. var score = 3 * (readability.brightness / 125) + (readability.color / 500);
  1490. if ((readable && ! bestIsReadable) ||
  1491. (readable && bestIsReadable && score > bestScore) ||
  1492. ((! readable) && (! bestIsReadable) && score > bestScore)) {
  1493. bestIsReadable = readable;
  1494. bestScore = score;
  1495. bestColor = tinycolor(colorList[i]);
  1496. }
  1497. }
  1498. return bestColor;
  1499. };
  1500. // Big List of Colors
  1501. // ------------------
  1502. // <http://www.w3.org/TR/css3-color/#svg-color>
  1503. var names = tinycolor.names = {
  1504. aliceblue: "f0f8ff",
  1505. antiquewhite: "faebd7",
  1506. aqua: "0ff",
  1507. aquamarine: "7fffd4",
  1508. azure: "f0ffff",
  1509. beige: "f5f5dc",
  1510. bisque: "ffe4c4",
  1511. black: "000",
  1512. blanchedalmond: "ffebcd",
  1513. blue: "00f",
  1514. blueviolet: "8a2be2",
  1515. brown: "a52a2a",
  1516. burlywood: "deb887",
  1517. burntsienna: "ea7e5d",
  1518. cadetblue: "5f9ea0",
  1519. chartreuse: "7fff00",
  1520. chocolate: "d2691e",
  1521. coral: "ff7f50",
  1522. cornflowerblue: "6495ed",
  1523. cornsilk: "fff8dc",
  1524. crimson: "dc143c",
  1525. cyan: "0ff",
  1526. darkblue: "00008b",
  1527. darkcyan: "008b8b",
  1528. darkgoldenrod: "b8860b",
  1529. darkgray: "a9a9a9",
  1530. darkgreen: "006400",
  1531. darkgrey: "a9a9a9",
  1532. darkkhaki: "bdb76b",
  1533. darkmagenta: "8b008b",
  1534. darkolivegreen: "556b2f",
  1535. darkorange: "ff8c00",
  1536. darkorchid: "9932cc",
  1537. darkred: "8b0000",
  1538. darksalmon: "e9967a",
  1539. darkseagreen: "8fbc8f",
  1540. darkslateblue: "483d8b",
  1541. darkslategray: "2f4f4f",
  1542. darkslategrey: "2f4f4f",
  1543. darkturquoise: "00ced1",
  1544. darkviolet: "9400d3",
  1545. deeppink: "ff1493",
  1546. deepskyblue: "00bfff",
  1547. dimgray: "696969",
  1548. dimgrey: "696969",
  1549. dodgerblue: "1e90ff",
  1550. firebrick: "b22222",
  1551. floralwhite: "fffaf0",
  1552. forestgreen: "228b22",
  1553. fuchsia: "f0f",
  1554. gainsboro: "dcdcdc",
  1555. ghostwhite: "f8f8ff",
  1556. gold: "ffd700",
  1557. goldenrod: "daa520",
  1558. gray: "808080",
  1559. green: "008000",
  1560. greenyellow: "adff2f",
  1561. grey: "808080",
  1562. honeydew: "f0fff0",
  1563. hotpink: "ff69b4",
  1564. indianred: "cd5c5c",
  1565. indigo: "4b0082",
  1566. ivory: "fffff0",
  1567. khaki: "f0e68c",
  1568. lavender: "e6e6fa",
  1569. lavenderblush: "fff0f5",
  1570. lawngreen: "7cfc00",
  1571. lemonchiffon: "fffacd",
  1572. lightblue: "add8e6",
  1573. lightcoral: "f08080",
  1574. lightcyan: "e0ffff",
  1575. lightgoldenrodyellow: "fafad2",
  1576. lightgray: "d3d3d3",
  1577. lightgreen: "90ee90",
  1578. lightgrey: "d3d3d3",
  1579. lightpink: "ffb6c1",
  1580. lightsalmon: "ffa07a",
  1581. lightseagreen: "20b2aa",
  1582. lightskyblue: "87cefa",
  1583. lightslategray: "789",
  1584. lightslategrey: "789",
  1585. lightsteelblue: "b0c4de",
  1586. lightyellow: "ffffe0",
  1587. lime: "0f0",
  1588. limegreen: "32cd32",
  1589. linen: "faf0e6",
  1590. magenta: "f0f",
  1591. maroon: "800000",
  1592. mediumaquamarine: "66cdaa",
  1593. mediumblue: "0000cd",
  1594. mediumorchid: "ba55d3",
  1595. mediumpurple: "9370db",
  1596. mediumseagreen: "3cb371",
  1597. mediumslateblue: "7b68ee",
  1598. mediumspringgreen: "00fa9a",
  1599. mediumturquoise: "48d1cc",
  1600. mediumvioletred: "c71585",
  1601. midnightblue: "191970",
  1602. mintcream: "f5fffa",
  1603. mistyrose: "ffe4e1",
  1604. moccasin: "ffe4b5",
  1605. navajowhite: "ffdead",
  1606. navy: "000080",
  1607. oldlace: "fdf5e6",
  1608. olive: "808000",
  1609. olivedrab: "6b8e23",
  1610. orange: "ffa500",
  1611. orangered: "ff4500",
  1612. orchid: "da70d6",
  1613. palegoldenrod: "eee8aa",
  1614. palegreen: "98fb98",
  1615. paleturquoise: "afeeee",
  1616. palevioletred: "db7093",
  1617. papayawhip: "ffefd5",
  1618. peachpuff: "ffdab9",
  1619. peru: "cd853f",
  1620. pink: "ffc0cb",
  1621. plum: "dda0dd",
  1622. powderblue: "b0e0e6",
  1623. purple: "800080",
  1624. red: "f00",
  1625. rosybrown: "bc8f8f",
  1626. royalblue: "4169e1",
  1627. saddlebrown: "8b4513",
  1628. salmon: "fa8072",
  1629. sandybrown: "f4a460",
  1630. seagreen: "2e8b57",
  1631. seashell: "fff5ee",
  1632. sienna: "a0522d",
  1633. silver: "c0c0c0",
  1634. skyblue: "87ceeb",
  1635. slateblue: "6a5acd",
  1636. slategray: "708090",
  1637. slategrey: "708090",
  1638. snow: "fffafa",
  1639. springgreen: "00ff7f",
  1640. steelblue: "4682b4",
  1641. tan: "d2b48c",
  1642. teal: "008080",
  1643. thistle: "d8bfd8",
  1644. tomato: "ff6347",
  1645. turquoise: "40e0d0",
  1646. violet: "ee82ee",
  1647. wheat: "f5deb3",
  1648. white: "fff",
  1649. whitesmoke: "f5f5f5",
  1650. yellow: "ff0",
  1651. yellowgreen: "9acd32"
  1652. };
  1653. // Make it easy to access colors via `hexNames[hex]`
  1654. var hexNames = tinycolor.hexNames = flip(names);
  1655. // Utilities
  1656. // ---------
  1657. // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
  1658. function flip(o) {
  1659. var flipped = { };
  1660. for (var i in o) {
  1661. if (o.hasOwnProperty(i)) {
  1662. flipped[o[i]] = i;
  1663. }
  1664. }
  1665. return flipped;
  1666. }
  1667. // Return a valid alpha value [0,1] with all invalid values being set to 1
  1668. function boundAlpha(a) {
  1669. a = parseFloat(a);
  1670. if (isNaN(a) || a < 0 || a > 1) {
  1671. a = 1;
  1672. }
  1673. return a;
  1674. }
  1675. // Take input from [0, n] and return it as [0, 1]
  1676. function bound01(n, max) {
  1677. if (isOnePointZero(n)) { n = "100%"; }
  1678. var processPercent = isPercentage(n);
  1679. n = mathMin(max, mathMax(0, parseFloat(n)));
  1680. // Automatically convert percentage into number
  1681. if (processPercent) {
  1682. n = parseInt(n * max, 10) / 100;
  1683. }
  1684. // Handle floating point rounding errors
  1685. if ((math.abs(n - max) < 0.000001)) {
  1686. return 1;
  1687. }
  1688. // Convert into [0, 1] range if it isn't already
  1689. return (n % max) / parseFloat(max);
  1690. }
  1691. // Force a number between 0 and 1
  1692. function clamp01(val) {
  1693. return mathMin(1, mathMax(0, val));
  1694. }
  1695. // Parse a base-16 hex value into a base-10 integer
  1696. function parseIntFromHex(val) {
  1697. return parseInt(val, 16);
  1698. }
  1699. // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
  1700. // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
  1701. function isOnePointZero(n) {
  1702. return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
  1703. }
  1704. // Check to see if string passed in is a percentage
  1705. function isPercentage(n) {
  1706. return typeof n === "string" && n.indexOf('%') != -1;
  1707. }
  1708. // Force a hex value to have 2 characters
  1709. function pad2(c) {
  1710. return c.length == 1 ? '0' + c : '' + c;
  1711. }
  1712. // Replace a decimal with it's percentage value
  1713. function convertToPercentage(n) {
  1714. if (n <= 1) {
  1715. n = (n * 100) + "%";
  1716. }
  1717. return n;
  1718. }
  1719. // Converts a decimal to a hex value
  1720. function convertDecimalToHex(d) {
  1721. return Math.round(parseFloat(d) * 255).toString(16);
  1722. }
  1723. // Converts a hex value to a decimal
  1724. function convertHexToDecimal(h) {
  1725. return (parseIntFromHex(h) / 255);
  1726. }
  1727. var matchers = (function() {
  1728. // <http://www.w3.org/TR/css3-values/#integers>
  1729. var CSS_INTEGER = "[-\\+]?\\d+%?";
  1730. // <http://www.w3.org/TR/css3-values/#number-value>
  1731. var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
  1732. // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
  1733. var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
  1734. // Actual matching.
  1735. // Parentheses and commas are optional, but not required.
  1736. // Whitespace can take the place of commas or opening paren
  1737. var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
  1738. var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
  1739. return {
  1740. rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
  1741. rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
  1742. hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
  1743. hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
  1744. hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
  1745. hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
  1746. hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
  1747. hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
  1748. };
  1749. })();
  1750. // `stringInputToObject`
  1751. // Permissive string parsing. Take in a number of formats, and output an object
  1752. // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
  1753. function stringInputToObject(color) {
  1754. color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
  1755. var named = false;
  1756. if (names[color]) {
  1757. color = names[color];
  1758. named = true;
  1759. }
  1760. else if (color == 'transparent') {
  1761. return { r: 0, g: 0, b: 0, a: 0, format: "name" };
  1762. }
  1763. // Try to match string input using regular expressions.
  1764. // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
  1765. // Just return an object and let the conversion functions handle that.
  1766. // This way the result will be the same whether the tinycolor is initialized with string or object.
  1767. var match;
  1768. if ((match = matchers.rgb.exec(color))) {
  1769. return { r: match[1], g: match[2], b: match[3] };
  1770. }
  1771. if ((match = matchers.rgba.exec(color))) {
  1772. return { r: match[1], g: match[2], b: match[3], a: match[4] };
  1773. }
  1774. if ((match = matchers.hsl.exec(color))) {
  1775. return { h: match[1], s: match[2], l: match[3] };
  1776. }
  1777. if ((match = matchers.hsla.exec(color))) {
  1778. return { h: match[1], s: match[2], l: match[3], a: match[4] };
  1779. }
  1780. if ((match = matchers.hsv.exec(color))) {
  1781. return { h: match[1], s: match[2], v: match[3] };
  1782. }
  1783. if ((match = matchers.hex8.exec(color))) {
  1784. return {
  1785. a: convertHexToDecimal(match[1]),
  1786. r: parseIntFromHex(match[2]),
  1787. g: parseIntFromHex(match[3]),
  1788. b: parseIntFromHex(match[4]),
  1789. format: named ? "name" : "hex8"
  1790. };
  1791. }
  1792. if ((match = matchers.hex6.exec(color))) {
  1793. return {
  1794. r: parseIntFromHex(match[1]),
  1795. g: parseIntFromHex(match[2]),
  1796. b: parseIntFromHex(match[3]),
  1797. format: named ? "name" : "hex"
  1798. };
  1799. }
  1800. if ((match = matchers.hex3.exec(color))) {
  1801. return {
  1802. r: parseIntFromHex(match[1] + '' + match[1]),
  1803. g: parseIntFromHex(match[2] + '' + match[2]),
  1804. b: parseIntFromHex(match[3] + '' + match[3]),
  1805. format: named ? "name" : "hex"
  1806. };
  1807. }
  1808. return false;
  1809. }
  1810. window.tinycolor = tinycolor;
  1811. })();
  1812. $(function () {
  1813. if ($.fn.spectrum.load) {
  1814. $.fn.spectrum.processNativeColorInputs();
  1815. }
  1816. });
  1817. })(window, jQuery);