API  0.9.9
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  };
100 }
101 
102 
103 #pragma mark -
104 #pragma mark Static methods
105 
119 + (CPColor)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
120 {
121  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))]];
122 }
123 
139 + (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
140 {
141  return [self colorWithRed:red green:green blue:blue alpha:alpha];
142 }
143 
144 
154 + (CPColor)colorWithWhite:(float)white alpha:(float)alpha
155 {
156  return [[CPColor alloc] _initWithRGBA:[white, white, white, alpha]];
157 }
158 
170 + (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha
171 {
172  return [self colorWithWhite:white alpha:alpha];
173 }
174 
188 + (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness
189 {
190  return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
191 }
192 
198 + (CPColor)colorWithCalibratedHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
199 {
200  return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha];
201 }
202 
217 + (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
218 {
219  // Clamp values.
220  hue = MAX(MIN(hue, 1.0), 0.0);
221  saturation = MAX(MIN(saturation, 1.0), 0.0);
222  brightness = MAX(MIN(brightness, 1.0), 0.0);
223 
224  if (saturation === 0.0)
225  return [CPColor colorWithCalibratedWhite:brightness alpha:alpha];
226 
227  var f = (hue * 360) % 60,
228  p = (brightness * (1 - saturation)),
229  q = (brightness * (60 - saturation * f)) / 60,
230  t = (brightness * (60 - saturation * (60 - f))) / 60,
231  b = brightness;
232 
233  switch (FLOOR(hue * 6))
234  {
235  case 0:
236  case 6:
237  return [CPColor colorWithCalibratedRed:b green:t blue:p alpha:alpha];
238  case 1:
239  return [CPColor colorWithCalibratedRed:q green:b blue:p alpha:alpha];
240  case 2:
241  return [CPColor colorWithCalibratedRed:p green:b blue:t alpha:alpha];
242  case 3:
243  return [CPColor colorWithCalibratedRed:p green:q blue:b alpha:alpha];
244  case 4:
245  return [CPColor colorWithCalibratedRed:t green:p blue:b alpha:alpha];
246  case 5:
247  return [CPColor colorWithCalibratedRed:b green:p blue:q alpha:alpha];
248  }
249 }
250 
261 + (CPColor)colorWithHexString:(string)hex
262 {
263  var rgba = hexToRGB(hex);
264  return rgba ? [[CPColor alloc] _initWithRGBA: rgba] : null;
265 }
266 
271 + (CPColor)colorWithSRGBRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
272 {
273  // TODO If Cappuccino is ported to a colorspace aware platform, this color should be in
274  // sRGBColorSpace.
275  return [self colorWithRed:red green:green blue:blue alpha:alpha];
276 }
277 
282 {
283  if (!cachedBlackColor)
284  cachedBlackColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0]];
285 
286  return cachedBlackColor;
287 }
288 
293 {
294  if (!cachedBlueColor)
295  cachedBlueColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 1.0, 1.0]];
296 
297  return cachedBlueColor;
298 }
299 
304 {
305  if (!cachedDarkGrayColor)
306  cachedDarkGrayColor = [CPColor colorWithCalibratedWhite:1.0 / 3.0 alpha:1.0];
307 
308  return cachedDarkGrayColor;
309 }
310 
315 {
316  if (!cachedGrayColor)
317  cachedGrayColor = [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0];
318 
319  return cachedGrayColor;
320 }
321 
326 {
327  if (!cachedGreenColor)
328  cachedGreenColor = [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 0.0, 1.0]];
329 
330  return cachedGreenColor;
331 }
332 
337 {
338  if (!cachedLightGrayColor)
339  cachedLightGrayColor = [CPColor colorWithCalibratedWhite:2.0 / 3.0 alpha:1.0];
340 
341  return cachedLightGrayColor;
342 }
343 
348 {
349  if (!cachedRedColor)
350  cachedRedColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 0.0, 1.0]];
351 
352  return cachedRedColor;
353 }
354 
359 {
360  if (!cachedWhiteColor)
361  cachedWhiteColor = [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 1.0, 1.0]];
362 
363  return cachedWhiteColor;
364 }
365 
370 {
371  if (!cachedYellowColor)
372  cachedYellowColor = [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 0.0, 1.0]];
373 
374  return cachedYellowColor;
375 }
376 
381 {
382  if (!cachedBrownColor)
383  cachedBrownColor = [[CPColor alloc] _initWithRGBA:[0.6, 0.4, 0.2, 1.0]];
384 
385  return cachedBrownColor;
386 }
387 
392 {
393  if (!cachedCyanColor)
394  cachedCyanColor = [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 1.0, 1.0]];
395 
396  return cachedCyanColor;
397 }
398 
403 {
404  if (!cachedMagentaColor)
405  cachedMagentaColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 1.0, 1.0]];
406 
407  return cachedMagentaColor;
408 }
409 
414 {
415  if (!cachedOrangeColor)
416  cachedOrangeColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.5, 0.0, 1.0]];
417 
418  return cachedOrangeColor;
419 }
420 
425 {
426  if (!cachedPurpleColor)
427  cachedPurpleColor = [[CPColor alloc] _initWithRGBA:[0.5, 0.0, 0.5, 1.0]];
428 
429  return cachedPurpleColor;
430 }
431 
437 {
438  if (!cachedShadowColor)
439  cachedShadowColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0 / 3.0]];
440 
441  return cachedShadowColor;
442 }
443 
449 {
450  if (!cachedClearColor)
451  cachedClearColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
452 
453  return cachedClearColor;
454 }
455 
456 + (CPColor)_cachedThemeColor
457 {
458  if (!cachedThemeColor)
459  cachedThemeColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
460 
461  return cachedThemeColor;
462 }
463 
465 {
466  return [[self _cachedThemeColor] valueForThemeAttribute:@"alternate-selected-control-color"];
467 }
468 
470 {
471  return [[self _cachedThemeColor] valueForThemeAttribute:@"secondary-selected-control-color"];
472 }
473 
479 + (CPColor)colorWithPatternImage:(CPImage)anImage
480 {
481  return [[CPColor alloc] _initWithPatternImage:anImage];
482 }
483 
490 + (CPColor)colorWithCSSString:(CPString)aString
491 {
492  return [[CPColor alloc] _initWithCSSString: aString];
493 }
494 
495 /* @ignore */
496 - (id)_initWithCSSString:(CPString)aString
497 {
498  if (aString.indexOf("rgb") == CPNotFound)
499  return nil;
500 
501  self = [super init];
502 
503  var startingIndex = aString.indexOf("("),
504  parts = aString.substring(startingIndex + 1).split(',');
505 
506  _components = [
507  parseInt(parts[0], 10) / 255.0,
508  parseInt(parts[1], 10) / 255.0,
509  parseInt(parts[2], 10) / 255.0,
510  parts[3] ? parseFloat(parts[3], 10) : 1.0
511  ];
512 
513  // We can't reuse aString as _cssString because the browser might not support the `rgba` syntax, and aString might
514  // use it (issue #1413.)
515  [self _initCSSStringFromComponents];
516 
517  _theme = [CPTheme defaultTheme];
518  _themeState = CPThemeStateNormal;
519  [self _loadThemeAttributes];
520 
521  return self;
522 }
523 
524 /* @ignore */
525 - (id)_initWithRGBA:(CPArray)components
526 {
527  self = [super init];
528 
529  if (self)
530  {
531  _components = components;
532 
533  [self _initCSSStringFromComponents];
534 
535  _theme = [CPTheme defaultTheme];
536  _themeState = CPThemeStateNormal;
537  [self _loadThemeAttributes];
538  }
539 
540  return self;
541 }
542 
543 - (void)_initCSSStringFromComponents
544 {
545  var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
546 
547  _cssString = (hasAlpha ? "rgba(" : "rgb(") +
548  parseInt(_components[0] * 255.0) + ", " +
549  parseInt(_components[1] * 255.0) + ", " +
550  parseInt(_components[2] * 255.0) +
551  (hasAlpha ? (", " + _components[3]) : "") + ")";
552 }
553 
554 /* @ignore */
555 - (id)_initWithPatternImage:(CPImage)anImage
556 {
557  self = [super init];
558 
559  if (self)
560  {
561  _patternImage = anImage;
562  _cssString = "url(\"" + [_patternImage filename] + "\")";
563  _components = [0.0, 0.0, 0.0, 1.0];
564 
565  _theme = [CPTheme defaultTheme];
566  _themeState = CPThemeStateNormal;
567  [self _loadThemeAttributes];
568  }
569 
570  return self;
571 }
572 
577 {
578  return _patternImage;
579 }
580 
585 {
586  return _components[3];
587 }
588 
593 {
594  return _components[2];
595 }
596 
601 {
602  return _components[1];
603 }
604 
608 - (float)redComponent
609 {
610  return _components[0];
611 }
612 
624 - (CPArray)components
625 {
626  return _components;
627 }
628 
636 - (CPColor)colorWithAlphaComponent:(float)anAlphaComponent
637 {
638  var components = _components.slice();
639 
640  components[components.length - 1] = anAlphaComponent;
641 
642  return [[[self class] alloc] _initWithRGBA:components];
643 }
644 
648 - (CPColor)colorUsingColorSpaceName:(id)aColorSpaceName
649 {
650  return self;
651 }
652 
666 - (CPArray)hsbComponents
667 {
668  var red = ROUND(_components[_redComponent] * 255.0),
669  green = ROUND(_components[_greenComponent] * 255.0),
670  blue = ROUND(_components[_blueComponent] * 255.0);
671 
672  var max = MAX(red, green, blue),
673  min = MIN(red, green, blue),
674  delta = max - min;
675 
676  var brightness = max / 255.0,
677  saturation = (max != 0) ? delta / max : 0;
678 
679  var hue;
680 
681  if (saturation == 0)
682  {
683  hue = 0;
684  }
685  else
686  {
687  var rr = (max - red) / delta,
688  gr = (max - green) / delta,
689  br = (max - blue) / delta;
690 
691  if (red == max)
692  hue = br - gr;
693  else if (green == max)
694  hue = 2 + rr - br;
695  else
696  hue = 4 + gr - rr;
697 
698  hue /= 6;
699  if (hue < 0)
700  hue++;
701  }
702 
703  return [
704  hue,
705  saturation,
706  brightness
707  ];
708 }
709 
713 - (float)hueComponent
714 {
715  return [self hsbComponents][0];
716 }
717 
722 {
723  return [self hsbComponents][1];
724 }
725 
730 {
731  return [self hsbComponents][2];
732 }
733 
744 {
745  return _cssString;
746 }
747 
752 {
753  return rgbToHex([self redComponent], [self greenComponent], [self blueComponent]);
754 }
755 
756 - (BOOL)isEqual:(CPColor)aColor
757 {
758  if (!aColor)
759  return NO;
760 
761  if (aColor === self)
762  return YES;
763 
764  if (![aColor isKindOfClass:CPColor])
765  return NO;
766 
767  if (_patternImage || [aColor patternImage])
768  return [_patternImage isEqual:[aColor patternImage]];
769 
770  // We don't require the components to be equal beyond 8 bits since otherwise
771  // simple rounding errors will make two colours which are exactly the same on
772  // screen compare unequal.
773  return ROUND([self redComponent] * 255.0) == ROUND([aColor redComponent] * 255.0) &&
774  ROUND([self greenComponent] * 255.0) == ROUND([aColor greenComponent] * 255.0) &&
775  ROUND([self blueComponent] * 255.0) == ROUND([aColor blueComponent] * 255.0) &&
776  [self alphaComponent] == [aColor alphaComponent];
777 }
778 
780 {
781  var description = [super description],
782  patternImage = [self patternImage];
783 
784  if (!patternImage)
785  return description + " " + [self cssString];
786 
787  description += " {\n";
788 
789  if ([patternImage isThreePartImage] || [patternImage isNinePartImage])
790  {
791  var slices = [patternImage imageSlices];
792 
793  if ([patternImage isThreePartImage])
794  description += " orientation: " + ([patternImage isVertical] ? "vertical" : "horizontal") + ",\n";
795 
796  description += " patternImage (" + slices.length + " part): [\n";
797 
798  for (var i = 0; i < slices.length; ++i)
799  {
800  var imgDescription = [slices[i] description] || "nil";
801 
802  description += imgDescription.replace(/^/mg, " ") + ",\n";
803  }
804 
805  description = description.substr(0, description.length - 2) + "\n ]\n}";
806  }
807  else
808  description += ([patternImage description] || "nil").replace(/^/mg, " ") + "\n}";
809 
810  return description;
811 }
812 
813 @end
814 
816 
820 - (void)set
821 {
822  [self setFill];
823  [self setStroke];
824 }
825 
829 - (void)setFill
830 {
832  CGContextSetFillColor(ctx, self);
833 }
834 
838 - (void)setStroke
839 {
841  CGContextSetStrokeColor(ctx, self);
842 }
843 
844 @end
845 
846 @implementation CPColor (Debugging)
847 
849 {
850  return [CPColor colorWithRed:RAND() green:RAND() blue:RAND() alpha:1.0];
851 }
852 
854 {
855  // Thanks to cocco http://stackoverflow.com/a/18368212/76900.
856  return [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEX////MzMw46qqDAAAAEElEQVQImWNg+M+AFeEQBgB+vw/xfUUZkgAAAABJRU5ErkJggg=="]];
857 }
858 
859 @end
860 
862 var CPColorComponentsKey = @"CPColorComponentsKey",
863  CPColorPatternImageKey = @"CPColorPatternImageKey";
865 
866 @implementation CPColor (CPCoding)
867 
872 - (id)initWithCoder:(CPCoder)aCoder
873 {
874  if ([aCoder containsValueForKey:CPColorPatternImageKey])
875  self = [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
876  else
877  self = [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
878 
879  [self _decodeThemeObjectsWithCoder:aCoder];
880 
881  return self;
882 }
883 
888 - (void)encodeWithCoder:(CPCoder)aCoder
889 {
890  if (_patternImage)
891  [aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey];
892  else
893  [aCoder encodeObject:_components forKey:CPColorComponentsKey];
894 
895  [self _encodeThemeObjectsWithCoder:aCoder];
896 }
897 
898 @end
899 
900 
902 var hexCharacters = "0123456789ABCDEF";
903 
904 /*
905  Used for the CPColor +colorWithHexString: implementation.
906  Returns an array of rgb components.
907 */
908 var hexToRGB = function(hex)
909 {
910  if (hex.length == 3)
911  hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
912 
913  if (hex.length != 6)
914  return null;
915 
916  hex = hex.toUpperCase();
917 
918  for (var i = 0; i < hex.length; i++)
919  if (hexCharacters.indexOf(hex.charAt(i)) == -1)
920  return null;
921 
922  var red = (hexCharacters.indexOf(hex.charAt(0)) * 16 + hexCharacters.indexOf(hex.charAt(1))) / 255.0,
923  green = (hexCharacters.indexOf(hex.charAt(2)) * 16 + hexCharacters.indexOf(hex.charAt(3))) / 255.0,
924  blue = (hexCharacters.indexOf(hex.charAt(4)) * 16 + hexCharacters.indexOf(hex.charAt(5))) / 255.0;
925 
926  return [red, green, blue, 1.0];
927 };
928 
929 var rgbToHex = function(r,g,b)
930 {
931  return byteToHex(r) + byteToHex(g) + byteToHex(b);
932 };
933 
934 var byteToHex = function(n)
935 {
936  if (!n || isNaN(n))
937  return "00";
938 
939  n = FLOOR(MIN(255, MAX(0, 256 * n)));
940 
941  return hexCharacters.charAt((n - n % 16) / 16) +
942  hexCharacters.charAt(n % 16);
943 };
944 
1164 function CPColorWithImages()
1165 {
1166  var slices = nil,
1167  numParts = 0,
1168  isVertical = false,
1169  imageFactory = CPImageInBundle,
1170  args = Array.prototype.slice.apply(arguments);
1171 
1172  if (typeof(args[args.length - 1]) === "function")
1173  imageFactory = args.pop();
1174 
1175  switch (args.length)
1176  {
1177  case 1:
1178  return imageFromSlices(args[0], isVertical, imageFactory);
1179 
1180  case 2:
1181  // New-style 3-part and 9-part images
1182  if (typeof(args[0]) === "string")
1183  return patternColorsFromPattern.call(this, args[0], args[1], imageFactory);
1184 
1185  return imageFromSlices(args[0], args[1], imageFactory);
1186 
1187  case 3:
1188  case 4:
1189  return [CPColor colorWithPatternImage:imageFactory(args[0], args[1], args[2], args[3])];
1190 
1191  default:
1192  throw("ERROR: Invalid argument count: " + args.length);
1193  }
1194 }
1195 
1196 var imageFromSlices = function(slices, isVertical, imageFactory)
1197 {
1198  var imageSlices = [];
1199 
1200  for (var i = 0; i < slices.length; ++i)
1201  {
1202  var slice = slices[i];
1203 
1204  imageSlices.push(slice ? imageFactory(slice[0], slice[1], slice[2], slice[3]) : nil);
1205  }
1206 
1207  switch (slices.length)
1208  {
1209  case 3:
1211 
1212  case 9:
1214 
1215  default:
1216  throw("ERROR: Invalid number of image slices: " + slices.length);
1217  }
1218 };
1219 
1220 var patternColorsFromPattern = function(pattern, attributes, imageFactory)
1221 {
1222  if (pattern.match(/^.*\{[^}]+\}/))
1223  {
1224  var width = attributes["width"],
1225  height = attributes["height"],
1226  separator = attributes["separator"] || "-",
1227  orientation = attributes["orientation"],
1228  rightWidth,
1229  bottomHeight,
1230  centerWidthHeight,
1231  centerIsNil,
1232  numParts;
1233 
1234  // positions are mandatory
1235  if (pattern.indexOf("{position}") < 0)
1236  throw("ERROR: Pattern strings must have a {position} placeholder (\"" + pattern + "\")");
1237 
1238  if (orientation === undefined)
1239  {
1240  numParts = 9;
1241 
1242  if (attributes["centerIsNil"] !== undefined)
1243  centerIsNil = attributes["centerIsNil"];
1244  }
1245  else
1246  {
1247  numParts = 3;
1248  isVertical = orientation === PatternIsVertical;
1249 
1250  if (isVertical)
1251  {
1252  if (attributes["centerHeight"])
1253  centerWidthHeight = attributes["centerHeight"];
1254  }
1255  else
1256  {
1257  if (attributes["centerWidth"])
1258  centerWidthHeight = attributes["centerWidth"];
1259  }
1260  }
1261 
1262  if (attributes["rightWidth"])
1263  rightWidth = attributes["rightWidth"];
1264 
1265  if (attributes["bottomHeight"])
1266  bottomHeight = attributes["bottomHeight"];
1267 
1268  var positions = attributes["positions"] || "@",
1269  states = nil,
1270  styles = nil;
1271 
1272  if (numParts === 3)
1273  {
1274  if (positions === "@")
1275  {
1276  if (isVertical)
1277  positions = ["top", "center", "bottom"];
1278  else
1279  positions = ["left", "center", "right"];
1280  }
1281  else if (positions === "#")
1282  positions = ["0", "1", "2"];
1283  else
1284  throw("ERROR: Invalid positions: " + positions)
1285  }
1286  else // numParts === 9
1287  {
1288  if (positions === "@" || positions === "abbrev")
1289  positions = ["top-left", "top", "top-right", "left", "center", "right", "bottom-left", "bottom", "bottom-right"];
1290  else if (positions === "full")
1291  positions = ["top-left", "top-center", "top-right", "center-left", "center-center", "center-right", "bottom-left", "bottom-center", "bottom-right"];
1292  else if (positions === "#")
1293  positions = ["0", "1", "2", "3", "4", "5", "6", "7", "8"];
1294  else
1295  throw("ERROR: Invalid positions: " + positions)
1296  }
1297 
1298  // states
1299  if (pattern.indexOf("{state}") >= 0)
1300  {
1301  states = attributes["states"];
1302 
1303  if (!states)
1304  throw("ERROR: {state} placeholder in the pattern (\"" + pattern + "\") but no states item in the attributes");
1305  }
1306 
1307  // styles
1308  if (pattern.indexOf("{style}") >= 0)
1309  {
1310  styles = attributes["styles"];
1311 
1312  if (!styles)
1313  throw("ERROR: {style} placeholder in the pattern (\"" + pattern + "\") but no styles item in the attributes");
1314  }
1315 
1316  // Now assemble the hierarchy
1317  var placeholder = "{position}",
1318  pos = pattern.indexOf(placeholder),
1319  i;
1320 
1321  for (i = 0; i < positions.length; ++i)
1322  positions[i] = pattern.replace(placeholder, pos === 0 ? positions[i] + separator : separator + positions[i]);
1323 
1324  var slices = positions,
1325  object = slices,
1326  key,
1327  sep;
1328 
1329  if (states)
1330  {
1331  placeholder = "{state}";
1332  pos = pattern.indexOf(placeholder);
1333  object = {};
1334 
1335  for (i = 0; i < states.length; ++i)
1336  {
1337  var state = states[i];
1338  key = state || "@";
1339  sep = state ? separator : "";
1340 
1341  object[key] = slices.slice(0);
1342  replacePlaceholderInArray(object[key], placeholder, pos === 0 ? state + sep : sep + state);
1343  }
1344  }
1345 
1346  if (styles)
1347  {
1348  placeholder = "{style}";
1349  pos = pattern.indexOf(placeholder);
1350 
1351  var styleObject = {};
1352 
1353  for (i = 0; i < styles.length; ++i)
1354  {
1355  var style = styles[i];
1356  key = style || "@";
1357  sep = style ? separator : "";
1358 
1359  if (states)
1360  {
1361  styleObject[key] = cloneObject(object);
1362  replacePlaceholderInObject(styleObject[key], placeholder, pos === 0 ? style + sep : sep + style);
1363  }
1364  else
1365  {
1366  styleObject[key] = slices.slice(0);
1367  replacePlaceholderInArray(styleObject[key], placeholder, pos === 0 ? style + sep : sep + style);
1368  }
1369  }
1370 
1371  object = styleObject;
1372  }
1373 
1374  if (styles || states)
1375  {
1376  if (numParts === 3)
1377  makeThreePartSlicesFromObject(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
1378  else
1379  makeNinePartSlicesFromObject(object, width, height, rightWidth, bottomHeight, centerIsNil);
1380 
1381  makeImagesFromObject(object, isVertical, imageFactory);
1382  return object;
1383  }
1384  else
1385  {
1386  if (numParts === 3)
1387  makeThreePartSlicesFromArray(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
1388  else
1389  makeNinePartSlicesFromArray(object, width, height, rightWidth, bottomHeight, centerIsNil);
1390 
1391  return imageFromSlices(object, isVertical, imageFactory);
1392  }
1393  }
1394  else
1395  throw("ERROR: No placeholders in slice pattern (\"" + pattern + "\")");
1396 };
1397 
1398 var replacePlaceholderInArray = function(array, find, replacement)
1399 {
1400  for (var i = 0; i < array.length; ++i)
1401  array[i] = array[i].replace(find, replacement);
1402 };
1403 
1404 var replacePlaceholderInObject = function(object, find, replacement)
1405 {
1406  for (var key in object)
1407  if (object.hasOwnProperty(key))
1408  if (object[key].constructor === Array)
1409  replacePlaceholderInArray(object[key], find, replacement);
1410  else
1411  replacePlaceholderInObject(object[key], find, replacement);
1412 };
1413 
1414 var cloneObject = function(object)
1415 {
1416  var clone = {};
1417 
1418  for (var key in object)
1419  if (object.hasOwnProperty(key))
1420  if (object[key].constructor === Array)
1421  clone[key] = object[key].slice(0);
1422  else if (typeof(object[key]) === "object")
1423  clone[key] = cloneObject(object[key]);
1424  else
1425  clone[key] = object[key];
1426 
1427  return clone;
1428 };
1429 
1430 var makeThreePartSlicesFromObject = function(object, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical)
1431 {
1432  for (var key in object)
1433  if (object.hasOwnProperty(key))
1434  if (object[key].constructor === Array)
1435  makeThreePartSlicesFromArray(object[key], width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
1436  else // object
1437  makeThreePartSlicesFromObject(object[key], width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical);
1438 };
1439 
1440 var makeThreePartSlicesFromArray = function(array, width, height, centerWidthHeight, rightWidth, bottomHeight, isVertical)
1441 {
1442  array[0] = [array[0], width, height];
1443 
1444  if (isVertical)
1445  {
1446  array[1] = [array[1], width, centerWidthHeight ? centerWidthHeight : 1.0];
1447  array[2] = [array[2], width, bottomHeight ? bottomHeight : height];
1448  }
1449  else
1450  {
1451  array[1] = [array[1], centerWidthHeight ? centerWidthHeight : 1.0, height];
1452  array[2] = [array[2], rightWidth ? rightWidth : width, height];
1453  }
1454 };
1455 
1456 var makeNinePartSlicesFromObject = function(object, width, height, rightWidth, bottomHeight, centerIsNil)
1457 {
1458  for (var key in object)
1459  if (object.hasOwnProperty(key))
1460  if (object[key].constructor === Array)
1461  makeNinePartSlicesFromArray(object[key], width, height, rightWidth, bottomHeight, centerIsNil);
1462  else // object
1463  makeNinePartSlicesFromObject(object[key], width, height, rightWidth, bottomHeight, centerIsNil);
1464 };
1465 
1466 var makeNinePartSlicesFromArray = function(array, width, height, rightWidth, bottomHeight, centerIsNil)
1467 {
1468  rightWidth = rightWidth ? rightWidth : width;
1469  bottomHeight = bottomHeight ? bottomHeight : height;
1470 
1471  array[0] = [array[0], width, height]; // top-left
1472  array[1] = [array[1], 1.0, height]; // top
1473  array[2] = [array[2], rightWidth, height]; // top-right
1474  array[3] = [array[3], width, 1.0]; // left
1475  array[4] = centerIsNil ? nil : [array[4], 1.0, 1.0]; // center
1476  array[5] = [array[5], rightWidth, 1.0]; // right
1477  array[6] = [array[6], width, bottomHeight]; // bottom-left
1478  array[7] = [array[7], 1.0, bottomHeight]; // bottom
1479  array[8] = [array[8], rightWidth, bottomHeight]; // bottom-right
1480 };
1481 
1482 var makeImagesFromObject = function(object, isVertical, imageFactory)
1483 {
1484  for (var key in object)
1485  if (object.hasOwnProperty(key))
1486  if (object[key].constructor === Array)
1487  object[key] = imageFromSlices(object[key], isVertical, imageFactory);
1488  else // object
1489  makeImagesFromObject(object[key], isVertical, imageFactory);
1490 };
1491 
function CPImageInBundle()
Definition: CPImage.j:72
CPColor lightGrayColor()
Definition: CPColor.j:336
CPColor randomColor()
Definition: CPColor.j:848
CPColor purpleColor()
Definition: CPColor.j:424
float greenComponent()
Definition: CPColor.j:600
id initWithImageSlices:isVertical:(CPArray imageSlices, [isVertical] BOOL isVertical)
Definition: CPImage.j:533
float hueComponent()
Definition: CPColor.j:713
CPCSSRGBAFeature
var isEqual
CPGraphicsContext currentContext()
An object representation of nil.
Definition: CPNull.h:2
CPColor blueColor()
Definition: CPColor.j:292
CPColor cyanColor()
Definition: CPColor.j:391
CPColor secondarySelectedControlColor()
Definition: CPColor.j:469
CPColor clearColor()
Definition: CPColor.j:448
CPColor colorWithPatternImage:(CPImage anImage)
Definition: CPColor.j:479
function CGContextSetStrokeColor(aContext, aColor)
Definition: CGContext.j:675
CPString cssString()
Definition: CPColor.j:743
CPColor colorWithCalibratedRed:green:blue:alpha:(float red, [green] float green, [blue] float blue, [alpha] float alpha)
Definition: CPColor.j:139
CPColor darkGrayColor()
Definition: CPColor.j:303
CPArray hsbComponents()
Definition: CPColor.j:666
CPColor shadowColor()
Definition: CPColor.j:436
CPColor whiteColor()
Definition: CPColor.j:358
id initWithImageSlices:(CPArray imageSlices)
Definition: CPImage.j:610
int width
float alphaComponent()
Definition: CPColor.j:584
CPColor grayColor()
Definition: CPColor.j:314
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPArray components()
Definition: CPColor.j:624
CPColor blackColor()
Definition: CPColor.j:281
CPColor alternateSelectedControlColor()
Definition: CPColor.j:464
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
void setStroke()
Definition: CPColor.j:838
Definition: CPImage.h:2
function CPFeatureIsCompatible(aFeature)
CPColor colorWithCalibratedWhite:alpha:(float white, [alpha] float alpha)
Definition: CPColor.j:170
CPColor colorWithRed:green:blue:alpha:(float red, [green] float green, [blue] float blue, [alpha] float alpha)
Definition: CPColor.j:119
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:217
CPColor magentaColor()
Definition: CPColor.j:402
CPColorPatternIsVertical
Definition: CPColor.j:30
int length()
Definition: CPString.j:186
id initWithContentsOfFile:(CPString aFilename)
Definition: CPImage.j:192
CPColor redColor()
Definition: CPColor.j:347
CPColorPatternIsHorizontal
Definition: CPColor.j:35
CPColor checkerBoardColor()
Definition: CPColor.j:853
var bottomHeight
Definition: CPAlert.j:53
CPTheme defaultTheme()
Definition: CPTheme.j:44
CPString hexString()
Definition: CPColor.j:751
CPColor colorWithWhite:alpha:(float white, [alpha] float alpha)
Definition: CPColor.j:154
float redComponent()
Definition: CPColor.j:608
float saturationComponent()
Definition: CPColor.j:721
CPColor greenColor()
Definition: CPColor.j:325
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPNotFound
Definition: CPObjJRuntime.j:62
float blueComponent()
Definition: CPColor.j:592
Definition: CPTheme.h:2
CPColor yellowColor()
Definition: CPColor.j:369
CPDictionary themeAttributes()
Definition: CPColor.j:94
float brightnessComponent()
Definition: CPColor.j:729
CPString description()
Definition: CPImage.j:440
CPImage patternImage()
Definition: CPColor.j:576
CPColor orangeColor()
Definition: CPColor.j:413
CPString description()
Definition: CPColor.j:779
void setFill()
Definition: CPColor.j:829
CPString defaultThemeClass()
Definition: CPColor.j:89
CPColor brownColor()
Definition: CPColor.j:380
id alloc()
Definition: CPObject.j:130