From 08a3de2ddaf4aca63d17553853c30cfb03edf125 Mon Sep 17 00:00:00 2001
From: Jonathan Wilkes <jon.w.wilkes@gmail.com>
Date: Wed, 15 Apr 2015 01:25:50 -0400
Subject: [PATCH] some code cleanup, and first attempt at array settings inside
 canvas dialog

---
 pd/nw/dialog_canvas.css           |  138 ++++
 pd/nw/dialog_canvas.html          |  678 +++++++++++++++++
 pd/nw/dialog_iemgui.css           |  113 +++
 pd/nw/dialog_iemgui.html          |  604 ++++++++++++++++
 pd/nw/index.html                  |    6 +-
 pd/nw/locales/en/translation.json |   59 ++
 pd/nw/pd_canvas.css               |  102 +++
 pd/nw/pd_canvas.html              | 1125 +++++++++++++++++++++++++++++
 pd/nw/pdgui.js                    |  246 +++++--
 pd/nw/todo.txt                    |   20 +
 pd/src/g_all_guis.c               |   13 +-
 pd/src/g_array.c                  |   57 +-
 pd/src/g_canvas.c                 |    2 +
 pd/src/g_editor.c                 |  114 ++-
 pd/src/g_graph.c                  |   15 +-
 pd/src/g_magicglass.c             |    4 +-
 pd/src/g_mycanvas.c               |    4 +-
 pd/src/g_rtext.c                  |    4 +-
 pd/src/g_scalar.c                 |    5 +-
 pd/src/g_template.c               |   99 ++-
 pd/src/g_text.c                   |   50 +-
 pd/src/s_inter.c                  |   10 +-
 22 files changed, 3317 insertions(+), 151 deletions(-)
 create mode 100644 pd/nw/dialog_canvas.css
 create mode 100644 pd/nw/dialog_canvas.html
 create mode 100644 pd/nw/dialog_iemgui.css
 create mode 100644 pd/nw/dialog_iemgui.html
 create mode 100644 pd/nw/pd_canvas.css
 create mode 100644 pd/nw/pd_canvas.html

