|
12 | 12 | "use strict";
|
13 | 13 | (function(){
|
14 | 14 |
|
15 |
| - var currentJSFileName = "code.js"; |
16 |
| - var currentXMLFileName = "code_blocks.xml"; |
17 |
| - var loadFileCallback; |
| 15 | + // how many millisecs between checking a file for modification? |
| 16 | + const WATCH_INTERVAL = 1000; |
| 17 | + // types of file we accept |
| 18 | + const MIMETYPE_JS = {"application/javascript": [".js", ".js"], "text/plain": [".txt"]}; |
| 19 | + const MIMETYPE_XML = {"text/xml": [".xml"]}; |
18 | 20 |
|
19 |
| - const MIMETYPE_JS = ".js,.txt,application/javascript,text/plain"; |
20 |
| - const MIMETYPE_XML = ".xml,text/xml"; |
| 21 | + var currentJSFile = { |
| 22 | + name:"code.js", |
| 23 | + mimeTypes : MIMETYPE_JS, |
| 24 | + // also setValue, getValue, handle, lastModified |
| 25 | + }; |
| 26 | + var currentXMLFile = { |
| 27 | + name:"code_blocks.xml", |
| 28 | + mimeTypes : MIMETYPE_XML, |
| 29 | + }; |
| 30 | + var currentFiles = [currentJSFile, currentXMLFile]; |
| 31 | + |
| 32 | + var iconOpenFile; |
| 33 | + var iconSaveFile; |
| 34 | + // interval used when checking files for modification |
| 35 | + var watchInterval; |
| 36 | + // What we should do when clicking the 'openFile' button |
| 37 | + var openFileMode = "open"; // open, reload, watch, upload |
21 | 38 |
|
22 |
| - function init() { |
23 |
| - // Configuration |
24 | 39 |
|
25 | 40 |
|
26 |
| - // Add stuff we need |
27 |
| - Espruino.Core.App.addIcon({ |
| 41 | + function init() { |
| 42 | + currentJSFile.setValue = Espruino.Core.EditorJavaScript.setCode; |
| 43 | + currentJSFile.getValue = Espruino.Core.EditorJavaScript.getCode; |
| 44 | + currentXMLFile.setValue = Espruino.Core.EditorBlockly.setXML; |
| 45 | + currentXMLFile.getValue = Espruino.Core.EditorBlockly.getXML; |
| 46 | + |
| 47 | + // Open file icon |
| 48 | + var icon = { |
28 | 49 | id: "openFile",
|
29 | 50 | icon: "folder-open",
|
30 | 51 | title : "Open File",
|
|
35 | 56 | },
|
36 | 57 | click: function() {
|
37 | 58 | if (Espruino.Core.Code.isInBlockly())
|
38 |
| - loadFile(Espruino.Core.EditorBlockly.setXML, currentXMLFileName, MIMETYPE_XML); |
| 59 | + loadFile(currentXMLFile); |
39 | 60 | else
|
40 |
| - loadFile(Espruino.Core.EditorJavaScript.setCode, currentJSFileName, MIMETYPE_JS); |
| 61 | + loadFile(currentJSFile); |
41 | 62 | }
|
42 |
| - }); |
43 |
| - |
44 |
| - Espruino.Core.App.addIcon({ |
| 63 | + }; |
| 64 | + // Only add extra options if showOpenFilePicker is available |
| 65 | + if (typeof window.showOpenFilePicker === 'function') { |
| 66 | + icon.more = function() { |
| 67 | + var popup = Espruino.Core.App.openPopup({ |
| 68 | + id: "fileopenmodeselector", |
| 69 | + title: "Open File should...", |
| 70 | + contents: Espruino.Core.HTML.domList([{ |
| 71 | + icon : "icon-folder-open", title : "Open File", |
| 72 | + description : "Load file from disk", callback : function() { setOpenFileMode("open");popup.close(); } |
| 73 | + },{ |
| 74 | + icon : "icon-folder-open", title : "Reload File", |
| 75 | + description : "Reload the current file", callback : function() { setOpenFileMode("reload");popup.close(); } |
| 76 | + },{ |
| 77 | + icon : "icon-refresh", title : "Watch File", |
| 78 | + description : "Watch for changes", callback : function() { setOpenFileMode("watch");popup.close(); } |
| 79 | + },{ |
| 80 | + icon : "icon-refresh", title : "Watch and Upload", |
| 81 | + description : "Watch for changes and upload", callback : function() { setOpenFileMode("upload");popup.close(); } |
| 82 | + } |
| 83 | + ]), |
| 84 | + position: "center", |
| 85 | + }); |
| 86 | + }; |
| 87 | + icon.info = "open"; |
| 88 | + } |
| 89 | + iconOpenFile = Espruino.Core.App.addIcon(icon); |
| 90 | + // Save file icon |
| 91 | + iconSaveFile = Espruino.Core.App.addIcon({ |
45 | 92 | id: "saveFile",
|
46 | 93 | icon: "save",
|
47 | 94 | title : "Save File",
|
|
52 | 99 | },
|
53 | 100 | click: function() {
|
54 | 101 | if (Espruino.Core.Code.isInBlockly())
|
55 |
| - Espruino.Core.Utils.fileSaveDialog(convertToOS(Espruino.Core.EditorBlockly.getXML()), currentXMLFileName, setCurrentFileName); |
| 102 | + saveFile(currentXMLFile); |
56 | 103 | else
|
57 |
| - Espruino.Core.Utils.fileSaveDialog(convertToOS(Espruino.Core.EditorJavaScript.getCode()), currentJSFileName, setCurrentFileName); |
| 104 | + saveFile(currentJSFile); |
58 | 105 | }
|
59 | 106 | });
|
60 | 107 | }
|
61 | 108 |
|
| 109 | + function setOpenFileMode(mode) { |
| 110 | + iconOpenFile.setInfo(mode); |
| 111 | + openFileMode = mode; |
| 112 | + if (watchInterval) clearInterval(watchInterval); |
| 113 | + watchInterval = undefined; |
| 114 | + // if we're in |
| 115 | + if (openFileMode == "watch" || openFileMode == "upload") { |
| 116 | + watchInterval = setInterval(function() { |
| 117 | + currentFiles.forEach(readFileContents); |
| 118 | + }, WATCH_INTERVAL); |
| 119 | + } |
| 120 | + } |
| 121 | + |
62 | 122 | function setCurrentFileName(filename) {
|
63 | 123 | if (Espruino.Core.Code.isInBlockly()) {
|
64 |
| - currentXMLFileName = filename; |
| 124 | + currentXMLFile.name = filename; |
65 | 125 | } else {
|
66 |
| - currentJSFileName = filename; |
| 126 | + currentJSFile.name = filename; |
67 | 127 | }
|
68 | 128 | }
|
69 | 129 |
|
|
79 | 139 | return chars.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n");
|
80 | 140 | };
|
81 | 141 |
|
82 |
| - function loadFile(callback, filename, mimeType) { |
83 |
| - if (chrome.fileSystem) { |
| 142 | + function loadFile(currentFile) { |
| 143 | + currentFile.lastModified = undefined; |
| 144 | + /* if clicking the button should just reload the existing file, |
| 145 | + do that */ |
| 146 | + if (openFileMode == "reload" && currentFile.handle) { |
| 147 | + readFileContents(currentFile); |
| 148 | + return; |
| 149 | + } |
| 150 | + currentFile.handle = undefined; |
| 151 | + |
| 152 | + if (typeof window.showOpenFilePicker === 'function') { |
| 153 | + window.showOpenFilePicker({types: [{accept: currentFile.mimeTypes}]}). |
| 154 | + then(function(fileHandles) { |
| 155 | + var fileHandle = fileHandles[0]; |
| 156 | + if (fileHandle.name) { |
| 157 | + setCurrentFileName(fileHandle.name); |
| 158 | + } |
| 159 | + currentFile.handle = fileHandle; |
| 160 | + readFileContents(currentFile); |
| 161 | + }); |
| 162 | + } else if (chrome.fileSystem) { |
84 | 163 | // Chrome Web App / NW.js
|
85 |
| - chrome.fileSystem.chooseEntry({type: 'openFile', suggestedName:filename}, function(fileEntry) { |
| 164 | + chrome.fileSystem.chooseEntry({type: 'openFile', suggestedName:currentFile.name}, function(fileEntry) { |
86 | 165 | if (!fileEntry) return;
|
87 | 166 | if (fileEntry.name) setCurrentFileName(fileEntry.name);
|
88 | 167 | fileEntry.file(function(file) {
|
89 | 168 | var reader = new FileReader();
|
90 | 169 | reader.onload = function(e) {
|
91 |
| - callback(convertFromOS(e.target.result)); |
| 170 | + currentFile.setValue(convertFromOS(e.target.result)); |
92 | 171 | };
|
93 | 172 | reader.onerror = function() {
|
94 | 173 | Espruino.Core.Notifications.error("Error Loading", true);
|
|
97 | 176 | });
|
98 | 177 | });
|
99 | 178 | } else {
|
100 |
| - Espruino.Core.Utils.fileOpenDialog({id:"code",type:"text",mimeType:mimeType}, function(data, mimeType, fileName) { |
| 179 | + var mimeTypeList = Object.values(currentFile.mimeTypes) + "," + Object.keys(currentFile.mimeTypes); |
| 180 | + Espruino.Core.Utils.fileOpenDialog({id:"code",type:"text",mimeType:mimeTypeList}, function(data, mimeType, fileName) { |
101 | 181 | if (fileName) setCurrentFileName(fileName);
|
102 |
| - callback(convertFromOS(data)); |
| 182 | + currentFile.setValue(convertFromOS(data)); |
103 | 183 | });
|
104 | 184 | }
|
105 | 185 | }
|
106 | 186 |
|
107 | 187 |
|
| 188 | + // read a file from window.showOpenFilePicker |
| 189 | + function readFileContents(currentFile) { |
| 190 | + if (!currentFile.handle) { |
| 191 | + return; |
| 192 | + } |
| 193 | + |
| 194 | + var file; |
| 195 | + currentFile.handle.getFile().then(function(f) { |
| 196 | + file = f; |
| 197 | + // if file is newer, proceed to load it |
| 198 | + if (!currentFile.lastModified || |
| 199 | + file.lastModified > currentFile.lastModified) |
| 200 | + return file.text(); |
| 201 | + else |
| 202 | + return undefined; |
| 203 | + }).then(function(contents) { |
| 204 | + if (!contents) return; |
| 205 | + // if loaded, update editor |
| 206 | + currentFile.lastModified = file.lastModified; |
| 207 | + currentFile.setValue(convertFromOS(contents)); |
| 208 | + if (openFileMode == "upload") { |
| 209 | + Espruino.Core.Notifications.info(new Date().toLocaleTimeString() + ": " + currentFile.name+" changed, uploading..."); |
| 210 | + Espruino.Plugins.KeyShortcuts.action("icon-deploy"); |
| 211 | + } |
| 212 | + }); |
| 213 | + } |
| 214 | + |
| 215 | + function saveFile(currentFile) { |
| 216 | + /* TODO: if currentFile.handle, we could write direct to this |
| 217 | + file without the dialog. But then what do we do for 'save as'? The down-arrow |
| 218 | + next to the icon? */ |
| 219 | + Espruino.Core.Utils.fileSaveDialog(convertToOS(currentFile.getValue()), currentFile.name, function(name) { |
| 220 | + currentFile.name = name; |
| 221 | + }); |
| 222 | + } |
| 223 | + |
108 | 224 | Espruino.Core.File = {
|
109 | 225 | init : init
|
110 | 226 | };
|
|
0 commit comments