├── .gitignore ├── LICENSE ├── README.md ├── generate.js ├── lib └── json2.js └── update_metadata.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | build 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 HashLips 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hashlips_art_engine_ps_script 2 | This script allows you to generate art works right from photoshop. 3 | 4 | 5 | Keep in mind this script is experimental and still in development, but you can try it out in the meantime. 6 | -------------------------------------------------------------------------------- /generate.js: -------------------------------------------------------------------------------- 1 | // Simple art generator by HashLips <-> 2 | 3 | #include "./lib/json2.js"; 4 | 5 | function main() { 6 | var continueConfirmation = confirm( 7 | "You are about to use the HashLips art generator. Are you sure you want to continue?" 8 | ); 9 | 10 | if (!continueConfirmation) return; 11 | 12 | var supply = prompt("How many images do you want to generate?", "10"); 13 | 14 | var name = prompt("What is the name of your collection?", ""); 15 | 16 | var description = prompt("What is the description for your collection?", ""); 17 | 18 | alert( 19 | supply + 20 | " images will be generated, so sit back relax and enjoy the art being generated." 21 | ); 22 | 23 | var groups = app.activeDocument.layerSets; 24 | 25 | resetLayers(groups); 26 | 27 | function getRWeights(_str) { 28 | var weight = Number(_str.split("#").pop()); 29 | if(isNaN(weight)){ 30 | weight = 1; 31 | } 32 | return weight; 33 | } 34 | 35 | function cleanName(_str) { 36 | return _str.split("#").shift(); 37 | } 38 | 39 | for (var h = 1; h < parseInt(supply) + 1; h++) { 40 | var obj = {}; 41 | obj.name = name + " #" + h; 42 | obj.description = description; 43 | obj.image = "To be replaced"; 44 | obj.edition = h; 45 | obj.attributes = []; 46 | for (var i = 0; i < groups.length; i++) { 47 | var totalWeight = 0; 48 | var layerMap = []; 49 | 50 | for(var j = 0; j < groups[i].layers.length; j++){ 51 | totalWeight += getRWeights(groups[i].layers[j].name); 52 | layerMap.push({ 53 | index: j, 54 | name: cleanName(groups[i].layers[j].name), 55 | weight: getRWeights(groups[i].layers[j].name) 56 | }); 57 | } 58 | 59 | var ran = Math.floor(Math.random() * totalWeight); 60 | 61 | (function() { 62 | for(var j = 0; j < groups[i].layers.length; j++){ 63 | ran -= layerMap[j].weight; 64 | if(ran < 0) { 65 | groups[i].layers[j].visible = true; 66 | obj.attributes.push({ 67 | trait_type: groups[i].name, 68 | value: layerMap[j].name 69 | }) 70 | return; 71 | } 72 | } 73 | })(); 74 | } 75 | saveImage(obj.edition); 76 | saveMetadata(obj); 77 | resetLayers(groups); 78 | } 79 | alert("Generation process is complete."); 80 | } 81 | 82 | function resetLayers(_groups) { 83 | for (var i = 0; i < _groups.length; i++) { 84 | _groups[i].visible = true; 85 | for (var j = 0; j < _groups[i].layers.length; j++) { 86 | _groups[i].layers[j].visible = false; 87 | } 88 | } 89 | } 90 | 91 | function saveImage(_edition) { 92 | var saveFile = new File(toFolder("build/images") + "/" + _edition + ".png"); 93 | exportOptions = new ExportOptionsSaveForWeb(); 94 | exportOptions.format = SaveDocumentType.PNG; 95 | exportOptions.PNG24 = false; 96 | exportOptions.transparency = true; 97 | exportOptions.interlaced = false; 98 | app.activeDocument.exportDocument( 99 | saveFile, 100 | ExportType.SAVEFORWEB, 101 | exportOptions 102 | ); 103 | } 104 | 105 | function saveMetadata(_data) { 106 | var file = new File(toFolder("build/metadata") + "/" + _data.edition + ".json"); 107 | file.open("w"); 108 | file.write(JSON.stringify(_data)); 109 | file.close(); 110 | } 111 | 112 | function toFolder(_name) { 113 | var path = app.activeDocument.path; 114 | var folder = new Folder(path + "/" + _name); 115 | if (!folder.exists) { 116 | folder.create(); 117 | } 118 | return folder; 119 | } 120 | 121 | 122 | main(); 123 | -------------------------------------------------------------------------------- /lib/json2.js: -------------------------------------------------------------------------------- 1 | // json2.js 2 | // 2017-06-12 3 | // Public Domain. 4 | // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 5 | 6 | // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 7 | // NOT CONTROL. 8 | 9 | // This file creates a global JSON object containing two methods: stringify 10 | // and parse. This file provides the ES5 JSON capability to ES3 systems. 11 | // If a project might run on IE8 or earlier, then this file should be included. 12 | // This file does nothing on ES5 systems. 13 | 14 | // JSON.stringify(value, replacer, space) 15 | // value any JavaScript value, usually an object or array. 16 | // replacer an optional parameter that determines how object 17 | // values are stringified for objects. It can be a 18 | // function or an array of strings. 19 | // space an optional parameter that specifies the indentation 20 | // of nested structures. If it is omitted, the text will 21 | // be packed without extra whitespace. If it is a number, 22 | // it will specify the number of spaces to indent at each 23 | // level. If it is a string (such as "\t" or " "), 24 | // it contains the characters used to indent at each level. 25 | // This method produces a JSON text from a JavaScript value. 26 | // When an object value is found, if the object contains a toJSON 27 | // method, its toJSON method will be called and the result will be 28 | // stringified. A toJSON method does not serialize: it returns the 29 | // value represented by the name/value pair that should be serialized, 30 | // or undefined if nothing should be serialized. The toJSON method 31 | // will be passed the key associated with the value, and this will be 32 | // bound to the value. 33 | 34 | // For example, this would serialize Dates as ISO strings. 35 | 36 | // Date.prototype.toJSON = function (key) { 37 | // function f(n) { 38 | // // Format integers to have at least two digits. 39 | // return (n < 10) 40 | // ? "0" + n 41 | // : n; 42 | // } 43 | // return this.getUTCFullYear() + "-" + 44 | // f(this.getUTCMonth() + 1) + "-" + 45 | // f(this.getUTCDate()) + "T" + 46 | // f(this.getUTCHours()) + ":" + 47 | // f(this.getUTCMinutes()) + ":" + 48 | // f(this.getUTCSeconds()) + "Z"; 49 | // }; 50 | 51 | // You can provide an optional replacer method. It will be passed the 52 | // key and value of each member, with this bound to the containing 53 | // object. The value that is returned from your method will be 54 | // serialized. If your method returns undefined, then the member will 55 | // be excluded from the serialization. 56 | 57 | // If the replacer parameter is an array of strings, then it will be 58 | // used to select the members to be serialized. It filters the results 59 | // such that only members with keys listed in the replacer array are 60 | // stringified. 61 | 62 | // Values that do not have JSON representations, such as undefined or 63 | // functions, will not be serialized. Such values in objects will be 64 | // dropped; in arrays they will be replaced with null. You can use 65 | // a replacer function to replace those with JSON values. 66 | 67 | // JSON.stringify(undefined) returns undefined. 68 | 69 | // The optional space parameter produces a stringification of the 70 | // value that is filled with line breaks and indentation to make it 71 | // easier to read. 72 | 73 | // If the space parameter is a non-empty string, then that string will 74 | // be used for indentation. If the space parameter is a number, then 75 | // the indentation will be that many spaces. 76 | 77 | // Example: 78 | 79 | // text = JSON.stringify(["e", {pluribus: "unum"}]); 80 | // // text is '["e",{"pluribus":"unum"}]' 81 | 82 | // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); 83 | // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 84 | 85 | // text = JSON.stringify([new Date()], function (key, value) { 86 | // return this[key] instanceof Date 87 | // ? "Date(" + this[key] + ")" 88 | // : value; 89 | // }); 90 | // // text is '["Date(---current time---)"]' 91 | 92 | // JSON.parse(text, reviver) 93 | // This method parses a JSON text to produce an object or array. 94 | // It can throw a SyntaxError exception. 95 | 96 | // The optional reviver parameter is a function that can filter and 97 | // transform the results. It receives each of the keys and values, 98 | // and its return value is used instead of the original value. 99 | // If it returns what it received, then the structure is not modified. 100 | // If it returns undefined then the member is deleted. 101 | 102 | // Example: 103 | 104 | // // Parse the text. Values that look like ISO date strings will 105 | // // be converted to Date objects. 106 | 107 | // myData = JSON.parse(text, function (key, value) { 108 | // var a; 109 | // if (typeof value === "string") { 110 | // a = 111 | // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 112 | // if (a) { 113 | // return new Date(Date.UTC( 114 | // +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6] 115 | // )); 116 | // } 117 | // return value; 118 | // } 119 | // }); 120 | 121 | // myData = JSON.parse( 122 | // "[\"Date(09/09/2001)\"]", 123 | // function (key, value) { 124 | // var d; 125 | // if ( 126 | // typeof value === "string" 127 | // && value.slice(0, 5) === "Date(" 128 | // && value.slice(-1) === ")" 129 | // ) { 130 | // d = new Date(value.slice(5, -1)); 131 | // if (d) { 132 | // return d; 133 | // } 134 | // } 135 | // return value; 136 | // } 137 | // ); 138 | 139 | // This is a reference implementation. You are free to copy, modify, or 140 | // redistribute. 141 | 142 | /*jslint 143 | eval, for, this 144 | */ 145 | 146 | /*property 147 | JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 148 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 149 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 150 | test, toJSON, toString, valueOf 151 | */ 152 | 153 | // Create a JSON object only if one does not already exist. We create the 154 | // methods in a closure to avoid creating global variables. 155 | 156 | if (typeof JSON !== "object") { 157 | JSON = {}; 158 | } 159 | 160 | (function () { 161 | "use strict"; 162 | 163 | var rx_one = /^[\],:{}\s]*$/; 164 | var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; 165 | var rx_three = 166 | /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; 167 | var rx_four = /(?:^|:|,)(?:\s*\[)+/g; 168 | var rx_escapable = 169 | /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 170 | var rx_dangerous = 171 | /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 172 | 173 | function f(n) { 174 | // Format integers to have at least two digits. 175 | return n < 10 ? "0" + n : n; 176 | } 177 | 178 | function this_value() { 179 | return this.valueOf(); 180 | } 181 | 182 | if (typeof Date.prototype.toJSON !== "function") { 183 | Date.prototype.toJSON = function () { 184 | return isFinite(this.valueOf()) 185 | ? this.getUTCFullYear() + 186 | "-" + 187 | f(this.getUTCMonth() + 1) + 188 | "-" + 189 | f(this.getUTCDate()) + 190 | "T" + 191 | f(this.getUTCHours()) + 192 | ":" + 193 | f(this.getUTCMinutes()) + 194 | ":" + 195 | f(this.getUTCSeconds()) + 196 | "Z" 197 | : null; 198 | }; 199 | 200 | Boolean.prototype.toJSON = this_value; 201 | Number.prototype.toJSON = this_value; 202 | String.prototype.toJSON = this_value; 203 | } 204 | 205 | var gap; 206 | var indent; 207 | var meta; 208 | var rep; 209 | 210 | function quote(string) { 211 | // If the string contains no control characters, no quote characters, and no 212 | // backslash characters, then we can safely slap some quotes around it. 213 | // Otherwise we must also replace the offending characters with safe escape 214 | // sequences. 215 | 216 | rx_escapable.lastIndex = 0; 217 | return rx_escapable.test(string) 218 | ? '"' + 219 | string.replace(rx_escapable, function (a) { 220 | var c = meta[a]; 221 | return typeof c === "string" 222 | ? c 223 | : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 224 | }) + 225 | '"' 226 | : '"' + string + '"'; 227 | } 228 | 229 | function str(key, holder) { 230 | // Produce a string from holder[key]. 231 | 232 | var i; // The loop counter. 233 | var k; // The member key. 234 | var v; // The member value. 235 | var length; 236 | var mind = gap; 237 | var partial; 238 | var value = holder[key]; 239 | 240 | // If the value has a toJSON method, call it to obtain a replacement value. 241 | 242 | if ( 243 | value && 244 | typeof value === "object" && 245 | typeof value.toJSON === "function" 246 | ) { 247 | value = value.toJSON(key); 248 | } 249 | 250 | // If we were called with a replacer function, then call the replacer to 251 | // obtain a replacement value. 252 | 253 | if (typeof rep === "function") { 254 | value = rep.call(holder, key, value); 255 | } 256 | 257 | // What happens next depends on the value's type. 258 | 259 | switch (typeof value) { 260 | case "string": 261 | return quote(value); 262 | 263 | case "number": 264 | // JSON numbers must be finite. Encode non-finite numbers as null. 265 | 266 | return isFinite(value) ? String(value) : "null"; 267 | 268 | case "boolean": 269 | case "null": 270 | // If the value is a boolean or null, convert it to a string. Note: 271 | // typeof null does not produce "null". The case is included here in 272 | // the remote chance that this gets fixed someday. 273 | 274 | return String(value); 275 | 276 | // If the type is "object", we might be dealing with an object or an array or 277 | // null. 278 | 279 | case "object": 280 | // Due to a specification blunder in ECMAScript, typeof null is "object", 281 | // so watch out for that case. 282 | 283 | if (!value) { 284 | return "null"; 285 | } 286 | 287 | // Make an array to hold the partial results of stringifying this object value. 288 | 289 | gap += indent; 290 | partial = []; 291 | 292 | // Is the value an array? 293 | 294 | if (Object.prototype.toString.apply(value) === "[object Array]") { 295 | // The value is an array. Stringify every element. Use null as a placeholder 296 | // for non-JSON values. 297 | 298 | length = value.length; 299 | for (i = 0; i < length; i += 1) { 300 | partial[i] = str(i, value) || "null"; 301 | } 302 | 303 | // Join all of the elements together, separated with commas, and wrap them in 304 | // brackets. 305 | 306 | v = 307 | partial.length === 0 308 | ? "[]" 309 | : gap 310 | ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" 311 | : "[" + partial.join(",") + "]"; 312 | gap = mind; 313 | return v; 314 | } 315 | 316 | // If the replacer is an array, use it to select the members to be stringified. 317 | 318 | if (rep && typeof rep === "object") { 319 | length = rep.length; 320 | for (i = 0; i < length; i += 1) { 321 | if (typeof rep[i] === "string") { 322 | k = rep[i]; 323 | v = str(k, value); 324 | if (v) { 325 | partial.push(quote(k) + (gap ? ": " : ":") + v); 326 | } 327 | } 328 | } 329 | } else { 330 | // Otherwise, iterate through all of the keys in the object. 331 | 332 | for (k in value) { 333 | if (Object.prototype.hasOwnProperty.call(value, k)) { 334 | v = str(k, value); 335 | if (v) { 336 | partial.push(quote(k) + (gap ? ": " : ":") + v); 337 | } 338 | } 339 | } 340 | } 341 | 342 | // Join all of the member texts together, separated with commas, 343 | // and wrap them in braces. 344 | 345 | v = 346 | partial.length === 0 347 | ? "{}" 348 | : gap 349 | ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" 350 | : "{" + partial.join(",") + "}"; 351 | gap = mind; 352 | return v; 353 | } 354 | } 355 | 356 | // If the JSON object does not yet have a stringify method, give it one. 357 | 358 | if (typeof JSON.stringify !== "function") { 359 | meta = { 360 | // table of character substitutions 361 | "\b": "\\b", 362 | "\t": "\\t", 363 | "\n": "\\n", 364 | "\f": "\\f", 365 | "\r": "\\r", 366 | '"': '\\"', 367 | "\\": "\\\\", 368 | }; 369 | JSON.stringify = function (value, replacer, space) { 370 | // The stringify method takes a value and an optional replacer, and an optional 371 | // space parameter, and returns a JSON text. The replacer can be a function 372 | // that can replace values, or an array of strings that will select the keys. 373 | // A default replacer method can be provided. Use of the space parameter can 374 | // produce text that is more easily readable. 375 | 376 | var i; 377 | gap = ""; 378 | indent = ""; 379 | 380 | // If the space parameter is a number, make an indent string containing that 381 | // many spaces. 382 | 383 | if (typeof space === "number") { 384 | for (i = 0; i < space; i += 1) { 385 | indent += " "; 386 | } 387 | 388 | // If the space parameter is a string, it will be used as the indent string. 389 | } else if (typeof space === "string") { 390 | indent = space; 391 | } 392 | 393 | // If there is a replacer, it must be a function or an array. 394 | // Otherwise, throw an error. 395 | 396 | rep = replacer; 397 | if ( 398 | replacer && 399 | typeof replacer !== "function" && 400 | (typeof replacer !== "object" || typeof replacer.length !== "number") 401 | ) { 402 | throw new Error("JSON.stringify"); 403 | } 404 | 405 | // Make a fake root object containing our value under the key of "". 406 | // Return the result of stringifying the value. 407 | 408 | return str("", { "": value }); 409 | }; 410 | } 411 | 412 | // If the JSON object does not yet have a parse method, give it one. 413 | 414 | if (typeof JSON.parse !== "function") { 415 | JSON.parse = function (text, reviver) { 416 | // The parse method takes a text and an optional reviver function, and returns 417 | // a JavaScript value if the text is a valid JSON text. 418 | 419 | var j; 420 | 421 | function walk(holder, key) { 422 | // The walk method is used to recursively walk the resulting structure so 423 | // that modifications can be made. 424 | 425 | var k; 426 | var v; 427 | var value = holder[key]; 428 | if (value && typeof value === "object") { 429 | for (k in value) { 430 | if (Object.prototype.hasOwnProperty.call(value, k)) { 431 | v = walk(value, k); 432 | if (v !== undefined) { 433 | value[k] = v; 434 | } else { 435 | delete value[k]; 436 | } 437 | } 438 | } 439 | } 440 | return reviver.call(holder, key, value); 441 | } 442 | 443 | // Parsing happens in four stages. In the first stage, we replace certain 444 | // Unicode characters with escape sequences. JavaScript handles many characters 445 | // incorrectly, either silently deleting them, or treating them as line endings. 446 | 447 | text = String(text); 448 | rx_dangerous.lastIndex = 0; 449 | if (rx_dangerous.test(text)) { 450 | text = text.replace(rx_dangerous, function (a) { 451 | return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 452 | }); 453 | } 454 | 455 | // In the second stage, we run the text against regular expressions that look 456 | // for non-JSON patterns. We are especially concerned with "()" and "new" 457 | // because they can cause invocation, and "=" because it can cause mutation. 458 | // But just to be safe, we want to reject all unexpected forms. 459 | 460 | // We split the second stage into 4 regexp operations in order to work around 461 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 462 | // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we 463 | // replace all simple value tokens with "]" characters. Third, we delete all 464 | // open brackets that follow a colon or comma or that begin the text. Finally, 465 | // we look to see that the remaining characters are only whitespace or "]" or 466 | // "," or ":" or "{" or "}". If that is so, then the text is safe for eval. 467 | 468 | if ( 469 | rx_one.test( 470 | text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, "") 471 | ) 472 | ) { 473 | // In the third stage we use the eval function to compile the text into a 474 | // JavaScript structure. The "{" operator is subject to a syntactic ambiguity 475 | // in JavaScript: it can begin a block or an object literal. We wrap the text 476 | // in parens to eliminate the ambiguity. 477 | 478 | j = eval("(" + text + ")"); 479 | 480 | // In the optional fourth stage, we recursively walk the new structure, passing 481 | // each name/value pair to a reviver function for possible transformation. 482 | 483 | return typeof reviver === "function" ? walk({ "": j }, "") : j; 484 | } 485 | 486 | // If the text is not JSON parseable, then a SyntaxError is thrown. 487 | 488 | throw new SyntaxError("JSON.parse"); 489 | }; 490 | } 491 | })(); 492 | -------------------------------------------------------------------------------- /update_metadata.js: -------------------------------------------------------------------------------- 1 | // Simple art generator by HashLips <-> 2 | 3 | #include "./lib/json2.js"; 4 | 5 | function main () { 6 | 7 | var continueConfirmation = confirm( 8 | "You are about to update the metadata of your collection. Are you sure you want to continue?" 9 | ); 10 | 11 | if(!continueConfirmation) return 12 | 13 | var prefix = prompt("What is your new metadata prefix?", ""); 14 | 15 | var suffix = prompt("What is you new metadata suffix?", ""); 16 | 17 | var path = app.activeDocument.path; 18 | var f = new Folder(path + "/build/metadata"); 19 | 20 | if (f != null) { 21 | var fileList = f.getFiles(/\.(json)$/i); 22 | } 23 | 24 | for (var a = 0; a < fileList.length; a++) { 25 | var file = File(fileList[a]); 26 | file.open("r"); 27 | var data = JSON.parse(file.read()); 28 | file.close(); 29 | data.image = prefix + data.edition + suffix; 30 | file.open("w"); 31 | file.write(JSON.stringify(data)); 32 | file.close(); 33 | } 34 | 35 | alert("Updating metadata process is complete."); 36 | 37 | } 38 | 39 | main() 40 | --------------------------------------------------------------------------------