diff --git a/pd/nw/dialog_canvas.css b/pd/nw/dialog_canvas.css
new file mode 100644
index 000000000..5a2e244d0
--- /dev/null
+++ b/pd/nw/dialog_canvas.css
@@ -0,0 +1,138 @@
+@font-face {
+    font-family: "DejaVu Sans Mono";
+    src: url("DejaVuSansMono.ttf");
+}
+
+body {
+    font-family:Verdana;
+    margin: 0px;
+}
+
+fieldset {
+    font-family:Georgia;
+    background-color:#eeeeee;
+    border-radius:3px;
+    border:2px solid black;
+    margin-left:auto;
+    margin-right:auto;
+    padding: 10px;
+}
+
+input[type="text"]{
+    width:3em;
+}
+
+input[type="number"]{
+    width:3em;
+}
+
+div.x-scale {
+    padding: 3px;
+    text-align: center;
+}
+
+div.gop-range {
+}
+
+div.y1 {
+    text-align: center;
+    padding: 3px;
+}
+
+div.x1 {
+    text-align: center;
+    padding: 3px;
+}
+
+div.y2 {
+    text-align: center;
+    padding: 3px;
+}
+
+
+.disabled {
+    color: #aaa;
+}
+
+.prop{
+}
+
+.hidden {
+    display: none;
+}
+
+.container{
+    display: none;
+}
+
+body {
+}
+
+.noselect {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+text {
+    // fill: red;
+    cursor: default;
+}
+
+.selected_border {
+    stroke: blue;
+    stroke-dasharray: none;
+    stroke-width: 1;
+}
+
+label {
+    text-align: right;
+//    margin-right: auto;
+//    margin-left: auto;
+}
+
+.pair {
+    width: 75%;
+    text-align: left;
+    align: left;
+}
+
+.item1 {
+    width: 50%;
+}
+
+.item2 {
+    width: 50%;
+}
+
+input[name="x-offset"] {
+    width: 2em;
+}
+
+input[name="y-offset"] {
+    width: 2em;
+}
+
+input[name="send-symbol"] {
+    width: 8em;
+}
+
+input[name="receive-symbol"] {
+    width: 8em;
+}
+
+input[name="label"] {
+    width: 8em;
+}
+
+input[name="font-size"] {
+    width: 3em;
+}
+
+.submit_buttons {
+    text-align: center;
+    padding: 8px;
+}
diff --git a/pd/nw/dialog_canvas.html b/pd/nw/dialog_canvas.html
new file mode 100644
index 000000000..f44304c8c
--- /dev/null
+++ b/pd/nw/dialog_canvas.html
@@ -0,0 +1,678 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="dialog_canvas.css">
+  </head>
+  <body>
+    <div class="container noselect">
+    <form> 
+
+      <fieldset> 
+        <legend data-i18n="canvas.prop.heading.gop"></legend> 
+
+        <table class="pairs">
+          <tr class="display-flags prop hidden" data-i18n="[title]canvas.prop.gop_tt">
+            <td>
+              <input type="checkbox" name="gop" value="on">
+            </td>
+            <td>
+              <span id="gop_label" data-i18n="canvas.prop.gop"></span>
+            </td>
+          </tr>
+          <tr class="x-pix prop hidden">
+            <td>
+            </td>
+            <td>
+              <table>
+                <tr>
+                  <td> 
+                    <label class="gop_opt" data-i18n="[title]canvas.prop.x_pix_tt">
+                      <span data-i18n="canvas.prop.x_pix"></span>
+                    </label>
+                  </td>
+                  <td data-i18n="[title]canvas.prop.x_pix_tt">
+                    <input class="gop_opt" type="text" name="x-pix">
+                  </td>
+                  <td>
+                    <label class="gop_opt" data-i18n="[title]canvas.prop.y_pix_tt">
+                      <span data-i18n="canvas.prop.y_pix"></span>
+                    </label>
+                  </td>
+                  <td data-i18n="[title]canvas.prop.y_pix_tt">
+                    <input class="gop_opt" type="text" name="y-pix">
+                  </td>
+                </tr>
+                <tr class="x-margin prop hidden">
+                  <td>
+                    <label class="gop_opt" data-i18n="[title]canvas.prop.x_margin_tt">
+                      <span data-i18n="canvas.prop.x_margin"></span>
+                    </label>
+                  </td>
+                  <td data-i18n="[title]canvas.prop.x_margin_tt">
+                    <input class="gop_opt" type="text" name="x-margin">
+                  </td>
+                  <td>
+                    <label class="gop_opt" data-i18n="[title]canvas.prop.y_margin_tt">
+                      <span data-i18n="canvas.prop.y_margin"></span>
+                    </label>
+                  </td>
+                  <td data-i18n="[title]canvas.prop.y_margin_tt">
+                    <input class="gop_opt" type="text" name="y-margin">
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+          <tr class="display-flags prop">
+            <td>
+            </td>
+            <td>
+              <label class="gop_opt" data-i18n="[title]canvas.prop.hide_name_tt">
+                <input class="gop_opt" type="checkbox" name="hide-name" value="on">
+                <span data-i18n="canvas.prop.hide_name"></span>
+              </label>
+            </td>
+          </tr>
+        </table>
+      </fieldset> 
+
+
+      <fieldset> 
+        <legend data-i18n="canvas.prop.heading.data_scaling"></legend> 
+
+          <div class="x-scale prop hidden">
+            <label class="no_gop_opt" data-i18n="[title]canvas.prop.x_scale_tt">
+              <span data-i18n="canvas.prop.x_scale"></span>
+              <input class="no_gop_opt" type="text" name="x-scale">
+            </label>
+            <label class="no_gop_opt" data-i18n="[title]canvas.prop.y_scale_tt">
+              <span data-i18n="canvas.prop.y_scale"></span>
+              <input class="no_gop_opt" type="text" name="y-scale">
+            </label>
+          </div>
+
+    <div class="gop-range">
+        <div class="y1 prop hidden">
+          <label class="gop_opt" data-i18n="[title]canvas.prop.y1_tt">
+            <span data-i18n="canvas.prop.y1"></span>
+            <br>
+          <input class="gop_opt" type="text" name="y1">
+          </label>
+          <br>
+        </div>
+
+        <div class="x1 prop hidden">
+          <label class="gop_opt" data-i18n="[title]canvas.prop.x1_tt">
+            <span data-i18n="canvas.prop.x1"></span>
+            <input class="gop_opt" type="text" name="x1">
+          </label>
+          <label class="gop_opt" data-i18n="[title]canvas.prop.x2_tt">
+            <input class="gop_opt" type="text" name="x2">
+            <span data-i18n="canvas.prop.x2"></span>
+          </label>
+          <br>
+       </div>
+
+       <div class="y2">
+         <label class="gop_opt" data-i18n="[title]canvas.prop.y2_tt">
+           <input class="gop_opt" type="text" name="y2">
+           <br>
+           <span data-i18n="canvas.prop.y2"></span>
+         </label>
+       </div>
+    </div>
+
+      </fieldset> 
+
+
+      <fieldset id="arrays" class="hidden">
+        <legend data-i18n="canvas.prop.heading.arrays"></legend> 
+
+        <label class="arrays" data-i18n="[title]canvas.prop.array_name">
+          <select id="arrays_select" class="hidden"></select>
+        </label>
+
+          <div class="array-name prop">
+            <label class="array-name" data-i18n="[title]canvas.prop.array_name_tt">
+              <span data-i18n="canvas.prop.array_name"></span>
+              <input onchange="attr_change(this);" class="array-name" type="text" name="array_name">
+            </label>
+            <label class="array-size" data-i18n="[title]canvas.prop.array_size_tt">
+              <span data-i18n="canvas.prop.array_size"></span>
+              <input onchange="attr_change(this);" class="array-size" type="text" name="array_size">
+            </label>
+
+            <label class="array-save" data-i18n="[title]canvas.prop.array_save_tt">
+              <span data-i18n="canvas.prop.array_save"></span>
+              <input onchange="flag_change(this);" type="checkbox" name="array_save" value="on">
+            </label>
+
+            <label class="array-jump" data-i18n="[title]canvas.prop.array_jump_tt">
+              <span data-i18n="canvas.prop.array_jump"></span>
+              <input onchange="flag_change(this);" type="checkbox" name="array_jump" value="on">
+            </label>
+
+              <span data-i18n="canvas.prop.array_style"></span>
+
+            <label class="polygon" data-i18n="[title]canvas.prop.array_polygon_tt">
+              <span data-i18n="canvas.prop.array_polygon"></span>
+              <input class="array-style"
+                     type="radio"
+                     id="polygon"
+                     value="0"
+                     name="array_style"
+                     onchange="flag_change(this);">
+            </label>
+
+
+            <label class="points" data-i18n="[title]canvas.prop.array_points_tt">
+              <span data-i18n="canvas.prop.array_points"></span>
+              <input class="array-style"
+                     type="radio"
+                     id="points"
+                     value="1"
+                     name="array_style"
+                     onchange="flag_change(this);">
+            </label>
+
+
+            <label class="bezier-curve" data-i18n="[title]canvas.prop.array_bezier_tt">
+              <span data-i18n="canvas.prop.array_bezier"></span>
+              <input class="array-style"
+                     type="radio"
+                     id="bezier"
+                     value="2"
+                     name="array_style"
+                     onchange="flag_change(this);">
+            </label>
+
+
+            <label class="bar-graph" data-i18n="[title]canvas.prop.array_bars_tt">
+              <span data-i18n="canvas.prop.array_bars"></span>
+              <input class="array-style"
+                     type="radio"
+                     id="bar-graph"
+                     value="3"
+                     name="array_style"
+                     onchange="flag_change(this);">
+            </label>
+
+
+          </div>
+
+      <div class="array-fill">
+        <label data-i18n="[title]canvas.prop.array_fill_tt">
+          <input onchange="attr_change(this);" type="color" name="array_fill">
+          <span data-i18n="canvas.prop.array_fill"></span>
+        </label>
+        <br>
+      </div>
+
+      <div class="array-outline">
+        <label data-i18n="[title]canvas.prop.array_outline_tt">
+          <input onchange="attr_change(this);" type="color" name="array_outline">
+          <span data-i18n="canvas.prop.array_outline"></span>
+        </label>
+        <br>
+      </div>
+
+
+
+    <div class="gop-range">
+        <div class="y1 prop hidden">
+          <label class="gop_opt" data-i18n="[title]canvas.prop.y1_tt">
+            <span data-i18n="canvas.prop.y1"></span>
+            <br>
+          <input class="gop_opt" type="text" name="y1">
+          </label>
+          <br>
+        </div>
+
+        <div class="x1 prop hidden">
+          <label class="gop_opt" data-i18n="[title]canvas.prop.x1_tt">
+            <span data-i18n="canvas.prop.x1"></span>
+            <input class="gop_opt" type="text" name="x1">
+          </label>
+          <label class="gop_opt" data-i18n="[title]canvas.prop.x2_tt">
+            <input class="gop_opt" type="text" name="x2">
+            <span data-i18n="canvas.prop.x2"></span>
+          </label>
+          <br>
+       </div>
+
+       <div class="y2">
+         <label class="gop_opt" data-i18n="[title]canvas.prop.y2_tt">
+           <input class="gop_opt" type="text" name="y2">
+           <br>
+           <span data-i18n="canvas.prop.y2"></span>
+         </label>
+       </div>
+    </div>
+
+      </fieldset> 
+
+
+    <div class="submit_buttons">
+      <button type="button" onClick="ok()" data-i18n="[title]iem.prop.ok_tt">
+        <span data-i18n="iem.prop.ok"></span>
+      </button>
+      <button type="button" onClick="apply()" data-i18n="[title]iem.prop.apply_tt">
+        <span data-i18n="iem.prop.apply"></span>
+      </button>
+      <button type="button" onClick="cancel()" data-i18n="[title]iem.prop.cancel_tt">
+        <span data-i18n="iem.prop.cancel"></span>
+      </button>
+    </div>
+
+  </form> 
+  </div>      
+
+  <script>
+    'use strict';
+    var nw = require('nw.gui'); 
+    var pdgui = require('./pdgui.js');
+
+    // For translations
+    var l = pdgui.get_local_string;
+
+    console.log("my working dire is " + pdgui.get_pwd());
+
+    var pd_object_callback;
+
+    // nested arrays of attributes for each garray
+    // in this canvas
+    var pd_garray_attrs;
+
+    function ok() {
+        apply();
+        cancel();
+    }
+
+//    function toggler(evt) {
+//        evt.value = evt.checked ? 1 : 0; 
+//    }
+
+    function flag_change(elem) {
+        var attr, arrays_select, name, value, flag;
+        arrays_select = document.getElementById('arrays_select');
+        attr = pd_garray_attrs[arrays_select.value];
+        name = elem.name;
+//        pdgui.gui_post("name is " + name);
+        // get value from radio group, checked from checkboxes
+        if (name === 'array_style') {
+            value = document.querySelector('input[name="array_style"]:checked').value;
+            pdgui.gui_post("array style found: " + value);
+        } else {
+            // '+' for casting boolean to number
+            value = +elem.checked;
+        }
+//        pdgui.gui_post("value is " + value);
+        flag = attr[attr.indexOf('array_flags') + 1];
+        pdgui.gui_post("flag before is " + flag);
+        switch (name) {
+            case "array_save":
+                flag &= ~1;    // clear the save bit
+                flag |= value; // set it
+                break;
+            case "array_style":
+                flag &= ~(1 << 2); // clear style bit 2...
+                flag &= ~(1 << 1); // ... and 1 ...
+                flag += (2 * value); // set them
+                break;
+            case "array_jump":
+                flag &= ~(1 << 3);
+                flag += (16 * value);
+                break;
+        }
+        attr[attr.indexOf('array_flags') + 1] = flag;
+        pdgui.gui_post("array is " + attr);
+    }
+
+    function attr_change(elem) {
+        var array_index, attr, arrays_select, name;
+        arrays_select = document.getElementById('arrays_select');
+        attr = pd_garray_attrs[arrays_select.value];
+        name = elem.name;
+        attr[attr.indexOf(name) + 1] = elem.value;
+        pdgui.gui_post("name is " + elem.name);
+        pdgui.gui_post("value is " + elem.value);
+    }
+
+    function array_choose(array_index) {
+        var i, name, value, elem, style_index, style_opts,
+            array_attr = pd_garray_attrs[array_index];
+        for (i = 0; i < array_attr.length; i+=2) {
+            name = array_attr[i];
+            value = array_attr[i+1];
+            switch (name) {
+                case "array_gfxstub": break;
+                case "array_flags":
+                    // save contents
+                    elem = document.getElementsByName('array_save')[0];
+                    elem.checked = (value & 1) != 0;
+                    // jump on click
+                    elem = document.getElementsByName('array_jump')[0];
+                    elem.checked = (value & 16) != 0;
+                    // draw style
+                    style_opts = document.getElementsByName('array_style');
+                    style_index = (value & 6) >> 1;
+                    elem = style_opts[style_index];
+                    elem.checked = true;
+                    break;
+                default:
+                    // name, size, fill, and outline
+                    pdgui.gui_post("name is " + name);
+                    elem = document.getElementsByName(name)[0];
+                    elem.value = value;
+                    break;
+            }
+        }
+    }
+
+    var arrays_select = document.getElementById('arrays_select');
+    arrays_select.addEventListener('change', function() {
+        pdgui.gui_post('changed the thing is ' + this.value);
+        array_choose(this.value);
+    });
+
+    var gop_label = document.getElementById('gop_label');
+    gop_label.addEventListener('click', function() {
+        document.getElementsByName('gop')[0].click();
+    });
+
+    var gop_button = document.getElementsByName('gop')[0];
+    gop_button.addEventListener('click', function(evt) {
+        set_gop(this.checked);
+    });
+
+    function update_gop_form(opts, state) {
+        var elem, i;
+        for(i = 0; i < opts.length; i++) {
+            elem = opts[i];
+            if (elem.type === 'checkbox' ||
+                elem.type === 'text') {
+                elem.disabled = state ? false : true;
+            }
+            if (state) {
+                elem.classList.remove('disabled');
+            } else {
+                elem.classList.add('disabled');
+            }
+        }
+    }
+
+    var gop_click_count = 0;
+
+    function show_sane_defaults() {
+        var w, h, xoff, yoff;
+        w = document.getElementsByName('x-pix')[0];
+        h = document.getElementsByName('y-pix')[0];
+        xoff = document.getElementsByName('x-margin')[0];
+        yoff = document.getElementsByName('y-margin')[0];
+        if (w.value === '0' && h.value === '0') {
+            w.value = 85;
+            h.value = 60;
+            xoff.value = 100;
+            yoff.value = 100;
+        }
+        gop_click_count++;
+    }
+
+    function set_gop(state) {
+        var gop_opts, no_gop_opts;
+        if (state === true && gop_click_count === 0) {
+            show_sane_defaults();
+        }
+        gop_opts = document.getElementsByClassName('gop_opt');
+        no_gop_opts = document.getElementsByClassName('no_gop_opt');
+        update_gop_form(gop_opts, state);
+        update_gop_form(no_gop_opts, state === false);
+    }
+
+
+    function substitute_space(arg) {
+        var fake_space = String.fromCharCode(11);
+        return arg.split(' ').join(fake_space);
+    }
+
+    function strip_problem_chars(arg) {
+        var problem_chars = [';', ',', '{', '}', '\\'];
+        var ret = arg;
+        for(var i = 0; i < problem_chars.length; i++) {
+            ret = ret.split(';').join('');
+        }
+        return ret;
+    }
+
+    function get_input(name) {
+        var val = document.getElementsByName(name)[0].value;
+        return val === 0 ? '0' : val;
+    }
+
+    // get a value from the garray attr array
+    function get_array_value(name, attrs) {
+        return attrs[attrs.indexOf(name) + 1];
+    }
+
+
+    function apply() {
+        var i, attrs;
+        pdgui.gui_post("we're applying shits!");
+
+        // Note: the "+" casts Boolean to Number
+        var gop = +document.getElementsByName('gop')[0].checked;
+        var hide_name = +document.getElementsByName('hide-name')[0].checked;
+
+        pdgui.pdsend([pd_object_callback, 'donecanvasdialog',
+            get_input('x-scale'),
+            get_input('y-scale'),
+            (gop + 2 * hide_name),
+            get_input('x1'),
+            get_input('y1'),
+            get_input('x2'),
+            get_input('y2'),
+            get_input('x-pix'),
+            get_input('y-pix'),
+            get_input('x-margin'),
+            get_input('y-margin'),
+            ].join(' '));
+
+        // Now send the array properties, in a separate
+        // message for each array
+        for (i = 0; i < pd_garray_attrs.length; i++) {
+            attrs = pd_garray_attrs[i];
+            name = get_array_value('array_name', attrs);
+            if (name.slice(0, 1) === '$') {
+                name = '#' + name.slice(1);
+            }
+            pdgui.pdsend([
+                get_array_value('array_gfxstub', attrs),
+                'arraydialog',
+                name,
+                get_array_value('array_size', attrs),
+                get_array_value('array_flags', attrs),
+                0, // create an array in a new graph -- we don't
+                   // need this in a prexisting graph
+                0, // xdraw-- not sure if this is still used
+                0, // ydraw-- not sure if this is still used
+                get_array_value('array_fill', attrs),
+                get_array_value('array_outline', attrs),
+            ].join(' '));
+
+        }
+/*
+    pd [concat $id donecanvasdialog \
+            $::dialog($vid:xscale) \
+            $::dialog($vid:yscale) \
+            [expr $::dialog($vid:graphme)+2*$::dialog($vid:hidetext)] \
+            $::dialog($vid:x1) \
+            $::dialog($vid:y1) \
+            $::dialog($vid:x2) \
+            $::dialog($vid:y2) \
+            $::dialog($vid:xpix) \
+            $::dialog($vid:ypix) \
+            $::dialog($vid:xmargin) \
+            $::dialog($vid:ymargin) \
+            \;]
+}
+*/
+    }
+
+    function cancel() {
+        var i, attrs, gfxstub;
+        pdgui.gui_post("closing the window at this point");
+//        window.close(true);
+        pdgui.pdsend(pd_object_callback + " cancel");
+        for (i = 0; i < pd_garray_attrs.length; i++) {
+            attrs = pd_garray_attrs[i];
+            gfxstub = attrs[attrs.indexOf("array_gfxstub") + 1];
+pdgui.gui_post("guistub is " + gfxstub);
+            pdgui.pdsend(gfxstub + " cancel");
+        }
+    }
+
+    function populate_array_form(arrays) {
+        var arrays_select, a_field = document.getElementById('arrays');
+        var i, opt;
+        a_field.classList.remove('hidden');
+        arrays_select = document.getElementById('arrays_select');
+        for (i = 0; i < arrays.length; i++) {
+            if (i > 0) {
+                // unhide select element if there's more than one array
+                arrays_select.classList.remove('hidden');
+            }
+            opt = document.createElement('option');
+            opt.setAttribute('value', i);
+            opt.textContent = 'Array #' + (i+1);
+            arrays_select.appendChild(opt);
+        }
+        array_choose(0);
+    }
+
+    // Set up arrays in an object
+    
+    function init_arrays(attr_arrays) {
+        pd_garray_attrs = attr_arrays; 
+        if (attr_arrays.length > 0) {
+            // populate form with first array
+            populate_array_form(attr_arrays);
+        }
+    }
+
+    // This gets called from the nw_create_window function in index.html
+    // It provides us with our window id from the C side.  Once we have it
+    // we can create the menu and register event callbacks
+    function register_canvas_id(gfxstub, attr_arrays) {
+        pd_object_callback = gfxstub;
+
+        // attr_arrays[0]: canvas properties
+        // attr_arrays[1...n-1]: array properties
+        for (var i = 0; i < attr_arrays.length; i+=2) {
+            pdgui.gui_post(attr_arrays[i] + ": " + attr_arrays[i+1]);
+        }
+        add_events(gfxstub);
+        // not sure that we need this for properties windows
+//        pdgui.canvas_map(gfxstub);
+        translate_form();
+        populate_form(attr_arrays[0]);
+        init_arrays(attr_arrays.slice(1));
+        // We don't turn on rendering of the "container" div until
+        // We've finished displaying all the spans and populating the
+        // labels and form elements.  That makes it more efficient and
+        // snappier, at least on older machines.
+        document.getElementsByClassName('container')[0].style.setProperty('display', 'inline');
+//        document.getElementsByClass("fumbles")[0].setAttribute('style', 'display: inline;');
+    }
+
+function tr_text(id) {
+    var elem = document.getElementById('iem.prop.' + id);
+    elem.textContent = l('iem.prop.' + id);
+}
+
+// Stop-gap translator
+function translate_form() {
+    var i
+    var elements = document.querySelectorAll('[data-i18n]');
+    for (i = 0; i < elements.length; i++) {
+        var data = elements[i].dataset.i18n;
+        if (data.slice(0,7) === '[title]') {
+            elements[i].title = l(data.slice(7));
+        } else {
+            elements[i].textContent = l(data);
+        }
+    }
+}
+
+function populate_form(attr_array) {
+
+    // First, let's put the translated text for the form labels:
+
+//    tr_text('heading.size');
+//    tr_text('heading.messages');
+//    tr_text('heading.label');
+//    tr_text('heading.colors');
+//    tr_prop('width');
+//    tr_tooltip('width');
+
+//    var headings = ["size", "messages", "label", "colors"];
+//    for (var i = 0; i < headings.length; i++) {
+//        var str = "iem.prop.heading." + headings[i];
+//        var heading = document.getElementById(str);
+//        heading.textContent = l(str);
+//    }
+
+    for(var i = 0; i < attr_array.length; i+=2) {
+        // Unhide the span with the class with the same name as the id
+        var prop_group = document.getElementsByClassName(attr_array[i])[0];
+        if (prop_group !== undefined) {
+            console.log("the thing here is " + attr_array[i]);
+            prop_group.classList.remove('hidden');
+        } else {
+            pdgui.gui_post("Error: couldn't find iemgui prop group for " + attr_array[i]);
+        }
+
+        if (attr_array[i] === 'display-flags') {
+            // protip: '!!' forces Boolean, '+' forces Number type
+            var flag = +attr_array[i+1];
+            document.getElementsByName('gop')[0].checked = !!flag;
+            document.getElementsByName('hide-name')[0].checked = !!(flag & 2);
+            // Set the gop-related parts of the form to be enabled/disabled based on state
+            set_gop(!!flag);
+        }            
+
+        var elem = document.getElementsByName(attr_array[i]);
+        if (elem.length > 0) {
+            if(attr_array[i].slice(-5) === 'color') {
+                var hex_string = Number(attr_array[i+1]).toString(16);
+                var color_string = "#" + (hex_string === '0' ? '000000' : hex_string);
+                pdgui.gui_post("color is " + color_string);
+                elem[0].value = color_string;
+            } else if (elem[0].type === 'checkbox') {
+                // The attr here is a string, so we need to
+                // force it to number, hence the "+" below
+                gui_post("found a CHECKED ITEM!!!");
+                elem[0].checked = +attr_array[i+1];
+            } else {
+                elem[0].value = attr_array[i+1];
+            }
+        }
+    }
+}
+
+function add_events(name) {
+    // let's handle some events for this window...
+
+    // closing the Window
+    nw.Window.get().on("close", function() {
+        // this needs to do whatever the "cancel" button does
+//        pdgui.pdsend(name + " menuclose 0");
+//        cancel();
+        pdgui.remove_dialogwin(pd_object_callback);
+        this.close(true);
+    });
+
+}
+
+  </script>
+  </body>
+</html>
diff --git a/pd/nw/dialog_iemgui.css b/pd/nw/dialog_iemgui.css
new file mode 100644
index 000000000..b62808614
--- /dev/null
+++ b/pd/nw/dialog_iemgui.css
@@ -0,0 +1,113 @@
+@font-face {
+    font-family: "DejaVu Sans Mono";
+    src: url("DejaVuSansMono.ttf");
+}
+
+body {
+    font-family:Verdana;
+    margin: 0px;
+}
+
+fieldset {
+    font-family:Georgia;
+    background-color:#eeeeee;
+    border-radius:3px;
+    border:2px solid black;
+    margin-left:auto;
+    margin-right:auto;
+    padding: 10px;
+}
+
+input[type="text"]{
+    width:3em;
+}
+
+input[type="number"]{
+    width:3em;
+}
+
+
+.prop{
+}
+
+.hidden {
+    display: none;
+}
+
+.container{
+    display: none;
+}
+
+
+
+body {
+}
+
+.noselect {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+text {
+    // fill: red;
+    cursor: default;
+}
+
+.selected_border {
+    stroke: blue;
+    stroke-dasharray: none;
+    stroke-width: 1;
+}
+
+label {
+    text-align: right;
+//    margin-right: auto;
+//    margin-left: auto;
+}
+
+.pair {
+    width: 75%;
+    text-align: left;
+    align: left;
+}
+
+.item1 {
+    width: 50%;
+}
+
+.item2 {
+    width: 50%;
+}
+
+input[name="x-offset"] {
+    width: 2em;
+}
+
+input[name="y-offset"] {
+    width: 2em;
+}
+
+input[name="send-symbol"] {
+    width: 8em;
+}
+
+input[name="receive-symbol"] {
+    width: 8em;
+}
+
+input[name="label"] {
+    width: 8em;
+}
+
+input[name="font-size"] {
+    width: 3em;
+}
+
+.submit_buttons {
+    text-align: center;
+    padding: 8px;
+}
diff --git a/pd/nw/dialog_iemgui.html b/pd/nw/dialog_iemgui.html
new file mode 100644
index 000000000..1db283656
--- /dev/null
+++ b/pd/nw/dialog_iemgui.html
@@ -0,0 +1,604 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="dialog_iemgui.css">
+  </head>
+  <body>
+    <div class="container">
+    <form> 
+      <fieldset> 
+        <legend data-i18n="iem.prop.heading.size"></legend> 
+
+        <table class="pairs">
+          <tr class="size prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.size_tt">
+                <span data-i18n="iem.prop.size"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.size_tt">
+              <input type="text" name="size">
+            </td>
+          </tr>
+          <tr class="selection-size prop hidden">
+            <td>
+              <label data-i18n="[title]iem.select_size_tt">
+                <span data-i18n="iem.prop.select_size"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.select_size_tt">
+              <input type="text" name="selection-size">
+            </td>
+          </tr>
+          <tr class="number prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.number_tt">
+                <span data-i18n="iem.prop.number"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.number_tt">
+              <input type="number" name="number">
+            </td>
+          </tr>
+          <tr class="nonzero-value prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.nonzero_value_tt">
+                <span data-i18n="iem.prop.nonzero_value"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.nonzero_value_tt">
+              <input type="text" name="nonzero-value">
+            </td>
+          </tr>
+          <tr class="width prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.width_tt"> 
+                <span data-i18n="iem.prop.width"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.width_tt">
+              <input type="text" name="width">
+            </td>
+            <td>
+              <label data-i18n="[title]iem.prop.height_tt">
+                <span data-i18n="iem.prop.height"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.height_tt">
+              <input type="text" name="height">
+            </td>
+          </tr>
+          <tr class="visible-width prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.visible_width_tt">
+                <span data-i18n="iem.prop.visible_width"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.visible_width_tt">
+              <input type="text" name="visible-width">
+            </td>
+            <td>
+              <label data-i18n="iem.prop.visible_height">
+                <span data-i18n="iem.prop.visible_height"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.visible_height_tt">
+              <input type="text" name="visible-height">
+            </td>
+          </tr>
+          <tr class="minimum-range prop pair hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.minimum_tt">
+                <span data-i18n="iem.prop.minimum"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.minimum_tt">
+              <input type="text" name="minimum-range">
+            </td>
+            <td>
+              <label data-i18n="[title]iem.prop.maximum_tt">
+                <span data-i18n="iem.prop.maximum"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.maximum_tt">
+              <input type="text" name="maximum-range">
+            </td>
+          </tr>
+          <tr class="flash-interrupt prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.flash_interrupt_tt">
+                <span data-i18n="iem.prop.flash_interrupt"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.flash_interrupt_tt">
+              <input type="text" name="flash-interrupt">
+            </td>
+            <td>
+              <label data-i18n="[title]iem.prop.flash_hold_tt">
+                <span data-i18n="iem.prop.flash_hold"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.flash_hold_tt">
+              <input type="text" name="flash-hold">
+            </td>
+          </tr>
+          <tr class="log-height prop hidden">
+            <td></td><td></td>
+            <td>
+              <label data-i18n="[title]iem.prop.log_height_tt">
+                <span data-i18n="iem.prop.log_height"></span>
+              </label>
+            </td>
+            <td>
+              <input type="text" name="log-height">
+            </td>
+          </tr>
+        </table>
+
+        <div class="init prop hidden">
+          <label data-i18n="[title]iem.prop.init_tt">
+            <input type="checkbox" name="init" value="on">
+            <span data-i18n="iem.prop.init"></span>
+          </label>
+          <br>
+        </div>
+
+        <div class="vu-scale prop hidden">
+          <label data-i18n="[title]iem.prop.vu_scale_tt">
+            <span data-i18n="iem.prop.vu_scale"></span>
+            <input type="checkbox" name="vu-scale" value="on">
+          </label>
+          <br>
+        </div>
+
+        <div class="log-scaling prop hidden">
+          <label data-i18n="[title]iem.prop.log_scale_tt">
+            <input type="checkbox" name="log-scaling" value="on">
+            <span data-i18n="iem.prop.log_scale"></span>
+          </label>
+          <br>
+        </div>
+
+        <div class="steady-on-click prop hidden">
+          <label data-i18n="[title]iem.prop.steady_tt">
+            <input type="checkbox" name="steady-on-click" value="on">
+            <span data-i18n="iem.prop.steady"></span>
+          </label>
+          <br>
+        </div>
+      </fieldset> 
+
+      <fieldset> 
+        <legend data-i18n="iem.prop.heading.messages"></legend> 
+
+        <table>
+          <tr class="send-symbol prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.send_tt">
+                <span data-i18n="iem.prop.send"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.send_tt">
+              <input type="text" name="send-symbol">
+            </td>
+            <td>
+          <tr class="receive-symbol prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.receive_tt">
+                <span data-i18n="iem.prop.receive"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.receive_tt">
+              <input type="text" name="receive-symbol">
+            </td>
+            <td>
+          </tr>
+        </table>
+      </fieldset> 
+
+      <fieldset> 
+        <legend data-i18n="iem.prop.heading.label">wrong stuff</legend> 
+
+        <table class="pairs">
+          <tr class="label prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.label_tt">
+                <span data-i18n="iem.prop.label"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.label_tt">
+              <input type="text" name="label">
+            </td>
+            <td>
+              <label data-i18n="[title]iem.prop.xoffset_tt">
+                <span data-i18n="iem.prop.xoffset"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.xoffset_tt">
+              <input type="text" name="x-offset">
+            </td>
+            <td>
+              <label data-i18n="[title]iem.prop.yoffset_tt">
+                <span data-i18n="iem.prop.yoffset"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.yoffset_tt">
+              <input type="text" name="y-offset">
+            </td>
+          </tr>
+          <tr class="font-style prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.font_tt">
+                <span data-i18n="iem.prop.font"></span>
+            </td>
+            <td data-i18n="[title]iem.prop.font_tt">
+              <select name="font-style">
+                <option>Font Number 0</option>
+                <option>Font #1</option>
+                <option>Font 2</option>
+              </select>
+            </td>
+            <td colspan="4">
+              <label data-i18n="[title]iem.prop.fontsize_tt">
+                <span data-i18n="iem.prop.fontsize"></span>
+                <input type="text" name="font-size">
+              <label>
+            </td>
+          </tr>
+        </table>
+      </fieldset> 
+
+      <fieldset> 
+      <legend data-i18n="iem.prop.heading.colors"></legend> 
+
+      <div class="background-color prop hidden">
+        <label data-i18n="[title]iem.prop.bgcolor_tt">
+          <input type="color" name="background-color">
+          <span data-i18n="iem.prop.bgcolor"></span>
+        </label>
+        <br>
+      </div>
+
+      <div class="foreground-color prop hidden">
+        <label data-i18n="[title]iem.prop.fgcolor_tt">
+          <input type="color" name="foreground-color">
+          <span data-i18n="iem.prop.fgcolor"></span>
+        </label>
+        <br>
+      </div>
+
+      <div class="label-color prop hidden">
+        <label data-i18n="[title]iem.prop.label_color_tt">
+          <input type="color" name="label-color">
+          <span data-i18n="iem.prop.label_color"></span>
+        </label>
+        <br>
+      </div>
+    </fieldset> 
+
+    <div class="prop hidden">
+      <input type="hidden" name="minimum-size">
+      <input type="hidden" name="range-schedule">
+      <input type="hidden" name="hide-frame">
+    </div>
+
+    <div class="submit_buttons">
+      <button type="button" onClick="ok()" data-i18n="[title]iem.prop.ok_tt">
+        <span data-i18n="iem.prop.ok"></span>
+      </button>
+      <button type="button" onClick="apply()" data-i18n="[title]iem.prop.apply_tt">
+        <span data-i18n="iem.prop.apply"></span>
+      </button>
+      <button type="button" onClick="cancel()" data-i18n="[title]iem.prop.cancel_tt">
+        <span data-i18n="iem.prop.cancel"></span>
+      </button>
+    </div>
+
+  </form> 
+  </div>      
+
+  <script>
+    'use strict';
+    var nw = require('nw.gui'); 
+    var pdgui = require('./pdgui.js');
+
+    // For translations
+    var l = pdgui.get_local_string;
+
+    console.log("my working dire is " + pdgui.get_pwd());
+
+    var pd_object_callback;
+
+    function ok() {
+        apply();
+        cancel();
+    }
+
+//    function toggler(evt) {
+//        evt.value = evt.checked ? 1 : 0; 
+//    }
+
+    function substitute_space(arg) {
+        var fake_space = String.fromCharCode(11);
+        return arg.split(' ').join(fake_space);
+    }
+
+    function strip_problem_chars(arg) {
+        var problem_chars = [';', ',', '{', '}', '\\'];
+        var ret = arg;
+        for(var i = 0; i < problem_chars.length; i++) {
+            ret = ret.split(';').join('');
+        }
+        return ret;
+    }
+
+    function apply() {
+        pdgui.gui_post("we're applying shits!");
+
+        /* Not sure what these are...
+            iemgui_clip_dim $id
+            iemgui_clip_num $id
+            iemgui_sched_rng $id
+            iemgui_verify_rng $id
+            iemgui_sched_rng $id
+            iemgui_clip_fontsize $id
+        */
+
+
+
+        var send_symbol = document.getElementsByName('send-symbol')[0].value;
+        var receive_symbol = document.getElementsByName('receive-symbol')[0].value;
+        var label =  document.getElementsByName('label')[0].value;
+        if (send_symbol === null || send_symbol === '') { send_symbol = 'empty'; }
+        if (receive_symbol === null || receive_symbol === '') { receive_symbol = 'empty'; }
+        if (label === null || label === '') { label = 'empty'; }
+
+        console.log("send_symbol is " + send_symbol);
+
+        if (send_symbol.charAt(0) === '$') {
+            send_symbol = '#' + send_symbol.slice(1);
+        }
+        if (receive_symbol.charAt(0) === '$') {
+            receive_symbol = '#' + receive_symbol.slice(1);
+        }
+        if (label.charAt(0) === '$') {
+            label = '#' + label.slice(1);
+        }
+
+        send_symbol = substitute_space(send_symbol);
+        receive_symbol = substitute_space(receive_symbol);
+        label = substitute_space(label);
+
+        send_symbol = strip_problem_chars(send_symbol);
+        receive_symbol = strip_problem_chars(receive_symbol);
+        label = strip_problem_chars(label);
+
+        var label_x_offset =  document.getElementsByName('x-offset')[0].value;
+        var label_y_offset =  document.getElementsByName('y-offset')[0].value;
+
+	// make sure the offset boxes have a value
+        if (label_x_offset === null) { label_x_offset = 0; }
+        if (label_y_offset === null) { label_y_offset = 0; }
+
+        var height, width;
+        var size = document.getElementsByName('size')[0].value;
+        if (size === '') {
+            var size = document.getElementsByName('selection-size')[0].value;
+        }
+
+        if (size !== '') {
+            width = size;
+            height = size;
+        } else {
+            width = document.getElementsByName('width')[0].value;
+            height = document.getElementsByName('height')[0].value;
+        }
+
+        var slot3 = document.getElementsByName('minimum-range')[0].value;
+        var slot4 = document.getElementsByName('maximum-range')[0].value;
+
+        if (slot3 === '') {
+            slot3 = document.getElementsByName('flash-interrupt')[0].value;
+            slot4 = document.getElementsByName('flash-hold')[0].value;
+        }
+
+        if (slot3 === '') {
+            slot3 = document.getElementsByName('visible-width')[0].value;
+            slot4 = document.getElementsByName('visible-height')[0].value;
+        }
+
+        if (slot3 === '') { // toggle
+            slot3 = document.getElementsByName('nonzero-value')[0].value;
+            if (slot3 === '') {
+                slot3 = 0;
+            }
+            slot4 = 0;
+        }
+
+        var slot5 = +document.getElementsByName('log-scaling')[0].checked;
+        // Hack to accomodate the vu-scale property, which exists in the same
+        // slot as this one
+        var log_scaling_spanner = document.getElementsByClassName('log-scaling')[0];
+        var log_display = log_scaling_spanner.style.getPropertyValue('display');
+
+        if (log_display === null) {
+            slot5 = +document.getElementsByName('vu-scale')[0].checked;
+            pdgui.gui_post('slot five is ' + slot5);
+        }
+        pdgui.gui_post('slot five is ' + slot5);
+
+        var init = +document.getElementsByName('init')[0].checked;
+        if (init === '') { init = 0; }
+
+        var slot7 = document.getElementsByName('log-height')[0].value;
+        if (slot7 === '') {
+            slot7 = document.getElementsByName('number')[0].value;
+        }
+        if (slot7 === '') {
+            slot7 = 0;
+        }
+
+        var font_style = document.getElementsByName('font-style')[0].value;
+        if (font_style !== null) { font_style = 0; }
+
+        var font_size = document.getElementsByName('font-size')[0].value;
+        if (font_size === '') { font_size = 0; }
+
+        var foreground_color = parseInt(document.getElementsByName('foreground-color')[0].value.slice(1), 16);
+        var background_color = parseInt(document.getElementsByName('background-color')[0].value.slice(1), 16);
+        var label_color = parseInt(document.getElementsByName('label-color')[0].value.slice(1), 16);
+
+        var slot18 = +document.getElementsByName('steady-on-click')[0].checked;
+
+        pdgui.pdsend([pd_object_callback, 'dialog',
+            width, height,
+            slot3, // bng: flash-interrupt
+                   // slider: min-range
+                   // toggle: nonzero-value
+                   // my_canvas: visible_width
+            slot4, // bng: flash-hold
+                   // slider: max-range
+                   // my_canvas: visible_height
+            slot5, // slider: lin/log thingy
+                   // nbx: lin/log
+                   // vu: vu-scale
+            init,
+            slot7, // log-height or vradio/hradio number
+            send_symbol, receive_symbol, label,
+            label_x_offset, label_y_offset,
+            font_style, font_size,
+            background_color, foreground_color,
+            label_color,
+            slot18, // steady on click
+            0].join(' '));
+/*
+	pd [concat $id dialog \
+		$::dialog($vid:wdt) $::dialog($vid:hgt) \
+		$::dialog($vid:min_rng) $::dialog($vid:max_rng) \
+		$::dialog($vid:lin0_log1) $::dialog($vid:loadbang) \
+		$::dialog($vid:num) \
+		$hhhsnd $hhhrcv $hhhgui_nam \
+		$::dialog($vid:gn_dx) $::dialog($vid:gn_dy) \
+		$::dialog($vid:gn_f) $::dialog($vid:gn_fs) \
+		$::dialog($vid:bcol) $::dialog($vid:fcol) \
+		$::dialog($vid:lcol) \
+		$::dialog($vid:steady) $::dialog($vid:hide) \;]
+*/
+
+    }
+
+    function cancel() {
+        pdgui.gui_post("closing the window at this point");
+//        window.close(true);
+        pdgui.pdsend(pd_object_callback + " cancel");
+    }
+
+    // This gets called from the nw_create_window function in index.html
+    // It provides us with our window id from the C side.  Once we have it
+    // we can create the menu and register event callbacks
+    function register_canvas_id(gfxstub, attr_array) {
+        pd_object_callback = gfxstub;
+
+        console.log('attr array is ' + attr_array.toString());
+        for (var i = 0; i < attr_array.length; i+=2) {
+            console.log(attr_array[i] + ": " + attr_array[i+1]);
+        }
+        add_events(gfxstub);
+        // not sure that we need this for properties windows
+//        pdgui.canvas_map(gfxstub);
+        translate_form();
+        populate_form(attr_array);
+        // We don't turn on rendering of the "container" div until
+        // We've finished displaying all the spans and populating the
+        // labels and form elements.  That makes it more efficient and
+        // snappier, at least on older machines.
+        document.getElementsByClassName('container')[0].style.setProperty('display', 'inline');
+//        document.getElementsByClass("fumbles")[0].setAttribute('style', 'display: inline;');
+    }
+
+function tr_text(id) {
+    var elem = document.getElementById('iem.prop.' + id);
+    elem.textContent = l('iem.prop.' + id);
+}
+
+// Stop-gap translator
+function translate_form() {
+    var i
+    var elements = document.querySelectorAll('[data-i18n]');
+    for (i = 0; i < elements.length; i++) {
+        var data = elements[i].dataset.i18n;
+        if (data.slice(0,7) === '[title]') {
+            elements[i].title = l(data.slice(7));
+        } else {
+            elements[i].textContent = l(data);
+        }
+    }
+}
+
+function populate_form(attr_array) {
+
+    // First, let's put the translated text for the form labels:
+
+//    tr_text('heading.size');
+//    tr_text('heading.messages');
+//    tr_text('heading.label');
+//    tr_text('heading.colors');
+//    tr_prop('width');
+//    tr_tooltip('width');
+
+//    var headings = ["size", "messages", "label", "colors"];
+//    for (var i = 0; i < headings.length; i++) {
+//        var str = "iem.prop.heading." + headings[i];
+//        var heading = document.getElementById(str);
+//        heading.textContent = l(str);
+//    }
+
+    for(var i = 0; i < attr_array.length; i+=2) {
+        // Unhide the span with the class with the same name as the id
+        var prop_group = document.getElementsByClassName(attr_array[i])[0];
+        if (prop_group !== undefined) {
+            console.log("the thing here is " + attr_array[i]);
+            prop_group.classList.remove('hidden');
+        } else {
+            pdgui.gui_post("Error: couldn't find iemgui prop group for " + attr_array[i]);
+        }
+        // iemguis use the string 'empty' for null because of
+        // the limitations of Pd's state-saving API.  So we have
+        // to filter that one out
+        if(attr_array[i+1] !== 'empty') {
+            var elem = document.getElementsByName(attr_array[i]);
+            if (elem.length > 0) {
+                if(attr_array[i].slice(-5) === 'color') {
+                    var hex_string = Number(attr_array[i+1]).toString(16);
+                    var color_string = "#" + (hex_string === '0' ? '000000' : hex_string);
+                    pdgui.gui_post("color is " + color_string);
+                    elem[0].value = color_string;
+                } else if (elem[0].type === 'checkbox') {
+                    // The attr here is a string, so we need to
+                    // force it to number, hence the "+" below
+                    elem[0].checked = +attr_array[i+1];
+                } else {
+                    elem[0].value = attr_array[i+1];
+                }
+            }
+        }
+    }
+}
+
+function add_events(name) {
+    // let's handle some events for this window...
+
+    // closing the Window
+    nw.Window.get().on("close", function() {
+        // this needs to do whatever the "cancel" button does
+//        pdgui.pdsend(name + " menuclose 0");
+//        cancel();
+        pdgui.remove_dialogwin(pd_object_callback);
+        this.close(true);
+    });
+
+}
+
+  </script>
+  </body>
+</html>
diff --git a/pd/nw/index.html b/pd/nw/index.html
index 46a70896f..acf725e54 100644
--- a/pd/nw/index.html
+++ b/pd/nw/index.html
@@ -494,7 +494,8 @@ function nw_create_window(cid, type, width, height, xpos, ypos, menu_flag, resiz
     canvas_string, dir, dirty_flag, cargs, attr_array) {
         // todo: make a separate way to format the title for OSX
     var my_title =  pdgui.format_window_title(canvas_string, dirty_flag, cargs, dir);
-    var my_file = (type === 'pd-canvas' ? 'pdcanvas.html' : 'pdproperties.html');
+    var my_file =
+        type === 'pd_canvas' ? 'pd_canvas.html' : 'dialog_' + type + '.html';
 
     var new_win = nw.Window.open(my_file, {
         title: my_title,
@@ -506,7 +507,8 @@ function nw_create_window(cid, type, width, height, xpos, ypos, menu_flag, resiz
         x: xpos,
         y: ypos
     });
-    var eval_string = "register_canvas_id('" + cid + "', [" + attr_array + "]);";
+    pdgui.gui_post("attr_array is " + attr_array);
+    var eval_string = "register_canvas_id(" + JSON.stringify(cid) + ", " + JSON.stringify(attr_array) + ");";
     pdgui.gui_post("eval string is " + eval_string);
     if (attr_array !== null) {
         pdgui.gui_post("attr_array is " + attr_array.toString());
diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index 0962fd7e2..f96cbaf5a 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -210,5 +210,64 @@
     "forums_tt": "Open a link in a browser for the Pd Forum",
     "irc": "IRC Chat",
     "irc_tt": "Open a link in a browser for IRC Chat"
+  },
+  "canvas": {
+    "prop": {
+      "heading": {
+        "gop": "appearance on parent",
+        "data_scaling": "data scaling",
+        "size": "size",
+        "viewbox_offsets": "viewbox offsets",
+        "arrays": "array options"
+      },
+      "gop": "graph on parent",
+      "gop_tt": "show the inner contents of this canvas in a rectangle on the containing canvas",
+      "hide_name": "hide object name and arguments",
+      "hide_name_tt": "blah blah blah",
+      "gop_0": "Object",
+      "gop_1": "Window",
+      "gop_2": "Window (no text)",
+      "x_pix": "width",
+      "x_pix_tt": "width of the object",
+      "y_pix": "height",
+      "y_pix_tt": "height of the object",
+      "x_scale": "x scale",
+      "x_scale_tt": "horizontal scaling factor",
+      "y_scale": "y scale",
+      "y_scale_tt": "vertical scaling factor",
+      "x_margin": "x offset",
+      "x_margin_tt": "horizontal offset into the subpatch for the view area",
+      "y_margin": "y offset",
+      "y_margin_tt": "vertical offset into the subpatch for the view area",
+      "x1": "left",
+      "x1_tt": "lowest x value (left edge)",
+      "x2": "right",
+      "x2_tt": "highest x value (right edge)",
+      "y1": "top",
+      "y1_tt": "highest y value (top)",
+      "y2": "bottom",
+
+      "array_name": "name",
+      "array_name_tt": "receiver name for the array",
+      "array_size": "size",
+      "array_size_tt": "number of elements in the array",
+      "array_polygon": "polygon",
+      "array_polygon_tt": "the array trace is drawn as a polygon",
+      "array_points": "points",
+      "array_points_tt": "the array trace is drawn as horizontal line segments",
+      "array_bezier": "Bezier curve",
+      "array_bezier_tt": "the array trace is drawn as a Bezier curve",
+      "array_bars": "bar graph",
+      "array_bars_tt": "each element of the array is drawn as a bar in a bar graph",
+      "array_save": "save contents",
+      "array_save_tt": "save array contents with this patch",
+      "array_jump": "jump on click",
+      "array_jump_tt": "update the element to the position of the click",
+      "array_style": "draw as:",
+      "array_outline": "outline color",
+      "array_outline_tt": "color for outline around the bars",
+      "array_fill": "fill color",
+      "array_fill_tt": "inner color of the bars"
+    }
   }
 }
diff --git a/pd/nw/pd_canvas.css b/pd/nw/pd_canvas.css
new file mode 100644
index 000000000..f78ee752b
--- /dev/null
+++ b/pd/nw/pd_canvas.css
@@ -0,0 +1,102 @@
+@font-face {
+    font-family: "DejaVu Sans Mono";
+    src: url("DejaVuSansMono.ttf");
+}
+
+body {
+    margin: 0px;
+    font-family: "DejaVu Sans Mono";
+}
+
+.noselect {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+text {
+    // fill: red;
+    cursor: default;
+}
+
+.selected_border {
+    stroke: blue;
+    stroke-dasharray: none;
+    stroke-width: 1;
+}
+
+/* a message when it flashes on a mouse-click */
+.msg.flashed .border {
+    stroke-width: 4;
+}
+
+/* Border color for selected objects
+   In plain English:
+       Any element with a class 'border' that is contained inside
+       a parent element that is not in class 'gop' but is
+       in class 'selected' (whew!)
+*/
+:not(.gop).selected .border {
+    stroke: blue;
+}
+
+.selected_line {
+    stroke: blue;
+}
+
+.broken_border {
+    fill: #f7f7f7;
+    stroke: red;
+    stroke-dasharray: 3 2;
+}
+
+.xlet_control {
+    stroke: red;
+    fill: gray;
+//    stroke-width: 1;
+}
+
+.xlet_signal {
+    stroke: green;
+    fill: green;
+    stroke-width: 1;
+}
+
+.xlet_iemgui {
+    stroke: black;
+    fill: black;
+    stroke-width: 1;
+}
+
+.iemgui_label_selected {
+    fill: blue;
+}
+
+@-webkit-keyframes fizzle {
+    0% {
+        stroke-width: 1;
+        stroke-opacity: 1;
+        x: 0;
+        rx: 1;
+        ry: 1;
+    }
+    100% {
+        stroke-width: 20;
+        stroke-opacity: 0.2;
+        x: 100;
+        rx: 50;
+        ry: 50;
+    }
+}
+
+.xlet_selected {
+    stroke: purple !important;
+    -webkit-animation: fizzle 0.5s linear 1;
+}
+
+//.xlet:hover {
+//    stroke: red;
+//}
diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html
new file mode 100644
index 000000000..d823ab07c
--- /dev/null
+++ b/pd/nw/pd_canvas.html
@@ -0,0 +1,1125 @@
+<!DOCTYPE html>
+<html>
+  <head>
+  <link rel="stylesheet" type="text/css" href="pd_canvas.css">
+  </head>
+  <body>
+  <input style="display:none;" id="fileDialog" type="file" multiple />
+  <input style="display:none;" id="saveDialog" type="file" nwsaveas nwworkingdir accept=".pd" />
+
+  <input style="display:none;" id="openpanel_dialog" type="file" />
+  <input style="display:none;" id="savepanel_dialog" type="file" nwsaveas nwworkingdir />
+
+
+  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="patchsvg" height="1000" width="1000" class="noselect">
+  </svg>
+  <script>
+    'use strict';
+    var nw = require('nw.gui'); 
+    var pdgui = require('./pdgui.js');
+
+    //var name = pdgui.last_loaded();
+    
+    var l = pdgui.get_local_string;
+
+    console.log("my working dire is " + pdgui.get_pwd());
+    document.getElementById("saveDialog").setAttribute("nwworkingdir", pdgui.get_pwd());
+
+    document.getElementById("fileDialog").setAttribute("nwworkingdir", pdgui.get_pwd());
+    document.getElementById("fileDialog").setAttribute("accept",
+        Object.keys(pdgui.pd_filetypes).toString());
+
+    var last_keydown = "";
+
+    // This gets called from the nw_create_window function in index.html
+    // It provides us with our canvas id from the C side.  Once we have it
+    // we can create the menu and register event callbacks
+    function register_canvas_id(cid) {
+console.log("fuck you");
+        create_popup_menu(cid);
+        add_events(cid);
+        nw_create_patch_window_menus(cid);
+        pdgui.canvas_map(cid);
+    }
+
+
+
+// This could probably be in pdgui.js
+function add_keymods(key, evt) {
+    var shift = evt.shiftKey ? "Shift" : "";
+    var ctrl = evt.ctrlKey ? "Ctrl" : "";
+    return shift + ctrl + key;
+}
+
+function create_popup_menu(name) {
+    // The right-click popup menu
+    var popup_menu = new nw.Menu();
+    pdgui.add_popup(name, popup_menu);
+
+    popup_menu.append(new nw.MenuItem({
+        label: 'Properties',
+        click: function() {
+            pdgui.popup_action(name, 0);
+        }
+    }));
+    popup_menu.append(new nw.MenuItem({
+        label: 'Open',
+        click: function() {
+            pdgui.popup_action(name, 1);
+        }
+    }));
+    popup_menu.append(new nw.MenuItem({
+        label: 'Help',
+        click: function() {
+            pdgui.popup_action(name, 2);
+        }
+    }));
+}
+
+function add_events(name) {
+    document.querySelector("#saveDialog").addEventListener("change", function(evt) {
+        pdgui.saveas_callback(name, this.value);
+        console.log("tried to open something");
+    }, false);
+
+
+    document.querySelector("#fileDialog").addEventListener("change", function(evt) {
+        var file_array = this.value;
+        // reset value so that we can open the same file twice
+        this.value = null;
+        pdgui.menu_open(file_array);
+        console.log("tried to open something");
+    }, false);
+
+    document.querySelector("#openpanel_dialog").addEventListener("change",
+        function(evt) {
+            var file_string = this.value;
+            // reset value so that we can open the same file twice
+            this.value = null;
+            pdgui.file_dialog_callback(file_string);
+            console.log("tried to openpanel something");
+        }, false
+    );
+
+    document.querySelector("#savepanel_dialog").addEventListener("change",
+        function(evt) {
+            var file_string = this.value;
+            // reset value so that we can open the same file twice
+            this.value = null;
+            pdgui.file_dialog_callback(file_string);
+            console.log("tried to savepanel something");
+        }, false
+    );
+
+
+    document.addEventListener("mousemove", function(evt) {
+        //pdgui.gui_post("x: " + evt.pageX + " y: " + evt.pageY +
+        //    " modifier: " + (evt.shiftKey + (evt.ctrlKey << 1)));
+        pdgui.pdsend(name + " motion " + evt.pageX + " " + evt.pageY + " " +
+            (evt.shiftKey + (evt.ctrlKey << 1)));
+        evt.stopPropagation();
+//        evt.preventDefault();
+        return false;
+    });
+
+    document.addEventListener("keydown", function(evt) {
+        var key_code = evt.keyCode; 
+        var hack = null; // hack for unprintable ascii codes
+        switch(key_code) {
+            case 8:
+            case 9:
+            case 10:
+            case 27:
+//            case 32:
+            case 127: hack = key_code; break;
+
+
+            case 37: hack = add_keymods('Left', evt); break;
+            case 38: hack = add_keymods('Up', evt); break;
+            case 39: hack = add_keymods('Right', evt); break;
+            case 40: hack = add_keymods('Down', evt); break;
+            case 33: hack = add_keymods('Prior', evt); break;
+            case 34: hack = add_keymods('Next', evt); break;
+            case 35: hack = add_keymods('End', evt); break;
+            case 36: hack = add_keymods('Home', evt); break;
+
+            // Need to handle Control key, Alt
+
+            case 16: hack = 'Shift'; break;
+            case 17: hack = 'Control'; break;
+            case 18: hack = 'Alt'; break;
+        }
+        if (hack !== null) {
+            pdgui.gui_canvas_sendkey(name, 1, evt, hack);
+            pdgui.set_keymap(key_code, hack);
+        }
+        pdgui.gui_post("keydown time: keycode is " + evt.keyCode);
+        last_keydown = evt.keyCode;
+        evt.stopPropagation();
+    });
+
+    document.addEventListener("keypress", function(evt) {
+        pdgui.gui_canvas_sendkey(name, 1, evt, evt.charCode);
+        pdgui.set_keymap(last_keydown, evt.charCode);
+        pdgui.gui_post("keypress time: charcode is " + evt.charCode);
+        // Don't do things like scrolling on space, arrow keys, etc.
+        evt.stopPropagation();
+        evt.preventDefault();
+    });
+
+    document.addEventListener("keyup", function(evt) {
+        var my_char_code = pdgui.get_char_code(evt.keyCode);
+        pdgui.gui_canvas_sendkey(name, 0, evt, my_char_code);
+        pdgui.gui_post("keyup time: charcode is: " + my_char_code);
+    });
+
+    // just left-clicks for the moment
+    document.onmousedown = function(evt) {
+        //pdgui.gui_post("mousedown: x: " + evt.pageX + " y: " + evt.pageY +
+        //    " button: " + (evt.button + 1) + " modifier: " + (evt.shiftKey + (evt.ctrlKey << 1)));
+        // tk events are one greater than html5...
+        var b = evt.button + 1;
+        var mod;
+        // For some reason right-click sends a modifier value of "8", and canvas_doclick in
+        // g_editor.c depends on that value to do the right thing.  So let's hack...
+        if (b === 3) { // right-click
+            mod = 8;
+        } else {
+            mod = (evt.shiftKey + (evt.ctrlKey << 1)); 
+        }
+        pdgui.pdsend(name + " mouse " + evt.pageX + " " + evt.pageY + " " +
+            b + " " + mod);
+        evt.stopPropagation();
+        evt.preventDefault();
+    }
+
+    document.onmouseup = function(evt) {
+        //pdgui.gui_post("mouseup: x: " + evt.pageX + " y: " + evt.pageY +
+        //    " button: " + (evt.button + 1));
+        pdgui.pdsend(name + " mouseup " + evt.pageX + " " + evt.pageY + " " +
+            (evt.button + 1));
+    }
+
+
+
+
+    // let's handle some events for this window...
+
+    // closing the Window
+    // this isn't actually closing the window yet
+    nw.Window.get().on("close", function() {
+        pdgui.pdsend(name + " menuclose 0");
+    });
+
+
+}
+
+
+
+
+
+
+//nw_create_patch_window_menus(name);
+
+        function menu_generic () {
+            alert("Please implement this");
+        }
+
+        function pdmenu_copy () {
+            alert("Please implement pdmenu_copy"); 
+        }
+
+        function pdmenu_selectall () {
+            alert("Please implement pdmenu_selectall"); 
+        }
+
+        function pdmenu_preferences () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_next_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_previous_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_parent_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_console_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_audio_on () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_audio_off () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_test_audio () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_load_meter () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_about_pd () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_manual () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_help_browser () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_l2ork_mailinglist () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_pd_mailinglists () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_forums () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_irc () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+// Menus for the Patch window
+function nw_create_patch_window_menus (name) {
+
+    // Window menu
+    var windowMenu = new nw.Menu({
+        type: 'menubar'
+    });
+
+    // File menu
+    var fileMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+        label: l('menu.file'),
+        submenu: fileMenu
+    }));
+
+    // File sub-entries
+    fileMenu.append(new nw.MenuItem({
+        label: l('menu.new'),
+        click: pdgui.menu_new,
+        key: 'n',
+        modifiers: "ctrl",
+        tooltip: l('menu.new_tt')
+    }));
+
+    fileMenu.append(new nw.MenuItem({
+        label: l('menu.open'),
+        key: 'o',
+        modifiers: "ctrl",
+        tooltip: l('menu.open_tt'),
+        click: function() {
+            var chooser = document.querySelector('#fileDialog');
+            chooser.click();
+        }
+    }));
+
+    if (pdgui.k12_mode == 1) {
+        fileMenu.append(new nw.MenuItem({
+        label: l('menu.k12_demos'),
+        tooltip: l('menu.k12_demos_tt'),
+        click: pdgui.menu_k12_open_demos
+        }));
+    }
+
+    fileMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    // Note: this must be different for the main Pd window
+    fileMenu.append(new nw.MenuItem({
+        label: l('menu.save'),
+        click: function () {
+            pdgui.menu_save(name);
+        },
+        key: 's',
+        modifiers: "ctrl",
+        tooltip: l('menu.save_tt')
+    }));
+
+    fileMenu.append(new nw.MenuItem({
+        label: l('menu.saveas'),
+        click: function (){
+            pdgui.menu_saveas(name);
+        },
+        key: 's',
+        modifiers: "ctrl+shift",
+        tooltip: l('menu.saveas_tt')
+    }));
+
+    if (pdgui.k12_mode == 0) {
+        fileMenu.append(new nw.MenuItem({
+            type: 'separator'
+        }));
+    }
+
+    fileMenu.append(new nw.MenuItem({
+        label: l('menu.message'),
+        click: pdgui.menu_send,
+        key: 'm',
+        modifiers: "ctrl",
+        tooltip: l('menu.message_tt')
+    }));
+
+    if (pdgui.k12_mode == 0) {
+        fileMenu.append(new nw.MenuItem({
+            type: 'separator'
+        }));
+    }
+
+    // recent files go here
+
+    // anther separator goes here if there are any recent files
+
+    fileMenu.append(new nw.MenuItem({
+        label: l('menu.close'),
+        tooltip: l('menu.close_tt'),
+        click: function() {
+            pdgui.menu_close(name);
+        }
+    }));
+
+    // Quit Pd
+    fileMenu.append(new nw.MenuItem({
+        label: l('menu.quit'),
+        click: pdgui.menu_quit,
+        key: 'q',
+        modifiers: "ctrl",
+        tooltip: l('menu.quit_tt')
+    }));
+
+
+    // Edit menu
+    var editMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: l('menu.edit'),
+    submenu: editMenu
+    }));
+
+    // Edit sub-entries
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.undo'),
+        click: menu_generic,
+        key: 'z',
+        modifiers: "ctrl",
+        tooltip: l('menu.undo_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.redo'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.redo_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.cut'),
+        click: function () {
+            pdgui.pdsend(name + " cut");
+        },
+        key: 'x',
+        modifiers: "ctrl",
+        tooltip: l('menu.cut_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.copy'),
+        click: function () {
+            pdgui.pdsend(name + " copy");
+        },
+        key: 'c',
+        modifiers: "ctrl",
+        tooltip: l('menu.copy_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.paste'),
+        click: function () {
+            pdgui.pdsend(name + " paste");
+        },
+        key: 'v',
+        modifiers: "ctrl",
+        tooltip: l('menu.paste_tt')
+    }));
+
+
+    editMenu.append(new nw.MenuItem({
+        label:  l('menu.duplicate'),
+        click: function () {
+            pdgui.pdsend(name + " duplicate");
+        },
+        key: 'd',
+        modifiers: "ctrl",
+        tooltip: l('menu.duplicate_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.selectall'),
+        click: function () {
+            pdgui.pdsend(name + " selectall");
+        },
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.selectall_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.reselect'),
+        // Unfortunately nw.js doesn't allow
+        // key: "Return" or key: "Enter", so we
+        // can't bind to ctrl-Enter here. (Even
+        // tried fromCharCode...)
+        click: function () {
+            pdgui.pdsend(name + " reselect");
+        },
+        key: String.fromCharCode(10),
+        modifiers: "ctrl",
+        tooltip: l('menu.reselect_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.zoomin'),
+        click: function () {
+            var z = nw.Window.get().zoomLevel;
+            if (z < 8) { z++; }
+            nw.Window.get().zoomLevel = z;
+            pdgui.gui_post("zoom level is " + nw.Window.get().zoomLevel);
+        },
+        key: '=',
+        modifiers: "ctrl",
+        tooltip: l('menu.zoomin')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.zoomout'),
+        click: function () {
+            var z = nw.Window.get().zoomLevel;
+            if (z > -7) { z--; } 
+            nw.Window.get().zoomLevel = z;
+            pdgui.gui_post("zoom level is " + nw.Window.get().zoomLevel);
+        },
+        key: '-',
+        modifiers: "ctrl",
+        tooltip: l('menu.zoomout_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.tidyup'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.tidyup_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.tofront'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.tofront_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.toback'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.toback_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.font'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.font_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.cordinspector'),
+        click: function() {
+            pdgui.pdsend(name + " magicglass 0");
+        },
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.cordinspector_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.find'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.find_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.findagain'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.findagain')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.finderror'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.finderror_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.autotips'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.autotips_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.editmode'),
+        click: function() { pdgui.pdsend(name + " editmode 0"); },
+        key: 'e',
+        modifiers: "ctrl",
+        tooltip: l('menu.editmode_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.preferences'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.preferences_tt')
+    }));
+
+    // Put menu
+    var putMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: l('menu.put'),
+    submenu: putMenu
+    }));
+
+    // Put menu sub-entries
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.object'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " obj 0");
+        },
+        key: '1',
+        modifiers: "ctrl",
+        tooltip: l('menu.object_tt'),
+    }));
+
+/*
+
+proc menu_floatatom {name accel} {
+	pd [concat $name dirty 1 \;]
+    pd [concat $name floatatom $accel \;]
+}
+
+proc menu_symbolatom {name accel} {
+	pd [concat $name dirty 1 \;]
+    pd [concat $name symbolatom $accel \;]
+}
+
+proc menu_comment {name accel} {
+	pd [concat $name dirty 1 \;]
+    pd [concat $name text $accel \;]
+}
+
+proc menu_graph {name} {
+	pd [concat $name dirty 1 \;]
+	set xdraw [expr int([$name.c canvasx 0])]
+	set ydraw [expr int([$name.c canvasy 0])]
+    pd [concat $name graph NULL 0 0 0 0 [expr $xdraw+30] [expr $ydraw+30] 0 [expr $ydraw+30]\;]
+    #pd [concat $name graph \;]
+}
+
+proc menu_array {name} {
+	pd [concat $name dirty 1 \;]
+    pd [concat $name menuarray \;]
+}
+
+
+*/
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.msgbox'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " msg 0");
+        },
+        key: '2',
+        modifiers: "ctrl",
+        tooltip: l('menu.msgbox_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.number'),
+        click: function() { 
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " floatatom 0");
+        },
+        key: '3',
+        modifiers: "ctrl",
+        tooltip: l('menu.number_tt')
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.symbol'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " symbolatom 0");
+        },
+        key: '4',
+        modifiers: "ctrl",
+        tooltip: l('menu.symbol_tt')
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.comment'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " text 0");
+        },
+        key: '5',
+        modifiers: "ctrl",
+        tooltip: l('menu.comment_tt')
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.bang'),
+        click: function(e) {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " bng 0");
+        },
+        key: 'b',
+        modifiers: "ctrl-shift",
+        tooltip: l('menu.bang_tt')
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.toggle'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " toggle 0");
+        },
+        key: 't',
+        modifiers: "ctrl-shift",
+        tooltip: l('menu.toggle_tt')
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.number2'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " numbox 0");
+        },
+        key: 'n',
+        modifiers: "ctrl-shift",
+        tooltip: l('menu.number2')
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.vslider'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " vslider 0");
+        },
+        key: 'v',
+        modifiers: "ctrl-shift",
+        tooltip: l('menu.vslider_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.hslider'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " hslider 0");
+        },
+        key: 'h',
+        modifiers: "ctrl-shift",
+        tooltip: l('menu.hslider_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.vradio'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " vradio 0");
+        },
+        key: 'd',
+        modifiers: "ctrl-shift",
+        tooltip: l('menu.vradio_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.hradio'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " hradio 0");
+        },
+        key: 'i',
+        modifiers: "ctrl",
+        tooltip: l('menu.hradio_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.vu'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " vumeter 0");
+        },
+        key: 'u',
+        modifiers: "ctrl",
+        tooltip: l('menu.vu_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.cnv'),
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " mycnv 0");
+        },
+        key: 'c',
+        modifiers: "ctrl-shift",
+        tooltip: l('menu.cnv_tt')
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.graph'),
+        click: function() {
+            pdgui.pdsend(name + " dirty 1");
+            // leaving out some placement logic... see pd.tk menu_graph
+            pdgui.pdsend(name + " graph NULL 0 0 0 0 30 30 0 30");
+        },
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.graph_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.array'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.array_tt'),
+    }));
+
+
+    // Windows menu... call it "winman" (i.e., window management) to avoid confusion
+    var winmanMenu = new nw.Menu();
+
+    // Add to windows menu
+    windowMenu.append(new nw.MenuItem({
+    label: l('menu.windows'),
+    submenu: winmanMenu
+    }));
+
+    // Winman sub-entries
+    winmanMenu.append(new nw.MenuItem({
+        label: l('menu.nextwin'),
+        click: menu_generic,
+        key: String.fromCharCode(12), // Page down
+        modifiers: "ctrl",
+        tooltip: l('menu.nextwin_tt'),
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: l('menu.prevwin'),
+        click: menu_generic,
+        key: String.fromCharCode(11), // Page up
+        modifiers: "ctrl",
+        tooltip: l('menu.prevwin_tt'),
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: l('menu.parentwin'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.parentwin_tt'),
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: l('menu.pdwin'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.pdwin_tt'),
+    }));
+
+    // Media menu
+    var mediaMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: l('menu.media'),
+    submenu: mediaMenu
+    }));
+
+    // Media sub-entries
+    mediaMenu.append(new nw.MenuItem({
+        label: l('menu.audio_on'),
+        click: menu_generic,
+        key: '/',
+        modifiers: "ctrl",
+        tooltip: l('menu.audio_on_tt'),
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: l('menu.audio_off'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.audio_off_tt'),
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: l('menu.test'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.test_tt'),
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: l('menu.loadmeter'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.loadmeter_tt'),
+    }));
+
+    // Help menu
+    var helpMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: l('menu.help'),
+    submenu: helpMenu
+    }));
+
+    // Help sub-entries
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.about'),
+        click: menu_generic,
+        //key: 'c',
+        //modifiers: "ctrl",
+        tooltip: l('menu.about_tt'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.manual'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.manual'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.browser'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.browser_tt'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.l2ork_list'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.l2ork_list_tt'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.pd_list'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.pd_list_tt'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.forums'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.forums_tt'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.irc'),
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl",
+        tooltip: l('menu.irc_tt'),
+    }));
+
+    // Assign to window
+    nw.Window.get().menu = windowMenu;
+
+}
+
+
+
+/*
+function nw_create_canvas_window_menus(name) {
+	// Window menu
+	var windowMenu = new nw.Menu;
+	var windowMenu = new nw.Menu({
+	    type: 'menubar'
+	});
+
+
+	// File menu
+	var fileMenu = new nw.Menu();
+
+	// Add to window menu
+	windowMenu.append(new nw.MenuItem({
+	    label: 'File',
+	    submenu: fileMenu
+	}));
+
+	// File sub-entries
+	fileMenu.append(new nw.MenuItem({
+	    label: 'New',
+	    click: pdgui.menu_new,
+	    key: 'n',
+	    modifiers: "ctrl"
+	}));
+
+	fileMenu.append(new nw.MenuItem({
+	    label: 'Open',
+	    key: 'o',
+	    modifiers: "ctrl",
+	    click: function (){
+		var chooser = document.querySelector('#fileDialog');
+		chooser.click();
+		chooser.addEventListener("change", function(evt) {
+		    menu_open(this.value);
+		    console.log("tried to open something");
+		}, false);
+	    }
+	}));
+
+	if (pdgui.k12_mode == 1) {
+	    fileMenu.append(new nw.MenuItem({
+		label: 'K12 Demos',
+		click: pdgui.menu_k12_open_demos
+	    }));
+	}
+
+	fileMenu.append(new nw.MenuItem({
+	    type: 'separator'
+	}));
+
+	// Note: this must be different for the main Pd window
+	fileMenu.append(new nw.MenuItem({
+	    label: 'Save',
+	    click: function (){
+		pdgui.menu_save(name);
+	    },
+	    key: 's',
+	    modifiers: "ctrl"
+	}));
+
+	fileMenu.append(new nw.MenuItem({
+	    label: 'Save as...',
+	    click: function (){
+		pdgui.menu_saveas(name);
+	    },
+	    key: 'S',
+	    modifiers: "ctrl"
+	}));
+
+	if (pdgui.k12_mode == 0) {
+	    fileMenu.append(new nw.MenuItem({
+		type: 'separator'
+	    }));
+	}
+
+	// Assign to window
+	nw.Window.get().menu = windowMenu;
+}
+*/
+
+  </script>
+  </body>
+</html>
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 62d144132..9400311c6 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -993,7 +993,7 @@ function gui_canvas_new(cid, width, height, geometry, editable, name, dir, dirty
     }
     last_loaded = cid;
     // Not sure why resize and topmost are here-- but we'll pass them on for the time being...
