API  0.9.10
CPColor.j
Go to the documentation of this file.
1 /*
2  * CPColor.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
26 
31 
36 
38 
39 var _redComponent = 0,
40  _greenComponent = 1,
41  _blueComponent = 2,
42  _alphaCompnent = 3;
43 
44 var _hueComponent = 0,
45  _saturationComponent = 1,
46  _brightnessComponent = 2;
47 
48 var cachedBlackColor,
49  cachedRedColor,
50  cachedGreenColor,
51  cachedBlueColor,
52  cachedYellowColor,
53  cachedGrayColor,
54  cachedLightGrayColor,
55  cachedDarkGrayColor,
56  cachedWhiteColor,
57  cachedBrownColor,
58  cachedCyanColor,
59  cachedMagentaColor,
60  cachedOrangeColor,
61  cachedPurpleColor,
62  cachedShadowColor,
63  cachedClearColor,
64  cachedThemeColor;
65 
67 
77 @implementation CPColor : CPObject <CPTheme>
78 {
79  CPArray _components;
80 
81  CPImage _patternImage;
82  CPString _cssString;
83 }
84 
85 
86 #pragma mark -
87 #pragma mark Theming
88 
90 {
91  return "color";
92 }
93 
95 {
96  return @{
97  @"alternate-selected-control-color": [CPNull null],
98  @"secondary-selected-control-color": [CPNull null],
99  @"selected-text-background-color": [CPNull null],
100  @"selected-text-inactive-background-color": [CPNull null]
101  };
102 }
103 
104 
105 #pragma mark -
106 #pragma mark Static methods
107 
121 + (CPColor)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
122 {
123  return [[CPColor alloc] _initWithRGBA:[MAX(0.0, MIN(1.0, red)), MAX(0.0, MIN(1.0, green)), MAX(0.0, MIN(1.0, blue)), MAX(0.0, MIN(1.0, alpha))]];
124 }
125 
141 + (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
142 {
143  return [self colorWithRed:red green:green blue:blue alpha:alpha];
144 }
145 
146 
156 + (CPColor)colorWithWhite:(float)white alpha:(float)alpha
157 {
158  return [[CPColor alloc] _initWithRGBA:[white, white, white, alpha]];
159 }
160 
172 + (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha
173 {
174  return [self colorWithWhite:white alpha:alpha];
175 }
176 
190 + (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness
191 {
192  return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
193 }
194 
200 + (CPColor)colorWithCalibratedHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
201 {
202  return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha];
203 }
204 
219 + (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
220 {
221  // Clamp values.
222  hue = MAX(MIN(hue, 1.0), 0.0);
223  saturation = MAX(MIN(saturation, 1.0), 0.0);
224  brightness = MAX(MIN(brightness, 1.0), 0.0);
225 
226  if (saturation === 0.0)
227  return [CPColor colorWithCalibratedWhite:brightness alpha:alpha];
228 
229  var f = (hue * 360) % 60,
230  p = (brightness * (1 - saturation)),
231  q = (brightness * (60 - saturation * f)) / 60,
232  t = (brightness * (60 - saturation * (60 - f))) / 60,
233  b = brightness;
234 
235  switch (FLOOR(hue * 6))
236  {
237  case 0:
238  case 6:
239  return [CPColor colorWithCalibratedRed:b green:t blue:p alpha:alpha];
240  case 1:
241  return [CPColor colorWithCalibratedRed:q green:b blue:p alpha:alpha];
242  case 2:
243  return [CPColor colorWithCalibratedRed:p green:b blue:t alpha:alpha];
244  case 3:
245  return [CPColor colorWithCalibratedRed:p green:q blue:b alpha:alpha];
246  case 4:
247  return [CPColor colorWithCalibratedRed:t green:p blue:b alpha:alpha];
248  case 5:
249  return [CPColor colorWithCalibratedRed:b green:p blue:q alpha:alpha];
250  }
251 }
252 
263 + (CPColor)colorWithHexString:(string)hex
264 {
265  var rgba = hexToRGB(hex);
266  return rgba ? [[CPColor alloc] _initWithRGBA: rgba] : null;
267 }
268 
273 + (CPColor)colorWithSRGBRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
274 {
275  // TODO If Cappuccino is ported to a colorspace aware platform, this color should be in
276  // sRGBColorSpace.
277  return [self colorWithRed:red green:green blue:blue alpha:alpha];
278 }
279 
284 {
285  if (!cachedBlackColor)
286  cachedBlackColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0]];
287 
288  return cachedBlackColor;
289 }
290 
295 {
296  if (!cachedBlueColor)
297  cachedBlueColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 1.0, 1.0]];
298 
299  return cachedBlueColor;
300 }
301 
306 {
307  if (!cachedDarkGrayColor)
308  cachedDarkGrayColor = [CPColor colorWithCalibratedWhite:1.0 / 3.0 alpha:1.0];
309 
310  return cachedDarkGrayColor;
311 }
312 
317 {
318  if (!cachedGrayColor)
319  cachedGrayColor = [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0];
320 
321  return cachedGrayColor;
322 }
323 
328 {
329  if (!cachedGreenColor)
330  cachedGreenColor = [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 0.0, 1.0]];
331 
332  return cachedGreenColor;
333 }
334 
339 {
340  if (!cachedLightGrayColor)
341  cachedLightGrayColor = [CPColor colorWithCalibratedWhite:2.0 / 3.0 alpha:1.0];
342 
343  return cachedLightGrayColor;
344 }
345 
350 {
351  if (!cachedRedColor)
352  cachedRedColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 0.0, 1.0]];
353 
354  return cachedRedColor;
355 }
356 
361 {
362  if (!cachedWhiteColor)
363  cachedWhiteColor = [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 1.0, 1.0]];
364 
365  return cachedWhiteColor;
366 }
367 
372 {
373  if (!cachedYellowColor)
374  cachedYellowColor = [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 0.0, 1.0]];
375 
376  return cachedYellowColor;
377 }
378 
383 {
384  if (!cachedBrownColor)
385  cachedBrownColor = [[CPColor alloc] _initWithRGBA:[0.6, 0.4, 0.2, 1.0]];
386 
387  return cachedBrownColor;
388 }
389 
394 {
395  if (!cachedCyanColor)
396  cachedCyanColor = [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 1.0, 1.0]];
397 
398  return cachedCyanColor;
399 }
400 
405 {
406  if (!cachedMagentaColor)
407  cachedMagentaColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 1.0, 1.0]];
408 
409  return cachedMagentaColor;
410 }
411 
416 {
417  if (!cachedOrangeColor)
418  cachedOrangeColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.5, 0.0, 1.0]];
419 
420  return cachedOrangeColor;
421 }
422 
427 {
428  if (!cachedPurpleColor)
429  cachedPurpleColor = [[CPColor alloc] _initWithRGBA:[0.5, 0.0, 0.5, 1.0]];
430 
431  return cachedPurpleColor;
432 }
433 
439 {
440  if (!cachedShadowColor)
441  cachedShadowColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0 / 3.0]];
442 
443  return cachedShadowColor;
444 }
445 
451 {
452  if (!cachedClearColor)
453  cachedClearColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
454 
455  return cachedClearColor;
456 }
457 
458 + (CPColor)_cachedThemeColor
459 {
460  if (!cachedThemeColor)
461  cachedThemeColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
462 
463  return cachedThemeColor;
464 }
465 
467 {
468  return [[self _cachedThemeColor] valueForThemeAttribute:@"alternate-selected-control-color"];
469 }
470 
472 {
473  return [[self _cachedThemeColor] valueForThemeAttribute:@"secondary-selected-control-color"];
474 }
475 
481 + (CPColor)colorWithPatternImage:(CPImage)anImage
482 {
483  return [[CPColor alloc] _initWithPatternImage:anImage];
484 }
485 
492 + (CPColor)colorWithCSSString:(CPString)aString
493 {
494  return [[CPColor alloc] _initWithCSSString: aString];
495 }
496 
498 {
499  return [[self _cachedThemeColor] valueForThemeAttribute:@"selected-text-background-color"] || [CPColor colorWithHexString:"99CCFF"];
500 }
501 
502 + (CPColor)_selectedTextBackgroundColorUnfocussed
503 {
504  return [[self _cachedThemeColor] valueForThemeAttribute:@"selected-text-inactive-background-color"] || [CPColor colorWithHexString:"CCCCCC"];
505 }
506 
507 /* @ignore */
508 - (id)_initWithCSSString:(CPString)aString
509 {
510  if (aString.indexOf("rgb") == CPNotFound)
511  return nil;
512 
513  self = [super init];
514 
515  var startingIndex = aString.indexOf("("),
516  parts = aString.substring(startingIndex + 1).split(',');
517 
518  _components = [
519  parseInt(parts[0], 10) / 255.0,
520  parseInt(parts[1], 10) / 255.0,
521  parseInt(parts[2], 10) / 255.0,
522  parts[3] ? parseFloat(parts[3], 10) : 1.0
523  ];
524 
525  // We can't reuse aString as _cssString because the browser might not support the `rgba` syntax, and aString might
526  // use it (issue #1413.)
527  [self _initCSSStringFromComponents];
528 
529  _theme = [CPTheme defaultTheme];
530  _themeState = CPThemeStateNormal;
531  [self _loadThemeAttributes];
532 
533  return self;
534 }
535 
536 /* @ignore */
537 - (id)_initWithRGBA:(CPArray)components
538 {
539  self = [super init];
540 
541  if (self)
542  {
543  _components = components;
544 
545  [self _initCSSStringFromComponents];
546 
547  _theme = [CPTheme defaultTheme];
548  _themeState = CPThemeStateNormal;
549  [self _loadThemeAttributes];
550  }
551 
552  return self;
553 }
554 
555 - (void)_initCSSStringFromComponents
556 {
557  var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
558 
559  _cssString = (hasAlpha ? "rgba(" : "rgb(") +
560  parseInt(_components[0] * 255.0) + ", " +
561  parseInt(_components[1] * 255.0) + ", " +
562  parseInt(_components[2] * 255.0) +
563  (hasAlpha ? (", " + _components[3]) : "") + ")";
564 }
565 
566 /* @ignore */
567 - (id)_initWithPatternImage:(CPImage)anImage
568 {
569  self = [super init];
570 
571  if (self)
572  {
573  _patternImage = anImage;
574  _cssString = "url(\"" + [_patternImage filename] + "\")";
575  _components = [0.0, 0.0, 0.0, 1.0];
576 
577  _theme = [CPTheme defaultTheme];
578  _themeState = CPThemeStateNormal;
579  [self _loadThemeAttributes];
580  }
581 
582  return self;
583 }
584 
589 {
590  return _patternImage;
591 }
592 
597 {
598  return _components[3];
599 }
600 
605 {
606  return _components[2];
607 }
608 
613 {
614  return _components[1];
615 }
616 
620 - (float)redComponent
621 {
622  return _components[0];
623 }
624 
636 - (CPArray)components
637 {
638  return _components;
639 }
640 
648 - (CPColor)colorWithAlphaComponent:(float)anAlphaComponent
649 {
650  var components = _components.slice();
651 
652  components[components.length - 1] = anAlphaComponent;
653 
654  return [[[self class] alloc] _initWithRGBA:components];
655 }
656 
660 - (CPColor)colorUsingColorSpaceName:(id)aColorSpaceName
661 {
662  return self;
663 }
664 
678 - (CPArray)hsbComponents
679 {
680  var red = ROUND(_components[_redComponent] * 255.0),
681  green = ROUND(_components[_greenComponent] * 255.0),
682  blue = ROUND(_components[_blueComponent] * 255.0);
683 
684  var max = MAX(red, green, blue),
685  min = MIN(red, green, blue),
686  delta = max - min;
687 
688  var brightness = max / 255.0,
689  saturation = (max != 0) ? delta / max : 0;
690 
691  var hue;
692 
693  if (saturation == 0)
694  {
695  hue = 0;
696  }
697  else
698  {
699  var rr = (max - red) / delta,
700  gr = (max - green) / delta,
701  br = (max - blue) / delta;
702 
703  if (red == max)
704  hue = br - gr;
705  else if (green == max)
706  hue = 2 + rr - br;
707  else
708  hue = 4 + gr - rr;
709 
710  hue /= 6;
711  if (hue < 0)
712  hue++;
713  }
714 
715  return [
716  hue,
717  saturation,
718  brightness
719  ];
720 }
721 
725 - (float)hueComponent
726 {
727  return [self hsbComponents][0];
728 }
729 
734 {
735  return [self hsbComponents][1];
736 }
737 
742 {
743  return [self hsbComponents][2];
744 }
745 
756 {
757  return _cssString;
758 }
759 
764 {
765  return rgbToHex([self redComponent], [self greenComponent], [self blueComponent]);
766 }
767 
768 - (BOOL)isEqual:(CPColor)aColor
769 {
770  if (!aColor)
771  return NO;
772 
773  if (aColor === self)
774  return YES;
775 
776  if (![aColor isKindOfClass:CPColor])
777  return NO;
778 
779  if (_patternImage || [aColor patternImage])
780  return [_patternImage isEqual:[aColor patternImage]];
781 
782  // We don't require the components to be equal beyond 8 bits since otherwise
783  // simple rounding errors will make two colours which are exactly the same on
784  // screen compare unequal.
785  return ROUND([self redComponent] * 255.0) == ROUND([aColor redComponent] * 255.0) &&
786  ROUND([self greenComponent] * 255.0) == ROUND([aColor greenComponent] * 255.0) &&
787  ROUND([self blueComponent] * 255.0) == ROUND([aColor blueComponent] * 255.0) &&
788  [self alphaComponent] == [aColor alphaComponent];
789 }
790 
792 {
793  var description = [super description],
794  patternImage = [self patternImage];
795 
796  if (!patternImage)
797  return description + " " + [self cssString];
798 
799  description += " {\n";
800 
801  if ([patternImage isThreePartImage] || [patternImage isNinePartImage])
802  {
803  var slices = [patternImage imageSlices];
804 
805  if ([patternImage isThreePartImage])
806  description += " orientation: " + ([patternImage isVertical] ? "vertical" : "horizontal") + ",\n";
807 
808  description += " patternImage (" + slices.length + " part): [\n";
809 
810  for (var i = 0; i < slices.length; ++i)
811  {
812  var imgDescription = [slices[i] description] || "nil";
813 
814  description += imgDescription.replace(/^/mg, " ") + ",\n";
815  }
816 
817  description = description.substr(0, description.length - 2) + "\n ]\n}";
818  }
819  else
820  description += ([patternImage description] || "nil").replace(/^/mg, " ") + "\n}";
821 
822  return description;
823 }
824 
825 @end
826 
828 
832 - (void)set
833 {
834  [self setFill];
835  [self setStroke];
836 }
837 
841 - (void)setFill
842 {
844  CGContextSetFillColor(ctx, self);
845 }
846 
850 - (void)setStroke
851 {
853  CGContextSetStrokeColor(ctx, self);
854 }
855 
856 @end
857 
858 @implementation CPColor (Debugging)
859 
861 {
862  return [CPColor colorWithRed:RAND() green:RAND() blue:RAND() alpha:1.0];
863 }
864 
866 {
867  // Thanks to cocco http://stackoverflow.com/a/18368212/76900.
868  return [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEX////MzMw46qqDAAAAEElEQVQImWNg+M+AFeEQBgB+vw/xfUUZkgAAAABJRU5ErkJggg=="]];
869 }
870 
871 @end
872 
874 var CPColorComponentsKey = @"CPColorComponentsKey",
875  CPColorPatternImageKey = @"CPColorPatternImageKey";
877 
878 @implementation CPColor (CPCoding)
879 
884 - (id)initWithCoder:(CPCoder)aCoder
885 {
886  if ([aCoder containsValueForKey:CPColorPatternImageKey])
887  self = [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
888  else
889  self = [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
890 
891  [self _decodeThemeObjectsWithCoder:aCoder];
892 
893  return self;
894 }
895 
900 - (void)encodeWithCoder:(CPCoder)aCoder
901 {
902  if (_patternImage)
903  [aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey];
904  else
905  [aCoder encodeObject:_components forKey:CPColorComponentsKey];
906 
907  [self _encodeThemeObjectsWithCoder:aCoder];
908 }
909 
910 @end
911 
912 
914 var hexCharacters = "0123456789ABCDEF";
915 
916 /*
917  Used for the CPColor +colorWithHexString: implementation.
918  Returns an array of rgb components.
919 */
920 var hexToRGB = function(hex)
921 {
922  if (hex.length == 3)
923  hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
924 
925  if (hex.length != 6)
926  return null;
927 
928  hex = hex.toUpperCase();
929 
930  for (var i = 0; i < hex.length; i++)
931  if (hexCharacters.indexOf(hex.charAt(i)) == -1)
932  return null;
933 
934  var red = (hexCharacters.indexOf(hex.charAt(0)) * 16 + hexCharacters.indexOf(hex.charAt(1))) / 255.0,
935  green = (hexCharacters.indexOf(hex.charAt(2)) * 16 + hexCharacters.indexOf(hex.charAt(3))) / 255.0,
936  blue = (hexCharacters.indexOf(hex.charAt(4)) * 16 + hexCharacters.indexOf(hex.charAt(5))) / 255.0;
937 
938  return [red, green, blue, 1.0];
939 };
940 
941 var rgbToHex = function(r,g,b)
942 {
943  return byteToHex(r) + byteToHex(g) + byteToHex(b);
944 };
945 
946 var byteToHex = function(n)
947 {
948  if (!n || isNaN(n))
949  return "00";
950 
951  n = FLOOR(MIN(255, MAX(0, 256 * n)));
952 
953  return hexCharacters.charAt((n - n % 16) / 16) +
954  hexCharacters.charAt(n % 16);
955 };
956 
1176 function CPColorWithImages()
1177 {
1178  var slices = nil,
1179  numParts = 0,
1180  isVertical = false,
1181  imageFactory = CPImageInBundle,
1182  args = Array.prototype.slice.apply(arguments);
1183 
1184  if (typeof(args[args.length - 1]) === "function")
1185  imageFactory = args.pop();
1186 
1187  switch (args.length)
1188  {
1189  case 1:
1190  return imageFromSlices(args[0], isVertical, imageFactory);
1191 
1192  case 2:
1193  // New-style 3-part and 9-part images
1194  if (typeof(args[0]) === "string")
1195  return patternColorsFromPattern.call(this, args[0], args[1], imageFactory);
1196 
1197  return imageFromSlices(args[0], args[1], imageFactory);
1198 
1199  case 3:
1200  case 4:
1201  return [CPColor colorWithPatternImage:imageFactory(args[0], args[1], args[2], args[3])];
1202 
1203  default:
1204  throw("ERROR: Invalid argument count: " + args.length);
1205  }
1206 }
1207 
1208 var imageFromSlices = function(slices, isVertical, imageFactory)
1209 {
1210  var imageSlices = [];
1211 
1212  for (var i = 0; i < slices.length; ++i)
1213  {
1214  var slice = slices[i];
1215 
1216  imageSlices.push(slice ? imageFactory(slice[0], slice[1], slice[2], slice[3]) : nil);
1217  }
1218 
1219  switch (slices.length)
1220  {
1221  case 3:
1223 
1224  case 9:
1226 
1227  default:
1228  throw("ERROR: Invalid number of image slices: " + slices.length);
1229  }
1230 };
1231 
1232 var patternColorsFromPattern = function(pattern, attributes, imageFactory)
1233 {
1234  if (pattern.match(/^.*\{[^}]+\}/))
1235  {
1236  var width = attributes["width"],
1237  height = attributes["height"],
1238  separator = attributes["separator"] || "-",
1239  orientation = attributes["orientation"],
1240  rightWidth,
1241  bottomHeight,
1242  centerWidthHeight,
1243  centerIsNil,
1244  numParts;
1245 
1246  // positions are mandatory
1247  if (pattern.indexOf("{position}") < 0)
1248  throw("ERROR: Pattern strings must have a {position} placeholder (\"" + pattern + "\")");
1249 
1250  if (orientation === undefined)
1251  {
1252  numParts = 9;
1253 
1254  if (attributes["centerIsNil"] !== undefined)
1255  centerIsNil = attributes["centerIsNil"];
1256  }
1257  else
1258  {
1259  numParts = 3;
1260  isVertical = orientation === PatternIsVertical;
1261 
1262  if (isVertical)
1263  {
1264  if (attributes["centerHeight"])
1265  centerWidthHeight = attributes["centerHeight"];
1266  }
1267  else
1268  {
1269  if (attributes["centerWidth"])
1270  centerWidthHeight = attributes["centerWidth"];
1271  }
1272  }
1273 
1274  if (attributes["rightWidth"])
1275  rightWidth = attributes["rightWidth"];
1276 
1277  if (attributes["bottomHeight"])
1278  bottomHeight = attributes["bottomHeight"];
1279 
1280  var positions = attributes["positions"] || "@",
1281  states = nil,
1282  styles = nil;
1283 
1284  if (numParts === 3)
1285  {
1286  if (positions === "@")
1287  {
1288  if (isVertical)
1289  positions = ["top", "center", "bottom"];
1290  else
1291  positions = ["left", "center", "right"];
1292  }
1293  else if (positions === "#")
1294  positions = ["0", "1", "2"];
1295  else
1296  throw("ERROR: Invalid positions: " + positions)
1297  }
1298  else // numParts === 9
1299  {
1300  if (positions === "@" || positions === "abbrev")
1301  positions = ["top-left", "top", "top-right", "left", "center", "right", "bottom-left", "bottom", "bottom-right"];
1302  else if (positions === "full")
1303  positions = ["top-left", "top-center", "top-right", "center-left", "center-center", "center-right", "bottom-left", "bottom-center", "bottom-right"];
1304  else if (positions === "#")
1305  positions = ["0", "1", "2", "3", "4", "5", "6", "7", "8"];
1306  else
1307  throw("ERROR: Invalid positions: " + positions)
1308  }
1309 
1310  // states
1311  if (pattern.indexOf("{state}") >= 0)
1312  {
1313  states = attributes["states"];
1314 
1315  if (!states)
1316  throw("ERROR: {state} placeholder in the pattern (\"" + pattern + "\") but no states item in the attributes");
1317  }
1318 
1319  // styles
1320  if (pattern.indexOf("{style}") >= 0)
1321  {
1322  styles = attributes["styles"];
1323 
1324  if (!styles)
1325  throw("ERROR: {style} placeholder in the pattern (\"" + pattern + "\") but no styles item in the attributes");
1326  }
1327 
1328  // Now assemble the hierarchy
1329  var placeholder = "{position}",
1330  pos = pattern.indexOf(placeholder),
1331  i;
1332 
1333  for (i = 0; i < positions.length; ++i)
1334  positions[i] = pattern.replace(placeholder, pos === 0 ? positions[i] + separator : separator + positions[i]);
1335 
1336  var slices = positions,
1337  object = slices,
1338  key,
1339  sep;
1340 
1341  if (states)
1342  {
1343  placeholder = "{state}";
1344  pos = pattern.indexOf(placeholder);
1345  object = {};
1346 
1347  for (i = 0; i < states.length; ++i)
1348  {
1349  var state = states[i];
1350  key = state || "@";
1351  sep = state ? separator : "";
1352 
1353  object[key] = slices.slice(0);
1354  replacePlaceholderInArray(object[key], placeholder, pos === 0 ? state + sep : sep + state);
1355  }
1356  }
1357 
1358  if (styles)
1359  {
1360  placeholder = "{style}";
1361  pos = pattern.indexOf(placeholder);
1362 
1363  var styleObject = {};
1364 
1365  for (i = 0; i < styles.length; ++i)
1366  {
1367  var style = styles[i];
1368  key = style || "@";
1369  sep = style ? separator : "";
1370 
1371  if (states)
1372  {
1373  styleObject[key] = cloneObject(object);
1374  replacePlaceholderInObject(styleObject[key], placeholder, pos === 0 ? style + sep : sep + style);
1375  }
1376  else
1377  {
1378  styleObject[key] = slices.slice(0);
1379  replacePlaceholderInArray(styleObject[key], placeholder, pos === 0 ? style + sep : sep + style);
1380  }
1381  }
1382 
1383  object = styleObject;
1384  }
1385 
1386  if (styles || states)
1387  {
1388  if (numParts === 3)
1389  makeThreePartSlicesFromObject(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
1390  else
1391  makeNinePartSlicesFromObject(object, width, height, rightWidth, bottomHeight, centerIsNil);
1392 
1393  makeImagesFromObject(object, isVertical, imageFactory);
1394  return object;
1395  }
1396  else
1397  {
1398  if (numParts === 3)
1399  makeThreePartSlicesFromArray(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
1400  else
1401  makeNinePartSlicesFromArray(object, width, height, rightWidth, bottomHeight, centerIsNil);
1402 
1403  return imageFromSlices(object, isVertical, imageFactory);
1404  }
1405  }
1406  else
1407  throw("ERROR: No placeholders in slice pattern (\"" + pattern + "\")");
1408 };
1409 
1410 var replacePlaceholderInArray = function(array, find, replacement)
1411 {
1412  for (var i = 0; i < array.length; ++i)
1413  array[i] = array[i].replace(find, replacement);
1414 };
1415 
1416 var replacePlaceholderInObject = function(object, find, replacement)
1417 {
1418  for (var key in object)
1419  if (object.hasOwnProperty(key))
1420  if (object[key].constructor === Array)
1421  replacePlaceholderInArray(object[key], find, replacement);
1422  else
1423  replacePlaceholderInObject(object[key], find, replacement);
1424 };
1425 
1426 var cloneObject = function(object)
1427 {
1428  var clone = {};
1429 
1430  for (var key in object)
1431  if (object.hasOwnProperty(key))
1432  if (object[key].constructor === Array)
1433  clone[key] = object[key].slice(0);
1434  else if (typeof(object[key]) === "object")
1435  clone[key] = cloneObject(object[key]);
1436  else
1437  clone[key] = object[key];
1438 
1439  return clone;
1440 };
1441 
1442 var makeThreePartSlicesFromObject = function(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical)
1443 {
1444  for (var key in object)
1445  if (object.hasOwnProperty(key))
1446  if (object[key].constructor === Array)
1447  makeThreePartSlicesFromArray(object[key], width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
1448  else // object
1449  makeThreePartSlicesFromObject(object[key], width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
1450 };
1451 
1452 var makeThreePartSlicesFromArray = function(array, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical)
1453 {
1454  array[0] = [array[0], width, height];
1455 
1456  if (isVertical)
1457  {
1458  array[1] = [array[1], width, centerWidthHeight ? centerWidthHeight : 1.0];
1459  array[2] = [array[2], width, bottomHeight ? bottomHeight : height];
1460  }
1461  else
1462  {
1463  array[1] = [array[1], centerWidthHeight ? centerWidthHeight : 1.0, height];
1464  array[2] = [array[2], rightWidth ? rightWidth : width, height];
1465  }
1466 };
1467 
1468 var makeNinePartSlicesFromObject = function(object, width, height, rightWidth, bottomHeight, centerIsNil)
1469 {
1470  for (var key in object)
1471  if (object.hasOwnProperty(key))
1472  if (object[key].constructor === Array)
1473  makeNinePartSlicesFromArray(object[key], width, height, rightWidth, bottomHeight, centerIsNil);
1474  else // object
1475  makeNinePartSlicesFromObject(object[key], width, height, rightWidth, bottomHeight, centerIsNil);
1476 };
1477 
1478 var makeNinePartSlicesFromArray = function(array, width, height, rightWidth, bottomHeight, centerIsNil)
1479 {
1480  rightWidth = rightWidth ? rightWidth : width;
1481  bottomHeight = bottomHeight ? bottomHeight : height;
1482 
1483  array[0] = [array[0], width, height]; // top-left
1484  array[1] = [array[1], 1.0, height]; // top
1485  array[2] = [array[2], rightWidth, height]; // top-right
1486  array[3] = [array[3], width, 1.0]; // left
1487  array[4] = centerIsNil ? nil : [array[4], 1.0, 1.0]; // center
1488  array[5] = [array[5], rightWidth, 1.0]; // right
1489  array[6] = [array[6], width, bottomHeight]; // bottom-left
1490  array[7] = [array[7], 1.0, bottomHeight]; // bottom
1491  array[8] = [array[8], rightWidth, bottomHeight]; // bottom-right
1492 };
1493 
1494 var makeImagesFromObject = function(object, isVertical, imageFactory)
1495 {
1496  for (var key in object)
1497  if (object.hasOwnProperty(key))
1498  if (object[key].constructor === Array)
1499  object[key] = imageFromSlices(object[key], isVertical, imageFactory);
1500  else // object
1501  makeImagesFromObject(object[key], isVertical, imageFactory);
1502 };
1503 
function CPImageInBundle()
Definition: CPImage.j:72
CPColor lightGrayColor()
Definition: CPColor.j:338
CPColor randomColor()
Definition: CPColor.j:860
CPColor purpleColor()
Definition: CPColor.j:426
float greenComponent()
Definition: CPColor.j:612
id initWithImageSlices:isVertical:(CPArray imageSlices, [isVertical] BOOL isVertical)
Definition: CPImage.j:533
CPColor colorWithHexString:(string hex)
Definition: CPColor.j:263
float hueComponent()
Definition: CPColor.j:725
CPCSSRGBAFeature
var isEqual
CPGraphicsContext currentContext()
An object representation of nil.
Definition: CPNull.h:2
CPColor blueColor()
Definition: CPColor.j:294
CPColor cyanColor()
Definition: CPColor.j:393
CPColor secondarySelectedControlColor()
Definition: CPColor.j:471
CPColor clearColor()
Definition: CPColor.j:450
CPColor colorWithPatternImage:(CPImage anImage)
Definition: CPColor.j:481
function CGContextSetStrokeColor(aContext, aColor)
Definition: CGContext.j:675
CPString cssString()
Definition: CPColor.j:755
CPColor colorWithCalibratedRed:green:blue:alpha:(float red, [green] float green, [blue] float blue, [alpha] float alpha)
Definition: CPColor.j:141
CPColor darkGrayColor()
Definition: CPColor.j:305
CPArray hsbComponents()
Definition: CPColor.j:678
CPColor shadowColor()
Definition: CPColor.j:438
CPColor whiteColor()
Definition: CPColor.j:360
id initWithImageSlices:(CPArray imageSlices)
Definition: CPImage.j:610
int width
float alphaComponent()
Definition: CPColor.j:596
CPColor grayColor()
Definition: CPColor.j:316
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPArray components()
Definition: CPColor.j:636
CPColor blackColor()
Definition: CPColor.j:283
CPColor alternateSelectedControlColor()
Definition: CPColor.j:466
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
void setStroke()
Definition: CPColor.j:850
Definition: CPImage.h:2
function CPFeatureIsCompatible(aFeature)
CPColor colorWithCalibratedWhite:alpha:(float white, [alpha] float alpha)
Definition: CPColor.j:172
CPColor colorWithRed:green:blue:alpha:(float red, [green] float green, [blue] float blue, [alpha] float alpha)
Definition: CPColor.j:121
function CGContextSetFillColor(aContext, aColor)
Definition: CGContext.j:663
CPColor colorWithHue:saturation:brightness:alpha:(float hue, [saturation] float saturation, [brightness] float brightness, [alpha] float alpha)
Definition: CPColor.j:219
CPColor magentaColor()
Definition: CPColor.j:404
CPColorPatternIsVertical
Definition: CPColor.j:30
int length()
Definition: CPString.j:186
id initWithContentsOfFile:(CPString aFilename)
Definition: CPImage.j:192
CPColor redColor()
Definition: CPColor.j:349
CPColorPatternIsHorizontal
Definition: CPColor.j:35
CPColor checkerBoardColor()
Definition: CPColor.j:865
var bottomHeight
Definition: CPAlert.j:53
CPTheme defaultTheme()
Definition: CPTheme.j:44
CPString hexString()
Definition: CPColor.j:763
CPColor colorWithWhite:alpha:(float white, [alpha] float alpha)
Definition: CPColor.j:156
float redComponent()
Definition: CPColor.j:620
float saturationComponent()
Definition: CPColor.j:733
CPColor greenColor()
Definition: CPColor.j:327
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPColor selectedTextBackgroundColor()
Definition: CPColor.j:497
CPNotFound
Definition: CPObjJRuntime.j:62
float blueComponent()
Definition: CPColor.j:604
Definition: CPTheme.h:2
CPColor yellowColor()
Definition: CPColor.j:371
CPDictionary themeAttributes()
Definition: CPColor.j:94
float brightnessComponent()
Definition: CPColor.j:741
CPString description()
Definition: CPImage.j:440
CPImage patternImage()
Definition: CPColor.j:588
CPColor orangeColor()
Definition: CPColor.j:415
CPString description()
Definition: CPColor.j:791
void setFill()
Definition: CPColor.j:841
CPString defaultThemeClass()
Definition: CPColor.j:89
CPColor brownColor()
Definition: CPColor.j:382
id alloc()
Definition: CPObject.j:130