From e66428d72b67dd53ee195d2cd95c4320bd9d1fda Mon Sep 17 00:00:00 2001 From: Jonathan Wilkes <jon.w.wilkes@gmail.com> Date: Tue, 14 Nov 2017 17:32:29 -0500 Subject: [PATCH] first draft of new pdgui interface using a small callback-based framework Some reference to patchwin remain... This refactors pdgui.js to prevent errors for early messages from Pd. Some references to raw patchwin array still remain, but on the whole this should make things more sane. There might also still be some places where a null-dereference error will still bite us. For example, I think we need a check in the "q" method for existence. But such changes can now happen in a single location inside the "gui" definition, rather than scattering them throughout the file as was done before as a stopgap. --- pd/nw/pdgui.js | 3093 ++++++++++++++++++++++++------------------------ 1 file changed, 1527 insertions(+), 1566 deletions(-) diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index 5be087848..338f9eb96 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. -- GitLab