-    patchwin[cid] = nw_create_window(cid, 'pd-canvas', width, height, xpos, ypos, menu_flag,
+    patchwin[cid] = nw_create_window(cid, 'pd_canvas', width, height, xpos, ypos, menu_flag,
         resize[cid], topmost[cid], my_canvas_color, name, dir, dirty_flag, cargs, null);
      
     // initialize variable to reflect that this window has been opened
@@ -1006,6 +1006,14 @@ function canvas_map(name) {
     pdsend(name + " map 1");
 }
 
+function gui_canvas_erase_all_gobjs(cid) {
+    var top = get_item(cid, 'patchsvg');
+    var elem;
+    while (elem = top.firstChild) {
+        top.removeChild(elem);
+    }
+}
+
 exports.canvas_map = canvas_map;
 
 /*    
@@ -1709,16 +1717,29 @@ exports.connect = connect;
 
 // can be removed.
 
+var plums = require('string_decoder').StringDecoder;
+var rufus = new plums('utf8');
+
 var nextCmd = ""; // Build up a command across lines (or buffers)
+var cmdHeader = 0;
 
 function init_socket_events () {
 	client.on('data', function(data) {
 	//    console.log('DATA: ' + data);
 	      var dataStr = data.toString('utf-8');
+
+              var mumps = rufus.write(data);
+              if (rufus.end() !== "") {
+                  console.log("fuckkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, we got hitchikers... " + rufus.end());
+              }
+
+
 //	      console.log("The whole buffer is " + dataStr);
+//              if (nextCmd !== "") {
+//                  console.log("fuckkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk nextCmd is " + nextCmd);
+//              }
 	      var arr = dataStr.split("\n");
 	      var arrLen = arr.length;
-	      var cmdHeader = 0;
 	      for (var i = 0; i < arrLen; i++) {
 		  var prefix = arr[i].substring(0, 2);
 		  if (prefix == 'nw' || prefix == 'nn') {
@@ -1733,12 +1754,12 @@ function init_socket_events () {
 		  }
 		  // check if we end with a semicolon followed by a newline
 		  if (nextCmd.slice(-1) === ";" && nextCmd.slice(-2) !== '\\') {
-// console.log("raw cmd is: " + nextCmd);
-		      nextCmd = nextCmd.replace(/\n/g, "\\n");
+//console.log("raw cmd is: " + nextCmd);
+//		      nextCmd = nextCmd.replace(/\n/g, "\\n");
 //		      nextCmd = nextCmd.replace(/'/g, "\\\'");
 		      var selector = nextCmd.slice(0, nextCmd.indexOf(" "));
 		      var args = nextCmd.slice(selector.length + 1, -1);
-//                      console.log(selector + '(' + args + ');');
+//                      console.log("About to eval: " + selector + '(' + args + ');');
                       eval(selector + '(' + args + ');');
 		      nextCmd = "";
 		      cmdHeader = 0;
@@ -1837,22 +1858,25 @@ function gui_configure_item(cid, tag, attributes) {
 }
 
 // Most of these map either to pd.tk procs, or in some cases Tk canvas subcommands
-function gui_text_create_gobj(cid, tag, xpos, ypos) {
+function gui_text_create_gobj(cid, tag, type, xpos, ypos, is_toplevel) {
     var svg = get_item(cid, "patchsvg"); // "patchsvg" is id for the svg in the DOM
     // Put objects on half-pixels to make them crisp (look in to the difference between
     // this and the object-rendering 'crispEdges' attribute)
     xpos += 0.5;
     ypos += 0.5;
-    var transform_string = 'translate(' + xpos + ',' + ypos + ')';
+    var transform_string = 'matrix(1,0,0,1,' + xpos + ',' + ypos + ')';
     var g = create_item(cid, 'g', {
             id: tag + 'gobj',
-            transform: transform_string
+            transform: transform_string,
+            class: type + (is_toplevel !== 0 ? '' : ' gop')
     });
     svg.appendChild(g);
+// hm... why returning g and not the return value of appendChild?
+//    console.log("create gobj tag is " + tag + " and ret is " + g);
     return g;
 }
 
-function gui_text_drawborder(cid, tag, isbroken, x1, y1, x2, y2) {
+function gui_text_drawborder(cid, tag, bgcolor, isbroken, x1, y1, x2, y2) {
     var g = get_gobj(cid, tag);
     // isbroken means either
     //     a) the object couldn't create or
@@ -1863,9 +1887,11 @@ function gui_text_drawborder(cid, tag, isbroken, x1, y1, x2, y2) {
         stroke: 'black',
         fill: 'none',
         'shape-rendering': 'optimizeSpeed',
-        id: tag + 'border',
-        class: (isbroken ? 'broken_border' : '')
+        class: 'border'
     });
+    if (isbroken === 1) {
+        rect.classList.add('broken_border');
+    }
     g.appendChild(rect);
 }
 
@@ -1898,6 +1924,11 @@ function gui_canvas_drawio(cid, parenttag, tag, x1, y1, x2, y2, basex, basey, ty
     gui_post("the tag for this XLET is " + tag);
 }
 
+function gui_canvas_redraw_io(cid, parenttag, tag, x, type, i, basex) {
+    var xlet = get_item(cid, tag + type + i); 
+    configure_item(xlet, { x: x - basex});
+}
+
 function gui_eraseio(cid, tag) {
     gui_post("the tag for this bout-to-ba-leted XLET is " + tag);
     var xlet = get_item(cid, tag);
@@ -1943,21 +1974,29 @@ function gui_message_drawborder(cid,tag,width,height) {
         points: p_array.join(),
         fill: 'none',
         stroke: 'black',
-        'stroke-width': 1,
-        id: tag + 'border'
+//        'stroke-width': 1,
+        class: 'border'
+//        id: tag + 'border'
     });
     g.appendChild(polygon);
 }
 
 function gui_message_flash(cid, tag, state) {
-    var b = get_item(cid, tag + 'border');
-    var w;
-    if (state != 0) { w = 4; } else { w = 1; }
-    configure_item(b, { 'stroke-width': w });
+    var g = get_gobj(cid, tag);
+    if (state !== 0) {
+        g.classList.add('flashed');
+    } else {
+        g.classList.remove('flashed');
+    }
+//    var b = get_item(cid, tag + 'border');
+//    var w;
+//    if (state != 0) { w = 4; } else { w = 1; }
+//    configure_item(b, { 'stroke-width': w });
 }
 
 function gui_message_redraw_border(cid,tag,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14) {
-    var b = get_item(cid, tag + 'border');
+    var g = get_gobj(cid, tag);
+    var b = g.querySelector('.border');
     var p_array = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14];
     configure_item(b, {
         points: p_array.join(" "),
@@ -2035,7 +2074,8 @@ function gui_canvas_updateline(cid,tag,x1,y1,x2,y2,yoff) {
     configure_item(cord, { d: d_array.join(" ") });
 }
 
-function gui_text_new(canvasname, myname, type, isselected, x, y, text, font) {
+function gui_text_new(canvasname, myname, type, isselected, left_margin, top_margin,
+    bottom_margin, pix_high, text, font) {
 //    gui_post("font is " + font);
 
     var g = get_gobj(canvasname, myname);
@@ -2044,8 +2084,8 @@ function gui_text_new(canvasname, myname, type, isselected, x, y, text, font) {
         // 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. :)
-        x: x,
-        y: y + 10,
+        x: left_margin,
+        y: pix_high - bottom_margin - top_margin - 1,
         // Turns out we can't do 'hanging' baseline
         // because it's borked when scaled. Bummer...
         // 'dominant-baseline': 'hanging',
@@ -2068,6 +2108,8 @@ function gui_text_new(canvasname, myname, type, isselected, x, y, text, font) {
     }
 }
 
+// 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 = get_gobj(cid, tag);
     if (g !== null) {
@@ -2087,42 +2129,40 @@ function gui_text_set (cid, tag, text) {
     }
 }
 
-// Not sure whether this is even used anymore
 function gui_text_redraw_border(cid, tag, x1, y1, x2, y2) {
-    var b = get_item(cid, tag + 'border');
-    configure_item(b, {
-        width: x2 - x1,
-        height: y2 - y1
-    });
+    var i;
+    var g = get_gobj(cid, tag);
+    var b = g.querySelectorAll('.border');
+    for (i = 0; i < b.length; b++) {
+        configure_item(b[i], {
+            width: x2 - x1,
+            height: y2 - y1
+        });
+    }
 }
 
 function gui_text_select(cid, tag) {
     var g = get_gobj(cid, tag);
-    var b = get_item(cid, tag + 'border');
+//    var b = get_item(cid, tag + 'border');
     if (g !== null) {
-        configure_item(g, { class: 'selected' });
+        g.classList.add('selected');
+//        configure_item(g, { class: 'selected' });
     } else {
         console.log("text_select: something wrong with group tag: " + tag);
     }
-    if (b !== null) {
-        configure_item(b, { class: 'selected_border' });
-    }
+//    if (b !== null) {
+//        configure_item(b, { class: 'selected_border' });
+//    }
 }
 
 function gui_text_deselect(cid, tag) {
-    gui_post("deselecting text with tag..." + tag);
+//    gui_post("deselecting text with tag..." + tag);
     var gobj = get_gobj(cid, tag)
-    var border = get_item(cid, tag + 'border');
     if (gobj !== null) {
-        configure_item(gobj, { class: "" });
+        gobj.classList.remove('selected');
     } else {
         console.log("text_deselect: something wrong with tag: " + tag + 'gobj');
     }
-    if (border !== null) {
-        configure_item(border, { class: "" });
-    } else {
-        console.log("text_select: something wrong with tag: " + tag + 'border');
-    }
 }
 
 function gui_text_select_color(cid, tag) {
@@ -2144,11 +2184,15 @@ function gui_canvas_displace_withtag(name, dx, dy) {
     var pwin = patchwin[name];
     var ol = pwin.window.document.getElementsByClassName('selected');
     for (var i = 0; i < ol.length; i++) {
-        var new_tx = dx + ol[i].transform.baseVal.getItem(0).matrix.e; 
-        var new_ty = dy + ol[i].transform.baseVal.getItem(0).matrix.f; 
-        ol[i].setAttributeNS(null, 'transform',
-            'translate(' + new_tx + ',' + new_ty + ')');
+        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;
     }
+//        elem.setAttributeNS(null, 'transform',
+//            'translate(' + new_tx + ',' + new_ty + ')');
+//    }
 }
 
 function gui_create_selection_rectangle(cid, x1, y1, x2, y2) {
@@ -2204,7 +2248,7 @@ function gui_bng_flash(cid, tag, color) {
     configure_item(button, { fill: color });
 }
 
-function gui_create_toggle(cid, tag, color, state, width, p1,p2,p3,p4,p5,p6,p7,p8,basex,basey) {
+function gui_create_toggle(cid, tag, color, width, state, p1,p2,p3,p4,p5,p6,p7,p8,basex,basey) {
     var g = get_gobj(cid, tag);
     var points_array = [p1 - basex, p2 - basey,
                         p3 - basex, p4 - basey
@@ -2257,7 +2301,7 @@ function gui_toggle_resize_cross(cid,tag,w,p1,p2,p3,p4,p5,p6,p7,p8,basex,basey)
 function gui_toggle_update(cid, tag, state, color) {
     var cross1 = get_item(cid, tag + 'cross1');
     var cross2 = get_item(cid, tag + 'cross2');
-    if (state) {
+    if (!!state) {
         configure_item(cross1, { display: 'inline', stroke: color });
         configure_item(cross2, { display: 'inline', stroke: color });
     } else {
@@ -2554,7 +2598,8 @@ function gui_iemgui_drawborder(cid, tag, bgcolor, x1, y1, x2, y2) {
         stroke: 'black',
         'shape-rendering': 'optimizeSpeed',
         'stroke-width': 1,
-        id: tag + 'border'
+        class: 'border'
+//        id: tag + 'border'
     });
     g.appendChild(rect);
 }
@@ -2580,6 +2625,9 @@ function gui_iemgui_label_new(cid, tag, x, y, color, text, font) {
     var text_node = patchwin[cid].window.document.createTextNode(text);
     svg_text.appendChild(text_node);
     g.appendChild(svg_text);
+    var foo = patchwin[cid].window.document.getElementById(tag + 'label');
+//    console.log("foo is " + foo);
+//    console.log("label_new tag is " + tag);
 }
 
 function gui_iemgui_label_set(cid, tag, text) {
@@ -2603,6 +2651,7 @@ function gui_iemgui_label_color(cid, tag, color) {
 
 function gui_iemgui_label_select(cid, tag, is_selected) {
     var svg_text = get_item(cid, tag + 'label');
+//    console.log("tag is " + tag + " and svg_text is " + svg_text);
     if (is_selected) {
         svg_text.classList.add('iemgui_label_selected'); 
     } else {
@@ -2664,9 +2713,13 @@ function gui_mycanvas_select_color(cid,tag,color) {
     var item = get_item(cid,tag + 'drag_handle');
     configure_item(item, {stroke: color});
 }
-
-// This should be merged with gui_create_gobj somehow
-function gui_create_scalar(cid, tag, isselected, t1, t2, t3, t4, t5, t6) {
+ 
+function gui_create_scalar(cid, tag, isselected, t1, t2, t3, t4, t5, t6,
+    is_toplevel) {
+gui_post("creating a scalar...");
+gui_post("is_toplevel is " + is_toplevel);
+    // we should probably use create_gobj here, but we're doing some initial 
+    // scaling that normal gobjs don't need...
     var svg = get_item(cid, "patchsvg"); // "patchsvg" is id for the svg in the DOM
     // Normally put objects on half-pixels to make them crisp, but if we create a
     // scalar in an object box we already did that. This unfortunately creates a 0.5
@@ -2680,26 +2733,34 @@ function gui_create_scalar(cid, tag, isselected, t1, t2, t3, t4, t5, t6) {
     var g = create_item(cid, 'g', {
             id: tag + 'gobj',
             transform: transform_string,
-            class: (isselected ? 'selected' : '')
     });
+    if (isselected !== 0) {
+        g.classList.add('selected');
+    }
+    if (is_toplevel === 0) {
+        g.classList.add('gop');
+    }
     // Let's make a selection rect... but we can't make it
     // a child of the gobj group because the getrect fn gives
     // us a bbox in the canvas coord system
     var selection_rect = create_item(cid, 'rect', {
-        id: tag + 'selection_rect',
+//        id: tag + 'selection_rect',
+        class: 'border',
         display: 'none',
         fill: 'none',
         'pointer-events': 'none'
     });
     g.appendChild(selection_rect);
     svg.appendChild(g);
-    gui_post("made a scalar...");
+//    gui_post("made a scalar...");
     return g;
 }
 
 function gui_scalar_erase(cid, tag) {
     var g = get_gobj(cid, tag);
-    g.parentNode.removeChild(g);
+    if (g !== null) {
+        g.parentNode.removeChild(g);
+    }
     // selection rect...
 //    var sr = get_item(cid, tag + 'selection_rect');
 //    sr.parentNode.removeChild(sr);
@@ -2712,14 +2773,15 @@ function gui_scalar_draw_select_rect(cid, tag, state, x1, y1, x2, y2, basex, bas
     // the coords relative to the scalar's x/y-- hence we subtract the scalar's basex/basey
     // from the coords below
     //gui_post("drawselectrect: " + x1 + " " + y1 + " " + x2 + " " + y2 + " " + basex + " " + basey);
-    var r = get_item(cid, tag + 'selection_rect');
-    configure_item(r, {
+    var g = get_gobj(cid, tag);
+    var b = g.querySelector('.border');
+    configure_item(b, {
         display: (state ? 'inline' : 'none'),
         x: (x1 - basex),
         y: (y1 - basey),
         width: x2 - x1,
         height: y2 - y1,
-        stroke: 'blue',
+//        stroke: 'blue',
     });
 }
 
@@ -2731,7 +2793,6 @@ function gui_create_scalar_group(cid, tag, parent_tag, attr_array) {
     attr_array.push("id", tag);
     var g = create_item(cid, 'g', attr_array);
     parent.appendChild(g);
-    gui_post("made an innder scalar group");
     return g; 
 }
 
@@ -2832,6 +2893,22 @@ function gui_draw_configure_all(cid, tag, attr_array) {
     configure_item(item, attr_array);
 }
 
+// Plots for arrays and data structures
+function gui_plot_vis(cid, basex, basey, data_array, attr_array, tag_array) {
+    var g = get_item(cid, tag_array[0]);
+    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);
+    if (g !== null) {
+        g.appendChild(p);
+    }
+}
+
 function add_popup(cid, popup) {
     popup_menu[cid] = popup;
 }
@@ -2906,11 +2983,16 @@ exports.popup_action = popup_action;
 //    g.appendChild(b);
 //}
 
+// refactor-- use a class so this can happen in css
 function gui_graph_fill_border(cid, tag) {
-    var b = get_item(cid, tag + 'border');
-    configure_item(b, {
-        fill: 'gray'
-    });
+    var i;
+    var g = get_gobj(cid, tag);
+    var b = g.querySelectorAll('.border');
+    for (i = 0; i < b.length; i++) {
+        configure_item(b[i], {
+            fill: 'gray'
+        });
+    }
 }
 
 function gui_graph_deleteborder(cid, tag) {
@@ -2995,7 +3077,18 @@ function gui_canvas_drawredrect(cid, x1, y1, x2, y2) {
 
 function gui_canvas_deleteredrect(cid) {
     var r = get_item(cid, 'GOP');
-    r.parentNode.removeChild(r);
+    // 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
+    // visible calls glist_redraw, and glist_redraw will try to delete
+    // the red rect _before_ it's been drawn in this case.
+    // Unfortunately, it's quite difficult to refactor those c
+    // 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);
+    }
 }
 
 // Magic Glass (aka Cord Inspector)
@@ -3098,16 +3191,33 @@ exports.file_dialog_callback = function(file_string) {
 function gui_iemgui_dialog(did, attr_array) {
     gui_post("got a gfxstub " + did + "!!!");
    
-    for (var i = 0; i < attr_array.length; i++) {
-        attr_array[i] = '"' + attr_array[i] + '"';
-    }
-    dialogwin[did] = nw_create_window(did, 'pd-properties', 265, 540, 20, 20, 0,
+//    for (var i = 0; i < attr_array.length; i++) {
+//        attr_array[i] = '"' + attr_array[i] + '"';
+//    }
+    dialogwin[did] = nw_create_window(did, 'iemgui', 265, 540, 20, 20, 0,
         0, 1, 'white', 'Properties', '', 0, null, attr_array);
 
 }
 
+function gui_canvas_dialog(did, attr_arrays) {
+    var i, j, inner_array;
+    gui_post("got a gfxstub " + did + "!!!");
+    gui_post("attr_arrays are " + attr_arrays);
+//    for (i = 0; i < attr_arrays.length; i++) {
+//        inner_array = attr_arrays[i];
+//        if (inner_array !== undefined) {
+//            for (j = 0; j < inner_array.length; j++) {
+//                inner_array[i] = '"' + inner_array[i] + '"';
+//            }
+//        }
+//    }
+    dialogwin[did] = nw_create_window(did, 'canvas', 265, 540, 20, 20, 0,
+        0, 1, 'white', 'Properties', '', 0, null, attr_arrays);
+}
+
 function gui_remove_gfxstub(did) {
-    if (dialogwin[did] !== null) {
+gui_post("did is " + did + " and dialogwin[did] is " + dialogwin[did]);
+    if (dialogwin[did] !== undefined && dialogwin[did] !== null) {
         dialogwin[did].window.close(true);
         dialogwin[did] = null;
     }
diff --git a/pd/nw/todo.txt b/pd/nw/todo.txt
index b6d02cf42..3bf0e2321 100644
--- a/pd/nw/todo.txt
+++ b/pd/nw/todo.txt
@@ -110,6 +110,26 @@ Everything else:
 * tgl 'X' doesn't show up
 * gui_graph_fill_border: use css class selectors instead of configuration
 * standardize javascript function names
+* in gui_plot_vis, we have to check for gobj existence.  For some reason, at
+  very fast drawing rates we end up with two svg elements if we don't check
+  for this.  Not sure why
+* add garray_update for changing the path coords without having to recreate
+  the entire scalar edifice
+* add "p" type to gui_vmess for pointers
+* when duplicating a large number of iemguis, the label or border will eventually try to append to null.
+  It looks like it's receiving the correct gui calls from Pd, in the correct order.  But for some reason the
+  calls from Pd are getting split in the middle of a string.  Not sure why, probably something to do
+  with the hacky parser I wrote to split on newlines.  Anyway, when we can just split on semis this problem
+  should hopefully go away.  Make sure to test for it once that happens.
+
+  Some gui_posts that show the problem-- the string is split between lines 2 and 3:
+    nextCmd is gui_create_toggle ".x98be5e8","x99c0740","#000000",1,0,181,67,192,78,181,78,192,67,179,65;
+    nextCmd is gui_iemgui_label_new ".x98be5e
+    8","x99c0740",17,7,"#000000","","{{DejaVu Sans Mono} -10 normal}";
+    nextCmd is gui_canvas_drawio ".x98be5e8","x99c0740",".x98be5e8.t99c0de0o0",179,79,186,80,179,65,"o",0,0,1;
+    the tag for this XLET is .x98be5e8.t99c0de0o0
+
+remove gui_text_select_color (css takes care of this)
 
 Crashers
 --------
diff --git a/pd/src/g_all_guis.c b/pd/src/g_all_guis.c
index bb97505d1..67e3cd43e 100644
--- a/pd/src/g_all_guis.c
+++ b/pd/src/g_all_guis.c
@@ -1209,13 +1209,16 @@ void iemgui_base_draw_new(t_iemgui *x) {
     //         "-stroke $pd_colors(iemgui_border) -fill #%6.6x "
     //         "-tags {%lxBASE x%lx text iemgui border}\n",
     //     canvas, x1,y1,x2,y2, x->x_bcol, x, x);
-    gui_vmess("gui_text_create_gobj", "ssii", canvas_tag(canvas), gobj_tag(x),
-        x1, y1);
+    gui_vmess("gui_text_create_gobj", "sssiii", canvas_tag(canvas), gobj_tag(x),
+        "iemgui", x1, y1, glist_istoplevel(x->x_glist));
     char colorbuf[MAXPDSTRING];
     sprintf(colorbuf, "#%6.6x", x->x_bcol);
-    gui_vmess("gui_iemgui_drawborder", "sssiiii",
-        canvas_tag(canvas), gobj_tag(x),
-        colorbuf, x1, y1, x2, y2);
+    gui_vmess("gui_text_drawborder", "sssiiiii",
+        canvas_tag(canvas),
+        gobj_tag(x),
+        colorbuf,
+        0,
+        x1, y1, x2, y2);
 }
 
 void iemgui_base_draw_move(t_iemgui *x) {
diff --git a/pd/src/g_array.c b/pd/src/g_array.c
index 796f1895f..2536eb759 100644
--- a/pd/src/g_array.c
+++ b/pd/src/g_array.c
@@ -152,7 +152,7 @@ static char garray_arraytemplatefile[] = "\
 #N canvas 0 0 458 153 10;\n\
 #X obj 43 31 struct _float_array array z float float style\n\
 float linewidth float color symbol fillcolor symbol outlinecolor;\n\
-#X obj 43 70 old_plot z color linewidth 0 0 1 style fillcolor outlinecolor;\n\
+#X obj 43 70 plot z color linewidth 0 0 1 style fillcolor outlinecolor;\n\
 ";
 static char garray_floattemplatefile[] = "\
 #N canvas 0 0 458 153 10;\n\
@@ -525,27 +525,49 @@ void canvas_menuarray(t_glist *canvas)
     gfxstub_new(&x->gl_pd, x, cmdbuf);
 }
 
-    /* called from graph_dialog to set properties */
-void garray_properties(t_garray *x, t_glist *canvas)
+    /* called from canvas_dialog to return array properties for the gui */
+int garray_properties(t_garray *x, t_symbol **gfxstubp, t_symbol **namep,
+    int *sizep, int *flagsp, t_symbol **fillp, t_symbol **outlinep)
 {
-    char cmdbuf[200];
+    char namebuf[MAXPDSTRING];
     t_array *a = garray_getarray(x);
     if (!a)
-        return;
-    gfxstub_deleteforkey(x);
-        /* create dialog window.  LATER fix this to escape '$'
+        return 0;
+    //gfxstub_deleteforkey(x);
+        /* LATER fix this to escape '$'
         properly; right now we just detect a leading '$' and escape
         it.  There should be a systematic way of doing this. */
+    /* still don't understand this filestyle business... */
     int filestyle = (x->x_style == 0 ? PLOTSTYLE_POLY :
         (x->x_style == 1 ? PLOTSTYLE_POINTS : x->x_style));
-    //fprintf(stderr," garray_properties %d\n", filestyle);
-    sprintf(cmdbuf, ((x->x_name->s_name[0] == '$') ?
-        "pdtk_array_dialog %%s \\%s %d %d 0 .x%lx %s %s\n" :
-        "pdtk_array_dialog %%s %s %d %d 0 .x%lx %s %s\n"), x->x_name->s_name,
-            a->a_n, x->x_saveit +  2 * filestyle + 8 * x->x_hidename +
-            16 * x->x_joc, (long unsigned int)glist_getcanvas(canvas),
-             x->x_fillcolor->s_name, x->x_outlinecolor->s_name);
-    gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf);
+    int flags = x->x_saveit + 2 * filestyle + 8 * x->x_hidename + 16 * x->x_joc;
+
+    if (x->x_name->s_name[0] == '$')
+    {
+        sprintf(namebuf, "\\%s", x->x_name->s_name);
+    }
+    else
+    {
+        sprintf(namebuf, "%s", x->x_name->s_name);
+    }
+
+    *gfxstubp = gensym(gfxstub_new2(&x->x_gobj.g_pd, x));
+    *namep = gensym(namebuf);
+    *sizep = a->a_n;
+    *flagsp = flags;
+    *fillp = x->x_fillcolor;
+    *outlinep = x->x_outlinecolor;
+    //sprintf(cmdbuf, ((x->x_name->s_name[0] == '$') ?
+    //    "pdtk_array_dialog %%s \\%s %d %d 0 .x%lx %s %s\n" :
+    //    "pdtk_array_dialog %%s %s %d %d 0 .x%lx %s %s\n"),
+    //        x->x_name->s_name,
+    //        a->a_n,
+    //        x->x_saveit +  2 * filestyle + 8 * x->x_hidename + 16 * x->x_joc,
+    //        (long unsigned int)glist_getcanvas(canvas),
+    //        x->x_fillcolor->s_name,
+    //        x->x_outlinecolor->s_name);
+    //gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf);
+    return 1;
 }
 
     /* this is called back from the dialog window to create a garray. 
@@ -1386,7 +1408,8 @@ static void garray_select(t_gobj *z, t_glist *glist, int state)
     t_garray *x = (t_garray *)z;
     sys_vgui("pdtk_select_all_gop_widgets .x%lx %lx %d\n",
         glist_getcanvas(glist), x->x_glist, state);
-    /* fill in later */
+
+    scalar_select((t_gobj *)x->x_scalar, glist, state);
 }
 
 static void garray_activate(t_gobj *z, t_glist *glist, int state)
