1 /*global one*/ 2 one.include('js:one/color.js'); 3 one.include('js:String-capitalize.js'); 4 5 /*jslint evil:true*/ 6 7 one.color.installedColorSpaces = []; 8 9 one.color.installColorSpace = function (colorSpaceName, propertyDefinitions, config) { 10 var propertyNames = propertyDefinitions.map(function (propertyDefinition) { 11 return propertyDefinition.match(/[A-Z]/)[0].toLowerCase(); 12 }), 13 longPropertyNames = propertyDefinitions.map(function (propertyDefinition) { 14 return propertyDefinition.toLowerCase().capitalize(); 15 }), 16 Constructor = one.color[colorSpaceName] = new Function(propertyNames.join(","), 17 // Allow passing an array to the constructor: 18 "if (Object.prototype.toString.apply(" + propertyNames[0] + ") === '[object Array]') {" + 19 propertyNames.map(function (propertyName, i) { 20 return propertyName + "=" + propertyNames[0] + "[" + i + "];"; 21 }).reverse().join("") + 22 "}" + 23 "if (" + propertyNames.filter(function (propertyName) { 24 return propertyName !== 'a'; 25 }).map(function (propertyName) { 26 return "isNaN(" + propertyName + ")"; 27 }).join("||") + "){" + "throw \"[one.color." + colorSpaceName + "]: Invalid color: (\"+" + propertyNames.join("+\",\"+") + "+\")\";}" + 28 propertyNames.map(function (propertyName) { 29 if (propertyName === 'h') { 30 return "this.h=h<0?h-Math.floor(h):h%1"; // Wrap 31 } else if (propertyName === 'a') { 32 return "this.a=(isNaN(a)||a>1)?1:(a<0?0:a);"; 33 } else { 34 return "this." + propertyName + "=" + propertyName + "<0?0:(" + propertyName + ">1?1:" + propertyName + ")"; 35 } 36 }).join(";") + ";" 37 ), 38 prototype = Constructor.prototype; 39 40 Constructor.propertyNames = propertyNames; 41 Constructor.longPropertyNames = longPropertyNames; 42 43 ['valueOf', 'toHex', 'toCSS', 'toCSSWithAlpha'].forEach(function (methodName) { 44 prototype[methodName] = prototype[methodName] || (colorSpaceName === 'RGB' ? prototype.toHex : new Function("return this.toRGB()." + methodName + "();")); 45 }); 46 47 prototype.isColor = true; 48 49 prototype.equals = function (otherColor, epsilon) { 50 if (typeof epsilon === 'undefined') { 51 epsilon = 1e-10; 52 } 53 54 otherColor = otherColor['to' + colorSpaceName](); 55 56 for (var i = 0; i < propertyNames.length; i = i + 1) { 57 if (Math.abs(this[propertyNames[i]] - otherColor[propertyNames[i]]) > epsilon) { 58 return false; 59 } 60 } 61 62 return true; 63 }; 64 65 prototype.toJSON = new Function( 66 "return ['" + colorSpaceName + "', " + 67 propertyNames.map(function (propertyName) { 68 return "this." + propertyName; 69 }, this).join(", ") + 70 "];" 71 ); 72 73 if (config.fromRGB) { 74 one.color.RGB.prototype['to' + colorSpaceName] = config.fromRGB; 75 delete config.fromRGB; 76 } 77 for (var prop in config) { 78 if (config.hasOwnProperty(prop)) { 79 prototype[prop] = config[prop]; 80 } 81 } 82 83 // It is pretty easy to implement the conversion to the same color space: 84 prototype['to' + colorSpaceName] = function () { 85 return this; 86 }; 87 prototype.toString = new Function("return \"[one.color." + colorSpaceName + ":\"+" + propertyNames.map(function (propertyName, i) { 88 return "\" " + longPropertyNames[i] + "=\"+this." + propertyName; 89 }).join("+") + "+\"]\";"); 90 91 // Generate getters and setters 92 propertyNames.forEach(function (propertyName, i) { 93 var longPropertyName = longPropertyNames[i]; 94 prototype['get' + longPropertyName] = new Function("return this." + propertyName + ";"); 95 prototype['set' + longPropertyName] = new Function("newValue", "return new this.constructor(" + propertyNames.map(function (otherPropertyName, i) { 96 return propertyName === otherPropertyName ? "newValue" : "this." + otherPropertyName; 97 }).join(", ") + ");"); 98 prototype['adjust' + longPropertyName] = new Function("delta", "return new this.constructor(" + propertyNames.map(function (otherPropertyName, i) { 99 return "this." + otherPropertyName + (propertyName === otherPropertyName ? "+delta" : ""); 100 }).join(", ") + ");"); 101 }); 102 103 function installForeignMethods(targetColorSpaceName, sourceColorSpaceName) { 104 var obj = {}; 105 obj['to' + sourceColorSpaceName] = new Function("return this.toRGB().to" + sourceColorSpaceName + "();"); // Fallback 106 one.color[sourceColorSpaceName].propertyNames.forEach(function (property, i) { 107 var longPropertyName = one.color[sourceColorSpaceName].longPropertyNames[i]; 108 obj['get' + longPropertyName] = new Function("return this.to" + sourceColorSpaceName + "().get" + longPropertyName + "();"); 109 obj['set' + longPropertyName] = new Function("newValue", "return this.to" + sourceColorSpaceName + "().set" + longPropertyName + "(newValue);"); 110 obj['adjust' + longPropertyName] = new Function("delta", "return this.to" + sourceColorSpaceName + "().adjust" + longPropertyName + "(delta);"); 111 }); 112 for (var prop in obj) { 113 if (obj.hasOwnProperty(prop) && one.color[targetColorSpaceName].prototype[prop] === undefined) { 114 one.color[targetColorSpaceName].prototype[prop] = obj[prop]; 115 } 116 } 117 } 118 119 one.color.installedColorSpaces.forEach(function (otherColorSpaceName) { 120 installForeignMethods(colorSpaceName, otherColorSpaceName); 121 installForeignMethods(otherColorSpaceName, colorSpaceName); 122 }); 123 124 one.color.installedColorSpaces.push(colorSpaceName); 125 }; 126