NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / color-harmony / color-harmony.js
blobf57d68938738cc9b154b3c35b1bccd3ef986cf76
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('color-harmony', function (Y, NAME) {
10 /**
11 Color Harmony provides methods useful for color combination discovery.
13 @module color
14 @submodule color-harmony
15 @class Harmony
16 @namespace Color
17 @since 3.8.0
19 var HSL = 'hsl',
20     RGB = 'rgb',
22     SPLIT_OFFSET = 30,
23     ANALOGOUS_OFFSET = 10,
24     TRIAD_OFFSET = 360/3,
25     TETRAD_OFFSET = 360/6,
26     SQUARE_OFFSET = 360/4 ,
28     DEF_COUNT = 5,
29     DEF_OFFSET = 10,
31     Color = Y.Color,
33     Harmony = {
35         // Color Groups
36         /**
37         Returns an Array of two colors. The first color in the Array
38           will be the color passed in. The second will be the
39           complementary color of the color provided
40         @public
41         @method getComplementary
42         @param {String} str
43         @param {String} [to]
44         @return {Array}
45         @since 3.8.0
46         **/
47         getComplementary: function(str, to) {
48             var c = Harmony._start(str),
49                 offsets = [];
51             to = to || Color.findType(str);
53             offsets.push({});
54             offsets.push({ h: 180 });
56             return Harmony._adjustOffsetAndFinish(c, offsets, to);
57         },
59         /**
60         Returns an Array of three colors. The first color in the Array
61           will be the color passed in. The second two will be split
62           complementary colors.
63         @public
64         @method getSplit
65         @param {String} str
66         @param {Number} [offset]
67         @param {String} [to]
68         @return {String}
69         @since 3.8.0
70         **/
71         getSplit: function(str, offset, to) {
72             var c = Harmony._start(str),
73                 offsets = [];
75             offset = offset || SPLIT_OFFSET;
77             to = to || Color.findType(str);
79             offsets.push({});
80             offsets.push({ h: 180 + offset });
81             offsets.push({ h: 180 - offset });
83             return Harmony._adjustOffsetAndFinish(c, offsets, to);
84         },
86         /**
87         Returns an Array of five colors. The first color in the Array
88           will be the color passed in. The remaining four will be
89           analogous colors two in either direction from the initially
90           provided color.
91         @public
92         @method getAnalogous
93         @param {String} str
94         @param {Number} [offset]
95         @param {String} [to]
96         @return {String}
97         @since 3.8.0
98         **/
99         getAnalogous: function(str, offset, to) {
100             var c = Harmony._start(str),
101                 offsets = [];
103             offset = offset || ANALOGOUS_OFFSET;
104             to = to || Color.findType(str);
106             offsets.push({});
107             offsets.push({ h: offset });
108             offsets.push({ h: offset * 2 });
109             offsets.push({ h: -offset });
110             offsets.push({ h: -offset * 2 });
112             return Harmony._adjustOffsetAndFinish(c, offsets, to);
113         },
115         /**
116         Returns an Array of three colors. The first color in the Array
117           will be the color passed in. The second two will be equidistant
118           from the start color and each other.
119         @public
120         @method getTriad
121         @param {String} str
122         @param {String} [to]
123         @return {String}
124         @since 3.8.0
125         **/
126         getTriad: function(str, to) {
127             var c = Harmony._start(str),
128                 offsets = [];
130             to = to || Color.findType(str);
132             offsets.push({});
133             offsets.push({ h: TRIAD_OFFSET });
134             offsets.push({ h: -TRIAD_OFFSET });
136             return Harmony._adjustOffsetAndFinish(c, offsets, to);
137         },
139         /**
140         Returns an Array of four colors. The first color in the Array
141           will be the color passed in. The remaining three colors are
142           equidistant offsets from the starting color and each other.
143         @public
144         @method getTetrad
145         @param {String} str
146         @param {Number} [offset]
147         @param {String} [to]
148         @return {String}
149         @since 3.8.0
150         **/
151         getTetrad: function(str, offset, to) {
152             var c = Harmony._start(str),
153                 offsets = [];
155             offset = offset || TETRAD_OFFSET;
156             to = to || Color.findType(str);
158             offsets.push({});
159             offsets.push({ h: offset });
160             offsets.push({ h: 180 });
161             offsets.push({ h: 180 + offset });
163             return Harmony._adjustOffsetAndFinish(c, offsets, to);
164         },
166         /**
167         Returns an Array of four colors. The first color in the Array
168           will be the color passed in. The remaining three colors are
169           equidistant offsets from the starting color and each other.
170         @public
171         @method getSquare
172         @param {String} str
173         @param {String} [to]
174         @return {String}
175         @since 3.8.0
176         **/
177         getSquare: function(str, to) {
178             var c = Harmony._start(str),
179                 offsets = [];
181             to = to || Color.findType(str);
183             offsets.push({});
184             offsets.push({ h: SQUARE_OFFSET });
185             offsets.push({ h: SQUARE_OFFSET * 2 });
186             offsets.push({ h: SQUARE_OFFSET * 3 });
188             return Harmony._adjustOffsetAndFinish(c, offsets, to);
189         },
191         /**
192         Calculates lightness offsets resulting in a monochromatic Array
193           of values.
194         @public
195         @method getMonochrome
196         @param {String} str
197         @param {Number} [count]
198         @param {String} [to]
199         @return {String}
200         @since 3.8.0
201         **/
202         getMonochrome: function(str, count, to) {
203             var c = Harmony._start(str),
204                 colors = [],
205                 i = 0,
206                 l,
207                 step,
208                 _c = c.concat();
210             count = count || DEF_COUNT;
211             to = to || Color.findType(str);
214             if (count < 2) {
215                 return str;
216             }
218             step = 100 / (count - 1);
220             for (; i <= 100; i += step) {
221                 _c[2] = Math.max(Math.min(i, 100), 0);
222                 colors.push(_c.concat());
223             }
225             l = colors.length;
227             for (i=0; i<l; i++) {
228                 colors[i] = Harmony._finish(colors[i], to);
229             }
231             return colors;
232         },
234         /**
235         Creates an Array of similar colors. Returned Array is prepended
236            with the color provided followed a number of colors decided
237            by count
238         @public
239         @method getSimilar
240         @param {String} str
241         @param {Number} [offset]
242         @param {Number} [count]
243         @param {String} [to]
244         @return {String}
245         @since 3.8.0
246         **/
247         getSimilar: function(str, offset, count, to) {
248             var c = Harmony._start(str),
249                 offsets = [],
250                 slOffset,
251                 s = +(c[1]),
252                 sMin,
253                 sMax,
254                 sRand,
255                 l = +(c[2]),
256                 lMin,
257                 lMax,
258                 lRand;
260             to = to || Color.findType(str);
261             count = count || DEF_COUNT;
262             offset = offset || DEF_OFFSET;
264             slOffset = (offset > 100) ? 100 : offset;
265             sMin = Math.max(0,   s - slOffset);
266             sMax = Math.min(100, s + slOffset);
267             lMin = Math.max(0,   l - slOffset);
268             lMax = Math.min(100, l + slOffset);
270             offsets.push({});
271             for (i = 0; i < count; i++) {
272                 sRand = ( Math.round( (Math.random() * (sMax - sMin)) + sMin ) );
273                 lRand = ( Math.round( (Math.random() * (lMax - lMin)) + lMin ) );
275                 offsets.push({
276                     h: ( Math.random() * (offset * 2)) - offset,
277                     // because getOffset adjusts from the existing color, we
278                     // need to adjust it negatively to get a good number for
279                     // saturation and luminance, otherwise we get a lot of white
280                     s: -(s - sRand),
281                     l: -(l - lRand)
282                 });
283             }
285             return Harmony._adjustOffsetAndFinish(c, offsets, to);
286         },
288         /**
289         Adjusts the provided color by the offset(s) given. You may
290           adjust hue, saturation, and/or luminance in one step.
291         @public
292         @method getOffset
293         @param {String} str
294         @param {Object} adjust
295           @param {Number} [adjust.h]
296           @param {Number} [adjust.s]
297           @param {Number} [adjust.l]
298         @param {String} [to]
299         @return {String}
300         @since 3.8.0
301         **/
302         getOffset: function(str, adjust, to) {
303             var started = Y.Lang.isArray(str),
304                 hsla,
305                 type;
307             if (!started) {
308                 hsla = Harmony._start(str);
309                 type = Color.findType(str);
310             } else {
311                 hsla = str;
312                 type = 'hsl';
313             }
315             to = to || type;
317             if (adjust.h) {
318                 hsla[0] = ((+hsla[0]) + adjust.h) % 360;
319             }
321             if (adjust.s) {
322                 hsla[1] = Math.max(Math.min((+hsla[1]) + adjust.s, 100), 0);
323             }
325             if (adjust.l) {
326                 hsla[2] = Math.max(Math.min((+hsla[2]) + adjust.l, 100), 0);
327             }
329             if (!started) {
330                 return Harmony._finish(hsla, to);
331             }
333             return hsla;
334         },
336         /**
337         Returns 0 - 100 percentage of brightness from `0` (black) being the
338           darkest to `100` (white) being the brightest.
339         @public
340         @method getBrightness
341         @param {String} str
342         @return {Number}
343         @since 3.8.0
344         **/
345         getBrightness: function(str) {
346             var c = Color.toArray(Color._convertTo(str, RGB)),
347                 r = c[0],
348                 g = c[1],
349                 b = c[2],
350                 weights = Y.Color._brightnessWeights;
353             return Math.round(Math.sqrt(
354                 (r * r * weights.r) +
355                 (g * g * weights.g) +
356                 (b * b * weights.b)
357             ) / 255 * 100);
358         },
360         /**
361         Returns a new color value with adjusted luminance so that the
362           brightness of the return color matches the perceived brightness
363           of the `match` color provided.
364         @public
365         @method getSimilarBrightness
366         @param {String} str
367         @param {String} match
368         @param {String} [to]
369         @return {String}
370         @since 3.8.0
371         **/
372         getSimilarBrightness: function(str, match, to){
373             var c = Color.toArray(Color._convertTo(str, HSL)),
374                 b = Harmony.getBrightness(match);
376             to = to || Color.findType(str);
378             if (to === 'keyword') {
379                 to = 'hex';
380             }
382             c[2] = Harmony._searchLuminanceForBrightness(c, b, 0, 100);
384             str = Color.fromArray(c, Y.Color.TYPES.HSLA);
386             return Color._convertTo(str, to);
387         },
389         //--------------------
390         // PRIVATE
391         //--------------------
392         /**
393         Converts the provided color from additive to subtractive returning
394           an Array of HSLA values
395         @private
396         @method _start
397         @param {String} str
398         @return {Array}
399         @since 3.8.0
400         */
401         _start: function(str) {
402             var hsla = Color.toArray(Color._convertTo(str, HSL));
403             hsla[0] = Harmony._toSubtractive(hsla[0]);
405             return hsla;
406         },
408         /**
409         Converts the provided HSLA values from subtractive to additive
410           returning a converted color string
411         @private
412         @method _finish
413         @param {Array} hsla
414         @param {String} [to]
415         @return {String}
416         @since 3.8.0
417         */
418         _finish: function(hsla, to) {
419             hsla[0] = Harmony._toAdditive(hsla[0]);
420             hsla = 'hsla(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%, ' + hsla[3] + ')';
422             if (to === 'keyword') {
423                 to = 'hex';
424             }
426             return Color._convertTo(hsla, to);
427         },
429         /**
430         Adjusts the hue degree from subtractive to additive
431         @private
432         @method _toAdditive
433         @param {Number} hue
434         @return {Number} Converted additive hue
435         @since 3.8.0
436         */
437         _toAdditive: function(hue) {
438             hue = Y.Color._constrainHue(hue);
440             if (hue <= 180) {
441                 hue /= 1.5;
442             } else if (hue < 240) {
443                 hue = 120 + (hue - 180) * 2;
444             }
446             return Y.Color._constrainHue(hue, 10);
447         },
449         /**
450         Adjusts the hue degree from additive to subtractive
451         @private
452         @method _toSubtractive
453         @param {Number} hue
454         @return {Number} Converted subtractive hue
455         @since 3.8.0
456         */
457         _toSubtractive: function(hue) {
458             hue = Y.Color._constrainHue(hue);
460             if (hue <= 120) {
461                 hue *= 1.5;
462             } else if (hue < 240) {
463                 hue = 180 + (hue - 120) / 2;
464             }
466             return Y.Color._constrainHue(hue, 10);
467         },
469         /**
470         Contrain the hue to a value between 0 and 360 for calculations
471             and real color wheel value space. Provide a precision value
472             to round return value to a decimal place
473         @private
474         @method _constrainHue
475         @param {Number} hue
476         @param {Number} [precision]
477         @return {Number} Constrained hue value
478         @since 3.8.0
479         **/
480         _constrainHue: function(hue, precision) {
481             while (hue < 0) {
482                 hue += 360;
483             }
484             hue %= 360;
486             if (precision) {
487                 hue = Math.round(hue * precision) / precision;
488             }
490             return hue;
491         },
493         /**
494         Brightness weight factors for perceived brightness calculations
496         "standard" values are listed as R: 0.241, G: 0.691, B: 0.068
497         These values were changed based on grey scale comparison of hsl
498           to new hsl where brightness is said to be within plus or minus 0.01.
499         @private
500         @property _brightnessWeights
501         @since 3.8.0
502         */
503         _brightnessWeights: {
504             r: 0.221,
505             g: 0.711,
506             b: 0.068
507         },
509         /**
510         Calculates the luminance as a mid range between the min and max
511           to match the brightness level provided
512         @private
513         @method _searchLuminanceForBrightness
514         @param {Array} color HSLA values
515         @param {Number} brightness Brightness to be matched
516         @param {Number} min Minimum range for luminance
517         @param {Number} max Maximum range for luminance
518         @return {Number} Found luminance to achieve requested brightness
519         @since 3.8.0
520         **/
521         _searchLuminanceForBrightness: function(color, brightness, min, max) {
522             var luminance = (max + min) / 2,
523                 b;
525             color[2] = luminance;
526             b = Harmony.getBrightness(Color.fromArray(color, Y.Color.TYPES.HSL));
528             if (b + 2 > brightness && b - 2 < brightness) {
529                 return luminance;
530             } else if (b > brightness) {
531                 return Harmony._searchLuminanceForBrightness(color, brightness, min, luminance);
532             } else {
533                 return Harmony._searchLuminanceForBrightness(color, brightness, luminance, max);
534             }
535         },
537         /**
538         Takes an HSL array, and an array of offsets and returns and array
539             of colors that have been adjusted. The returned colors will
540             match the array of offsets provided. If you wish you have the
541             same color value returned, you can provide null or an empty
542             object to the offsets. The returned array will contain color
543             value strings that have been adjusted from subtractive to
544             additive.
545         @private
546         @method _adjustOffsetAndFinish
547         @param {Array} color
548         @param {Array} offsets
549         @param {String} to
550         @return {Array}
551         @since 3.8.0
552         **/
553         _adjustOffsetAndFinish: function(color, offsets, to) {
554             var colors = [],
555                 i,
556                 l = offsets.length,
557                 _c;
559             for (i = 0; i < l; i++ ) {
560                 _c = color.concat();
561                 if (offsets[i]) {
562                     _c = Harmony.getOffset(_c, offsets[i]);
563                 }
564                 colors.push(Harmony._finish(_c, to));
565             }
567             return colors;
568         }
570     };
572 Y.Color = Y.mix(Y.Color, Harmony);
575 }, '3.13.0', {"requires": ["color-hsl"]});