@@ -1564,7 +1587,7 @@ void garray_redraw(t_garray *x)
     /* } jsarlo */
 }
 
-   /* This functiopn gets the template of an array; if we can't figure
+   /* This function gets the template of an array; if we can't figure
    out what template an array's elements belong to we're in grave trouble
    when it's time to free or resize it.  */
 t_template *garray_template(t_garray *x)
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index 62f94ca00..9d5b89b39 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -862,6 +862,8 @@ void canvas_map(t_canvas *x, t_floatarg f)
             sys_vgui(".x%lx.c dtag all selected\n", x);
             //sys_vgui(".x%lx.c delete all\n", x);
             sys_vgui("foreach item [.x%lx.c find withtag {(!root)}] { .x%lx.c delete $item }\n", x, x);
+            gui_vmess("gui_canvas_erase_all_gobjs", "s",
+                canvas_tag(x));
             x->gl_mapped = 0;
         }
     }
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index df54b6a97..6593e85b6 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -1858,6 +1858,7 @@ void canvas_undo_canvas_apply(t_canvas *x, void *z, int action)
         //redraw
         canvas_setgraph(x, x->gl_isgraph + 2*x->gl_hidetext, 0);
         canvas_dirty(x, 1);
+
         if (x->gl_havewindow)
         {
             canvas_redraw(x);
@@ -2658,13 +2659,15 @@ void canvas_setgraph(t_glist *x, int flag, int nogoprect)
         }
         if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
         {
+            gobj_vis(&x->gl_gobj, x->gl_owner, 0);
             gobj_vis(&x->gl_gobj, x->gl_owner, 1);
             canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
         }
     }
 }
 
