var Max = new Object(); // I define a "Functional" as either a function or a map (which is any // Javascript object). var Functional = new Object(); // Apply returns f(x) or f[x], appropriately. Functional.Apply = function(f, x) { return f && ((typeof(f) == 'function') ? f(x) : f[x]); }; // Also see: // http://www.optimalworks.net/blog/2007/web-development/javascript/array-detection Array.Is = function(array) { return (array !== null) && (typeof(array) == 'object') && ((array.prototype === Array.prototype) || (array.constructor && !array.nodeType && !array.item && array.length !== undefined)); }; // Chain calls Apply(f, x) for each value given, chaining results so that the // result of the previous step is the functional used in the next step. Functional.Chain = function(f, values) { if (!Array.Is(values)) values = [values]; for (var i = 0; i < values.length; ++i) f = f && Functional.Apply(f, values[i]); return f; }; Max.Deref = function(objects, x) { if (x == 'box') return Max.box; if (typeof(x) == 'string') x = x.split('.'); return Functional.Chain(objects, x); }; Max.patcher = this.patcher; Max.box = this.box; Max.ConnectOne = function(max_objects, from, from_inlet, to, to_inlet, hidden) { from = Max.Deref(max_objects, from); to = Max.Deref(max_objects, to); if (hidden) Max.patcher.hiddenconnect(from, from_inlet, to, to_inlet); else Max.patcher.connect(from, from_inlet, to, to_inlet); }; // Connect a list of Max items together. // // Each entry in connections looks like: // [from, from_index, to, to_index, hidden] // // to and from are string, or lists of strings. Max.Connect = function(connections, max_objects) { for (var i = 0; i < connections.length; ++i) Max.ConnectOne.apply(this, [max_objects].concat(connections[i])); }; var Creator = new Object(); Creator.CreateObject = function(description, context) { var define = context.define || {}; function Sub(v) { return (v in define) ? define[v] : v; }; var args = description.concat(context.messages || []); var class = args[0].split(' '); for (var i = 0; i < class.length; ++i) class[i] = Sub(class[i]); if (class[0] == 'message') { args.push(['set'].concat(class.slice(1))); class = ['message']; } var x = context.x + args[1]; var y = context.y + args[2]; var maxobj = Max.patcher.newdefault.apply(Max.patcher, [x, y].concat(class)); maxobj.rect = [x, y, x + args[3], y + args[4]]; for (var i = 5; i < args.length; ++i) { var message = []; for (var j in args[i]) { var part = args[i][j]; if (typeof(part) == 'string') { var index = part.indexOf('#'); if (index >= 0) part = part.slice(0, index) + Sub(part.slice(index)); } message.push(part); } if (message[0] == 'presentation_rect') { var r = message[1]; var s = maxobj.rect; message[1] = [r[0] + s[0], r[1] + s[1], r[2] + s[2], r[3] + s[3]]; } maxobj.message.apply(maxobj, message); } return maxobj; }; Creator.Create = function(creator, context) { if (context.define && creator in context.define) creator = context.define[creator]; // Check if we failed to find a definition. if (typeof(creator) == 'string') { for (var n in context.define) post('define', n, context.define[n], '\n'); throw new RangeError("Can't understand definition '" + creator + "'"); } if (typeof(creator) == 'function') // If it's a function, we simply execute it. return creator(context); if (Array.Is(creator)) // Arrays create a single Max object using CreateObject. return Creator.CreateObject(creator, context); // Otherwise, we call ourselves recursively and create a dictionary. var results = {}; for (var name in creator) results[name] = Creator.Create(creator[name], context); return results; }; var DictUtils = new Object(); DictUtils.Keys = function(d) { var r = []; for (var i in d) r.push(i); return r; }; DictUtils.GetOrAddDefault = function(table, key, dflt) { if (key in table) return table[key]; table[key] = dflt; return dflt; }; DictUtils.Copy = function(dict) { var result = {}; for (var name in dict) result[name] = dict[name]; return result; }; Creator.CreateAndConnect = function(builder, context) { context.define = builder.define; var maxobjects = Creator.Create(builder.create, context); Max.Connect(builder.connect || [], maxobjects); return maxobjects; }; Creator.Builder = function(builder) { if (typeof(builder) == 'string') builder = eval('(' + builder + ')', {Extend: Creator.Builder.Extend}); return builder; }; Creator.Builder.Extend = function(creator, x, y, _) { var msgs1 = arrayfromargs(arguments).slice(3); return function(context) { context = DictUtils.Copy(context); context.x += x; context.y += y; context.messages = Array.Concat(msgs1, context.messages); return Creator.Create(creator, context); }; }; Creator.WriteBuilder = function(builder, write) { write('{\n'); write(" 'create': {\n"); for (var name in builder.create) { write(" '" + name + "':\n"); var creator = builder.create[name]; var parts = ["'" + creator[0] + "'"].concat(creator.slice(1, 5)); write(" [" + parts.join(', ')); if (creator.length > 5) { write(',\n'); for (var i = 5; i < creator.length; ++i) write(" " + Print(creator[i]) + ",\n"); write(" "); } write("],\n\n"); }; write(" },\n\n"); if (builder.connect.length) { write(" 'connect': [\n"); for (var i = 0; i < builder.connect.length; ++i) write(" " + Print(builder.connect[i]) + ",\n"); write(" ]\n"); write("}\n"); } }; function Set(_) { var items = {}; this.Has = function(x) { return x in items; }; this.Add = function(_) { for (var i = 0; i < arguments.length; ++i) items[arguments[i]] = 1; }; this.Add.apply(this, arguments); }; Creator.BuilderFromPatcher = function(boxes, lines) { boxes = boxes || []; lines = lines || []; builder = {}; builder.create = {}; var defaults = { 'fontname': 'Arial', 'fontsize': 12, 'mode': 0, 'parameter_enable': 1, }; for (var i = 0; i < boxes.length; ++i) { var ignore = new Set(); var box = boxes[i].box; var class = box.maxclass; if (class == 'newobj') { class = box.text; ignore.Add('text'); } for (var name in defaults) { if (box[name] == defaults[name]) ignore.Add(name); } var rect = box.patching_rect; var builder = [class].concat(rect); for (var name in box) { if (!(ignore.Has(name) || Creator.MaxFieldsToIgnore.Has(name))) { var v = box[name]; if (name == 'presentation_rect') { v = [v[0] - rect[0], v[1] - rect[1], v[2] - rect[2], v[3] - rect[3]]; if (!(v[0] || v[1] || v[2] || v[3])) continue; } builder.push([name, v]); } } builder.create[box.id] = builder; } builder.connect = []; for (var i = 0; i < lines.length; ++i) { var line = lines[i].patchline; builder.connect.push([line.source[0], line.source[1], line.destination[0], line.destination[1], line.hidden]) } return builder; }; Creator.MaxFieldsToIgnore = new Set( 'id', 'maxclass', 'numinlets', 'numoutlets', 'outlettype', 'patching_rect', 'saved_attribute_attributes' ); // Read characters or JSON data from files. var Reader = new Object(); Reader.root_directory = new File('_').foldername; Reader.separator = '/'; Reader.Filename = function(filename) { if (filename.indexOf(':' ) == -1 && filename[0] != this.separator) return Reader.root_directory + Reader.separator + filename; else return filename; }; Reader.ReadFile = function(filename, length) { return new File(Reader.Filename(filename)).readstring(length || 1000000); }; Reader.ReadData = function(filename, context, length) { return eval('(' + Reader.ReadFile(filename, length) + ')', context || this); }; autowatch = 1; var _builder; function inputfile(filename) { var data = Reader.ReadData(filename); _builder = Creator.BuilderFromPatcher(data); }; function outputfile(filename) { var out = new File(filename, 'write'); Max.WriteBuilder(_builder, function(x) { out.writestring(x); }); out.close(); }; function create(x, y) { Max.CreateAndConnect(_builder, {'x': x | 0, 'y': y | 0}); }; post('recompiled clone_patcher.\n');