From 9744d551a4a015cc9b2ea296e46c43bef2cef10d Mon Sep 17 00:00:00 2001
From: Jonathan Wilkes <jon.w.wilkes@gmail.com>
Date: Sun, 20 Sep 2015 17:55:01 -0400
Subject: [PATCH] separate pd_canvas.js from pd_canvas.html

---
 pd/nw/pd_canvas.html | 1200 +-----------------------------------------
 pd/nw/pd_canvas.js   | 1173 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1188 insertions(+), 1185 deletions(-)
 create mode 100644 pd/nw/pd_canvas.js

diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html
index d66f61372..252014c03 100644
--- a/pd/nw/pd_canvas.html
+++ b/pd/nw/pd_canvas.html
@@ -1,1192 +1,22 @@
 <!DOCTYPE html>
 <html>
   <head>
-  <link id="page_style" rel="stylesheet" type="text/css" href="css/default.css">
+    <link id="page_style" rel="stylesheet"
+          type="text/css" href="css/default.css">
   </head>
   <body id="patch_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" overflow="visible" class="noselect">
-  </svg>
-  <script>
-'use strict';
-var nw = require('nw.gui'); 
-var pdgui = require('./pdgui.js');
-
-// Apply gui preset to this canvas
-pdgui.skin.apply(this);
-
-//var name = pdgui.last_loaded();
-   
-var l = pdgui.get_local_string;
-
-console.log("my working dir 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 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 text_to_fudi(text) {
-    text = text.trim();
-    text = text.replace(/(\$[0-9]+)/g, '\\$1');    // escape dollar signs
-    text = text.replace(/(?!\\)(,|;)/g, ' \\$1 '); // escape ',' and ';'
-    text = text.replace(/\{|\}/g, '');             // filter '{' and '}'
-    text = text.replace(/\s+/g, ' ');              // filter consecutive /s
-
-    return text;
-}
-
-var canvas_events = (function() {
-    var name,
-        state,
-        svg_view = document.getElementById('patchsvg').viewBox.baseVal,
-        textbox = function () {
-            return document.getElementById('new_object_textentry');
-        },
-        events = {
-            mousemove: function(evt) {
-                //pdgui.gui_post("x: " + evt.pageX + " y: " + evt.pageY +
-                //    " modifier: " + (evt.shiftKey + (evt.ctrlKey << 1)));
-                pdgui.pdsend(name, "motion",
-                    (evt.pageX + svg_view.x),
-                    (evt.pageY + svg_view.y),
-                    (evt.shiftKey + (evt.ctrlKey << 1))
-                );
-                evt.stopPropagation();
-                evt.preventDefault();
-                return false;
-            },
-            mousedown: function(evt) {
-                // tk events (and, therefore, Pd evnets) 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 + svg_view.x),
-                    (evt.pageY + svg_view.y),
-                    b, mod
-                );
-                //evt.stopPropagation();
-                evt.preventDefault();
-            },
-            mouseup: function(evt) {
-                //pdgui.gui_post("mouseup: x: " +
-                //    evt.pageX + " y: " + evt.pageY +
-                //    " button: " + (evt.button + 1));
-                pdgui.pdsend(name, "mouseup",
-                    (evt.pageX + svg_view.x),
-                    (evt.pageY + svg_view.y),
-                    (evt.button + 1)
-                );
-                evt.stopPropagation();
-                evt.preventDefault();
-            },
-            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;
-
-                    // These may be different on Safari...
-                    case 112: hack = add_keymods('F1', evt); break;
-                    case 113: hack = add_keymods('F2', evt); break;
-                    case 114: hack = add_keymods('F3', evt); break;
-                    case 115: hack = add_keymods('F4', evt); break;
-                    case 116: hack = add_keymods('F5', evt); break;
-                    case 117: hack = add_keymods('F6', evt); break;
-                    case 118: hack = add_keymods('F7', evt); break;
-                    case 119: hack = add_keymods('F8', evt); break;
-                    case 120: hack = add_keymods('F9', evt); break;
-                    case 121: hack = add_keymods('F10', evt); break;
-                    case 122: hack = add_keymods('F11', evt); break;
-                    case 123: hack = add_keymods('F12', evt); break;
-
-                    // Handle weird behavior for clipboard shortcuts
-                    // Which don't fire a keypress for some odd reason
-
-                    case 65:
-                        if (evt.ctrlKey === true) {
-                            pdgui.pdsend(name, "selectall");
-                            hack = 0; // not sure what to report here...
-                        }
-                        break;
-                    case 88:
-                        if (evt.ctrlKey === true) {
-                            pdgui.pdsend(name, "cut");
-                            hack = 0; // not sure what to report here...
-                        }
-                        break;
-                    case 67:
-                        if (evt.ctrlKey === true) {
-                            pdgui.pdsend(name, "copy");
-                            hack = 0; // not sure what to report here...
-                        }
-                        break;
-                    case 86:
-                        if (evt.ctrlKey === true) {
-                            pdgui.pdsend(name, "paste");
-                            hack = 0; // not sure what to report here...
-                        }
-                        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();
-                //evt.preventDefault();
-            },
-            keypress: function(evt) {
-                // Hack to handle undo/redo shortcuts
-                if (evt.charCode === 26) {
-                    if (evt.ctrlKey === true) {
-                        if (evt.shiftKey === true) { // ctrl-Shift-z
-                            pdgui.pdsend(name, "redo");
-                        } else { // ctrl-z
-                            pdgui.pdsend(name, "undo");
-                        }
-                        return;
-                    }
-                }
-
-                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();
-            },
-            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);
-                if (evt.keyCode === 13 && evt.ctrlKey) {
-                    pdgui.pdsend(name, "reselect");
-                }
-                evt.stopPropagation();
-                evt.preventDefault();
-            },
-            text_mousemove: function(evt) {
-                evt.stopPropagation();    
-                //evt.preventDefault();
-                return false;
-            },
-            text_mousedown: function(evt) {
-                if (textbox() !== evt.target) {
-                    // Yes: I _really_ want .innerText and NOT .textContent
-                    // here.  I want those newlines: although that isn't
-                    // standard in Pd-Vanilla, Pd-l2ork uses and preserves
-                    // them inside comments
-                    utils.create_obj();
-//                    var fudi_msg = text_to_fudi(textbox().innerText);
-//                    pdgui.pdsend(name, "createobj", fudi_msg);
-//                    pdgui.gui_post("formatted content is " + fudi_msg);
-                    events.mousedown(evt);
-                    canvas_events.normal();
-                }
-                evt.stopPropagation();    
-                //evt.preventDefault();
-                return false;
-            },
-            text_mouseup: function(evt) {
-                pdgui.gui_post("mouseup target is " +
-                    evt.target + " and textbox is " + textbox());
-                //evt.stopPropagation();    
-                //evt.preventDefault();
-                return false;
-            },
-            text_keydown: function(evt) {
-                evt.stopPropagation();    
-                //evt.preventDefault();
-                return false;
-            },
-            text_keyup: function(evt) {
-                evt.stopPropagation();    
-                //evt.preventDefault();
-                // ctrl-Enter to reselect
-                if (evt.keyCode === 13 && evt.ctrlKey) {
-                    canvas_events.set_obj();
-                    pdgui.pdsend(name, "reselect");
-                }
-                return false;
-            },
-            text_keypress: function(evt) {
-                evt.stopPropagation();    
-                //evt.preventDefault();
-                return false;
-            },
-            floating_text_click: function(evt) {
-                //pdgui.gui_post("leaving floating mode");
-                canvas_events.text();
-                evt.stopPropagation();
-                evt.preventDefault();
-                return false;
-            },
-            floating_text_keypress: function(evt) {
-                //pdgui.gui_post("leaving floating mode");
-                canvas_events.text();
-                //evt.stopPropagation();
-                //evt.preventDefault();
-                //return false;
-            }
-        },
-        utils = {
-            create_obj: function() {
-                var fudi_msg = text_to_fudi(textbox().innerText);
-                pdgui.pdsend(name, "createobj", fudi_msg);
-                //pdgui.gui_post("formatted content is " + fudi_msg);
-            },
-            set_obj: function() {
-                var fudi_msg = text_to_fudi(textbox().innerText);
-                pdgui.pdsend(name, "setobj", fudi_msg);
-                //pdgui.gui_post("formatted content is " + fudi_msg);
-            }
-        }
-    ;
-
-    // Dialog events
-    document.querySelector("#saveDialog").addEventListener("change",
-        function(evt) {
-            pdgui.saveas_callback(name, this.value);
-            // reset value so that we can open the same file twice
-            this.value = null;
-            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
-    );
-
-    // closing the Window
-    // this isn't actually closing the window yet
-    nw.Window.get().on("close", function() {
-        pdgui.pdsend(name, "menuclose 0");
-    });
-
-    return {
-        none: function() {
-            var name;
-            state = 'none';
-            for (var prop in events) {
-                if (events.hasOwnProperty(prop)) {
-                    name = prop.split('_');
-                    name = name[name.length -1];
-                    document.removeEventListener(name, events[prop], false);
-                }
-            }
-        },
-        normal: function() {
-            //pdgui.gui_post("resetting to normal...");
-            this.none();
-
-            document.addEventListener("mousemove", events.mousemove, false);
-            document.addEventListener("keydown", events.keydown, false);
-            document.addEventListener("keypress", events.keypress, false);
-            document.addEventListener("keyup", events.keyup, false);
-            document.addEventListener("mousedown", events.mousedown, false);
-            document.addEventListener("mouseup", events.mouseup, false);
-            state = 'normal';
-            set_edit_menu_modals(true);
-        },
-        text: function() {
-            this.none();
-
-            document.addEventListener("mousemove", events.text_mousemove, false);
-            document.addEventListener("keydown", events.text_keydown, false);
-            document.addEventListener("keypress", events.text_keypress, false);
-            document.addEventListener("keyup", events.text_keyup, false);
-            document.addEventListener("mousedown", events.text_mousedown, false);
-            document.addEventListener("mouseup", events.text_mouseup, false);
-            state = 'text';
-            set_edit_menu_modals(false);
-        },
-        floating_text: function() {
-            this.none();
-            this.text();
-            document.removeEventListener("mousedown", events.text_mousedown, false);
-            document.removeEventListener("mouseup", events.text_mouseup, false);
-            document.removeEventListener("keypress", events.text_keypress, false);
-            document.removeEventListener("mousemove", events.text_mousemove, false);
-            document.addEventListener("click", events.floating_text_click, false);
-            document.addEventListener("keypress", events.floating_text_keypress, false);
-            document.addEventListener("mousemove", events.mousemove, false);
-            state = 'floating_text';
-            set_edit_menu_modals(false);
-        },
-        register: function(n) {
-            name = n;
-        },
-        get_state: function() {
-            return state;
-        },
-        set_obj: function() {
-            utils.set_obj();
-        }
-    }
-}());
-
-// 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) {
-    name = cid; // hack
-    create_popup_menu(cid);
-    canvas_events.register(cid);
-    canvas_events.normal();
-    nw_create_patch_window_menus(cid);
-    pdgui.canvas_map(cid);
-}
-
-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);
-        }
-    }));
-}
-
-// stop-gap
-function menu_generic () {
-    alert("Please implement this");
-}
-
-var modals = {}; // Edit menu items that should be disabled when editing
-                 // an object box
-
-function set_edit_menu_modals(state) {
-    var item;
-    for (item in modals) {
-        if (modals.hasOwnProperty(item)) {
-            modals[item].enabled = state; 
-        }
-    }
-}
-
-function nw_undo_menu(undo_text, redo_text) {
-    if (undo_text === 'no') {
-        modals.undo.enabled = false;
-    } else {
-        modals.undo.enabled = true;
-        modals.undo.label = l('menu.undo') + " " + undo_text;
-    }
-    if (redo_text === 'no') {
-        modals.redo.enabled = false;
-    } else {
-        modals.redo.enabled = true;
-        modals.redo.label = l('menu.redo') + " " + redo_text;
-    }
-}
-
-function check_box_for_text() {
-    var state = canvas_events.get_state();
-    if (state === 'text' || state === 'floating_text') {
-        canvas_events.set_obj();
-    }
-}
-
-// 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.canvas_check_geometry(name);
-            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.canvas_check_geometry(name);
-            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);
-        },
-        key: 'w',
-        modifiers: "ctrl"
-    }));
-
-    // 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(modals.undo = new nw.MenuItem({
-        label: l('menu.undo'),
-        click: function () {
-            pdgui.pdsend(name, "undo");
-        },
-        tooltip: l('menu.undo_tt'),
-    }));
-
-    editMenu.append(modals.redo = new nw.MenuItem({
-        label: l('menu.redo'),
-        click: function () {
-            pdgui.pdsend(name, "redo");
-        },
-        tooltip: l('menu.redo_tt'),
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        type: 'separator'
-    }));
-
-    editMenu.append(modals.cut = new nw.MenuItem({
-        label: l('menu.cut'),
-        click: function () {
-            pdgui.pdsend(name, "cut");
-        },
-        tooltip: l('menu.cut_tt'),
-    }));
-
-    editMenu.append(modals.copy = new nw.MenuItem({
-        label: l('menu.copy'),
-        click: function () {
-            pdgui.pdsend(name, "copy");
-        },
-        tooltip: l('menu.copy_tt'),
-    }));
-
-    editMenu.append(modals.paste = new nw.MenuItem({
-        label: l('menu.paste'),
-        click: function () {
-            pdgui.pdsend(name, "paste");
-        },
-        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(modals.selectall = new nw.MenuItem({
-        label: l('menu.selectall'),
-        click: function (evt) {
-            if (canvas_events.get_state() === 'normal') {
-                pdgui.pdsend(name, "selectall");
-            }
-        },
-        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: function() {
-            pdgui.pdsend(name, "tidy");
-        },
-        key: 'y',
-        modifiers: "ctrl",
-        tooltip: l('menu.tidyup_tt')
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        label: l('menu.tofront'),
-        click: function() {
-            pdgui.popup_action(name, 3);
-        },
-        tooltip: l('menu.tofront_tt'),
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        label: l('menu.toback'),
-        click: function() {
-            pdgui.popup_action(name, 4);
-        },
-        tooltip: l('menu.toback_tt'),
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        type: 'separator'
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        label: l('menu.font'),
-        click: function () {
-            pdgui.pdsend(name, "menufont");
-        },
-        tooltip: l('menu.font_tt'),
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        label: l('menu.cordinspector'),
-        click: function() {
-            pdgui.pdsend(name, "magicglass 0");
-        },
-        key: 'r',
-        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: 'f',
-        modifiers: "ctrl",
-        tooltip: l('menu.find_tt'),
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        label: l('menu.findagain'),
-        click: menu_generic,
-        key: 'g',
-        modifiers: "ctrl",
-        tooltip: l('menu.findagain')
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        label: l('menu.finderror'),
-        click: function() {
-            pdgui.pdsend("pd finderror");
-        },
-        tooltip: l('menu.finderror_tt'),
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        type: 'separator'
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        label: l('menu.autotips'),
-        click: menu_generic,
-        tooltip: l('menu.autotips_tt'),
-    }));
-
-    editMenu.append(new nw.MenuItem({
-        label: l('menu.editmode'),
-        click: function() {
-            check_box_for_text();
-            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: pdgui.open_prefs,
-        key: 'p',
-        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() {
-            check_box_for_text();
-            pdgui.pdsend(name, "dirty 1");
-            pdgui.pdsend(name, "obj 0");
-        },
-        key: '1',
-        modifiers: "ctrl",
-        tooltip: l('menu.object_tt'),
-    }));
-
-    putMenu.append(new nw.MenuItem({
-        label: l('menu.msgbox'),
-        click: function() {
-            check_box_for_text();
-            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() { 
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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) {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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() {
-            check_box_for_text();
-            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");
-        },
-        tooltip: l('menu.graph_tt'),
-    }));
-
-    putMenu.append(new nw.MenuItem({
-        label: l('menu.array'),
-        click: function() {
-                check_box_for_text();
-                pdgui.pdsend(name, "dirty 1");
-                pdgui.pdsend(name, "menuarray");
-            },
-        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.fullscreen'),
-        click: function() {
-            var win = nw.Window.get();
-            var fullscreen = win.isFullscreen;
-            win.isFullscreen = !fullscreen;
-            pdgui.gui_post("fullscreen is " + fullscreen);
-        },
-        key: "f11",
-        //modifiers: "ctrl",
-        tooltip: l('menu.nextwin_tt'),
-    }));
-
-    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,
-        tooltip: l('menu.parentwin_tt'),
-    }));
-
-    winmanMenu.append(new nw.MenuItem({
-        label: l('menu.pdwin'),
-        click: menu_generic,
-        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: function() {
-            pdgui.pdsend("pd dsp 1");
-        },
-        key: '/',
-        modifiers: "ctrl",
-        tooltip: l('menu.audio_on_tt'),
-    }));
-
-    mediaMenu.append(new nw.MenuItem({
-        label: l('menu.audio_off'),
-        click: function() {
-            pdgui.pdsend("pd dsp 0");
-        },
-        key: '.',
-        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,
-        tooltip: l('menu.test_tt'),
-    }));
-
-    mediaMenu.append(new nw.MenuItem({
-        label: l('menu.loadmeter'),
-        click: menu_generic,
-        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,
-        tooltip: l('menu.manual'),
-    }));
-
-    helpMenu.append(new nw.MenuItem({
-        label: l('menu.browser'),
-        click: menu_generic,
-        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,
-        tooltip: l('menu.l2ork_list_tt'),
-    }));
-
-    helpMenu.append(new nw.MenuItem({
-        label: l('menu.pd_list'),
-        click: menu_generic,
-        tooltip: l('menu.pd_list_tt'),
-    }));
-
-    helpMenu.append(new nw.MenuItem({
-        label: l('menu.forums'),
-        click: menu_generic,
-        tooltip: l('menu.forums_tt'),
-    }));
-
-    helpMenu.append(new nw.MenuItem({
-        label: l('menu.irc'),
-        click: menu_generic,
-        tooltip: l('menu.irc_tt'),
-    }));
-
-    // Assign to window
-    nw.Window.get().menu = windowMenu;
-
-}
-
-  </script>
+    <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"
+         overflow="visible"
+         class="noselect">
+    </svg>
+    <script type="text/javascript" src="./pd_canvas.js"></script>
   </body>
 </html>
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
new file mode 100644
index 000000000..1982bb5b2
--- /dev/null
+++ b/pd/nw/pd_canvas.js
@@ -0,0 +1,1173 @@
+'use strict';
+var nw = require('nw.gui'); 
+var pdgui = require('./pdgui.js');
+
+// Apply gui preset to this canvas
+pdgui.skin.apply(this);
+
+//var name = pdgui.last_loaded();
+   
+var l = pdgui.get_local_string;
+
+console.log("my working dir 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 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 text_to_fudi(text) {
+    text = text.trim();
+    text = text.replace(/(\$[0-9]+)/g, '\\$1');    // escape dollar signs
+    text = text.replace(/(?!\\)(,|;)/g, ' \\$1 '); // escape ',' and ';'
+    text = text.replace(/\{|\}/g, '');             // filter '{' and '}'
+    text = text.replace(/\s+/g, ' ');              // filter consecutive /s
+
+    return text;
+}
+
+var canvas_events = (function() {
+    var name,
+        state,
+        svg_view = document.getElementById('patchsvg').viewBox.baseVal,
+        textbox = function () {
+            return document.getElementById('new_object_textentry');
+        },
+        events = {
+            mousemove: function(evt) {
+                //pdgui.gui_post("x: " + evt.pageX + " y: " + evt.pageY +
+                //    " modifier: " + (evt.shiftKey + (evt.ctrlKey << 1)));
+                pdgui.pdsend(name, "motion",
+                    (evt.pageX + svg_view.x),
+                    (evt.pageY + svg_view.y),
+                    (evt.shiftKey + (evt.ctrlKey << 1))
+                );
+                evt.stopPropagation();
+                evt.preventDefault();
+                return false;
+            },
+            mousedown: function(evt) {
+                // tk events (and, therefore, Pd evnets) 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 + svg_view.x),
+                    (evt.pageY + svg_view.y),
+                    b, mod
+                );
+                //evt.stopPropagation();
+                evt.preventDefault();
+            },
+            mouseup: function(evt) {
+                //pdgui.gui_post("mouseup: x: " +
+                //    evt.pageX + " y: " + evt.pageY +
+                //    " button: " + (evt.button + 1));
+                pdgui.pdsend(name, "mouseup",
+                    (evt.pageX + svg_view.x),
+                    (evt.pageY + svg_view.y),
+                    (evt.button + 1)
+                );
+                evt.stopPropagation();
+                evt.preventDefault();
+            },
+            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;
+
+                    // These may be different on Safari...
+                    case 112: hack = add_keymods('F1', evt); break;
+                    case 113: hack = add_keymods('F2', evt); break;
+                    case 114: hack = add_keymods('F3', evt); break;
+                    case 115: hack = add_keymods('F4', evt); break;
+                    case 116: hack = add_keymods('F5', evt); break;
+                    case 117: hack = add_keymods('F6', evt); break;
+                    case 118: hack = add_keymods('F7', evt); break;
+                    case 119: hack = add_keymods('F8', evt); break;
+                    case 120: hack = add_keymods('F9', evt); break;
+                    case 121: hack = add_keymods('F10', evt); break;
+                    case 122: hack = add_keymods('F11', evt); break;
+                    case 123: hack = add_keymods('F12', evt); break;
+
+                    // Handle weird behavior for clipboard shortcuts
+                    // Which don't fire a keypress for some odd reason
+
+                    case 65:
+                        if (evt.ctrlKey === true) {
+                            pdgui.pdsend(name, "selectall");
+                            hack = 0; // not sure what to report here...
+                        }
+                        break;
+                    case 88:
+                        if (evt.ctrlKey === true) {
+                            pdgui.pdsend(name, "cut");
+                            hack = 0; // not sure what to report here...
+                        }
+                        break;
+                    case 67:
+                        if (evt.ctrlKey === true) {
+                            pdgui.pdsend(name, "copy");
+                            hack = 0; // not sure what to report here...
+                        }
+                        break;
+                    case 86:
+                        if (evt.ctrlKey === true) {
+                            pdgui.pdsend(name, "paste");
+                            hack = 0; // not sure what to report here...
+                        }
+                        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();
+                //evt.preventDefault();
+            },
+            keypress: function(evt) {
+                // Hack to handle undo/redo shortcuts
+                if (evt.charCode === 26) {
+                    if (evt.ctrlKey === true) {
+                        if (evt.shiftKey === true) { // ctrl-Shift-z
+                            pdgui.pdsend(name, "redo");
+                        } else { // ctrl-z
+                            pdgui.pdsend(name, "undo");
+                        }
+                        return;
+                    }
+                }
+
+                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();
+            },
+            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);
+                if (evt.keyCode === 13 && evt.ctrlKey) {
+                    pdgui.pdsend(name, "reselect");
+                }
+                evt.stopPropagation();
+                evt.preventDefault();
+            },
+            text_mousemove: function(evt) {
+                evt.stopPropagation();    
+                //evt.preventDefault();
+                return false;
+            },
+            text_mousedown: function(evt) {
+                if (textbox() !== evt.target) {
+                    // Yes: I _really_ want .innerText and NOT .textContent
+                    // here.  I want those newlines: although that isn't
+                    // standard in Pd-Vanilla, Pd-l2ork uses and preserves
+                    // them inside comments
+                    utils.create_obj();
+//                    var fudi_msg = text_to_fudi(textbox().innerText);
+//                    pdgui.pdsend(name, "createobj", fudi_msg);
+//                    pdgui.gui_post("formatted content is " + fudi_msg);
+                    events.mousedown(evt);
+                    canvas_events.normal();
+                }
+                evt.stopPropagation();    
+                //evt.preventDefault();
+                return false;
+            },
+            text_mouseup: function(evt) {
+                pdgui.gui_post("mouseup target is " +
+                    evt.target + " and textbox is " + textbox());
+                //evt.stopPropagation();    
+                //evt.preventDefault();
+                return false;
+            },
+            text_keydown: function(evt) {
+                evt.stopPropagation();    
+                //evt.preventDefault();
+                return false;
+            },
+            text_keyup: function(evt) {
+                evt.stopPropagation();    
+                //evt.preventDefault();
+                // ctrl-Enter to reselect
+                if (evt.keyCode === 13 && evt.ctrlKey) {
+                    canvas_events.set_obj();
+                    pdgui.pdsend(name, "reselect");
+                }
+                return false;
+            },
+            text_keypress: function(evt) {
+                evt.stopPropagation();    
+                //evt.preventDefault();
+                return false;
+            },
+            floating_text_click: function(evt) {
+                //pdgui.gui_post("leaving floating mode");
+                canvas_events.text();
+                evt.stopPropagation();
+                evt.preventDefault();
+                return false;
+            },
+            floating_text_keypress: function(evt) {
+                //pdgui.gui_post("leaving floating mode");
+                canvas_events.text();
+                //evt.stopPropagation();
+                //evt.preventDefault();
+                //return false;
+            }
+        },
+        utils = {
+            create_obj: function() {
+                var fudi_msg = text_to_fudi(textbox().innerText);
+                pdgui.pdsend(name, "createobj", fudi_msg);
+                //pdgui.gui_post("formatted content is " + fudi_msg);
+            },
+            set_obj: function() {
+                var fudi_msg = text_to_fudi(textbox().innerText);
+                pdgui.pdsend(name, "setobj", fudi_msg);
+                //pdgui.gui_post("formatted content is " + fudi_msg);
+            }
+        }
+    ;
+
+    // Dialog events
+    document.querySelector("#saveDialog").addEventListener("change",
+        function(evt) {
+            pdgui.saveas_callback(name, this.value);
+            // reset value so that we can open the same file twice
+            this.value = null;
+            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
+    );
+
+    // closing the Window
+    // this isn't actually closing the window yet
+    nw.Window.get().on("close", function() {
+        pdgui.pdsend(name, "menuclose 0");
+    });
+
+    return {
+        none: function() {
+            var name;
+            state = 'none';
+            for (var prop in events) {
+                if (events.hasOwnProperty(prop)) {
+                    name = prop.split('_');
+                    name = name[name.length -1];
+                    document.removeEventListener(name, events[prop], false);
+                }
+            }
+        },
+        normal: function() {
+            //pdgui.gui_post("resetting to normal...");
+            this.none();
+
+            document.addEventListener("mousemove", events.mousemove, false);
+            document.addEventListener("keydown", events.keydown, false);
+            document.addEventListener("keypress", events.keypress, false);
+            document.addEventListener("keyup", events.keyup, false);
+            document.addEventListener("mousedown", events.mousedown, false);
+            document.addEventListener("mouseup", events.mouseup, false);
+            state = 'normal';
+            set_edit_menu_modals(true);
+        },
+        text: function() {
+            this.none();
+
+            document.addEventListener("mousemove", events.text_mousemove, false);
+            document.addEventListener("keydown", events.text_keydown, false);
+            document.addEventListener("keypress", events.text_keypress, false);
+            document.addEventListener("keyup", events.text_keyup, false);
+            document.addEventListener("mousedown", events.text_mousedown, false);
+            document.addEventListener("mouseup", events.text_mouseup, false);
+            state = 'text';
+            set_edit_menu_modals(false);
+        },
+        floating_text: function() {
+            this.none();
+            this.text();
+            document.removeEventListener("mousedown", events.text_mousedown, false);
+            document.removeEventListener("mouseup", events.text_mouseup, false);
+            document.removeEventListener("keypress", events.text_keypress, false);
+            document.removeEventListener("mousemove", events.text_mousemove, false);
+            document.addEventListener("click", events.floating_text_click, false);
+            document.addEventListener("keypress", events.floating_text_keypress, false);
+            document.addEventListener("mousemove", events.mousemove, false);
+            state = 'floating_text';
+            set_edit_menu_modals(false);
+        },
+        register: function(n) {
+            name = n;
+        },
+        get_state: function() {
+            return state;
+        },
+        set_obj: function() {
+            utils.set_obj();
+        }
+    }
+}());
+
+// 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) {
+    name = cid; // hack
+    create_popup_menu(cid);
+    canvas_events.register(cid);
+    canvas_events.normal();
+    nw_create_patch_window_menus(cid);
+    pdgui.canvas_map(cid);
+}
+
+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);
+        }
+    }));
+}
+
+// stop-gap
+function menu_generic () {
+    alert("Please implement this");
+}
+
+var modals = {}; // Edit menu items that should be disabled when editing
+                 // an object box
+
+function set_edit_menu_modals(state) {
+    var item;
+    for (item in modals) {
+        if (modals.hasOwnProperty(item)) {
+            modals[item].enabled = state; 
+        }
+    }
+}
+
+function nw_undo_menu(undo_text, redo_text) {
+    if (undo_text === 'no') {
+        modals.undo.enabled = false;
+    } else {
+        modals.undo.enabled = true;
+        modals.undo.label = l('menu.undo') + " " + undo_text;
+    }
+    if (redo_text === 'no') {
+        modals.redo.enabled = false;
+    } else {
+        modals.redo.enabled = true;
+        modals.redo.label = l('menu.redo') + " " + redo_text;
+    }
+}
+
+function check_box_for_text() {
+    var state = canvas_events.get_state();
+    if (state === 'text' || state === 'floating_text') {
+        canvas_events.set_obj();
+    }
+}
+
+// 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.canvas_check_geometry(name);
+            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.canvas_check_geometry(name);
+            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);
+        },
+        key: 'w',
+        modifiers: "ctrl"
+    }));
+
+    // 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(modals.undo = new nw.MenuItem({
+        label: l('menu.undo'),
+        click: function () {
+            pdgui.pdsend(name, "undo");
+        },
+        tooltip: l('menu.undo_tt'),
+    }));
+
+    editMenu.append(modals.redo = new nw.MenuItem({
+        label: l('menu.redo'),
+        click: function () {
+            pdgui.pdsend(name, "redo");
+        },
+        tooltip: l('menu.redo_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(modals.cut = new nw.MenuItem({
+        label: l('menu.cut'),
+        click: function () {
+            pdgui.pdsend(name, "cut");
+        },
+        tooltip: l('menu.cut_tt'),
+    }));
+
+    editMenu.append(modals.copy = new nw.MenuItem({
+        label: l('menu.copy'),
+        click: function () {
+            pdgui.pdsend(name, "copy");
+        },
+        tooltip: l('menu.copy_tt'),
+    }));
+
+    editMenu.append(modals.paste = new nw.MenuItem({
+        label: l('menu.paste'),
+        click: function () {
+            pdgui.pdsend(name, "paste");
+        },
+        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(modals.selectall = new nw.MenuItem({
+        label: l('menu.selectall'),
+        click: function (evt) {
+            if (canvas_events.get_state() === 'normal') {
+                pdgui.pdsend(name, "selectall");
+            }
+        },
+        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: function() {
+            pdgui.pdsend(name, "tidy");
+        },
+        key: 'y',
+        modifiers: "ctrl",
+        tooltip: l('menu.tidyup_tt')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.tofront'),
+        click: function() {
+            pdgui.popup_action(name, 3);
+        },
+        tooltip: l('menu.tofront_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.toback'),
+        click: function() {
+            pdgui.popup_action(name, 4);
+        },
+        tooltip: l('menu.toback_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.font'),
+        click: function () {
+            pdgui.pdsend(name, "menufont");
+        },
+        tooltip: l('menu.font_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.cordinspector'),
+        click: function() {
+            pdgui.pdsend(name, "magicglass 0");
+        },
+        key: 'r',
+        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: 'f',
+        modifiers: "ctrl",
+        tooltip: l('menu.find_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.findagain'),
+        click: menu_generic,
+        key: 'g',
+        modifiers: "ctrl",
+        tooltip: l('menu.findagain')
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.finderror'),
+        click: function() {
+            pdgui.pdsend("pd finderror");
+        },
+        tooltip: l('menu.finderror_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.autotips'),
+        click: menu_generic,
+        tooltip: l('menu.autotips_tt'),
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: l('menu.editmode'),
+        click: function() {
+            check_box_for_text();
+            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: pdgui.open_prefs,
+        key: 'p',
+        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() {
+            check_box_for_text();
+            pdgui.pdsend(name, "dirty 1");
+            pdgui.pdsend(name, "obj 0");
+        },
+        key: '1',
+        modifiers: "ctrl",
+        tooltip: l('menu.object_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.msgbox'),
+        click: function() {
+            check_box_for_text();
+            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() { 
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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) {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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() {
+            check_box_for_text();
+            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");
+        },
+        tooltip: l('menu.graph_tt'),
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: l('menu.array'),
+        click: function() {
+                check_box_for_text();
+                pdgui.pdsend(name, "dirty 1");
+                pdgui.pdsend(name, "menuarray");
+            },
+        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.fullscreen'),
+        click: function() {
+            var win = nw.Window.get();
+            var fullscreen = win.isFullscreen;
+            win.isFullscreen = !fullscreen;
+            pdgui.gui_post("fullscreen is " + fullscreen);
+        },
+        key: "f11",
+        //modifiers: "ctrl",
+        tooltip: l('menu.nextwin_tt'),
+    }));
+
+    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,
+        tooltip: l('menu.parentwin_tt'),
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: l('menu.pdwin'),
+        click: menu_generic,
+        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: function() {
+            pdgui.pdsend("pd dsp 1");
+        },
+        key: '/',
+        modifiers: "ctrl",
+        tooltip: l('menu.audio_on_tt'),
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: l('menu.audio_off'),
+        click: function() {
+            pdgui.pdsend("pd dsp 0");
+        },
+        key: '.',
+        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,
+        tooltip: l('menu.test_tt'),
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: l('menu.loadmeter'),
+        click: menu_generic,
+        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,
+        tooltip: l('menu.manual'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.browser'),
+        click: menu_generic,
+        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,
+        tooltip: l('menu.l2ork_list_tt'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.pd_list'),
+        click: menu_generic,
+        tooltip: l('menu.pd_list_tt'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.forums'),
+        click: menu_generic,
+        tooltip: l('menu.forums_tt'),
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: l('menu.irc'),
+        click: menu_generic,
+        tooltip: l('menu.irc_tt'),
+    }));
+
+    // Assign to window
+    nw.Window.get().menu = windowMenu;
+
+}
-- 
GitLab