-void garray_properties(t_garray *x, t_glist *canvas);
+int garray_properties(t_garray *x, t_symbol **gfxstubp, t_symbol **namep,
+    int *sizep, int *flagsp, t_symbol **fillp, t_symbol **outlinep);
 
     /* tell GUI to create a properties dialog on the canvas.  We tell
     the user the negative of the "pixel" y scale to make it appear to grow
@@ -2672,27 +2675,98 @@ void garray_properties(t_garray *x, t_glist *canvas);
 void canvas_properties(t_glist *x)
 {
     t_gobj *y;
-    char graphbuf[200];
+    char graphbuf[200], *gfx_tag;
+
+    gfx_tag = gfxstub_new2(&x->gl_pd, x);
+
+    /* We need to go through and delete any
+       gfxstubs for the arrays in this glist.
+       Otherwise we could get a message to the
+       GUI in the middle of our properties
+       message below.  This is needed because
+       the array properties just share the
+       same window with the canvas properties.
+    */
+    for (y = x->gl_list; y; y = y->g_next)
+        if (pd_class(&y->g_pd) == garray_class) 
+            gfxstub_deleteforkey((t_garray *)y);
+
+    gui_start_vmess("gui_canvas_dialog", "s", gfx_tag);
+    gui_start_array(); /* Main array for nested arrays of attributes */
+
+    gui_start_array(); /* Nested array for canvas attributes */
     if (glist_isgraph(x) != 0)
