diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index 5be087848d076ed88e4adaa3f88b9769c2ceef43..338f9eb960e8a84b21e544d62ba529ed16b6fd0f 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -637,10 +637,12 @@ function pd_geo_string(w, h, x, y) { // have it affect an empty canvas' geometry // requires nw.js API function gui_canvas_change_geometry(cid, w, h, x, y) { - patchwin[cid].width = w; - patchwin[cid].height = h + 23; // 23 is a kludge to account for menubar - patchwin[cid].x = x; - patchwin[cid].y = y; + gui(cid).get_nw_window(function(nw_win) { + nw_win.width = w; + nw_win.height = h + 23; // 23 is a kludge to account for menubar + nw_win.x = x; + nw_win.y = y; + }); } // In tcl/tk, this function had some checks to apparently @@ -847,11 +849,11 @@ function gui_window_close(cid) { // for some edge cases like [vis 1, vis 0(--[send subpatch] we // may not have finished creating the window yet. So we check to // make sure the canvas cid exists... - if (patchwin[cid]) { - nw_close_window(patchwin[cid]); - } + gui(cid).get_nw_window(function(nw_win) { + nw_close_window(nw_win); + }); // remove reference to the window from patchwin object - patchwin[cid] = null; + set_patchwin(cid, null); loading[cid] = null; } @@ -974,13 +976,14 @@ function menu_send(name) { // requires nw.js API (Menuitem) function canvas_set_editmode(cid, state) { - var patchsvg = patchwin[cid].window.document.querySelector("#patchsvg"); - patchwin[cid].window.set_editmode_checkbox(state !== 0 ? true : false); - if (state !== 0) { - patchsvg.classList.add("editmode"); - } else { - patchsvg.classList.remove("editmode"); - } + gui(cid).get_elem("patchsvg", function(patchsvg, w) { + w.set_editmode_checkbox(state !== 0 ? true : false); + if (state !== 0) { + patchsvg.classList.add("editmode"); + } else { + patchsvg.classList.remove("editmode"); + } + }); } exports.canvas_set_editmode = canvas_set_editmode; @@ -1258,10 +1261,17 @@ exports.get_patchwin = function(name) { return patchwin[name]; } -exports.set_patchwin = function(cid, win) { +var set_patchwin = function(cid, win) { patchwin[cid] = win; + if (win) { + gui.add(cid, win); + } else { + gui.remove(cid, win); + } } +exports.set_patchwin = set_patchwin; + exports.get_dialogwin = function(name) { return dialogwin[name]; } @@ -1284,52 +1294,53 @@ exports.last_loaded = function () { // close a canvas window function gui_canvas_cursor(cid, pd_event_type) { - if (!patchwin[cid]) { - return; - } - var patch = get_item(cid, "patchsvg"), - c; - // A quick mapping of events to pointers-- these can - // be revised later - switch(pd_event_type) { - case "cursor_runmode_nothing": - c = "default"; - break; - case "cursor_runmode_clickme": - // The "pointer" icon seems the natural choice for "clickme" here, - // but unfortunately it creates ambiguity with the default editmode - // pointer icon. Not sure what the best solution is, but for now - // we'll just use "default" for clickme. That creates another - // ambiguity, but it's less of an issue since most of the - // clickable runtime items are fairly obvious anyway. - //c = "pointer"; - c = "default"; - break; - case "cursor_runmode_thicken": - c = "inherit"; - break; - case "cursor_runmode_addpoint": - c = "cell"; - break; - case "cursor_editmode_nothing": - c = "pointer"; - break; - case "cursor_editmode_connect": - c = "-webkit-grabbing"; - break; - case "cursor_editmode_disconnect": - c = "no-drop"; - break; - case "cursor_editmode_resize": - c = "ew-resize"; - break; - case "cursor_editmode_resize_bottom_right": c = "se-resize"; - break; - case "cursor_scroll": - c = "all-scroll"; - break; - } - patch.style.cursor = c; + gui(cid).get_elem("patchsvg", function(patch) { + // A quick mapping of events to pointers-- these can + // be revised later + var c; + switch(pd_event_type) { + case "cursor_runmode_nothing": + c = "default"; + break; + case "cursor_runmode_clickme": + // The "pointer" icon seems the natural choice for "clickme" + // here, but unfortunately it creates ambiguity with the + // default editmode pointer icon. Not sure what the best + // solution is, but for now so we use "default" for clickme. + // That creates another ambiguity, but it's less of an issue + // since most of the clickable runtime items are fairly obvious + // anyway. + + //c = "pointer"; + + c = "default"; + break; + case "cursor_runmode_thicken": + c = "inherit"; + break; + case "cursor_runmode_addpoint": + c = "cell"; + break; + case "cursor_editmode_nothing": + c = "pointer"; + break; + case "cursor_editmode_connect": + c = "-webkit-grabbing"; + break; + case "cursor_editmode_disconnect": + c = "no-drop"; + break; + case "cursor_editmode_resize": + c = "ew-resize"; + break; + case "cursor_editmode_resize_bottom_right": c = "se-resize"; + break; + case "cursor_scroll": + c = "all-scroll"; + break; + } + patch.style.cursor = c; + }); } // Note: cid can either be a real canvas id, or the string "pd" for the @@ -1461,14 +1472,12 @@ function canvas_map(name) { } function gui_canvas_erase_all_gobjs(cid) { - var svg_elem, - elem; - if (patchwin[cid]) { - svg_elem = get_item(cid, "patchsvg"); + gui(cid).get_elem("patchsvg", function(svg_elem) { + var elem; while (elem = svg_elem.firstChild) { svg_elem.removeChild(elem); } - } + }); } exports.canvas_map = canvas_map; @@ -1808,14 +1817,110 @@ function configure_item(item, attributes) { // The GUI side probably shouldn't know about "items" on SVG. function gui_configure_item(cid, tag, attributes) { - var item = get_item(cid, tag); - configure_item(item, attributes); + gui(cid).get_elem(tag, attributes); } function add_gobj_to_svg(svg, gobj) { svg.insertBefore(gobj, svg.querySelector(".cord")); } +// New interface to incrementally move away from the tcl-like functions +// we're currently using. Basically like a trimmed down jquery, except +// we're dealing with multiple toplevel windows so we need an window id +// to get the correct Pd canvas context. Also, some of Pd's t_text tags +// still have "." in them which unfortunately means we must wrap +// getElementById instead of the more expressive querySelector[All] where +// the "." is interpreted as a CSS class selector. + +// Methods: +// get_gobj(id, callbackOrObject) returns a reference to this little canvas +// interface +// get_elem(id, callbackOrObject) returns a reference to this little canvas +// interface +// get_nw_window(callback) returns a reference to the nw.js Window +// wrapper. We keep this separate from the +// others in order to annotate those parts +// of the code which rely on the nw API (and +// abstract them out later if we need to) + +// objects are used to set SVG attributes (in the SVG namespace) +// function callbacks have the following args: (DOMElement, window) + +// Note about checking for existence: +// Why? Because an iemgui inside a gop canvas will send drawing updates, +// __even__ __if__ that iemgui is outside the bounds of the gop and thus +// not displayed. This would be best fixed in the C code, but I'm not +// exactly sure where or how yet. +// Same problem on Pd Vanilla, except that tk canvas commands on +// non-existent tags don't throw an error. + +var gui = (function() { + var c = {}; // object to hold references to all our canvas closures + var last_thing; // last thing we got + var null_fn, null_canvas; + var create_canvas = function(cid, w) { + var get = function(parent, sel, arg, suffix) { + sel = sel + (suffix ? "gobj" : ""); + var elem = parent ? + parent.querySelector(sel) : + w.window.document.getElementById(sel); + last_thing = parent ? last_thing : elem; + if (elem) { + if (arg && typeof arg === "object") { + configure_item(elem, arg); + } else if (typeof arg === "function") { + arg(elem, w.window, c[cid]); + } + } + return c[cid]; + } + return { + append: !w ? null_fn: function(cb) { + var frag = w.window.document.createDocumentFragment(); + frag = cb(frag, w.window); + last_thing.appendChild(frag); + return c[cid]; + }, + get_gobj: !w ? null_fn : function(sel, arg) { + return get(null, sel, arg, "gobj"); + }, + get_elem: !w ? null_fn : function(sel, arg) { + return get(null, sel, arg); + }, + get_nw_window: !w ? null_fn : function(cb) { + cb(w); + return c[cid]; + }, + q: !w ? null_fn : function(sel, arg) { + return last_thing ? get(last_thing, sel, arg) : c[cid]; + }, + debug: function() { return last_thing; } + } + }; + // The tcl/tk interface ignores calls to configure non-existent items on + // canvases. Additionally, its interface was synchronous so that the window + // would always be guaranteed to exist. So we create a null canvas to keep + // from erroring out when things don't exist. + null_fn = function() { + return null_canvas; + } + null_canvas = create_canvas(null); + var canvas_container = function(cid) { + last_thing = c[cid] ? c[cid] : null_canvas; + return last_thing; + } + canvas_container.add = function(cid, nw_win) { + c[cid] = create_canvas(cid, nw_win); + } + canvas_container.remove = function(cid, nw_win) { + c[cid] = null; + } + return canvas_container; +}()); + +// For debugging +exports.gui = gui; + // Most of the following functions map either to pd.tk procs, or in some cases // tk canvas subcommands @@ -1844,34 +1949,28 @@ function add_gobj_to_svg(svg, gobj) { // creation, in which case a flag to toggle the offset would be appropriate. function gui_gobj_new(cid, tag, type, xpos, ypos, is_toplevel) { - var svg, - g, - transform_string; - if (patchwin[cid]) { - svg = get_item(cid, "patchsvg"), // id for the svg element - xpos += 0.5; - ypos += 0.5; - transform_string = "matrix(1,0,0,1," + xpos + "," + ypos + ")", + var g; + xpos += 0.5, + ypos += 0.5, + gui(cid).get_elem("patchsvg", function(svg_elem) { + var transform_string = "matrix(1,0,0,1," + xpos + "," + ypos + ")"; g = create_item(cid, "g", { id: tag + "gobj", transform: transform_string, class: type + (is_toplevel !== 0 ? "" : " gop") }); - add_gobj_to_svg(svg, g); - // hm... why returning g and not the return value of appendChild? - return g; - } + add_gobj_to_svg(svg_elem, g); + }); + return g; } function gui_text_draw_border(cid, tag, bgcolor, isbroken, x1, y1, x2, y2) { - var g, - rect; - if (patchwin[cid]) { - g = get_gobj(cid, tag), + gui(cid).get_gobj(tag) + .append(function(frag) { // isbroken means either // a) the object couldn't create or // b) the box is empty - rect = create_item(cid, "rect", { + var rect = create_item(cid, "rect", { width: x2 - x1, height: y2 - y1, //"shape-rendering": "crispEdges", @@ -1880,46 +1979,46 @@ function gui_text_draw_border(cid, tag, bgcolor, isbroken, x1, y1, x2, y2) { if (isbroken === 1) { rect.classList.add("broken_border"); } - g.appendChild(rect); - } + frag.appendChild(rect); + return frag; + }); } function gui_gobj_draw_io(cid, parenttag, tag, x1, y1, x2, y2, basex, basey, type, i, is_signal, is_iemgui) { - var xlet_class, xlet_id, rect, g; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, parenttag); - if (is_iemgui) { - xlet_class = "xlet_iemgui"; - // We have an inconsistency here. We're setting the tag using - // string concatenation below, but the "tag" for iemguis arrives - // to us pre-concatenated. We need to remove that formatting in c, and - // in general try to simplify tag creation on the c side as much - // as possible. - xlet_id = tag; - } else if (is_signal) { - xlet_class = "xlet_signal"; - xlet_id = tag + type + i; - } else { - xlet_class = "xlet_control"; - xlet_id = tag + type + i; - } - rect = create_item(cid, "rect", { - width: x2 - x1, - height: y2 - y1, - x: x1 - basex, - y: y1 - basey, - id: xlet_id, - class: xlet_class, - //"shape-rendering": "crispEdges" + gui(cid).get_gobj(parenttag) + .append(function(frag) { + var xlet_class, xlet_id, rect; + if (is_iemgui) { + xlet_class = "xlet_iemgui"; + // We have an inconsistency here. We're setting the tag using + // string concatenation below, but the "tag" for iemguis arrives + // to us pre-concatenated. We need to remove that formatting in c, + // and in general try to simplify tag creation on the c side as + // much as possible. + xlet_id = tag; + } else if (is_signal) { + xlet_class = "xlet_signal"; + xlet_id = tag + type + i; + } else { + xlet_class = "xlet_control"; + xlet_id = tag + type + i; + } + rect = create_item(cid, "rect", { + width: x2 - x1, + height: y2 - y1, + x: x1 - basex, + y: y1 - basey, + id: xlet_id, + class: xlet_class, + //"shape-rendering": "crispEdges" + }); + frag.appendChild(rect); + return frag; }); - g.appendChild(rect); } function gui_gobj_redraw_io(cid, parenttag, tag, x, y, type, i, basex, basey) { - var xlet = get_item(cid, tag + type + i); // We have to check for null. Here's why... // if you create a gatom: // canvas_atom -> glist_add -> text_vis -> glist_retext -> @@ -1927,45 +2026,40 @@ function gui_gobj_redraw_io(cid, parenttag, tag, x, y, type, i, basex, basey) { // text_drawborder (firsttime=0) -> glist_drawiofor (firsttime=0) // This means that a new gatom tries to redraw its inlets before // it has created them. - if (xlet !== null) { - configure_item(xlet, { x: x - basex, y: y - basey }); - } + gui(cid).get_elem(tag + type + i, { + x: x - basex, + y: y - basey + }); } function gui_gobj_erase_io(cid, tag) { - var xlet = get_item(cid, tag); - xlet.parentNode.removeChild(xlet); + gui(cid).get_elem(tag, function(e) { + e.parentNode.removeChild(e); + }); } function gui_gobj_configure_io(cid, tag, is_iemgui, is_signal, width) { - var xlet = get_item(cid, tag); - // We have to check for null here. Empty/broken object boxes - // can have "phantom" xlets as placeholders for connections - // to other objects. This may happen due to: - // * autopatching - // * objects which fail to create when loading a patch - if (xlet !== null) { - configure_item(xlet, { - "stroke-width": width, - }); + gui(cid).get_elem(tag, { + "stroke-width": width + }) + .get_elem(tag, function(e) { + var type; if (is_iemgui) { - xlet.classList.add("xlet_iemgui"); + type = "xlet_iemgui"; } else if (is_signal) { - xlet.classList.add("xlet_signal"); + type = "xlet_signal"; } else { - xlet.classList.add("xlet_control"); + "xlet_control"; } - // remove xlet_selected tag - xlet.classList.remove("xlet_selected"); - } + e.classList.add(type); + e.classList.remove("xlet_selected"); + }); } function gui_gobj_highlight_io(cid, tag) { - var xlet = get_item(cid, tag); - // must check for null (see gui_gobj_configure_io) - if (xlet !== null) { - xlet.classList.add("xlet_selected"); - } + gui(cid).get_elem(tag, function(e) { + e.classList.add("xlet_selected"); + }); } function message_border_points(width, height) { @@ -1980,42 +2074,35 @@ function message_border_points(width, height) { } function gui_message_draw_border(cid, tag, width, height) { - var g, - polygon; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag); - polygon = create_item(cid, "polygon", { - points: message_border_points(width, height), - fill: "none", - stroke: "black", - class: "border" - //id: tag + "border" + gui(cid).get_gobj(tag) + .append(function(frag) { + var polygon = create_item(cid, "polygon", { + points: message_border_points(width, height), + fill: "none", + stroke: "black", + class: "border" + //id: tag + "border" + }); + frag.appendChild(polygon); + return frag; }); - g.appendChild(polygon); } function gui_message_flash(cid, tag, state) { - var g = get_gobj(cid, tag); - if (state !== 0) { - g.classList.add("flashed"); - } else { - g.classList.remove("flashed"); - } + gui(cid).get_gobj(tag, function(e) { + if (state !== 0) { + e.classList.add("flashed"); + } else { + e.classList.remove("flashed"); + } + }); } function gui_message_redraw_border(cid, tag, width, height) { - var g, b; - if (patchwin[cid]) { - g = get_gobj(cid, tag); - if (g) { - b = g.querySelector(".border"); - configure_item(b, { - points: message_border_points(width, height), - }); - } - } + gui(cid).get_gobj(tag) + .q(".border", { + points: message_border_points(width, height) + }); } function atom_border_points(width, height, is_dropdown) { @@ -2040,67 +2127,56 @@ function atom_arrow_points(width, height) { ].join(" "); } - function gui_atom_draw_border(cid, tag, type, width, height) { - var g, polygon, arrow, m; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag), - polygon = create_item(cid, "polygon", { - points: atom_border_points(width, height, type !== 0), - fill: "none", - stroke: "gray", - "stroke-width": 1, - class: "border" - //id: tag + "border" - }); - g.appendChild(polygon); - if (type !== 0) { // dropdown - // 1 = output index - // 2 = output value - // Let's make the two visually distinct so that the user can still - // reason about the patch functionality merely by reading the diagram - m = height < 20 ? 1 : height / 12; - arrow = create_item(cid, "polygon", { - points: atom_arrow_points(width, height), - "class": type === 1 ? "index_arrow" : "value_arrow" + gui(cid).get_gobj(tag) + .append(function(frag) { + var polygon = create_item(cid, "polygon", { + points: atom_border_points(width, height, type !== 0), + fill: "none", + stroke: "gray", + "stroke-width": 1, + class: "border" + //id: tag + "border" }); - g.appendChild(arrow); - } + + frag.appendChild(polygon); + if (type !== 0) { // dropdown + // 1 = output index + // 2 = output value + // Let's make the two visually distinct so that the user can still + // reason about the patch functionality merely by reading the + // diagram + var m = height < 20 ? 1 : height / 12; + var arrow = create_item(cid, "polygon", { + points: atom_arrow_points(width, height), + "class": type === 1 ? "index_arrow" : "value_arrow" + }); + frag.appendChild(arrow); + } + return frag; + }); } function gui_atom_redraw_border(cid, tag, type, width, height) { - var g = get_gobj(cid, tag), - p, a; - // Unfortunately Pd will send updates for gui objects that - // lie outside the bounding box of a graph-on-parent subpach. - // We should refrain from sending such messages from Pd, but for - // now this conditional guards against calling a method on null... - if (g) { - p = g.querySelector("polygon"); - // When creating a new gatom, the C code sends messages - // to redraw the border before the border exists. - // So we have to check for existence here... - if (p) { - configure_item(p, { - points: atom_border_points(width, height, type !== 0) + gui(cid).get_elem(tag) + .q("polygon", { + points: atom_border_points(width, height, type !== 0) + }); + if (type !== 0) { + gui(cid).get_elem(tag, function(e) { + var a = e.querySelectorAll("polygon")[1]; + configure_item(a, { + points: atom_arrow_points(width, height) }); - if (type !== 0) { - a = g.querySelectorAll("polygon")[1]; - configure_item(a , { - points: atom_arrow_points(width, height), - }); - } - } + }); } } // draw a patch cord function gui_canvas_line(cid,tag,type,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10) { - var svg, xoff, d_array, path; - if (patchwin[cid]) { - svg = get_item(cid, "patchsvg"), + gui(cid).get_elem("patchsvg") + .append(function(frag) { + var svg = get_item(cid, "patchsvg"), // xoff is for making sure straight lines are crisp. An SVG stroke // straddles the coordinate, with 1/2 the width on each side. // Control cords are 1 px wide, which requires a 0.5 x-offset to align @@ -2118,59 +2194,47 @@ function gui_canvas_line(cid,tag,type,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10) { id: tag, "class": "cord " + type }); - svg.appendChild(path); - } + frag.appendChild(path); + return frag; + }); } function gui_canvas_select_line(cid, tag) { - var line = get_item(cid, tag); - if (line !== null) { - line.classList.add("selected_line"); - } else { - //post("gui_canvas_select_line: can't find line"); - } + gui(cid).get_elem(tag, function(e) { + e.classList.add("selected_line"); + }); } function gui_canvas_deselect_line(cid, tag) { - var line = get_item(cid, tag); - if (line !== null) { - line.classList.remove("selected_line"); - } else { - //post("gui_canvas_select_line: can't find line"); - } + gui(cid).get_elem(tag, function(e) { + e.classList.remove("selected_line"); + }); } // rename to erase_line (or at least standardize with gobj_erase) function gui_canvas_delete_line(cid, tag) { - var line; - if (patchwin[cid]) { - line = get_item(cid, tag); - if (line !== null) { - line.parentNode.removeChild(line); - } else { - //post("canvas_delete_line: error: the line doesn't exist"); - } - } + gui(cid).get_elem(tag, function(e) { + e.parentNode.removeChild(e); + }); } function gui_canvas_update_line(cid, tag, x1, y1, x2, y2, yoff) { - var halfx = parseInt((x2 - x1)/2), - halfy = parseInt((y2 - y1)/2), - cord = get_item(cid, tag), - xoff, // see comment in gui_canvas_line about xoff - d_array; // We have to check for existence here for the special case of // preset_node which hides a wire that feeds back from the downstream // object to its inlet. Pd refrains from drawing this hidden wire at all. // It should also suppress a call here to update that line, but it // currently doesn't. So we check for existence. - if (cord) { - xoff = cord.classList.contains("signal") ? 0: 0.5; + gui(cid).get_elem(tag, function(e) { + var halfx = parseInt((x2 - x1)/2), + halfy = parseInt((y2 - y1)/2), + xoff, // see comment in gui_canvas_line about xoff + d_array; + xoff = e.classList.contains("signal") ? 0: 0.5; d_array = ["M",x1+xoff,y1+xoff, "Q",x1+xoff,y1+yoff+xoff,x1+halfx+xoff,y1+halfy+xoff, "Q",x2+xoff,y2-yoff+xoff,x2+xoff,y2+xoff]; - configure_item(cord, { d: d_array.join(" ") }); - } + configure_item(e, { d: d_array.join(" ") }); + }); } function text_line_height_kludge(fontsize, fontsize_type) { @@ -2188,19 +2252,19 @@ function text_line_height_kludge(fontsize, fontsize_type) { } } -function text_to_tspans(canvasname, svg_text, text) { +function text_to_tspans(cid, svg_text, text) { var lines, i, len, tspan, fontsize, text_node; lines = text.split("\n"); len = lines.length; // Get fontsize (minus the trailing "px") fontsize = svg_text.getAttribute("font-size").slice(0, -2); for (i = 0; i < len; i++) { - tspan = create_item(canvasname, "tspan", { + tspan = create_item(cid, "tspan", { dy: i == 0 ? 0 : text_line_height_kludge(+fontsize, "gui") + "px", x: 0 }); // find a way to abstract away the canvas array and the DOM here - text_node = patchwin[canvasname].window.document + text_node = patchwin[cid].window.document .createTextNode(lines[i]); tspan.appendChild(text_node); svg_text.appendChild(tspan); @@ -2285,136 +2349,92 @@ function gobj_font_y_kludge(fontsize) { } } -function gui_text_new(canvasname, myname, type, isselected, left_margin, font_height, text, font) { - var lines, i, len, tspan, - g, - svg_text; - if (!patchwin[canvasname]) { - return; - } - g = get_gobj(canvasname, myname), - svg_text = create_item(canvasname, "text", { - // Maybe it's just me, but the svg spec's explanation of how - // text x/y and tspan x/y interact is difficult to understand. - // So here we just translate by the right amount for the left-margin, - // guaranteeing all tspan children will line up where they should be. - - // Another anomaly-- we add 0.5 to the translation so that the font - // hinting works correctly. This effectively cancels out the 0.5 pixel - // alignment done in the gobj, so it might be better to specify the - // offset in whatever is calling this function. - - // I don't know how svg text grid alignment relates to other svg shapes, - // and I haven't yet found any documentation for it. All I know is - // an integer offset results in blurry text, and the 0.5 offset doesn't. - transform: "translate(" + (left_margin - 0.5) + ")", - y: font_height + gobj_font_y_kludge(font), - // Turns out we can't do 'hanging' baseline - // because it's borked when scaled. Bummer, because that's how Pd's - // text is handled under tk... - // 'dominant-baseline': 'hanging', - "shape-rendering": "crispEdges", - "font-size": pd_fontsize_to_gui_fontsize(font) + "px", - "font-weight": "normal", - id: myname + "text", - "class": "box_text" - }); - // trim off any extraneous leading/trailing whitespace. Because of - // the way binbuf_gettext works we almost always have a trailing - // whitespace. - text = text.trim(); - // fill svg_text with tspan content by splitting on '\n' - text_to_tspans(canvasname, svg_text, text); - if (g !== null) { - g.appendChild(svg_text); - } else { - post("gui_text_new: can't find parent group " + myname); - } - if (isselected) { - gui_gobj_select(canvasname, myname); - } +function gui_text_new(cid, tag, type, isselected, left_margin, font_height, text, font) { + gui(cid).get_gobj(tag) + .append(function(frag) { + var svg_text = create_item(cid, "text", { + // Maybe it's just me, but the svg spec's explanation of how + // text x/y and tspan x/y interact is difficult to understand. + // So here we just translate by the right amount for the + // left-margin, guaranteeing all tspan children will line up where + // they should be. + + // Another anomaly-- we add 0.5 to the translation so that the font + // hinting works correctly. This effectively cancels out the 0.5 + // pixel alignment done in the gobj, so it might be better to + // specify the offset in whatever is calling this function. + + // I don't know how svg text grid alignment relates to other svg + // shapes, and I haven't yet found any documentation for it. All I + // know is an integer offset results in blurry text, and the 0.5 + // offset doesn't. + transform: "translate(" + (left_margin - 0.5) + ")", + y: font_height + gobj_font_y_kludge(font), + // Turns out we can't do 'hanging' baseline + // because it's borked when scaled. Bummer, because that's how Pd's + // text is handled under tk... + // 'dominant-baseline': 'hanging', + "shape-rendering": "crispEdges", + "font-size": pd_fontsize_to_gui_fontsize(font) + "px", + "font-weight": "normal", + id: tag + "text", + "class": "box_text" + }); + // trim off any extraneous leading/trailing whitespace. Because of + // the way binbuf_gettext works we almost always have a trailing + // whitespace. + text = text.trim(); + // fill svg_text with tspan content by splitting on '\n' + text_to_tspans(cid, svg_text, text); + frag.appendChild(svg_text); + if (isselected) { + gui_gobj_select(cid, tag); + } + return frag; + }); } // Because of the overly complex code path inside // canvas_setgraph, multiple erasures can be triggered in a row. function gui_gobj_erase(cid, tag) { - var g; - if (patchwin[cid]) { - g = get_gobj(cid, tag); - if (g !== null) { - g.parentNode.removeChild(g); - } else { - // Unfortunately Pd can send messages - // to erase objects before they got created, - // or extra messages to delete objects. So - // we can't report an error here... - //post("gui_gobj_erase: gobj " + tag + - // " didn't exist in the first place!"); - } - } + gui(cid).get_gobj(tag, function(e) { + e.parentNode.removeChild(e); + }); } function gui_text_set (cid, tag, text) { - var svg_text; - if (patchwin[cid]) { - svg_text = get_item(cid, tag + "text"); - if (svg_text !== null) { - // trim leading/trailing whitespace - text = text.trim(); - svg_text.textContent = ""; - text_to_tspans(cid, svg_text, text); - } else { - // In tk, setting an option for a non-existent canvas - // item is ignored. Because of that, Miller didn't pay - // attention to parts of the implementation which attempted - // to set options before creating the item. To get a sense - // of where this is happening, uncomment the following line: - - //post("gui_text_set: svg_text doesn't exist: tag: " + tag); - } - } + gui(cid).get_elem(tag + "text", function(e) { + text = text.trim(); + e.textContent = ""; + text_to_tspans(cid, e, text); + }); } function gui_text_redraw_border(cid, tag, x1, y1, x2, y2) { - var g = get_gobj(cid, tag), - b = g.querySelectorAll(".border"), + // Hm, need to figure out how to refactor to get rid of + // configure_item call... + gui(cid).get_gobj(tag, function(e) { + var b = e.querySelectorAll(".border"), i; - for (i = 0; i < b.length; b++) { - configure_item(b[i], { - width: x2 - x1, - height: y2 - y1 - }); - } + for (i = 0; i < b.length; b++) { + configure_item(b[i], { + width: x2 - x1, + height: y2 - y1 + }); + } + }); } function gui_gobj_select(cid, tag) { - var g; - // We need to check if the window exists, because Pd will send - // messages to select the object before it (or the window) actually exists - - // For example, this happens when using the "Find" menu. If Pd finds the - // match in a subpatch that isn't visible, it will open the subpatch and - // try to select the matching object before the subpatch has been mapped. - if (patchwin[cid]) { - g = get_gobj(cid, tag); - if (g !== null) { - g.classList.add("selected"); - } else { - console.log("text_select: something wrong with group tag: " + tag); - } - } + gui(cid).get_gobj(tag, function(e) { + e.classList.add("selected"); + }); } function gui_gobj_deselect(cid, tag) { - var gobj; - if (patchwin[cid]) { - gobj = get_gobj(cid, tag); - if (gobj !== null) { - gobj.classList.remove("selected"); - } else { - console.log("text_deselect: error with tag: " + tag + "gobj"); - } - } + gui(cid).get_gobj(tag, function(e) { + e.classList.remove("selected"); + }); } // This adds a 0.5 offset to align to pixel grid, so it should @@ -2458,67 +2478,60 @@ function textentry_displace(t, dx, dy) { (y + dy) + "px)"); } -function gui_canvas_displace_withtag(name, dx, dy) { - var pwin = patchwin[name], i, textentry, - ol = pwin.window.document.getElementsByClassName("selected"); - for (i = 0; i < ol.length; i++) { - elem_displace(ol[i], dx, dy); - //var elem = ol[i].transform.baseVal.getItem(0); - //var new_tx = dx + elem.matrix.e; - //var new_ty = dy + elem.matrix.f; - //elem.matrix.e = new_tx; - //elem.matrix.f = new_ty; - } - textentry = patchwin[name].window.document - .getElementById("new_object_textentry"); - if (textentry !== null) { +function gui_canvas_displace_withtag(cid, dx, dy) { + gui(cid) + .get_elem("patchsvg", function(svg_elem, w) { + var i, ol; + ol = w.document.getElementsByClassName("selected"); + for (i = 0; i < ol.length; i++) { + elem_displace(ol[i], dx, dy); + } + }) + .get_elem("new_object_textentry", function(textentry) { textentry_displace(textentry, dx, dy); - } - //elem.setAttributeNS(null, "transform", - //"translate(" + new_tx + "," + new_ty + ")"); - //} + }); } function gui_canvas_draw_selection(cid, x1, y1, x2, y2) { - var svg = get_item(cid, "patchsvg"), - rect, - points_array = [x1 + 0.5, y1 + 0.5, - x2 + 0.5, y1 + 0.5, - x2 + 0.5, y2 + 0.5, - x1 + 0.5, y2 + 0.5 - ]; - rect = create_item(cid, "polygon", { - points: points_array.join(" "), - fill: "none", - //"shape-rendering": "optimizeSpeed", - "stroke-width": 1, - id: "selection_rectangle", - display: "inline" + gui(cid).get_elem("patchsvg", function(svg_elem) { + var points_array = [x1 + 0.5, y1 + 0.5, + x2 + 0.5, y1 + 0.5, + x2 + 0.5, y2 + 0.5, + x1 + 0.5, y2 + 0.5 + ]; + var rect = create_item(cid, "polygon", { + points: points_array.join(" "), + fill: "none", + //"shape-rendering": "optimizeSpeed", + "stroke-width": 1, + id: "selection_rectangle", + display: "inline" + }); + svg_elem.appendChild(rect); }); - svg.appendChild(rect); } function gui_canvas_move_selection(cid, x1, y1, x2, y2) { - var rect = get_item(cid, "selection_rectangle"), - points_array = [x1 + 0.5, y1 + 0.5, x2 + 0.5, y1 + 0.5, + var points_array = [x1 + 0.5, y1 + 0.5, x2 + 0.5, y1 + 0.5, x2 + 0.5, y2 + 0.5, x1 + 0.5, y2 + 0.5]; - configure_item(rect, { points: points_array }); + gui(cid).get_elem("selection_rectangle", { + points: points_array + }); + } function gui_canvas_hide_selection(cid) { - var rect = get_item(cid, "selection_rectangle"); - rect.parentElement.removeChild(rect); + gui(cid).get_elem("selection_rectangle", function(e) { + e.parentElement.removeChild(e); + }); } // iemguis function gui_bng_new(cid, tag, cx, cy, radius) { - var g, circle; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag); - circle = create_item(cid, "circle", { + gui(cid).get_gobj(tag) + .append(function(frag) { + var circle = create_item(cid, "circle", { cx: cx, cy: cy, r: radius, @@ -2527,23 +2540,20 @@ function gui_bng_new(cid, tag, cx, cy, radius) { stroke: "black", "stroke-width": 1, id: tag + "button" + }); + frag.appendChild(circle); + return frag; }); - g.appendChild(circle); } function gui_bng_button_color(cid, tag, color) { - var button; - if (patchwin[cid]) { - button = get_item(cid, tag + "button"); - if (button) { - configure_item(button, { fill: color }); - } - } + gui(cid).get_elem(tag + "button", { + fill: color + }); } function gui_bng_configure(cid, tag, color, cx, cy, r) { - var b = get_item(cid, tag + "button"); - configure_item(b, { + gui(cid).get_elem(tag + "button", { cx: cx, cy: cy, r: r, @@ -2552,81 +2562,65 @@ function gui_bng_configure(cid, tag, color, cx, cy, r) { } function gui_toggle_new(cid, tag, color, width, state, p1,p2,p3,p4,p5,p6,p7,p8,basex,basey) { - var g, - points_array, - cross1, cross2; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag); - points_array = [p1 - basex, p2 - basey, - p3 - basex, p4 - basey - ]; - cross1 = create_item(cid, "polyline", { - points: points_array.join(" "), - stroke: color, - fill: "none", - id: tag + "cross1", - display: state ? "inline" : "none", - "stroke-width": width - }); - points_array = [p5 - basex, p6 - basey, - p7 - basex, p8 - basey - ]; - cross2 = create_item(cid, "polyline", { - points: points_array.join(" "), - stroke: color, - fill: "none", - id: tag + "cross2", - display: state ? "inline" : "none", - "stroke-width": width + gui(cid).get_gobj(tag) + .append(function(frag) { + var points = [p1 - basex, p2 - basey, + p3 - basex, p4 - basey + ].join(" "); + var cross1 = create_item(cid, "polyline", { + points: points, + stroke: color, + fill: "none", + id: tag + "cross1", + display: state ? "inline" : "none", + "stroke-width": width + }); + points = [p5 - basex, p6 - basey, + p7 - basex, p8 - basey + ].join(" "); + var cross2 = create_item(cid, "polyline", { + points: points, + stroke: color, + fill: "none", + id: tag + "cross2", + display: state ? "inline" : "none", + "stroke-width": width + }); + frag.appendChild(cross1); + frag.appendChild(cross2); + return frag; }); - g.appendChild(cross1); - g.appendChild(cross2); } function gui_toggle_resize_cross(cid,tag,w,p1,p2,p3,p4,p5,p6,p7,p8,basex,basey) { - var g = get_gobj(cid, tag), - points_array, - cross1, cross2; - points_array = [p1 - basex, p2 - basey, - p3 - basex, p4 - basey - ]; - cross1 = get_item(cid, tag + "cross1"); - configure_item(cross1, { - points: points_array.join(" "), + var points1 = [p1 - basex, p2 - basey, + p3 - basex, p4 - basey + ].join(" "), + points_array = [p5 - basex, p6 - basey, + p7 - basex, p8 - basey + ].join(" "); + gui(cid) + .get_elem(tag + "cross1", { + points1: points, "stroke-width": w - }); - - points_array = [p5 - basex, p6 - basey, - p7 - basex, p8 - basey - ]; - cross2 = get_item(cid, tag + "cross2"); - configure_item(cross2, { - points: points_array.join(" "), + }) + .get_elem(tag + "cross2", { + points2: points, "stroke-width": w }); } function gui_toggle_update(cid, tag, state, color) { - var cross1 = get_item(cid, tag + "cross1"), - cross2 = get_item(cid, tag + "cross2"); - // We have to check for existence here. - // Why? Because a [tgl] inside a gop canvas will send drawing updates, - // __even__ __if__ that [tgl] is outside the bounds of the gop and thus - // not displayed. This would be best fixed in the C code, but I'm not - // exactly sure where or how yet. - // Same problem on Pd Vanilla, except that tk canvas commands on - // non-existent tags don't throw an error. - if (cross1) { - if (!!state) { - configure_item(cross1, { display: "inline", stroke: color }); - configure_item(cross2, { display: "inline", stroke: color }); - } else { - configure_item(cross1, { display: "none", stroke: color }); - configure_item(cross2, { display: "none", stroke: color }); - } - } + var disp = !!state ? "inline" : "none"; + gui(cid) + .get_elem(tag + "cross1", { + display: disp, + stroke: color + }) + .get_elem(tag + "cross2", { + display: disp, + stroke: color + }) } function numbox_data_string(w, h) { @@ -2646,28 +2640,23 @@ function numbox_data_string(w, h) { function gui_numbox_new(cid, tag, color, x, y, w, h, is_toplevel) { // numbox doesn't have a standard iemgui border, // so we must create its gobj manually - var g, - data, - border; - if (!patchwin[cid]) { - return; - } - g = gui_gobj_new(cid, tag, "iemgui", x, y, is_toplevel); - data = numbox_data_string(w, h); - border = create_item(cid, "path", { - d: data, - fill: color, - stroke: "black", - "stroke-width": 1, - id: (tag + "border"), - "class": "border" + gui(cid).get_elem("patchsvg", function() { + var g = gui_gobj_new(cid, tag, "iemgui", x, y, is_toplevel); + var data = numbox_data_string(w, h); + var border = create_item(cid, "path", { + d: data, + fill: color, + stroke: "black", + "stroke-width": 1, + id: (tag + "border"), + "class": "border" + }); + g.appendChild(border); }); - g.appendChild(border); } function gui_numbox_coords(cid, tag, w, h) { - var b = get_item(cid, tag + "border"); - configure_item(b, { + gui(cid).get_elem(tag + "border", { d: numbox_data_string(w, h) }); } @@ -2675,8 +2664,9 @@ function gui_numbox_coords(cid, tag, w, h) { function gui_numbox_draw_text(cid,tag,text,font_size,color,xpos,ypos,basex,basey) { // kludge alert -- I'm not sure why I need to add half to the ypos // below. But it works for most font sizes. - var g = get_gobj(cid, tag), - svg_text = create_item(cid, "text", { + gui(cid).get_gobj(tag) + .append(function(frag, w) { + var svg_text = create_item(cid, "text", { transform: "translate(" + (xpos - basex) + "," + ((ypos - basey + (ypos - basey) * 0.5)|0) + ")", @@ -2684,136 +2674,131 @@ function gui_numbox_draw_text(cid,tag,text,font_size,color,xpos,ypos,basex,basey fill: color, id: tag + "text" }), - text_node = patchwin[cid].window.document.createTextNode(text); - svg_text.appendChild(text_node); - g.appendChild(svg_text); + text_node = w.document.createTextNode(text); + svg_text.appendChild(text_node); + frag.appendChild(svg_text); + return frag; + }); } function gui_numbox_update(cid, tag, fcolor, bgcolor, font_name, font_size, font_weight) { - var b = get_item(cid, tag + "border"), - text = get_item(cid, tag + "text"), - label = get_item(cid, tag + "label"); - configure_item(b, { fill: bgcolor }); - configure_item(text, { fill: fcolor, "font-size": font_size }); - // Update the label if one exists - if (label) { + gui(cid) + .get_elem(tag + "border", { + fill: bgcolor + }) + .get_elem(tag + "text", { + fill: fcolor, + "font-size": font_size + }) + // label may or may not exist, but that's covered by the API + .get_elem(tag + "label", function() { gui_iemgui_label_font(cid, tag, font_name, font_weight, font_size); - } + }); } function gui_numbox_update_text_position(cid, tag, x, y) { - var text = get_item(cid, tag + "text"); - configure_item(text, { + gui(cid).get_elem(tag + "text", { transform: "translate( " + x + "," + ((y + y*0.5)|0) + ")" }); } function gui_slider_new(cid, tag, color, p1, p2, p3, p4, basex, basey) { - var g, - indicator; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag); - indicator = create_item(cid, "line", { + gui(cid).get_gobj(tag) + .append(function(frag) { + var indicator = create_item(cid, "line", { + x1: p1 - basex, + y1: p2 - basey, + x2: p3 - basex, + y2: p4 - basey, + stroke: color, + "stroke-width": 3, + fill: "none", + id: tag + "indicator" + }); + frag.appendChild(indicator); + return frag; + }); +} + +function gui_slider_update(cid, tag, p1, p2, p3, p4, basex, basey) { + gui(cid).get_elem(tag + "indicator", { x1: p1 - basex, y1: p2 - basey, x2: p3 - basex, - y2: p4 - basey, - stroke: color, - "stroke-width": 3, - fill: "none", - id: tag + "indicator" + y2: p4 - basey }); - g.appendChild(indicator); +} -} - -function gui_slider_update(cid, tag, p1, p2, p3, p4, basex, basey) { - var indicator; - if (patchwin[cid]) { - indicator = get_item(cid, tag + "indicator"); - if (indicator) { - configure_item(indicator, { - x1: p1 - basex, - y1: p2 - basey, - x2: p3 - basex, - y2: p4 - basey - }); - } - } -} - -function gui_slider_indicator_color(cid, tag, color) { - var i = get_item(cid, tag + "indicator"); - configure_item(i, { - stroke: color - }); +function gui_slider_indicator_color(cid, tag, color) { + gui(cid).get_elem(tag + "indicator", { + stroke: color + }); } function gui_radio_new(cid, tag, p1, p2, p3, p4, i, basex, basey) { - var g, - cell; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag); - cell = create_item(cid, "line", { - x1: p1 - basex, - y1: p2 - basey, - x2: p3 - basex, - y2: p4 - basey, - // stroke is just black for now - stroke: "black", - "stroke-width": 1, - fill: "none", - id: tag + "cell_" + i + gui(cid).get_gobj(tag) + .append(function(frag) { + var cell = create_item(cid, "line", { + x1: p1 - basex, + y1: p2 - basey, + x2: p3 - basex, + y2: p4 - basey, + // stroke is just black for now + stroke: "black", + "stroke-width": 1, + fill: "none", + id: tag + "cell_" + i + }); + frag.appendChild(cell); + return frag; }); - g.appendChild(cell); } function gui_radio_create_buttons(cid,tag,color,p1,p2,p3,p4,basex,basey,i,state) { - var g = get_gobj(cid, tag), - b; - b = create_item(cid, "rect", { - x: p1 - basex, - y: p2 - basey, - width: p3 - p1, - height: p4 - p2, - stroke: color, - fill: color, - id: tag + "button_" + i, - display: state ? "inline" : "none" + gui(cid).get_gobj(tag) + .append(function(frag) { + var b = create_item(cid, "rect", { + x: p1 - basex, + y: p2 - basey, + width: p3 - p1, + height: p4 - p2, + stroke: color, + fill: color, + id: tag + "button_" + i, + display: state ? "inline" : "none" + }); + frag.appendChild(b); + return frag; }); - g.appendChild(b); } function gui_radio_button_coords(cid, tag, x1, y1, xi, yi, i, s, d, orient) { - var button = get_item(cid, tag + "button_" + i), - cell = get_item(cid, tag + "cell_" + i); + gui(cid) + .get_elem(tag + "button_" + i, { + x: orient ? s : xi+s, + y: orient ? yi+s : s, + width: d-(s*2), + height: d-(s*2) + }) // the line to draw the cell for i=0 doesn't exist. Probably was not worth // the effort, but it's easier just to check for that here atm. if (i > 0) { - configure_item(cell, { + gui(cid) + .get_elem(tag + "cell_" + i, { x1: orient ? 0 : xi, y1: orient ? yi : 0, x2: orient ? d : xi, y2: orient ? yi : d }); } - configure_item(button, { - x: orient ? s : xi+s, - y: orient ? yi+s : s, - width: d-(s*2), - height: d-(s*2) - }); } function gui_radio_update(cid, tag, fgcolor, prev, next) { - var prev = get_item(cid, tag + "button_" + prev), - next = get_item(cid, tag + "button_" + next); - configure_item(prev, { display: "none" }); - configure_item(next, { + gui(cid) + .get_elem(tag + "button_" + prev, { + display: "none" + }) + .get_elem(tag + "button_" + next, { display: "inline", fill: fgcolor, stroke: fgcolor @@ -2821,8 +2806,9 @@ function gui_radio_update(cid, tag, fgcolor, prev, next) { } function gui_vumeter_draw_text(cid,tag,color,xpos,ypos,text,index,basex,basey, font_size, font_weight) { - var g = get_gobj(cid, tag), - svg_text = create_item(cid, "text", { + gui(cid).get_gobj(tag) + .append(function(frag, w) { + var svg_text = create_item(cid, "text", { x: xpos - basex, y: ypos - basey, "font-family": iemgui_fontfamily(fontname), @@ -2830,9 +2816,11 @@ function gui_vumeter_draw_text(cid,tag,color,xpos,ypos,text,index,basex,basey, f "font-weight": font_weight, id: tag + "text_" + index }), - text_node = patchwin[cid].window.document.createTextNode(text); - svg_text.appendChild(text_node); - g.appendChild(svg_text); + text_node = w.document.createTextNode(text); + svg_text.appendChild(text_node); + frag.appendChild(svg_text); + return frag; + }); } // Oh, what a terrible interface this is! @@ -2845,48 +2833,49 @@ function gui_vumeter_draw_text(cid,tag,color,xpos,ypos,text,index,basex,basey, f // To get on to other work we just parrot the insanity here, // and silently ignore calls to update non-existent text. function gui_vumeter_update_text(cid, tag, text, font, selected, color, i) { - var svg_text = get_item(cid, tag + "text_" + i); - if (!selected) { - // Hack... - if (svg_text !== null) { - configure_item(svg_text, { fill: color }); - } - } + gui(cid).get_elem(tag + "text_" + i, { + fill: color + }); } function gui_vumeter_text_coords(cid, tag, i, xpos, ypos, basex, basey) { - var t = get_item(cid, tag + "text_" + i); - configure_item(t, { x: xpos - basex, y: ypos - basey }); + gui(cid).get_elem(tag + "text_" + i, { + x: xpos - basex, + y: ypos - basey + }); } function gui_vumeter_erase_text(cid, tag, i) { - var t = get_item(cid, tag + "text_" + i); - t.parentNode.removeChild(t); + gui(cid).get_elem(tag + "text_" + i, function(e) { + e.parentNode.removeChild(e); + }); } function gui_vumeter_create_steps(cid,tag,color,p1,p2,p3,p4,width,basex,basey,i) { - var g = get_gobj(cid, tag), - l; - l = create_item(cid, "line", { - x1: p1 - basex, - y1: p2 - basey, - x2: p3 - basex, - y2: p4 - basey, - stroke: color, - "stroke-width": width, - "id": tag + "led_" + i + gui(cid).get_gobj(tag) + .append(function(frag) { + var l = create_item(cid, "line", { + x1: p1 - basex, + y1: p2 - basey, + x2: p3 - basex, + y2: p4 - basey, + stroke: color, + "stroke-width": width, + "id": tag + "led_" + i + }); + frag.appendChild(l); + return frag; }); - g.appendChild(l); } function gui_vumeter_update_steps(cid, tag, i, width) { - var step = get_item(cid, tag + "led_" + i); - configure_item(step, { "stroke-width": width }); + gui(cid).get_elem(tag + "led_" + i, { + "stroke-width": width + }); } function gui_vumeter_update_step_coords(cid,tag,i,x1,y1,x2,y2,basex,basey) { - var l = get_item(cid, tag + "led_" + i); - configure_item(l, { + gui(cid).get_elem(tag + "led_" + i, { x1: x1 - basex, y1: y1 - basey, x2: x2 - basex, @@ -2895,62 +2884,65 @@ function gui_vumeter_update_step_coords(cid,tag,i,x1,y1,x2,y2,basex,basey) { } function gui_vumeter_draw_rect(cid,tag,color,p1,p2,p3,p4,basex,basey) { - var g = get_gobj(cid, tag), - rect; - rect = create_item(cid, "rect", { - x: p1 - basex, - y: p2 - basey, - width: p3 - p1, - height: p4 + 1 - p2, - stroke: color, - fill: color, - id: tag + "rect" + gui(cid).get_gobj(tag) + .append(function(frag) { + var rect = create_item(cid, "rect", { + x: p1 - basex, + y: p2 - basey, + width: p3 - p1, + height: p4 + 1 - p2, + stroke: color, + fill: color, + id: tag + "rect" + }); + frag.appendChild(rect); + return frag; }); - g.appendChild(rect); } function gui_vumeter_update_rect(cid, tag, color) { - var r = get_item(cid, tag + "rect"); - configure_item(r, { fill: color, stroke: color }); + gui(cid).get_elem(tag + "rect", { + fill: color, + stroke: color + }); } // Oh hack upon hack... why doesn't the iemgui base_config just take care // of this? function gui_vumeter_border_size(cid, tag, width, height) { - var g = get_gobj(cid, tag), - r; - // also need to check for existence-- apparently the iemgui - // dialog will delete the vu and try to set this before recreating it... - if (g) { - r = g.querySelector(".border"); - configure_item(r, { width: width, height: height }); - } + gui(cid).get_gobj(tag) + .q(".border", { + width: width, + height: height + }); } function gui_vumeter_update_peak_width(cid, tag, width) { - var r = get_item(cid, tag + "rect"); - configure_item(r, { "stroke-width": width }); + gui(cid).get_elem(tag + "rect", { + "stroke-width": width + }); } function gui_vumeter_draw_peak(cid,tag,color,p1,p2,p3,p4,width,basex,basey) { - var g = get_gobj(cid, tag), - line; - line = create_item(cid, "line", { - x1: p1 - basex, - y1: p2 - basey, - x2: p3 - basex, - y2: p4 - basey, - stroke: color, - "stroke-width": width, - id: tag + "peak" + gui(cid).get_gobj(tag) + .append(function(frag) { + var line = create_item(cid, "line", { + x1: p1 - basex, + y1: p2 - basey, + x2: p3 - basex, + y2: p4 - basey, + stroke: color, + "stroke-width": width, + id: tag + "peak" + }); + frag.appendChild(line); + return frag; }); - g.appendChild(line); } // probably should change tag from "rect" to "cover" function gui_vumeter_update_rms(cid, tag, p1, p2, p3, p4, basex, basey) { - var rect = get_item(cid, tag + "rect"); - configure_item(rect, { + gui(cid).get_elem(tag + "rect", { x: p1 - basex, y: p2 - basey, width: p3 - p1, @@ -2959,39 +2951,27 @@ function gui_vumeter_update_rms(cid, tag, p1, p2, p3, p4, basex, basey) { } function gui_vumeter_update_peak(cid,tag,color,p1,p2,p3,p4,basex,basey) { - var line; - if (patchwin[cid]) { - line = get_item(cid, tag + "peak"); - if (line) { - configure_item(line, { - x1: p1 - basex, - y1: p2 - basey, - x2: p3 - basex, - y2: p4 - basey, - stroke: color - }); - } - } + gui(cid).get_elem(tag + "peak", { + x1: p1 - basex, + y1: p2 - basey, + x2: p3 - basex, + y2: p4 - basey, + stroke: color + }); } function gui_iemgui_base_color(cid, tag, color) { - var g, - b; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag); - if (g) { - b = g.querySelector(".border"); - configure_item(b, { fill: color }); - } + gui(cid).get_gobj(tag) + .q(".border", { + fill: color + }); } function gui_iemgui_move_and_resize(cid, tag, x1, y1, x2, y2) { - var gobj = get_gobj(cid, tag), - item = gobj.querySelector(".border"); - elem_move(gobj, x1, y1); - configure_item(item, { + gui(cid).get_gobj(tag, function(e) { + elem_move(e, x1, y1); + }) + .q(".border", { width: x2 - x1, height: y2 - y1 }); @@ -3040,123 +3020,112 @@ function iemgui_fontfamily(name) { function gui_iemgui_label_new(cid, tag, x, y, color, text, fontname, fontweight, fontsize) { - var g, - svg_text, text_node; - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag); - svg_text = create_item(cid, "text", { - // x and y need to be relative to baseline instead of nw anchor - x: x, - y: y, - //"font-size": font + "px", - "font-family": iemgui_fontfamily(fontname), - // for some reason the font looks bold in Pd-Vanilla-- not sure why - "font-weight": fontweight, - "font-size": fontsize + "px", - fill: color, - // Iemgui labels are anchored "w" (left-aligned to non-tclers). - // For no good reason, they are also centered vertically, unlike - // object box text. Since svg text uses the baseline as a reference - // by default, we just take half the pixel font size and use that - // as an additional offset. - // - // There is an alignment-baseline property in svg that - // is supposed to do this for us. However, when I tried choosing - // "hanging" to get tcl's equivalent of "n", I ran into a bug - // where the text gets positioned incorrectly when zooming. - transform: "translate(0," + - iemgui_font_height(fontname, fontsize) / 2 + ")", - id: tag + "label" + gui(cid).get_gobj(tag) + .append(function(frag, w) { + var svg_text = create_item(cid, "text", { + // x and y need to be relative to baseline instead of nw anchor + x: x, + y: y, + //"font-size": font + "px", + "font-family": iemgui_fontfamily(fontname), + // for some reason the font looks bold in Pd-Vanilla-- not sure why + "font-weight": fontweight, + "font-size": fontsize + "px", + fill: color, + // Iemgui labels are anchored "w" (left-aligned to non-tclers). + // For no good reason, they are also centered vertically, unlike + // object box text. Since svg text uses the baseline as a reference + // by default, we just take half the pixel font size and use that + // as an additional offset. + // + // There is an alignment-baseline property in svg that + // is supposed to do this for us. However, when I tried choosing + // "hanging" to get tcl's equivalent of "n", I ran into a bug + // where the text gets positioned incorrectly when zooming. + transform: "translate(0," + + iemgui_font_height(fontname, fontsize) / 2 + ")", + id: tag + "label" + }); + var text_node = w.document.createTextNode(text); + svg_text.appendChild(text_node); + frag.appendChild(svg_text); + return frag; }); - text_node = patchwin[cid].window.document.createTextNode(text); - svg_text.appendChild(text_node); - g.appendChild(svg_text); } function gui_iemgui_label_set(cid, tag, text) { - var svg_text = get_item(cid, tag + "label") - if (svg_text) { - svg_text.textContent = text; - } + gui(cid).get_elem(tag + "label", function(e) { + e.textContent = text; + }); } function gui_iemgui_label_coords(cid, tag, x, y) { - var svg_text = get_item(cid, tag + "label"); - if (svg_text) { - configure_item(svg_text, { - x: x, - y: y - }); - } + gui(cid).get_elem(tag + "label", { + x: x, + y: y + }); } function gui_iemgui_label_color(cid, tag, color) { - var svg_text = get_item(cid, tag + "label"); - if (svg_text) { - configure_item(svg_text, { - fill: color - }); - } + gui(cid).get_elem(tag + "label", { + fill: color + }); +} + +function gui_iemgui_label_color(cid, tag, color) { + gui(cid).get_elem(tag + "label", { + fill: color + }); } function gui_iemgui_label_select(cid, tag, is_selected) { - var svg_text; - if (patchwin[cid]) { - svg_text = get_item(cid, tag + "label"); - if (svg_text) { - if (is_selected) { - svg_text.classList.add("iemgui_label_selected"); - } else { - svg_text.classList.remove("iemgui_label_selected"); - } + gui(cid).get_elem(tag + "label", function(e) { + if (!!is_selected) { + e.classList.add("iemgui_label_selected"); + } else { + e.classList.remove("iemgui_label_selected"); } - } + }); } function gui_iemgui_label_font(cid, tag, fontname, fontweight, fontsize) { - var svg_text = get_item(cid, tag + "label"); - if (svg_text) { - configure_item(svg_text, { - "font-family": iemgui_fontfamily(fontname), - "font-weight": fontweight, - "font-size": fontsize + "px", - transform: "translate(0," + iemgui_font_height(fontname, fontsize) / 2 + ")" - }); - } + gui(cid).get_elem(tag + "label", { + "font-family": iemgui_fontfamily(fontname), + "font-weight": fontweight, + "font-size": fontsize + "px", + transform: "translate(0," + iemgui_font_height(fontname, fontsize) / 2 + ")" + }); } // Show or hide little handle for dragging around iemgui labels function gui_iemgui_label_show_drag_handle(cid, tag, state, x, y, cnv_resize) { - var gobj, - rect; - if (!patchwin[cid]) { - return; - } - gobj = get_gobj(cid, tag); if (state !== 0) { - // Here we use a "line" shape so that we can control its color - // using the "border" class (for iemguis) or the "gop_rect" class - // for the graph-on-parent rectangle anchor. In both cases the styles - // set a stroke property, and a single thick line is easier to define - // than a "rect" for that case. - rect = create_item(cid, "line", { - x1: x, - y1: y + 3, - x2: x, - y2: y + 10, - "stroke-width": 7, - class: (cid === tag) ? "gop_drag_handle move_handle gop_rect" : - cnv_resize !== 0 ? "cnv_resize_handle border" : - "label_drag_handle move_handle border" + gui(cid).get_gobj(tag) + .append(function(frag) { + var rect; + // Here we use a "line" shape so that we can control its color + // using the "border" class (for iemguis) or the "gop_rect" class + // for the graph-on-parent rectangle anchor. In both cases the + // styles set a stroke property, and a single thick line is easier + // to define than a "rect" for that case. + rect = create_item(cid, "line", { + x1: x, + y1: y + 3, + x2: x, + y2: y + 10, + "stroke-width": 7, + class: (cid === tag) ? "gop_drag_handle move_handle gop_rect" : + cnv_resize !== 0 ? "cnv_resize_handle border" : + "label_drag_handle move_handle border" + }); + rect.classList.add("clickable_resize_handle"); + frag.appendChild(rect); + return frag; }); - rect.classList.add("clickable_resize_handle"); - gobj.appendChild(rect); } else { - if (gobj) { - rect = - gobj.getElementsByClassName((cid === tag) ? "gop_drag_handle" : + gui(cid).get_gobj(tag, function(e) { + var rect = + e.getElementsByClassName((cid === tag) ? "gop_drag_handle" : cnv_resize !== 0 ? "cnv_resize_handle" : "label_drag_handle")[0]; //rect = get_item(cid, "clickable_resize_handle"); @@ -3166,109 +3135,108 @@ function gui_iemgui_label_show_drag_handle(cid, tag, state, x, y, cnv_resize) { } else { post("error: couldn't delete the iemgui drag handle!"); } - } + }); } } function gui_mycanvas_new(cid,tag,color,x1,y1,x2_vis,y2_vis,x2,y2) { - var rect_vis, rect, g; - if (!patchwin[cid]) { - return; - } - rect_vis = create_item(cid, "rect", { - width: x2_vis - x1, - height: y2_vis - y1, - fill: color, - stroke: color, - id: tag + "rect" - } - ); - - // we use a drag_handle, which is square outline with transparent fill - // that shows the part of the rectangle that may be dragged in editmode. - // Clicking the rectangle outside of that square will have no effect. - // Unlike a 'border' it takes the same color as the visible rectangle when - // deselected. - // I'm not sure why it was decided to define this object's bbox separate - // from the visual rectangle. That causes all kinds of usability problems. - // For just one example, it means we can't simply use the "resize" cursor - // like all the other iemguis. - // Unfortunately its ingrained as a core object in Pd, so we have to - // support it here. - rect = create_item(cid, "rect", { - width: x2 - x1, - height: y2 - y1, - fill: "none", - stroke: color, - id: tag + "drag_handle", - "class": "border mycanvas_border" - } - ); - g = get_gobj(cid, tag); - g.appendChild(rect_vis); - g.appendChild(rect); + gui(cid).get_gobj(tag) + .append(function(frag) { + var rect_vis, rect, g; + rect_vis = create_item(cid, "rect", { + width: x2_vis - x1, + height: y2_vis - y1, + fill: color, + stroke: color, + id: tag + "rect" + } + ); + // we use a drag_handle, which is square outline with transparent fill + // that shows the part of the rectangle that may be dragged in editmode. + // Clicking the rectangle outside of that square will have no effect. + // Unlike a 'border' it takes the same color as the visible rectangle + // when deselected. + // I'm not sure why it was decided to define this object's bbox separate + // from the visual rectangle. That causes all kinds of usability + // problems. + // For just one example, it means we can't simply use the "resize" + // cursor like all the other iemguis. + // Unfortunately its ingrained as a core object in Pd, so we have to + // support it here. + rect = create_item(cid, "rect", { + width: x2 - x1, + height: y2 - y1, + fill: "none", + stroke: color, + id: tag + "drag_handle", + "class": "border mycanvas_border" + } + ); + frag.appendChild(rect_vis); + frag.appendChild(rect); + return frag; + }); } function gui_mycanvas_update(cid, tag, color, selected) { - var r = get_item(cid, tag + "rect"), - h = get_item(cid, tag + "drag_handle"); - configure_item(r, { + gui(cid) + .get_elem(tag + "rect", { fill: color, stroke: color - }); - configure_item(h, { + }) + .get_elem(tag + "drag_handle", { stroke: color }); } function gui_mycanvas_coords(cid, tag, vis_width, vis_height, select_width, select_height) { - var r = get_item(cid, tag + "rect"), - h = get_item(cid, tag + "drag_handle"); - configure_item(r, { width: vis_width, height: vis_height }); - configure_item(h, { width: select_width, height: select_height }); + gui(cid) + .get_elem(tag + "rect", { + width: vis_width, + height: vis_height + }) + .get_elem(tag + "drag_handle", { + width: select_width, + height: select_height + }); } function gui_scalar_new(cid, tag, isselected, t1, t2, t3, t4, t5, t6, is_toplevel) { + var g; // we should probably use gui_gobj_new here, but we"re doing some initial // scaling that normal gobjs don't need... - var svg, matrix, transform_string, g, selection_rect; - if (!patchwin[cid]) { - return; - } - svg = get_item(cid, "patchsvg"), // id for the svg in the DOM - matrix = [t1,t2,t3,t4,t5,t6]; - transform_string = "matrix(" + matrix.join() + ")"; - g = create_item(cid, "g", { + gui(cid).get_elem("patchsvg", function(svg_elem) { + var matrix, transform_string, selection_rect; + matrix = [t1,t2,t3,t4,t5,t6]; + transform_string = "matrix(" + matrix.join() + ")"; + g = create_item(cid, "g", { id: tag + "gobj", transform: transform_string, + }); + if (isselected !== 0) { + g.classList.add("selected"); + } + if (is_toplevel === 0) { + g.classList.add("gop"); + } + // Let's make a selection rect... + selection_rect = create_item(cid, "rect", { + class: "border", + display: "none", + fill: "none", + "pointer-events": "none" + }); + g.appendChild(selection_rect); + add_gobj_to_svg(svg_elem, g); }); - if (isselected !== 0) { - g.classList.add("selected"); - } - if (is_toplevel === 0) { - g.classList.add("gop"); - } - // Let's make a selection rect... - selection_rect = create_item(cid, "rect", { - class: "border", - display: "none", - fill: "none", - "pointer-events": "none" - }); - g.appendChild(selection_rect); - add_gobj_to_svg(svg, g); return g; } function gui_scalar_erase(cid, tag) { - var g = get_gobj(cid, tag); - if (g !== null) { - g.parentNode.removeChild(g); - } - // selection rect... - // var sr = get_item(cid, tag + "selection_rect"); - // sr.parentNode.removeChild(sr); + gui(cid).get_gobj(tag, function(e) { + e.parentNode.removeChild(e); + }); } // This is unnecessarily complex-- the select rect is a child of the parent @@ -3285,95 +3253,87 @@ function gui_scalar_erase(cid, tag) { // for selected borders because somehow calling properties on a graph // triggers this function. I have no idea why it does that. function gui_scalar_draw_select_rect(cid, tag, state, x1, y1, x2, y2, basex, basey) { - var g = get_gobj(cid, tag), - b; - // somehow the scalar can unvis before calling this, so we check for - // its existence here... - if (g) { - b = g.querySelector(".border"); - configure_item(b, { - x: (x1 - basex) + 0.5, - y: (y1 - basey) + 0.5, - width: x2 - x1, - height: y2 - y1 - }); - } + gui(cid).get_gobj(tag) + .q(".border", { + x: (x1 - basex) + 0.5, + y: (y1 - basey) + 0.5, + width: x2 - x1, + height: y2 - y1 + }); } function gui_scalar_draw_group(cid, tag, parent_tag, type, attr_array) { - var parent_elem, - group; - if (!patchwin[cid]) { - return; - } - parent_elem = get_item(cid, parent_tag); - if (attr_array === undefined) { - attr_array = []; - } - attr_array.push("id", tag); - group = create_item(cid, type, attr_array); - parent_elem.appendChild(group); - return group; + gui(cid).get_elem(parent_tag) + .append(function(frag) { + if (!attr_array) { + attr_array = []; + } + attr_array.push("id", tag); + var group = create_item(cid, type, attr_array); + frag.appendChild(group); + return frag; + }); } function gui_scalar_configure_gobj(cid, tag, isselected, t1, t2, t3, t4, t5, t6) { - var gobj = get_gobj(cid, tag), - matrix = [t1,t2,t3,t4,t5,t6], + var matrix = [t1,t2,t3,t4,t5,t6], transform_string = "matrix(" + matrix.join() + ")"; - configure_item(gobj, { transform: transform_string }); + gui(cid).get_gobj(tag, { + transform: transform_string + }); } function gui_draw_vis(cid, type, attr_array, tag_array) { - var g = get_item(cid, tag_array[0]), - item; - attr_array.push("id", tag_array[1]); - item = create_item(cid, type, attr_array); - g.appendChild(item); + gui(cid).get_elem(tag_array[0]) + .append(function(frag) { + var item; + attr_array.push("id", tag_array[1]); + item = create_item(cid, type, attr_array); + frag.appendChild(item); + return frag; + }); } // This is a stop gap to update the old draw commands like [drawpolygon] // without having to erase and recreate their DOM elements function gui_draw_configure_old_command(cid, type, attr_array, tag_array) { - var elem = get_item(cid, tag_array[1]); - if (elem !== null) { - configure_item(elem, attr_array); - } + gui(cid).get_elem(tag_array[1], function(e) { + configure_item(e, attr_array); + }); } function gui_draw_erase_item(cid, tag) { - var item = get_item(cid, tag); - if (item !== null) { - item.parentNode.removeChild(item); - } else { - //post("uh oh... gui_draw_erase_item couldn't find the item..."); - } + gui(cid).get_elem(tag, function(e) { + e.parentNode.removeChild(e); + }); } function gui_draw_coords(cid, tag, shape, points) { - var elem = get_item(cid, tag); - switch (shape) { - case "rect": - configure_item(elem, { - x: points[0], - y: points[1], - width: points[2], - height: points[3] - }); - break; - case "circle": - configure_item(elem, { - cx: points[0], - cy: points[1] - }); - break; - case "polyline": - case "polygon": - configure_item(elem, { - points: points - }); - break; - default: - } + gui(cid).get_elem(tag, function(elem) { + switch (shape) { + case "rect": + configure_item(elem, { + x: points[0], + y: points[1], + width: points[2], + height: points[3] + }); + break; + case "circle": + configure_item(elem, { + cx: points[0], + cy: points[1] + }); + break; + case "polyline": + case "polygon": + configure_item(elem, { + points: points + }); + break; + default: + } + }); } // set a drag event for a shape that's part of a scalar. @@ -3382,41 +3342,44 @@ function gui_draw_coords(cid, tag, shape, points) { // (Attempting to set the event more than once is ignored.) function gui_draw_drag_event(cid, tag, scalar_sym, drawcommand_sym, event_name, array_sym, index, state) { - var win = patchwin[cid].window; - if (state === 0) { - win.canvas_events.remove_scalar_draggable(tag); - } else { - win.canvas_events.add_scalar_draggable(cid, tag, scalar_sym, - drawcommand_sym, event_name, array_sym, index); - } + gui(cid).get_elem("patchsvg", function(svg_elem, w) { + if (state === 0) { + w.canvas_events.remove_scalar_draggable(tag); + } else { + w.canvas_events.add_scalar_draggable(cid, tag, scalar_sym, + drawcommand_sym, event_name, array_sym, index); + } + }); } // Events for scalars-- mouseover, mouseout, etc. function gui_draw_event(cid, tag, scalar_sym, drawcommand_sym, event_name, array_sym, index, state) { - var item = get_item(cid, tag), - event_type = "on" + event_name; - if (state === 1) { - item[event_type] = function(e) { - pdsend(cid, "scalar_event", scalar_sym, drawcommand_sym, - array_sym, index, event_name, e.pageX, e.pageY); - }; - } else { - item[event_type] = null; - } + gui(cid).get_elem(tag, function(e) { + var event_type = "on" + event_name; + if (state === 1) { + e[event_type] = function(e) { + pdsend(cid, "scalar_event", scalar_sym, drawcommand_sym, + array_sym, index, event_name, e.pageX, e.pageY); + }; + } else { + e[event_type] = null; + } + }); } // Configure one attr/val pair at a time, received from Pd function gui_draw_configure(cid, tag, attr, val) { - var item = get_item(cid, tag); - var obj = {}; - if (Array.isArray(val)) { - obj[attr] = val.join(" "); - } else { - // strings or numbers - obj[attr] = val; - } - configure_item(item, obj); + gui(cid).get_elem(tag, function(e) { + var obj = {}; + if (Array.isArray(val)) { + obj[attr] = val.join(" "); + } else { + // strings or numbers + obj[attr] = val; + } + configure_item(e, obj); + }); } // Special case for viewBox which, in addition to its inexplicably inconsistent @@ -3425,38 +3388,35 @@ function gui_draw_configure(cid, tag, attr, val) { // the default behavior. function gui_draw_viewbox(cid, tag, attr, val) { // Value will be an empty array if the user provided no values - if (val.length) { - gui_draw_configure(cid, tag, attr, val) - } else { - get_item(cid, tag).removeAttribute("viewBox"); - } + gui(cid).get_elem("patchsvg", function(svg_elem) { + if (val.length) { + gui_draw_configure(cid, tag, attr, val) + } else { + get_item(cid, tag).removeAttribute("viewBox"); + } + }); } // Configure multiple attr/val pairs (this should be merged with gui_draw_configure at some point function gui_draw_configure_all(cid, tag, attr_array) { - var item = get_item(cid, tag); - configure_item(item, attr_array); + gui(cid).get_elem(tag, attr_array); } // Plots for arrays and data structures function gui_plot_vis(cid, basex, basey, data_array, attr_array, tag_array) { - var g, - p; - if (!patchwin[cid]) { - return; - } - g = get_item(cid, tag_array[0]), - p = create_item(cid, "path", { - d: data_array.join(" "), - id: tag_array[1], - //stroke: "red", - //fill: "black", - //"stroke-width": "0" - }); - configure_item(p, attr_array); - if (g !== null) { - g.appendChild(p); - } + gui(cid).get_elem(tag_array[0]) + .append(function(frag) { + var p = create_item(cid, "path", { + d: data_array.join(" "), + id: tag_array[1], + //stroke: "red", + //fill: "black", + //"stroke-width": "0" + }); + configure_item(p, attr_array); + frag.appendChild(p); + return frag; + }); } // This function doubles as a visfn for drawnumber. Furthermore it doubles @@ -3465,54 +3425,53 @@ function gui_plot_vis(cid, basex, basey, data_array, attr_array, tag_array) { // and -1 to set attributes on the existing object. function gui_drawnumber_vis(cid, parent_tag, tag, x, y, scale_x, scale_y, font, fontsize, fontcolor, text, flag, visibility) { - var lines, i, len, tspan, - g = get_item(cid, parent_tag), - svg_text; if (flag === 1) { - svg_text = create_item(cid, "text", { - // x and y are fudge factors. Text on the tk canvas used an anchor - // at the top-right corner of the text's bbox. SVG uses the - // baseline. There's probably a programmatic way to do this, but - // for now-- fudge factors based on the DejaVu Sans Mono font. :) - - // For an explanation of why we translate by "x" instead of setting - // the x attribute, see comment in gui_text_new - transform: "scale(" + scale_x + "," + scale_y + ") " + - "translate(" + x + ")", - y: y + fontsize, - // Turns out we can't do 'hanging' baseline because it's borked - // when scaled. Bummer... - // "dominant-baseline": "hanging", - //"shape-rendering": "optimizeSpeed", - "font-size": fontsize + "px", - fill: fontcolor, - visibility: visibility === 1 ? "normal" : "hidden", - id: tag + gui(cid).get_elem(parent_tag) + .append(function(frag) { + var svg_text = create_item(cid, "text", { + // x and y are fudge factors. Text on the tk canvas used an + // anchor at the top-right corner of the text's bbox. SVG uses + // the baseline. There's probably a programmatic way to do this, + // but for now-- fudge factors based on the DejaVu Sans Mono + // font. :) + + // For an explanation of why we translate by "x" instead of + // setting the x attribute, see comment in gui_text_new + transform: "scale(" + scale_x + "," + scale_y + ") " + + "translate(" + x + ")", + y: y + fontsize, + // Turns out we can't do 'hanging' baseline because it's borked + // when scaled. Bummer... + // "dominant-baseline": "hanging", + //"shape-rendering": "optimizeSpeed", + "font-size": fontsize + "px", + fill: fontcolor, + visibility: visibility === 1 ? "normal" : "hidden", + id: tag + }); + // fill svg_text with tspan content by splitting on "\n" + text_to_tspans(cid, svg_text, text); + frag.appendChild(svg_text); + return frag; }); - // fill svg_text with tspan content by splitting on "\n" - text_to_tspans(cid, svg_text, text); - if (g !== null) { - g.appendChild(svg_text); - } else { - post("gui_drawnumber: can't find parent group" + parent_tag); - } } else { - svg_text = get_item(cid, tag); - configure_item(svg_text, { - transform: "scale(" + scale_x + "," + scale_y + ") " + - "translate(" + x + ")", - y: y + fontsize, - // Turns out we can't do 'hanging' baseline because it's borked - // when scaled. Bummer... - // "dominant-baseline": "hanging", - //"shape-rendering": "optimizeSpeed", - "font-size": fontsize + "px", - fill: fontcolor, - visibility: visibility === 1 ? "normal" : "hidden", - id: tag + gui(cid).get_elem(tag, function(svg_text) { + configure_item(svg_text, { + transform: "scale(" + scale_x + "," + scale_y + ") " + + "translate(" + x + ")", + y: y + fontsize, + // Turns out we can't do 'hanging' baseline because it's borked + // when scaled. Bummer... + // "dominant-baseline": "hanging", + //"shape-rendering": "optimizeSpeed", + "font-size": fontsize + "px", + fill: fontcolor, + visibility: visibility === 1 ? "normal" : "hidden", + id: tag + }); + svg_text.textContent = ""; + text_to_tspans(cid, svg_text, text); }); - svg_text.textContent = ""; - text_to_tspans(cid, svg_text, text); } } @@ -3653,56 +3612,60 @@ function img_size_setter(cid, svg_image_tag, type, data, tk_anchor) { } function gui_drawimage_vis(cid, x, y, obj, data, seqno, parent_tag) { - var item, - g = get_item(cid, parent_tag), // main <g> within the scalar - image_array = pd_cache.get(obj), - len = image_array.length, - i, - image_container, - obj_tag = "draw" + obj.slice(1) + "." + data.slice(1); - if (len < 1) { - return; - } - // Wrap around for out-of-bounds sequence numbers - if (seqno >= len || seqno < 0) { - seqno %= len; - } - image_container = create_item(cid, "g", { - id: obj_tag - }); - for (i = 0; i < len; i++) { - item = create_item(cid, "image", { - x: x, - y: y, - id: obj_tag + i, - visibility: seqno === i ? "visible" : "hidden", - preserveAspectRatio: "xMinYMin meet" + gui(cid).get_elem(parent_tag) // main <g> within the scalar + .append(function(frag) { + var item, + image_array = pd_cache.get(obj), + len = image_array.length, + i, + image_container, + obj_tag = "draw" + obj.slice(1) + "." + data.slice(1); + if (len < 1) { + return; + } + // Wrap around for out-of-bounds sequence numbers + if (seqno >= len || seqno < 0) { + seqno %= len; + } + image_container = create_item(cid, "g", { + id: obj_tag }); - item.setAttributeNS("http://www.w3.org/1999/xlink", "href", - "data:image/" + image_array[i].type + ";base64," + - image_array[i].data); - image_container.appendChild(item); - } - g.appendChild(image_container); - - // Hack to set correct width and height - for (i = 0; i < len; i++) { - img_size_setter(cid, obj_tag+i, pd_cache.get(obj)[i].type, - pd_cache.get(obj)[i].data); - } + for (i = 0; i < len; i++) { + item = create_item(cid, "image", { + x: x, + y: y, + id: obj_tag + i, + visibility: seqno === i ? "visible" : "hidden", + preserveAspectRatio: "xMinYMin meet" + }); + item.setAttributeNS("http://www.w3.org/1999/xlink", "href", + "data:image/" + image_array[i].type + ";base64," + + image_array[i].data); + image_container.appendChild(item); + } + frag.appendChild(image_container); + // Hack to set correct width and height + for (i = 0; i < len; i++) { + img_size_setter(cid, obj_tag+i, pd_cache.get(obj)[i].type, + pd_cache.get(obj)[i].data); + } + return frag; + }); } function gui_drawimage_index(cid, obj, data, index) { - var obj_tag = "draw" + obj.slice(1) + "." + data.slice(1), - i, - image_container = get_item(cid, obj_tag), - len = image_container.childNodes.length, - image = image_container.childNodes[((index % len) + len) % len], - last_image = image_container.querySelectorAll('[visibility="visible"]'); - for (i = 0; i < last_image.length; i++) { - configure_item(last_image[i], { visibility: "hidden" }); - } - configure_item(image, { visibility: "visible" }); + var obj_tag = "draw" + obj.slice(1) + "." + data.slice(1); + gui(cid).get_elem(obj_tag, function(image_container) { + var len = image_container.childNodes.length, + image = image_container.childNodes[((index % len) + len) % len], + last_image = + image_container.querySelectorAll('[visibility="visible"]'), + i; + for (i = 0; i < last_image.length; i++) { + configure_item(last_image[i], { visibility: "hidden" }); + } + configure_item(image, { visibility: "visible" }); + }); } // Default png image data @@ -3740,10 +3703,9 @@ function gui_load_image(cid, key, filepath) { // interface assumes there is only one image per gobject. If you try to // set more you'll get duplicate ids. function gui_gobj_draw_image(cid, tag, image_key, tk_anchor) { - var g, i; - if (patchwin[cid]) { - g = get_gobj(cid, tag); - i = create_item(cid, "image", { + gui(cid).get_gobj(tag) + .append(function(frag) { + var i = create_item(cid, "image", { id: tag, preserveAspectRatio: "xMinYMin meet" }); @@ -3752,8 +3714,9 @@ function gui_gobj_draw_image(cid, tag, image_key, tk_anchor) { pd_cache.get(image_key).data); img_size_setter(cid, tag, pd_cache.get(image_key).type, pd_cache.get(image_key).data, tk_anchor); - g.appendChild(i); - } + frag.appendChild(i); + return frag; + }); } function gui_image_size_callback(cid, key, callback) { @@ -3766,10 +3729,9 @@ function gui_image_size_callback(cid, key, callback) { } function gui_image_draw_border(cid, tag, x, y, w, h) { - var g, b; - if (patchwin[cid]) { - g = get_gobj(cid, tag); - b = create_item(cid, "path", { + gui(cid).get_gobj(tag) + .append(function(frag) { + var b = create_item(cid, "path", { "stroke-width": "1", fill: "none", d: ["m", x, y, w, 0, @@ -3780,35 +3742,32 @@ function gui_image_draw_border(cid, tag, x, y, w, h) { visibility: "hidden", class: "border" }); - g.appendChild(b); - } + frag.appendChild(b); + return frag; + }); } function gui_image_toggle_border(cid, tag, state) { - var g = get_gobj(cid, tag), - b = g.querySelector(".border"); - // We have to check for b since the border is only created after - // the callback from the GUI provides the size. - if (b) { - configure_item(b, { - visibility: state === 0 ? "hidden" : "visible" - }); - } + gui(cid).get_gobj(tag) + .q(".border", { + visibility: state === 0 ? "hidden" : "visible" + }); } // Switch the data for an existing svg image function gui_image_configure(cid, tag, image_key, tk_anchor) { - var i = get_item(cid, tag); - if (pd_cache.get(image_key)) { - i.setAttributeNS("http://www.w3.org/1999/xlink", "href", - "data:image/" + pd_cache.get(image_key).type + ";base64," + - pd_cache.get(image_key).data); - img_size_setter(cid, tag, pd_cache.get(image_key).type, - pd_cache.get(image_key).data, tk_anchor); - } else { - // need to change this to an actual error - post("image: error: can't find image"); - } + gui(cid).get_elem(tag, function(e) { + if (pd_cache.get(image_key)) { + e.setAttributeNS("http://www.w3.org/1999/xlink", "href", + "data:image/" + pd_cache.get(image_key).type + ";base64," + + pd_cache.get(image_key).data); + img_size_setter(cid, tag, pd_cache.get(image_key).type, + pd_cache.get(image_key).data, tk_anchor); + } else { + // need to change this to an actual error + post("image: error: can't find image"); + } + }); } // Move an image @@ -3816,15 +3775,17 @@ function gui_image_coords(cid, tag, x, y) { // ggee/image accepts a message that can trigger this, meaning // [loadbang] can end up calling this before the patchwindow exists. // So we have to check for existence below - if (patchwin[cid]) { - elem_move(get_gobj(cid, tag), x, y); - } + gui(cid).get_gobj(tag, function(e) { + elem_move(e, x, y); + }); } // Scope~ function gui_scope_draw_bg(cid, tag, fg_color, bg_color, w, h, grid_width, dx, dy) { - var g = get_gobj(cid, tag), - bg = create_item(cid, "rect", { + gui(cid) + .get_gobj(tag) + .append(function(frag) { + var bg = create_item(cid, "rect", { width: w, height: h, fill: bg_color, @@ -3837,48 +3798,50 @@ function gui_scope_draw_bg(cid, tag, fg_color, bg_color, w, h, grid_width, dx, d fg_xy_path, // to be used for the foreground lines fg_mono_path, i, x, y, align_x, align_y; - // Path strings for the grid lines - // vertical lines... - for (i = 0, x = dx; i < 7; i++, x += dx) { - align_x = (x|0) === x ? x : Math.round(x); - path_string += ["M", align_x, 0, "V", h].join(" "); - } - // horizontal lines... - for (i = 0, y = dy; i < 3; i++, y += dy) { - align_y = (y|0) === y ? y : Math.round(y); - path_string += ["M", 0, align_y, "H", w].join(" "); - } - path = create_item(cid, "path", { - d: path_string, - fill: "none", - stroke: "black", - "stroke-width": grid_width, - }); - // We go ahead and create a path to be used in the foreground. We'll - // set the actual path data in the draw/redraw functions. Doing it this - // way will save us having to create and destroy DOM objects each time - // we redraw the foreground - fg_xy_path = create_item(cid, "path", { - fill: "none", - stroke: fg_color, - class: "fgxy" - }); - fg_mono_path = create_item(cid, "path", { - fill: "none", - stroke: fg_color, - class: "fgmono" + // Path strings for the grid lines + // vertical lines... + for (i = 0, x = dx; i < 7; i++, x += dx) { + align_x = (x|0) === x ? x : Math.round(x); + path_string += ["M", align_x, 0, "V", h].join(" "); + } + // horizontal lines... + for (i = 0, y = dy; i < 3; i++, y += dy) { + align_y = (y|0) === y ? y : Math.round(y); + path_string += ["M", 0, align_y, "H", w].join(" "); + } + path = create_item(cid, "path", { + d: path_string, + fill: "none", + stroke: "black", + "stroke-width": grid_width, + }); + // We go ahead and create a path to be used in the foreground. We'll + // set the actual path data in the draw/redraw functions. Doing it this + // way will save us having to create and destroy DOM objects each time + // we redraw the foreground + fg_xy_path = create_item(cid, "path", { + fill: "none", + stroke: fg_color, + class: "fgxy" + }); + fg_mono_path = create_item(cid, "path", { + fill: "none", + stroke: fg_color, + class: "fgmono" + }); + frag.appendChild(bg); + frag.appendChild(path); + frag.appendChild(fg_xy_path); + frag.appendChild(fg_mono_path); + return frag; }); - g.appendChild(bg); - g.appendChild(path); - g.appendChild(fg_xy_path); - g.appendChild(fg_mono_path); } function scope_configure_fg(cid, tag, type, data_array) { - var g = get_gobj(cid, tag), - fg_path = g.querySelector(type); // class ".fgxy" or ".fgmono" - configure_item(fg_path, { - d: data_array.join(" ") + gui(cid) + .get_gobj(tag) + .q(type, { // class ".fgxy" or ".fgmono" + d: data_array.join(" ") }); } @@ -3891,17 +3854,16 @@ function gui_scope_configure_fg_mono(cid, tag, data_array) { } function gui_scope_configure_bg_color(cid, tag, color) { - var g = get_gobj(cid, tag), - elem = g.querySelector(".bg"); - configure_item(elem, { fill: color }); + gui(cid).get_gobj(tag) + .query(".bg", { + fill: color + }); } function gui_scope_configure_fg_color(cid, tag, color) { - var g = get_gobj(cid, tag), - xy = g.querySelector(".fgxy"), - mono = g.querySelector(".fgmono"); - configure_item(xy, { stroke: color }); - configure_item(mono, { stroke: color }); + gui(cid).get_gobj(tag) + .q(".fgxy", { stroke: color }) + .q(".fgmono", { stroke: color }); } function gui_scope_clear_fg(cid, tag) { @@ -3934,24 +3896,15 @@ function get_grid_data(w, h, x_l, y_l) { } function gui_configure_grid(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { - var g, - grid_d_string, + var grid_d_string = !!has_grid ? get_grid_data(w, h, x_l, y_l) : "", point_size = 5; - // Quick bugfix for messages that arrive to the GUI before the - // window is mapped. This can happen when the user connects - // [loadbang] to a [grid] method that changes visual display (like "color") - // We need a way to prevent sending such messages - if (!patchwin[cid]) { - return; - } - g = get_gobj(cid, tag); - // configure each element in the grid - configure_item(g.querySelector(".bg"), { + gui(cid).get_gobj(tag) + .q(".bg", { width: w, height: h, fill: bg_color, - }); - configure_item(g.querySelector(".border"), { + }) + .q(".border", { d: ["M", 0, 0, w, 0, "M", 0, h, w, h, "M", 0, 0, 0, h, @@ -3960,16 +3913,16 @@ function gui_configure_grid(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { fill: "none", stroke: "black", "stroke-width": 1 - }); - configure_item(g.querySelector(".out_0"), { + }) + .q(".out_0", { y: h + 1, width: 7, height: 1, fill: "none", stroke: "black", "stroke-width": 1 - }); - configure_item(g.querySelector(".out_1"), { + }) + .q(".out_1", { x: w - 7, y: h + 1, width: 7, @@ -3977,16 +3930,13 @@ function gui_configure_grid(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { fill: "none", stroke: "black", "stroke-width": 1 - }); - - grid_d_string = !!has_grid ? get_grid_data(w, h, x_l, y_l) : ""; - configure_item(g.querySelector(".grid"), { + }) + .q(".grid", { d: grid_d_string, stroke: "white", "stroke-width": 1 - }); - - configure_item(g.querySelector(".point"), { + }) + .q(".point", { style: "visibility: none;", width: 5, height: 5, @@ -3997,8 +3947,12 @@ function gui_configure_grid(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { } function gui_grid_new(cid, tag, x, y, is_toplevel) { - var g = gui_gobj_new(cid, tag, "obj", x, y, is_toplevel), - bg = create_item(cid, "rect", { + gui(cid).get_elem("patchsvg", function(svg_elem) { + gui_gobj_new(cid, tag, "obj", x, y, is_toplevel); + }); + gui(cid).get_gobj(tag) + .append(function(frag) { + var bg = create_item(cid, "rect", { class: "bg" }), border = create_item(cid, "path", { @@ -4018,19 +3972,20 @@ function gui_grid_new(cid, tag, x, y, is_toplevel) { point = create_item(cid, "rect", { class: "point" }); - g.appendChild(bg); - g.appendChild(out_0); - g.appendChild(out_1); - g.appendChild(grid); - g.appendChild(point); - g.appendChild(border); + frag.appendChild(bg); + frag.appendChild(out_0); + frag.appendChild(out_1); + frag.appendChild(grid); + frag.appendChild(point); + frag.appendChild(border); + return frag; + }); } function gui_grid_point(cid, tag, x, y) { - var g = get_gobj(cid, tag), - p = g.querySelector(".point"); - configure_item(p, { + gui(cid).get_gobj(tag) + .q(".point", { x: x, y: y, style: "visibility: visible;" @@ -4039,7 +3994,11 @@ function gui_grid_point(cid, tag, x, y) { // mknob from moonlib function gui_mknob_new(cid, tag, x, y, is_toplevel, show_in, show_out) { - var g = gui_gobj_new(cid, tag, "obj", x, y, is_toplevel), + gui(cid).get_elem("patchsvg", function(svg_elem) { + gui_gobj_new(cid, tag, "obj", x, y, is_toplevel); + }); + gui(cid).get_gobj(tag) + .append(function(frag) { border = create_item(cid, "path", { class: "border" // now we can inherit the css border styles }), @@ -4049,48 +4008,45 @@ function gui_mknob_new(cid, tag, x, y, is_toplevel, show_in, show_out) { line = create_item(cid, "line", { class: "dial" }); - g.appendChild(border); - g.appendChild(circle); - g.appendChild(line); + frag.appendChild(border); + frag.appendChild(circle); + frag.appendChild(line); + return frag; + }); } function gui_configure_mknob(cid, tag, size, bg_color, fg_color) { - var g = get_gobj(cid, tag); - configure_item(g.querySelector(".border"), { + gui(cid).get_gobj(tag) + .q(".border", { d: ["M", 0, 0, size, 0, "M", 0, size, size, size, "M", 0, 0, 0, size, "M", size, 0, size, size ].join(" "), fill: "none", - }); - configure_item(g.querySelector(".circle"), { + }) + .q(".circle", { cx: size / 2, cy: size / 2, r: size / 2, fill: bg_color, stroke: "black", "stroke-width": 1 - }); - configure_item(g.querySelector(".dial"), { + }) + .q(".dial", { "stroke-width": 2, stroke: fg_color }); } function gui_turn_mknob(cid, tag, x1, y1, x2, y2) { - var g; - if (patchwin[cid]) { - g = get_gobj(cid, tag); - if (g) { - configure_item(g.querySelector(".dial"), { - x1: x1, - y1: y1, - x2: x2, - y2: y2 - }); - } - } + gui(cid).get_gobj(tag) + .q(".dial", { + x1: x1, + y1: y1, + x2: x2, + y2: y2 + }); } function add_popup(cid, popup) { @@ -4099,107 +4055,106 @@ function add_popup(cid, popup) { // envgen function gui_envgen_draw_bg(cid, tag, bg_color, w, h, points_array) { - var g = get_gobj(cid, tag), - bg, border, pline; - bg = create_item(cid, "rect", { - width: w, - height: h, - fill: bg_color, - stroke: "black", - "stroke-width": "2", - transform: "translate(0.5, 0.5)" - }); - // draw an extra path so we can give envgen - // a border class without affecting the - // background color of envgen - border = create_item(cid, "path", { - "stroke-width": 1, - d: ["M", 0, 0, w+1, 0, - "M", w+1, 0, w+1, h+1, - "M", w+1, h+1, 0, h+1, - "M", 0, h+1, 0, 0].join(" "), - "class": "border", - }); - pline = create_item(cid, "polyline", { - stroke: "black", - fill: "none", - transform: "translate(2, 2)", - points: points_array.join(" ") + gui(cid).get_gobj(tag) + .append(function(frag) { + var bg, border, pline; + bg = create_item(cid, "rect", { + width: w, + height: h, + fill: bg_color, + stroke: "black", + "stroke-width": "2", + transform: "translate(0.5, 0.5)" + }); + // draw an extra path so we can give envgen + // a border class without affecting the + // background color of envgen + border = create_item(cid, "path", { + "stroke-width": 1, + d: ["M", 0, 0, w+1, 0, + "M", w+1, 0, w+1, h+1, + "M", w+1, h+1, 0, h+1, + "M", 0, h+1, 0, 0].join(" "), + "class": "border", + }); + pline = create_item(cid, "polyline", { + stroke: "black", + fill: "none", + transform: "translate(2, 2)", + points: points_array.join(" ") + }); + frag.appendChild(bg); + frag.appendChild(border); + frag.appendChild(pline); + return frag; }); - g.appendChild(bg); - g.appendChild(border); - g.appendChild(pline); } function gui_envgen_draw_doodle(cid, tag, cx, cy) { - var g = get_gobj(cid, tag), - d; - - d = create_item(cid, "circle", { - r: "2", - cx: cx + 2, - cy: cy + 2 + gui(cid).get_gobj(tag) + .append(function(frag) { + var d = create_item(cid, "circle", { + r: "2", + cx: cx + 2, + cy: cy + 2 + }); + frag.appendChild(d); + return frag; }); - - g.appendChild(d); } function gui_envgen_erase_doodles(cid, tag) { - var g = get_gobj(cid, tag), - elem_array = g.querySelectorAll("circle"), + gui(cid).get_gobj(tag, function(e) { + var elem_array = e.querySelectorAll("circle"), i; - if (elem_array.length > 0) { - for (i = 0; i < elem_array.length; i++) { - elem_array[i].parentNode.removeChild(elem_array[i]); + if (elem_array.length > 0) { + for (i = 0; i < elem_array.length; i++) { + elem_array[i].parentNode.removeChild(elem_array[i]); + } } - } + }); } function gui_envgen_coords(cid, tag, w, h, points_array) { - var g = get_gobj(cid, tag), - bg = g.querySelector("rect"), - border = g.querySelector(".border"), - polyline = g.querySelector("polyline"); - configure_item(bg, { - width: w, - height: h - }); - configure_item(border, { + gui(cid).get_gobj(tag) + .q(".border", { d: ["M", 0, 0, w+1, 0, "M", w+1, 0, w+1, h+1, "M", w+1, h+1, 0, h+1, "M", 0, h+1, 0, 0].join(" ") - }); - configure_item(polyline, { + }) + .q(".rect", { + width: w, + height: h + }) + .q("polyline", { points: points_array.join(" ") }); } function gui_envgen_text(cid, tag, x, y, value, duration) { - var g = get_gobj(cid, tag), - svg_text; - svg_text = create_item(cid, "text", { - transform: "translate(" + x + ")", - y: y, - "font-size": "12px" + gui(cid).get_gobj(tag) + .append(function(frag) { + var svg_text = create_item(cid, "text", { + transform: "translate(" + x + ")", + y: y, + "font-size": "12px" + }); + text_to_tspans(cid, svg_text, value + "x" + duration); + frag.appendChild(svg_text); + return frag; }); - text_to_tspans(cid, svg_text, value + "x" + duration); - g.appendChild(svg_text); } function gui_envgen_erase_text(cid, tag) { - var g = get_gobj(cid, tag), - svg_text = g.querySelector("text"); - // Pd preemptively erases the text, so we must check - // for existence here - if (svg_text) { + gui(cid).get_gobj(tag) + .q("text", function(svg_text) { svg_text.parentNode.removeChild(svg_text); - } + }); } function gui_envgen_move_xlet(cid, tag, type, i, x, y, basex, basey) { - var xlet = get_item(cid, tag + type + i); - configure_item(xlet, { + gui(cid).get_elem(tag + type + i, { x: x - basex, y: y - basey }); @@ -4239,47 +4194,49 @@ function zoom_kludge(zoom_level) { function gui_canvas_popup(cid, xpos, ypos, canprop, canopen, isobject) { // Get page coords for top of window, in case we're scrolled - var win_left = patchwin[cid].window.document.body.scrollLeft, - win_top = patchwin[cid].window.document.body.scrollTop, - zoom_level = patchwin[cid].zoomLevel, // these were used to work - zfactor, // around an old nw.js popup pos - // bug. Now it's only necessary - // on Windows, which uses v.0.14 - svg_view_box = patchwin[cid].window.document.getElementById("patchsvg") - .getAttribute("viewBox").split(" "); // need top-left svg origin - - // Check nw.js version-- if its lts then we need the zoom_kludge... - zfactor = process.versions.nw === "0.14.7" ? zoom_kludge(zoom_level) : 1; - // Set the global popup x/y so they can be retrieved by the relevant - // document's event handler - popup_coords[0] = xpos; - popup_coords[1] = ypos; - //popup_coords[0] = xpos; - //popup_coords[1] = ypos; - popup_menu[cid].items[0].enabled = canprop; - popup_menu[cid].items[1].enabled = canopen; - - // We'll use "isobject" to enable/disable "To Front" and "To Back" - //isobject; - - // We need to round win_left and win_top because the popup menu - // interface expects an int. Otherwise the popup position gets wonky - // when you zoom and scroll... - xpos = Math.floor(xpos * zfactor) - Math.floor(win_left * zfactor); - ypos = Math.floor(ypos * zfactor) - Math.floor(win_top * zfactor); - - // Now subtract the x and y offset for the top left corner of the svg. - // We need to do this because a Pd canvas can have objects with negative - // coordinates. Thus the SVG viewbox will have negative values for the - // top left corner, and those must be subtracted from xpos/ypos to get - // the proper window coordinates. - xpos -= Math.floor(svg_view_box[0] * zfactor); - ypos -= Math.floor(svg_view_box[1] * zfactor); - - popup_coords[2] = xpos + patchwin[cid].x; - popup_coords[3] = ypos + patchwin[cid].y; - - popup_menu[cid].popup(xpos, ypos); + gui(cid).get_nw_window(function(nw_win) { + var win_left = nw_win.window.document.body.scrollLeft, + win_top = nw_win.window.document.body.scrollTop, + zoom_level = nw_win.zoomLevel, // these were used to work + zfactor, // around an old nw.js popup pos + // bug. Now it's only necessary + // on Windows, which uses v.0.14 + svg_view_box = nw_win.window.document.getElementById("patchsvg") + .getAttribute("viewBox").split(" "); // need top-left svg origin + + // Check nw.js version-- if its lts then we need the zoom_kludge... + zfactor = process.versions.nw === "0.14.7" ? zoom_kludge(zoom_level) : 1; + // Set the global popup x/y so they can be retrieved by the relevant + // document's event handler + popup_coords[0] = xpos; + popup_coords[1] = ypos; + //popup_coords[0] = xpos; + //popup_coords[1] = ypos; + popup_menu[cid].items[0].enabled = canprop; + popup_menu[cid].items[1].enabled = canopen; + + // We'll use "isobject" to enable/disable "To Front" and "To Back" + //isobject; + + // We need to round win_left and win_top because the popup menu + // interface expects an int. Otherwise the popup position gets wonky + // when you zoom and scroll... + xpos = Math.floor(xpos * zfactor) - Math.floor(win_left * zfactor); + ypos = Math.floor(ypos * zfactor) - Math.floor(win_top * zfactor); + + // Now subtract the x and y offset for the top left corner of the svg. + // We need to do this because a Pd canvas can have objects with negative + // coordinates. Thus the SVG viewbox will have negative values for the + // top left corner, and those must be subtracted from xpos/ypos to get + // the proper window coordinates. + xpos -= Math.floor(svg_view_box[0] * zfactor); + ypos -= Math.floor(svg_view_box[1] * zfactor); + + popup_coords[2] = xpos + nw_win.x; + popup_coords[3] = ypos + nw_win.y; + + popup_menu[cid].popup(xpos, ypos); + }); } function popup_action(cid, index) { @@ -4310,124 +4267,131 @@ exports.popup_action = popup_action; // GOP will get erased and redrawn when its time to show the contents // again. function gui_graph_fill_border(cid, tag) { - var g = get_gobj(cid, tag); - g.classList.add("has_window"); + gui(cid).get_gobj(tag, function(e) { + e.classList.add("has_window"); + }); } function gui_graph_deleteborder(cid, tag) { - var b = get_item(cid, tag); - b.parentNode.removeChild(b); + gui(cid).get_elem(tag, function(e) { + e.parentNode.removeChild(b); + }); } function gui_graph_label(cid, tag, label_number, font_height, array_name, font, font_size, font_weight, is_selected) { - var y = font_height * label_number * -1; - gui_text_new(cid, tag, "graph_label", 0, 0, y, array_name, font_size); + gui(cid).get_elem("patchsvg", function(e) { + var y = font_height * label_number * -1; + gui_text_new(cid, tag, "graph_label", 0, 0, y, array_name, font_size); + }); } function gui_graph_vtick(cid, tag, x, up_y, down_y, tick_pix, basex, basey) { - var g = get_gobj(cid, tag), - up_tick, - down_tick; - // Don't think these need an ID... - up_tick = create_item(cid, "line", { - stroke: "black", - x1: x - basex, - y1: up_y - basey, - x2: x - basex, - y2: up_y - tick_pix - basey - }); - down_tick = create_item(cid, "line", { - stroke: "black", - x1: x - basex, - y1: down_y - basey, - x2: x - basex, - y2: down_y + tick_pix - basey + gui(cid).get_gobj(tag) + .append(function(frag) { + var up_tick, + down_tick; + // Don't think these need an ID... + up_tick = create_item(cid, "line", { + stroke: "black", + x1: x - basex, + y1: up_y - basey, + x2: x - basex, + y2: up_y - tick_pix - basey + }); + down_tick = create_item(cid, "line", { + stroke: "black", + x1: x - basex, + y1: down_y - basey, + x2: x - basex, + y2: down_y + tick_pix - basey + }); + frag.appendChild(up_tick); + frag.appendChild(down_tick); + return frag; }); - g.appendChild(up_tick); - g.appendChild(down_tick); } function gui_graph_htick(cid, tag, y, r_x, l_x, tick_pix, basex, basey) { - var g = get_gobj(cid, tag), - left_tick, - right_tick; - // Don't think these need an ID... - left_tick = create_item(cid, "line", { - stroke: "black", - x1: l_x - basex, - y1: y - basey, - x2: l_x - tick_pix - basex, - y2: y - basey, - id: "tick" + y - }); - right_tick = create_item(cid, "line", { - stroke: "black", - x1: r_x - basex, - y1: y - basey, - x2: r_x + tick_pix - basex, - y2: y - basey + gui(cid).get_gobj(tag) + .append(function(frag) { + var left_tick, + right_tick; + // Don't think these need an ID... + left_tick = create_item(cid, "line", { + stroke: "black", + x1: l_x - basex, + y1: y - basey, + x2: l_x - tick_pix - basex, + y2: y - basey, + id: "tick" + y + }); + right_tick = create_item(cid, "line", { + stroke: "black", + x1: r_x - basex, + y1: y - basey, + x2: r_x + tick_pix - basex, + y2: y - basey + }); + frag.appendChild(left_tick); + frag.appendChild(right_tick); + return frag; }); - g.appendChild(left_tick); - g.appendChild(right_tick); } function gui_graph_tick_label(cid, tag, x, y, text, font, font_size, font_weight, basex, basey, tk_label_anchor) { - var g = get_gobj(cid, tag), - svg_text, text_node, text_anchor, alignment_baseline; - // We use anchor identifiers from the tk toolkit: - // - // "n" for north, or aligned at the top of the text - // "s" for south, or default baseline alignment - // "e" for east, or text-anchor at the end of the text - // "w" for west, or default text-anchor for left-to-right languages - // - // For x labels the tk_label_anchor will either be "n" for labels at the - // bottom of the graph, or "s" for labels at the top of the graph - // - // For y labels the tk_label_anchor will either be "e" for labels at the - // right of the graph, or "w" for labels at the right. - // - // In each case we want the label to be centered around the tick mark. - // So we default to value "middle" if we didn't get a value for that - // axis. - text_anchor = tk_label_anchor === "e" ? "end" : - tk_label_anchor === "w" ? "start" : "middle"; - alignment_baseline = tk_label_anchor === "n" ? "hanging" : - tk_label_anchor === "s" ? "auto" : "middle"; - svg_text = create_item(cid, "text", { - // need a label "y" relative to baseline - x: x - basex, - y: y - basey, - "text-anchor": text_anchor, - "alignment-baseline": alignment_baseline, - "font-size": pd_fontsize_to_gui_fontsize(font_size) + "px", + gui(cid).get_gobj(tag) + .append(function(frag, w) { + var svg_text, text_node, text_anchor, alignment_baseline; + // We use anchor identifiers from the tk toolkit: + // + // "n" for north, or aligned at the top of the text + // "s" for south, or default baseline alignment + // "e" for east, or text-anchor at the end of the text + // "w" for west, or default text-anchor for left-to-right languages + // + // For x labels the tk_label_anchor will either be "n" for labels at the + // bottom of the graph, or "s" for labels at the top of the graph + // + // For y labels the tk_label_anchor will either be "e" for labels at the + // right of the graph, or "w" for labels at the right. + // + // In each case we want the label to be centered around the tick mark. + // So we default to value "middle" if we didn't get a value for that + // axis. + text_anchor = tk_label_anchor === "e" ? "end" : + tk_label_anchor === "w" ? "start" : "middle"; + alignment_baseline = tk_label_anchor === "n" ? "hanging" : + tk_label_anchor === "s" ? "auto" : "middle"; + svg_text = create_item(cid, "text", { + // need a label "y" relative to baseline + x: x - basex, + y: y - basey, + "text-anchor": text_anchor, + "alignment-baseline": alignment_baseline, + "font-size": pd_fontsize_to_gui_fontsize(font_size) + "px", + }); + text_node = w.document.createTextNode(text); + svg_text.appendChild(text_node); + frag.appendChild(svg_text); + return frag; }); - text_node = patchwin[cid].window.document.createTextNode(text); - svg_text.appendChild(text_node); - g.appendChild(svg_text); } function gui_canvas_drawredrect(cid, x1, y1, x2, y2) { - var svgelem, - g, - r; - if (!patchwin[cid]) { - return; - } - svgelem = get_item(cid, "patchsvg"); - g = gui_gobj_new(cid, cid, "gop_rect", x1, y1, 1); - r = create_item(cid, "rect", { - width: x2 - x1, - height: y2 - y1, - id: "gop_rect" + gui(cid).get_elem("patchsvg", function(svg_elem) { + var g = gui_gobj_new(cid, cid, "gop_rect", x1, y1, 1); + var r = create_item(cid, "rect", { + width: x2 - x1, + height: y2 - y1, + id: "gop_rect" + }); + g.appendChild(r); + svg_elem.appendChild(g); }); - g.appendChild(r); - svgelem.appendChild(g); } function gui_canvas_deleteredrect(cid) { - var r = get_gobj(cid, cid); // We need to check for existence here, because the first // time setting GOP in properties, there is no red rect yet. // But setting properties when the subpatch's window is @@ -4437,16 +4401,16 @@ function gui_canvas_deleteredrect(cid) { // functions without knowing the side effects. But ineffectual // gui calls should really be minimized-- otherwise it's simply // too difficult to debug what's being passed over the socket. - if (r !== null) { - r.parentNode.removeChild(r); - } + gui(cid).get_gobj(cid, function(e) { + e.parentNode.removeChild(r); + }); } function gui_canvas_redrect_coords(cid, x1, y1, x2, y2) { - var g = get_gobj(cid, cid), - r = get_item(cid, "gop_rect"); - elem_move(g, x1, y1); - configure_item(r, { + gui(cid).get_gobj(cid, function(e) { + elem_move(e, x1, y1); + }) + .get_elem("gop_rect", { width: x2 - x1, height: y2 - y1 }); @@ -4501,25 +4465,19 @@ function gui_cord_inspector_update(cid, text, basex, basey, bg_size, y1, y2, mov } function gui_cord_inspector_erase(cid) { - var ci = get_gobj(cid, "cord_inspector"); - if (ci !== null) { - ci.parentNode.removeChild(ci); - } else { - //post("oops, trying to erase cord inspector that doesn't exist!"); - } + gui(cid).get_gobj("cord_inspector", function(e) { + e.parentNode.removeChild(ci); + }); } function gui_cord_inspector_flash(cid, state) { - var ct = get_item(cid, "cord_inspector_text"); - if (ct !== null) { + gui(cid).get_elem("cord_inspector_text", function(e) { if (state === 1) { - ct.classList.add("flash"); + e.classList.add("flash"); } else { - ct.classList.remove("flash"); + e.classList.remove("flash"); } - } else { - //post("gui_cord_inspector_flash: cord inspector doesn't exist!"); - } + }); } // Window functions @@ -4527,9 +4485,9 @@ function gui_cord_inspector_flash(cid, state) { function gui_raise_window(cid) { // Check if the window exists, for edge cases like // [vis 1, vis1(---[send this_canvas] - if (patchwin[cid]) { - patchwin[cid].focus(); - } + gui(cid).get_nw_window(function(nw_win) { + nw_win.focus(); + }); } // Unfortunately DOM window.focus doesn't actually focus the window, so we @@ -4668,15 +4626,13 @@ function gui_gatom_dialog(did, attr_array) { } function gui_gatom_activate(cid, tag, state) { - var g; - if (patchwin[cid]) { - g = get_gobj(cid, tag); + gui(cid).get_gobj(tag, function(e) { if (state !== 0) { - g.classList.add("activated"); + e.classList.add("activated"); } else { - g.classList.remove("activated"); + e.classList.remove("activated"); } - } + }); } function gui_dropdown_dialog(did, attr_array) { @@ -4686,13 +4642,12 @@ function gui_dropdown_dialog(did, attr_array) { attr_array_to_object(attr_array)); } -function dropdown_populate(cid, label_array, current_index) { - var ol = patchwin[cid] - .window.document.querySelector("#dropdown_list ol"); +function dropdown_populate(w, label_array, current_index) { + var ol = w.document.querySelector("#dropdown_list ol"); // clear it out ol.innerHTML = ''; label_array.forEach(function(text, i) { - var li = patchwin[cid].window.document.createElement("li"); + var li = w.document.createElement("li"); li.textContent = text; li.setAttribute("data-index", i); if (i === current_index) { @@ -4712,14 +4667,12 @@ function gui_dropdown_activate(cid, obj_tag, tag, current_index, font_size, stat offset_anchor; // top or bottom // Annoying: obj_tag is just the "x"-prepended hex value for the object, // and tag is the one from rtext_gettag that is used as our gobj id - if (patchwin[cid]) { + gui(cid).get_elem("patchsvg", function(svg_elem, w) { g = get_gobj(cid, tag); if (state !== 0) { - svg_view = patchwin[cid].window.document.getElementById("patchsvg") - .viewBox.baseVal; - select_elem = patchwin[cid] - .window.document.querySelector("#dropdown_list"); - dropdown_populate(cid, label_array, current_index); + svg_view = svg_elem.viewBox.baseVal; + select_elem = w.document.querySelector("#dropdown_list"); + dropdown_populate(w, label_array, current_index); // stick the obj_tag in a data field select_elem.setAttribute("data-callback", obj_tag); // display the menu so we can measure it @@ -4729,14 +4682,12 @@ function gui_dropdown_activate(cid, obj_tag, tag, current_index, font_size, stat // method is the only reliable one I've found. And even here, // if you display the select_elem as inline _before_ measuring // the doc height, the result ends up being _smaller_. No idea. - doc_height = - patchwin[cid].window.document.documentElement - .clientHeight; + doc_height = w.document.documentElement.clientHeight; // Now let's display the select_elem div so we can measure it select_elem.style.setProperty("display", "inline"); menu_height = select_elem.querySelector("ol") .getBoundingClientRect().height; - scroll_y = patchwin[cid].window.scrollY; + scroll_y = w.scrollY; // If the area below the object is smaller than 75px, then // display the menu above the object. // If the entire menu won't fit below the object but _will_ @@ -4768,13 +4719,13 @@ function gui_dropdown_activate(cid, obj_tag, tag, current_index, font_size, stat select_elem.style.setProperty("font-size", pd_fontsize_to_gui_fontsize(font_size) + "px"); select_elem.style.setProperty("min-width", g.getBBox().width + "px"); - patchwin[cid].window.canvas_events.dropdown_menu(); + w.canvas_events.dropdown_menu(); } else { post("deactivating dropdown menu"); // Probably want to send this pdsend(cid, "key 0 Control 0 1 0"); } - } + }); } function gui_iemgui_dialog(did, attr_array) { @@ -5066,17 +5017,17 @@ var skin = exports.skin = (function () { }()); function select_text(cid, elem) { - var range, win = patchwin[cid].window; - if (win.document.selection) { - range = win.document.body.createTextRange(); - range.moveToElementText(elem); - range.select(); - } else if (win.getSelection) { - range = win.document.createRange(); - range.selectNodeContents(elem); - win.getSelection().removeAllRanges(); - win.getSelection().addRange(range); - } + var range, win = patchwin[cid].window; + if (win.document.selection) { + range = win.document.body.createTextRange(); + range.moveToElementText(elem); + range.select(); + } else if (win.getSelection) { + range = win.document.createRange(); + range.selectNodeContents(elem); + win.getSelection().removeAllRanges(); + win.getSelection().addRange(range); + } } // CSS: Cleanly separate style from content. @@ -5240,18 +5191,20 @@ function gui_undo_menu(cid, undo_text, redo_text) { // there may be some calls to subpatches after updating a dialog // (like turning on GOP) which call this for a canvas that has // been destroyed. - if (cid !== "nobody" && patchwin[cid]) { - patchwin[cid].window.nw_undo_menu(undo_text, redo_text); - } + gui(cid).get_nw_window(function(nw_win) { + if (cid !== "nobody") { + nw_win.window.nw_undo_menu(undo_text, redo_text); + } + }); } -function canvas_params(cid) +// leverages the get_nw_window method in the callers... +function canvas_params(nw_win, svg_elem) { // calculate the canvas parameters (svg bounding box and window geometry) // for do_getscroll and do_optimalzoom - var bbox, width, height, min_width, min_height, x, y, svg; - svg = get_item(cid, "patchsvg"); - bbox = svg.getBBox(); + var bbox, width, height, min_width, min_height, x, y; + bbox = svg_elem.getBBox(); // We try to do Pd-extended style canvas origins. That is, coord (0, 0) // should be in the top-left corner unless there are objects with a // negative x or y. @@ -5276,8 +5229,8 @@ function canvas_params(cid) // the scrollbars from appearing. Here, we just subtract 4 from both // of them. This could lead to some problems with event handlers but I // haven't had a problem with it yet. - min_width = patchwin[cid].window.innerWidth - 4; - min_height = patchwin[cid].window.innerHeight - 4; + min_width = nw_win.window.innerWidth - 4; + min_height = nw_win.window.innerHeight - 4; // Since we don't do any transformations on the patchsvg, // let's try just using ints for the height/width/viewBox // to keep things simple. @@ -5287,7 +5240,7 @@ function canvas_params(cid) min_height |= 0; x |= 0; y |= 0; - return { svg: svg, x: x, y: y, w: width, h: height, + return { x: x, y: y, w: width, h: height, mw: min_width, mh: min_height }; } @@ -5299,24 +5252,26 @@ function do_getscroll(cid) { // This is an awfully bad pattern. The whole scroll-checking mechanism // needs to be rethought, but in the meantime this should prevent any // errors wrt the rendering context disappearing. - if (!patchwin[cid]) { return; } - var { svg: svg, x: x, y: y, w: width, h: height, - mw: min_width, mh: min_height } = canvas_params(cid); - if (width < min_width) { - width = min_width; - } - // If the svg extends beyond the viewport, it might be nice to pad - // both the height/width and the x/y coords so that there is extra - // room for making connections and manipulating the objects. As it - // stands objects will be flush with the scrollbars and window - // edges. - if (height < min_height) { - height = min_height; - } - configure_item(svg, { - viewBox: [x, y, width, height].join(" "), - width: width, - height: height + gui(cid).get_nw_window(function(nw_win) { + var svg_elem = nw_win.window.document.getElementById("patchsvg"); + var { x: x, y: y, w: width, h: height, + mw: min_width, mh: min_height } = canvas_params(nw_win, svg_elem); + if (width < min_width) { + width = min_width; + } + // If the svg extends beyond the viewport, it might be nice to pad + // both the height/width and the x/y coords so that there is extra + // room for making connections and manipulating the objects. As it + // stands objects will be flush with the scrollbars and window + // edges. + if (height < min_height) { + height = min_height; + } + configure_item(svg_elem, { + viewBox: [x, y, width, height].join(" "), + width: width, + height: height + }); }); } @@ -5348,36 +5303,37 @@ exports.gui_canvas_get_scroll = gui_canvas_get_scroll; function do_optimalzoom(cid, hflag, vflag) { // determine an optimal zoom level that makes the entire patch fit within // the window - if (!patchwin[cid]) { return; } - var { x: x, y: y, w: width, h: height, mw: min_width, mh: min_height } = - canvas_params(cid); - // Calculate the optimal horizontal and vertical zoom values, - // using floor to always round down to the nearest integer. Note - // that these may well be negative, if the viewport is too small - // for the patch at the current zoom level. XXXREVIEW: We assume a - // zoom factor of 1.2 here; this works for me on Linux, but I'm - // not sure how portable it is. -ag - var zx = 0, zy = 0; - if (width>0) zx = Math.floor(Math.log(min_width/width)/Math.log(1.2)); - if (height>0) zy = Math.floor(Math.log(min_height/height)/Math.log(1.2)); - // Optimal zoom is the minimum of the horizontal and/or the vertical zoom - // values, depending on the h and v flags. This gives us the offset to the - // current zoom level. We then need to clamp the resulting new zoom level - // to the valid zoom level range of -8..+7. - var actz = patchwin[cid].zoomLevel, z = 0; - if (hflag && vflag) - z = Math.min(zx, zy); - else if (hflag) - z = zx; - else if (vflag) - z = zy; - z += actz; - if (z < -8) z = -8; if (z > 7) z = 7; - //post("bbox: "+width+"x"+height+"+"+x+"+"+y+" window size: "+min_width+"x"+min_height+" current zoom level: "+actz+" optimal zoom level: "+z); - if (z != actz) { - patchwin[cid].zoomLevel = z; - pdsend(cid, "zoom", z); - } + gui(cid).get_nw_window(function(nw_win) { + var { x: x, y: y, w: width, h: height, mw: min_width, mh: min_height } = + canvas_params(cid); + // Calculate the optimal horizontal and vertical zoom values, + // using floor to always round down to the nearest integer. Note + // that these may well be negative, if the viewport is too small + // for the patch at the current zoom level. XXXREVIEW: We assume a + // zoom factor of 1.2 here; this works for me on Linux, but I'm + // not sure how portable it is. -ag + var zx = 0, zy = 0; + if (width>0) zx = Math.floor(Math.log(min_width/width)/Math.log(1.2)); + if (height>0) zy = Math.floor(Math.log(min_height/height)/Math.log(1.2)); + // Optimal zoom is the minimum of the horizontal and/or the vertical + // zoom values, depending on the h and v flags. This gives us the offset + // to the current zoom level. We then need to clamp the resulting new + // zoom level to the valid zoom level range of -8..+7. + var actz = nw_win.zoomLevel, z = 0; + if (hflag && vflag) + z = Math.min(zx, zy); + else if (hflag) + z = zx; + else if (vflag) + z = zy; + z += actz; + if (z < -8) z = -8; if (z > 7) z = 7; + //post("bbox: "+width+"x"+height+"+"+x+"+"+y+" window size: "+min_width+"x"+min_height+" current zoom level: "+actz+" optimal zoom level: "+z); + if (z != actz) { + nw_win.zoomLevel = z; + pdsend(cid, "zoom", z); + } + }); } var optimalzoom_var = {}; @@ -5395,24 +5351,27 @@ exports.gui_canvas_optimal_zoom = gui_canvas_optimal_zoom; // handling the selection function gui_lower(cid, tag) { - var svg = patchwin[cid].window.document.getElementById("patchsvg"), - first_child = svg.firstElementChild, + gui(cid).get_elem("patchsvg", function(svg_elem) { + var first_child = svg_elem.firstElementChild, selection = null, - gobj, len, i; - if (tag === "selected") { - selection = svg.getElementsByClassName("selected"); - } else { - gobj = get_gobj(cid, tag); - if (gobj !== null) { - selection = [gobj]; + gobj, + len, + i; + if (tag === "selected") { + selection = svg_elem.getElementsByClassName("selected"); + } else { + gobj = get_gobj(cid, tag); + if (gobj !== null) { + selection = [gobj]; + } } - } - if (selection !== null) { - len = selection.length; - for (i = len - 1; i >= 0; i--) { - svg.insertBefore(selection[i], first_child); + if (selection !== null) { + len = selection.length; + for (i = len - 1; i >= 0; i--) { + svg_elem.insertBefore(selection[i], first_child); + } } - } + }); } // This only differs from gui_raise by setting first_child to @@ -5420,36 +5379,38 @@ function gui_lower(cid, tag) { // all three of these should be combined into a single function (plus // all the silly logic on the C side moved here function gui_raise(cid, tag) { - var svg = patchwin[cid].window.document.getElementById("patchsvg"), - first_child = svg.querySelector(".cord"), + gui(cid).get_elem("patchsvg", function(svg_elem) { + var first_child = svg_elem.querySelector(".cord"), selection = null, - gobj, len, i; - if (tag === "selected") { - selection = svg.getElementsByClassName("selected"); - } else { - gobj = get_gobj(cid, tag); - if (gobj !== null) { - selection = [gobj]; + gobj, + len, + i; + if (tag === "selected") { + selection = svg_elem.getElementsByClassName("selected"); + } else { + gobj = get_gobj(cid, tag); + if (gobj !== null) { + selection = [gobj]; + } } - } - if (selection !== null) { - len = selection.length; - for (i = len - 1; i >= 0; i--) { - svg.insertBefore(selection[i], first_child); + if (selection !== null) { + len = selection.length; + for (i = len - 1; i >= 0; i--) { + svg_elem.insertBefore(selection[i], first_child); + } } - } + }); } function gui_find_lowest_and_arrange(cid, reference_element_tag, objtag) { - var ref_elem = get_gobj(cid, reference_element_tag), - svg = patchwin[cid].window.document.getElementById("patchsvg"), + gui(cid).get_gobj(reference_element_tag, function(ref_elem, w) { + var svg_elem = w.document.getElementById("patchsvg"), selection = null, gobj, len, - i; - if (ref_elem !== null) { + i; if (objtag === "selected") { - selection = svg.getElementsByClassName("selected"); + selection = svg_elem.getElementsByClassName("selected"); } else { gobj = get_gobj(cid, objtag); if (gobj !== null) { @@ -5459,10 +5420,10 @@ function gui_find_lowest_and_arrange(cid, reference_element_tag, objtag) { if (selection !== null) { len = selection.length; for (i = len - 1; i >= 0; i--) { - svg.insertBefore(selection[i], ref_elem); + svg_elem.insertBefore(selection[i], ref_elem); } } - } + }); } // Bindings for dialog menu of iemgui, canvas, etc.