-        sprintf(graphbuf,
-            "pdtk_canvas_dialog %%s %g %g %d %g %g %g %g %d %d %d %d\n",
-                0., 0.,
-                glist_isgraph(x) ,//1,
-                x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2, 
-                (int)x->gl_pixwidth, (int)x->gl_pixheight,
-                (int)x->gl_xmargin, (int)x->gl_ymargin);
-    else sprintf(graphbuf,
-            "pdtk_canvas_dialog %%s %g %g %d %g %g %g %g %d %d %d %d\n",
-                glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
-                0,
-                0., -1., 1., 1., 
-                (int)x->gl_pixwidth, (int)x->gl_pixheight,
-                (int)x->gl_xmargin, (int)x->gl_ymargin);
-    gfxstub_new(&x->gl_pd, x, graphbuf);
-        /* if any arrays are in the graph, put out their dialogs too */
+    {
+        //sprintf(graphbuf,
+        //    "pdtk_canvas_dialog %%s %g %g %d %g %g %g %g %d %d %d %d\n",
+        //        0., 0.,
+        //        glist_isgraph(x) ,//1,
+        //        x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2, 
+        //        (int)x->gl_pixwidth, (int)x->gl_pixheight,
+        //        (int)x->gl_xmargin, (int)x->gl_ymargin);
+        gui_s("x-scale");  gui_f(0.);
+        gui_s("y-scale");  gui_f(0.);
+        gui_s("display-flags"); gui_i(glist_isgraph(x));
+        gui_s("x1");       gui_f(x->gl_x1);
+        gui_s("y1");       gui_f(x->gl_y1);
+        gui_s("x2");       gui_f(x->gl_x2);
+        gui_s("y2");       gui_f(x->gl_y2);
+        gui_s("x-pix");    gui_i((int)x->gl_pixwidth);
+        gui_s("y-pix");    gui_i((int)x->gl_pixheight);
+        gui_s("x-margin"); gui_i((int)x->gl_xmargin);
+        gui_s("y-margin"); gui_i((int)x->gl_ymargin);
+    }
+    else
+    {
+        //sprintf(graphbuf,
+        //    "pdtk_canvas_dialog %%s %g %g %d %g %g %g %g %d %d %d %d\n",
+        //        glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
+        //        0,
+        //        0., -1., 1., 1., 
+        //        (int)x->gl_pixwidth, (int)x->gl_pixheight,
+        //        (int)x->gl_xmargin, (int)x->gl_ymargin);
+        gui_s("x-scale");  gui_f(glist_dpixtodx(x, 1));
+        gui_s("y-scale");  gui_f(-glist_dpixtody(x, 1));
+        gui_s("display-flags"); gui_i(0);
+        gui_s("x1");       gui_f(0.);
+        gui_s("y1");       gui_f(-1.);
+        gui_s("x2");       gui_f(1.);
+        gui_s("y2");       gui_f(1.);
+        gui_s("x-pix");    gui_i((int)x->gl_pixwidth);
+        gui_s("y-pix");    gui_i((int)x->gl_pixheight);
+        gui_s("x-margin"); gui_i((int)x->gl_xmargin);
+        gui_s("y-margin"); gui_i((int)x->gl_ymargin);
+    }
+    //gfxstub_new(&x->gl_pd, x, graphbuf);
+
+    gui_end_array(); /* end of nested array for canvas attributes */
+
+        /* if any arrays are in the graph, pull out their attributes */
     for (y = x->gl_list; y; y = y->g_next)
+    {
         if (pd_class(&y->g_pd) == garray_class) 
-            garray_properties((t_garray *)y, x);
+        {
+            t_garray *garray = (t_garray *)y;
+            t_symbol *gfxstub, *name, *fill, *outline;
+            int size, flags;
+            /* garray_properties can fail to find an array, so we won't
+               send props if it has a return value of zero */
+            if (garray_properties((t_garray *)y, &gfxstub, &name, &size, &flags,
+                &fill, &outline))
+            {
+                gui_start_array(); /* inner array for this array's attributes */
+                gui_s("array_gfxstub"); gui_s(gfxstub->s_name);
+                gui_s("array_name"); gui_s(name->s_name);
+                gui_s("array_size"); gui_i(size);
+                gui_s("array_flags"); gui_i(flags);
+                gui_s("array_fill"); gui_s(fill->s_name);
+                gui_s("array_outline"); gui_s(outline->s_name);
+                gui_end_array();
+            }
+        }
+    }
+    gui_end_array();
+    gui_end_vmess();
 }
 
     /* called from the gui when "OK" is selected on the canvas properties
@@ -2798,6 +2872,8 @@ static void canvas_donecanvasdialog(t_glist *x,
     canvas_setgraph(x, graphme, 0);
     canvas_dirty(x, 1);
 
+// we're drawn at this point
+
     // make sure gop is never smaller than its text
     // if one wants smaller gop window, make sure to disable text
     if (x->gl_isgraph && !x->gl_hidetext && x->gl_owner)
diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c
index 9d1a8cca9..2071cc48d 100644
--- a/pd/src/g_graph.c
+++ b/pd/src/g_graph.c
@@ -862,6 +862,9 @@ int garray_getname(t_garray *x, t_symbol **namep);
     graph decorations in toplevels... */
 static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
 {
+
+post("inside graph vis");
+
     t_glist *x = (t_glist *)gr;
     //fprintf(stderr,"graph vis canvas=%lx gobj=%lx %d\n",
     //    (t_int)parent_glist, (t_int)gr, vis);
@@ -897,9 +900,9 @@ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
         int xpix, ypix;
         xpix = text_xpix(&x->gl_obj, parent_glist);
         ypix = text_ypix(&x->gl_obj, parent_glist);
-        gui_vmess("gui_text_create_gobj", "ssii",
+        gui_vmess("gui_text_create_gobj", "sssiii",
             canvas_tag(glist_getcanvas(x->gl_owner)),
-            tag, xpix, ypix);
+            tag, "graph", xpix, ypix, 1);
         if (canvas_showtext(x))
             rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));
     }
@@ -930,9 +933,10 @@ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
             //    glist_getcanvas(x->gl_owner),
                 ////parent_glist,
             //    x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
-            gui_vmess("gui_text_drawborder", "ssiiiii",
+            gui_vmess("gui_text_drawborder", "sssiiiii",
                 canvas_tag(glist_getcanvas(x->gl_owner)),
                 tag,
+                "none",
                 0, x1, y1, x2, y2);
             glist_noselect(x->gl_owner);
             gui_vmess("gui_graph_fill_border", "ssi",
@@ -973,9 +977,10 @@ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
         char tagbuf[MAXPDSTRING];
         sprintf(tagbuf, "%sR", tag);
 
-        gui_vmess("gui_text_drawborder", "ssiiiii",
+        gui_vmess("gui_text_drawborder", "sssiiiii",
             canvas_tag(glist_getcanvas(x->gl_owner)),
             tag,
+            "none",
             0, x1, y1, x2, y2);
             /* write garrays' names along the top */
         for (i = (y1 < y2 ? y1 : y2)-1, g = x->gl_list; g; g = g->g_next)
@@ -1521,6 +1526,7 @@ static void graph_select(t_gobj *z, t_glist *glist, int state)
                     (g->g_pd->c_wb->w_displacefnwtag != NULL) ||
                     (g && pd_class((t_pd *)g) == garray_class)))
                 {
+                    post("inside flub... selecting a fluarg");
                     gobj_select(g, x, state);
                 }
             }
@@ -1558,6 +1564,7 @@ static void graph_delete(t_gobj *z, t_glist *glist)
 static void graph_delete(t_gobj *z, t_glist *glist)
 {
     //fprintf(stderr,"graph_delete\n");
+    post("how many more times...");
     t_glist *x = (t_glist *)z;
     t_gobj *y;
     text_widgetbehavior.w_deletefn(z, glist);
diff --git a/pd/src/g_magicglass.c b/pd/src/g_magicglass.c
index efacecd13..9a7f36c63 100644
--- a/pd/src/g_magicglass.c
+++ b/pd/src/g_magicglass.c
@@ -144,8 +144,8 @@ void magicGlass_drawNew(t_magicGlass *x)
              x->x_c);
     sys_vgui(".x%x.c raise magicGlassText\n",
              x->x_c);
-    gui_vmess("gui_text_create_gobj", "ssii",
-        canvas_tag(x->x_c), "cord_inspector", 0, 0);
+    gui_vmess("gui_text_create_gobj", "sssiii",
+        canvas_tag(x->x_c), "cord_inspector", "cord_inspector", 0, 0, 0);
     gui_vmess("gui_create_cord_inspector", "s",
         canvas_tag(x->x_c)); 
     magicGlass_updateText(x, 0);
diff --git a/pd/src/g_mycanvas.c b/pd/src/g_mycanvas.c
index 18c504bd5..00fbf8024 100644
--- a/pd/src/g_mycanvas.c
+++ b/pd/src/g_mycanvas.c
@@ -35,8 +35,8 @@ void my_canvas_draw_new(t_my_canvas *x, t_glist *glist)
         x->x_gui.x_bcol, x, x);
     char colorbuf[MAXPDSTRING];
     sprintf(colorbuf, "#%6.6x", x->x_gui.x_bcol);
-    gui_vmess("gui_text_create_gobj", "ssii", canvas_tag(canvas),
-        gobj_tag(x), x1, y1);
+    gui_vmess("gui_text_create_gobj", "sssiii", canvas_tag(canvas),
+        gobj_tag(x), "iemgui", x1, y1, glist_istoplevel(canvas));
     gui_vmess("gui_create_mycanvas", "sssiiiiii", canvas_tag(canvas),
         gobj_tag(x), colorbuf, x1, y1, x1+x->x_vis_w, y1+x->x_vis_h,
         x1+x->x_gui.x_w, y1+x->x_gui.x_h);
diff --git a/pd/src/g_rtext.c b/pd/src/g_rtext.c
index 116f01ed1..4d9596770 100644
--- a/pd/src/g_rtext.c
+++ b/pd/src/g_rtext.c
@@ -362,11 +362,13 @@ static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp,
             //    dispx + LMARGIN, dispy + TMARGIN,
             //    outchars_b, tempbuf, sys_hostfontsize(font),
             //    (glist_isselected(x->x_glist, ((t_gobj*)x->x_text)) ? "$pd_colors(selection)" : "$pd_colors(text)"));
-            gui_vmess("gui_text_new", "sssiiisi",
+            gui_vmess("gui_text_new", "sssiiiiisi",
                 canvas_tag(canvas), x->x_tag, rtext_gettype(x)->s_name,
                 glist_isselected(x->x_glist, ((t_gobj*)x->x_text)),
                 LMARGIN,
                 TMARGIN,
+                BMARGIN,
+                pixhigh,
 //                dispx + LMARGIN,
 //                dispy + TMARGIN,
                 tempbuf,
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index cc4616c7c..a0b4cd11e 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -861,13 +861,14 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
         //    );
         char tagbuf[MAXPDSTRING];
         sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
-        gui_vmess("gui_create_scalar", "ssiffffii",
+        gui_vmess("gui_create_scalar", "ssiffffiii",
             canvas_tag(glist_getcanvas(owner)), 
             tagbuf,
             glist_isselected(owner, &x->sc_gobj),
             xscale, 0.0, 0.0, yscale,
             (int)glist_xtopixels(owner, basex),
-            (int)glist_ytopixels(owner, basey));
+            (int)glist_ytopixels(owner, basey),
+            glist_istoplevel(owner));
         //sys_vgui(".x%lx.c create group -tags {.dgroup%lx.%lx} "
         //         "-parent {.scalar%lx}\n",
         //    glist_getcanvas(owner), templatecanvas, x->sc_vec, x->sc_vec);
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
index bf5bdf153..0ffd8ad67 100644
--- a/pd/src/g_template.c
+++ b/pd/src/g_template.c
@@ -5456,6 +5456,20 @@ static void plot_groupvis(t_scalar *x, t_glist *owner, t_word *data,
     }
 }
 
+/* see if the elements we're plotting have any drawing commands */
+int plot_has_drawcommand(t_canvas *elemtemplatecanvas)
+{
+    t_gobj *y;
+    for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
+    {
+        if (pd_class(&y->g_pd) == canvas_class && ((t_glist *)y)->gl_svg)
+            return 1;
+        else if (class_isdrawcommand(y->g_pd))
+            return 1;
+    }
+    return 0;
+}
+
 static void plot_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
     t_scalar *sc, t_word *data, t_template *template,
     t_float basex, t_float basey, int tovis)
@@ -5515,6 +5529,7 @@ static void plot_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
     if (tovis)
     {
         int in_array = (sc->sc_vec == data) ? 0 : 1;
+        int draw_scalars = plot_has_drawcommand(elemtemplatecanvas);
         /* check if old 3-digit color field is being used... */
         int dscolor = fielddesc_getfloat(&x->x_outlinecolor, template, data, 1);
         if (dscolor != 0)
@@ -5525,12 +5540,26 @@ static void plot_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
         }
         if (symoutline == &s_) symoutline = gensym("#000000");
         if (symfill == &s_) symfill = gensym("#000000");
+
         if (style == PLOTSTYLE_POINTS || style == PLOTSTYLE_BARS)
         {
+            t_float xscale = glist_xtopixels(glist, 1) - glist_xtopixels(glist, 0);
+            t_float yscale = glist_ytopixels(glist, 1) - glist_ytopixels(glist, 0);
+            t_float testy_y = 1 / yscale;
+//            post("testy_y is %g", testy_y);
+
             symfill = (style == PLOTSTYLE_POINTS ? symoutline : symfill);
             t_float minyval = 1e20, maxyval = -1e20;
             int ndrawn = 0;
-            sys_vgui(".x%lx.c create path { \\\n", glist_getcanvas(glist));
+            //sys_vgui(".x%lx.c create path { \\\n", glist_getcanvas(glist));
+
+            gui_start_vmess("gui_plot_vis", "sii",
+                canvas_tag(glist_getcanvas(glist)),
+                basex,
+                basey);
+                
+            gui_start_array();
+
             for (xsum = xloc, i = 0; i < nelem; i++)
             {
                 t_float yval;
@@ -5618,12 +5647,30 @@ static void plot_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
                     /* For efficiency, we make a single path item
                        for the trace or bargraph */
                     int mex1 = ixpix;
-                    int mey1 = (int)fielddesc_cvttocoord(yfielddesc, minyval) - 1;
+                    t_float mey1 = fielddesc_cvttocoord(yfielddesc, minyval) - 1;
 
                     int mex2 = inextx;
                     int mey2 = py2;
-                    sys_vgui("M %d %d H %d V %d H %d z \\\n",
-                        mex1, mey1, mex2, mey2, mex1);
+                    //sys_vgui("M %d %d H %d V %d H %d z \\\n",
+                    //    mex1, mey1, mex2, mey2, mex1);
+
+                    gui_s("M");
+                    gui_i(mex1);
+                    gui_f(yval);
+//                    gui_f(mey1);
+
+                    gui_s("H");
+                    gui_i(mex2);
+
+                    gui_s("V");
+                    gui_f(yval + testy_y * linewidth);
+//                    gui_i(mey2);
+
+                    gui_s("H");
+                    gui_i(mex1);
+
+                    gui_s("z");
+
                //} //part of experimental code above
                     ndrawn++;
                     minyval = 1e20;
@@ -5636,13 +5683,41 @@ static void plot_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
             // already gives us the thickness matching that of vanilla pd
             // The code is left here in its redundant form for future reference
             // in case we encounter regressions.
-            sys_vgui("} -fill %s -stroke %s -strokewidth %d "
-                     "-parent {.dgroup%lx.%lx} "
-                     "-tags {.x%lx.x%lx.template%lx array}\n",
-                symfill->s_name, symoutline->s_name,
-                style == PLOTSTYLE_POINTS ? 0 : 0,
-                x->x_canvas, data,
-                glist_getcanvas(glist), glist, data);
+            //sys_vgui("} -fill %s -stroke %s -strokewidth %d "
+            //         "-parent {.dgroup%lx.%lx} "
+            //         "-tags {.x%lx.x%lx.template%lx array}\n",
+            //    symfill->s_name, symoutline->s_name,
+            //    style == PLOTSTYLE_POINTS ? 0 : 0,
+            //    x->x_canvas, data,
+            //    glist_getcanvas(glist), glist, data);
+
+            gui_end_array();
+
+            gui_start_array();
+            gui_s("fill");
+            gui_s(symfill->s_name);
+
+            gui_s("stroke");
+            gui_s(symoutline->s_name);
+
+            gui_s("stroke-width");
+            gui_i(style == PLOTSTYLE_POINTS ? 0 : 0);
+            gui_end_array();
+
+            gui_start_array();
+            char pbuf[MAXPDSTRING];
+            char tbuf[MAXPDSTRING];
+            sprintf(pbuf, "dgroup%lx.%lx", (long unsigned int)x->x_canvas,
+                (long unsigned int)data);
+            sprintf(tbuf, ".x%lx.x%lx.template%lx",
+                (long unsigned int)glist_getcanvas(glist),
+                (long unsigned int)glist,
+                (long unsigned int)data);
+            gui_s(pbuf);
+            gui_s(pbuf);
+            gui_end_array();
+
+            gui_end_vmess();
         }
         else
         {
@@ -5794,7 +5869,7 @@ static void plot_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
             /* We're done with the outline; now draw all the points.
             This code is inefficient since the template has to be
             searched for drawing instructions for every last point. */
-        if (scalarvis != 0)
+        if (scalarvis != 0 && draw_scalars)
         {
             //t_float xoffset = in_array ? basex: 0;
             //t_float yoffset = in_array ? basey: 0;
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index ffcca0418..c7c1aeb73 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -1562,6 +1562,7 @@ static void text_select(t_gobj *z, t_glist *glist, int state)
     {
         if (glist_istoplevel(glist))
         {
+post("inside text selecting...");
             sys_vgui(".x%lx.c itemconfigure %sR -stroke %s\n",
                 glist_getcanvas(glist), rtext_gettag(y),
                 (state? "$pd_colors(selection)" : outline));
@@ -1660,12 +1661,25 @@ static void text_delete(t_gobj *z, t_glist *glist)
     canvas_deletelinesfor(glist, x);
 }
 
+static void text_get_typestring(int type, char *buf)
+{
+    if (type == T_OBJECT)
+        sprintf(buf, "%s", "obj");
+    else if (type == T_MESSAGE)
+        sprintf(buf, "%s", "msg");
+    else if (type == T_TEXT)
+        sprintf(buf, "%s", "comment");
+    else
+        sprintf(buf, "%s", "atom");
+}
+
 static void text_vis(t_gobj *z, t_glist *glist, int vis)
 {
     //fprintf(stderr,"text_vis %d\n", vis);
     t_text *x = (t_text *)z;
     int x1, y1, x2, y2;
-
+    char type[8];
+    text_get_typestring(x->te_type, type);
 #ifdef PDL2ORK
     //if we are in k12 mode and this is hub with level 1 (global)
     //don't draw it and make its width/height 0
@@ -1692,8 +1706,9 @@ static void text_vis(t_gobj *z, t_glist *glist, int vis)
 
                 // make a group
                 text_getrect(&x->te_g, glist, &x1, &y1, &x2, &y2);
-                gui_vmess("gui_text_create_gobj", "ssii",
-                    canvas_tag(glist), rtext_gettag(y), x1, y1);
+                gui_vmess("gui_text_create_gobj", "sssiii",
+                    canvas_tag(glist_getcanvas(glist)),
+                    rtext_gettag(y), type, x1, y1, glist_istoplevel(glist));
 
                 if (x->te_type == T_ATOM)
                     glist_retext(glist, x);
@@ -1920,16 +1935,20 @@ void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,
         else
         {
             //fprintf(stderr,"glist_drawiofor o redraw\n");
-            sys_vgui(".x%lx.c coords %so%d %d %d %d %d\n",
-                glist_getcanvas(glist), tag, i,
-                onset, y2 - 2,
-                onset + IOWIDTH, y2);
+            //sys_vgui(".x%lx.c coords %so%d %d %d %d %d\n",
+            //    glist_getcanvas(glist), tag, i,
+            //    onset, y2 - 2,
+            //    onset + IOWIDTH, y2);
                 // jsarlo
                 /*sys_vgui(".x%x.c raise %so%d\n",
                          glist_getcanvas(glist),
                          tag,
                          i);*/
                 // end jsarlo
+
+            gui_vmess("gui_canvas_redraw_io", "sssisii",
+                canvas_tag(glist_getcanvas(glist)), rtext_gettag(y), tag,
+                onset, "o", i, x1);
         }
     }
     n = obj_ninlets(ob);
@@ -1960,16 +1979,20 @@ void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,
         else
         {
             //fprintf(stderr,"glist_drawiofor i firsttime\n");
-            sys_vgui(".x%lx.c coords %si%d %d %d %d %d\n",
-                glist_getcanvas(glist), tag, i,
-                onset, y1,
-                onset + IOWIDTH, y1 + EXTRAPIX);
+            //sys_vgui(".x%lx.c coords %si%d %d %d %d %d\n",
+            //    glist_getcanvas(glist), tag, i,
+            //    onset, y1,
+            //    onset + IOWIDTH, y1 + EXTRAPIX);
                 // jsarlo   
                 /*sys_vgui(".x%x.c raise %si%d\n",
                          glist_getcanvas(glist),
                          tag,
                          i);*/
                 // end jsarlo
+
+            gui_vmess("gui_canvas_redraw_io", "sssisii",
+                canvas_tag(glist_getcanvas(glist)), rtext_gettag(y), tag,
+                onset, "i", i, x1);
         }
     }
 }
@@ -2110,8 +2133,9 @@ void text_drawborder(t_text *x, t_glist *glist,
                     fill, tag, tag, box_tag,
                     (selected ? "selected" : ""));
             */
-            gui_vmess("gui_text_drawborder", "ssiiiii",
-                canvas_tag(glist_getcanvas(glist)), tag, broken, x1, y1, x2, y2);
+            gui_vmess("gui_text_drawborder", "sssiiiii",
+                canvas_tag(glist_getcanvas(glist)), tag, "none",
+                broken, x1, y1, x2, y2);
                
                 //-dash %s -> pattern disabled for tkpath
         }
diff --git a/pd/src/s_inter.c b/pd/src/s_inter.c
index 11c9ba329..15658b24b 100644
--- a/pd/src/s_inter.c
+++ b/pd/src/s_inter.c
@@ -823,8 +823,11 @@ void gui_start_vmess(const char *sel, char *fmt, ...)
 static int gui_array_head;
 void gui_start_array(void)
 {
+    if (gui_array_head)
+        sys_gui("[");
+    else
+        sys_gui(",[");
     gui_array_head = 1;
-    sys_gui(",[");
 }
 
 void gui_f(t_float f)
@@ -1267,9 +1270,8 @@ int sys_startgui(const char *guidir)
 
 /* SUPERHACK - let's just load node-webkit and see what happens */
             sprintf(cmdbuf,
-                  "/home/nu/Downloads/nwjs-v0.12.0-alpha2-linux-ia32/nw "
-//                "/home/user/Downloads/node-webkit-v0.11.6-linux-ia32/nw "
-//                  "/home/user/Downloads/atom/atom/atom "
+                  "/home/nu/Downloads/nwjs-v0.12.0-linux-ia32/nw "
+//                  "/home/nu/Downloads/nwjs-v0.12.0-alpha2-linux-ia32/nw "
                 "/home/nu/Downloads/test/ %d localhost %s\n",
                 portno,
                 (sys_k12_mode ? "pd-l2ork-k12" : "pd-l2ork"));
-- 
GitLab