diff --git a/package-lock.json b/package-lock.json index 115a786..9417f9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -775,9 +775,9 @@ } }, "electron": { - "version": "11.2.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-11.2.3.tgz", - "integrity": "sha512-6yxOc42nDAptHKNlUG/vcOh2GI9x2fqp2nQbZO0/3sz2CrwsJkwR3i3oMN9XhVJaqI7GK1vSCJz0verOkWlXcQ==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-11.3.0.tgz", + "integrity": "sha512-MhdS0gok3wZBTscLBbYrOhLaQybCSAfkupazbK1dMP5c+84eVMxJE/QGohiWQkzs0tVFIJsAHyN19YKPbelNrQ==", "dev": true, "requires": { "@electron/get": "^1.0.1", @@ -1249,9 +1249,9 @@ } }, "globalthis": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz", - "integrity": "sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", "dev": true, "optional": true, "requires": { @@ -1499,9 +1499,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index 859e2d9..7fcb72c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "homepage": "https://github.com/ocogeclub/ocoge#readme", "devDependencies": { - "electron": "11.2.3", + "electron": "11.3.0", "electron-rebuild": "^2.3.4" }, "dependencies": { diff --git a/scripts/eleclib.js b/scripts/eleclib.js index 173d418..48cbd3f 100644 --- a/scripts/eleclib.js +++ b/scripts/eleclib.js @@ -18,7 +18,7 @@ const clipboard = require('electron').clipboard; var saveFilepath = null; var wsChanged = false; -var mascotFilePath = './img/cogechee.png'; +var mascotFilePath = mascotDefPath + 'cogechee.png'; // 0で数値の桁合わせ // NUM=値 LEN=桁数 @@ -26,10 +26,10 @@ const zeroPadding = (NUM, LEN) => (Array(LEN).join('0') + NUM).slice(-LEN); // 現在の日付時刻から workspace フォルダ内のユニークなファイルパスを作成 const getUniqueFilepath = () => { - let today = new Date(); - let filename = today.getFullYear() + '-' + zeroPadding((today.getMonth() + 1), 2) + '-' + zeroPadding(today.getDate(), 2) + '-' + zeroPadding(today.getHours(), 2) + '-' + zeroPadding(today.getMinutes(), 2) + '-' + zeroPadding(today.getSeconds(), 2); - let filepath = path.join(defpath, filename); - return filepath; + let today = new Date(); + let filename = today.getFullYear() + '-' + zeroPadding((today.getMonth() + 1), 2) + '-' + zeroPadding(today.getDate(), 2) + '-' + zeroPadding(today.getHours(), 2) + '-' + zeroPadding(today.getMinutes(), 2) + '-' + zeroPadding(today.getSeconds(), 2); + let filepath = path.join(defpath, filename); + return filepath; } // クリップボードにコピー @@ -37,7 +37,7 @@ exports.copyText = text => clipboard.writeText(text); // リンクを外部ブラウザで開く exports.openURL = (url) => { - shell.openExternal(url); + shell.openExternal(url); } // タイトルバーにファイル名を表示 @@ -50,23 +50,23 @@ exports.openURL = (url) => { // saveFilepath を更新 // ウィンドウタイトルバーテキストを変更 const setSaveFilepath = filepath => { - let title; - saveFilepath = filepath; - // if (filepath) title = filepath + ' - ' + appName; - // else title = appName; - // mainWin.setTitle(title); - this.setWsChanged(false); + let title; + saveFilepath = filepath; + // if (filepath) title = filepath + ' - ' + appName; + // else title = appName; + // mainWin.setTitle(title); + this.setWsChanged(false); } // ワークスペースが変更された・保存された // ウィンドウタイトルバーテキストを変更 exports.setWsChanged = changed => { - let title; - wsChanged = changed; - if (saveFilepath) title = saveFilepath + ' - ' + appName; - else title = appName; - if (changed) title = '*' + title; - mainWin.setTitle(title); + let title; + wsChanged = changed; + if (saveFilepath) title = saveFilepath + ' - ' + appName; + else title = appName; + if (changed) title = '*' + title; + mainWin.setTitle(title); } // 保存ファイルプロパティを更新 @@ -74,159 +74,161 @@ exports.newFile = () => setSaveFilepath(null); // ワークスペースファイル読み込みの一連の動作のラッパ exports.loadWsFile = () => { - let filepath = openFile('xml', defpath); - if (filepath.length > 0) { - if (saveFilepath === null) { - setSaveFilepath(filepath); - } //読み込みに失敗してもsaveFilepathが更新されてしまうのはちょっと具合が悪いかも - return readFromFile(filepath); - } else { - return ''; - } + let filepath = openFile('xml', defpath); + if (filepath.length > 0) { + if (saveFilepath === null) { + setSaveFilepath(filepath); + } //読み込みに失敗してもsaveFilepathが更新されてしまうのはちょっと具合が悪いかも + return readFromFile(filepath); + } else { + return ''; + } } // その他ファイル読み込みの一連の動作のラッパ exports.loadFile = ext => { - let filepath = openFile(ext, defpath); - if (filepath.length > 0) { - return readFromFile(filepath); - } else { - return ''; - } + let filepath = openFile(ext, defpath); + if (filepath.length > 0) { + return readFromFile(filepath); + } else { + return ''; + } } exports.selectMascotFile = () => { - return openFile('png', mascotDefPath); + return openFile('png', mascotDefPath); } // オープンファイルダイアログ const openFile = (ext, dpath) => { - let filter; - if (ext == 'xml') { - filter = { name: 'XML - Extensible Markup Language', extensions: ['xml'] }; - } else if (ext == 'js') { - filter = { name: 'JS - JavaScript', extensions: ['js'] }; - } else if (ext == 'png') { - filter = { name: 'PNG - Portable Network Graphics', extensions: ['png'] }; - } else { - filter = { name: 'text file', extensions: ['txt'] }; - } - let filepaths = dialog.showOpenDialogSync(mainWin, { - properties: ['openFile'], - title: 'Select a file', - defaultPath: dpath, - filters: [ - filter - ] - }); - if (filepaths == undefined) { - return ''; - } else { - return filepaths[0]; - } + let filter; + if (ext == 'xml') { + filter = { name: 'XML - Extensible Markup Language', extensions: ['xml'] }; + } else if (ext == 'js') { + filter = { name: 'JS - JavaScript', extensions: ['js'] }; + } else if (ext == 'png') { + filter = { name: 'PNG - Portable Network Graphics', extensions: ['png'] }; + } else { + filter = { name: 'text file', extensions: ['txt'] }; + } + let filepaths = dialog.showOpenDialogSync(mainWin, { + properties: ['openFile'], + title: 'Select a file', + defaultPath: dpath, + filters: [ + filter + ] + }); + if (filepaths == undefined) { + return ''; + } else { + return filepaths[0]; + } } // ファイルからデータを読み込み const readFromFile = filepath => { - let data = ''; - try { - data = fs.readFileSync(filepath, 'utf-8'); - } - catch (err) { - console.log(err); - } - return data; + let data = ''; + try { + data = fs.readFileSync(filepath, 'utf-8'); + } + catch (err) { + console.log(err); + } + return data; } // テキストファイル読み込み: 外部スクリプト動的読み込みに使用 exports.readTextFile = filepath => { - return readFromFile(filepath); + return readFromFile(filepath); } // ワークスペースファイル保存の一連の動作のラッパ exports.saveWsFile = data => { - if (saveFilepath === null) { - let filepath = selectSaveFile('xml'); - if (filepath === undefined) { //キャンセル - return undefined; - } else { - setSaveFilepath(filepath); - } //これも保存が成功したら変更するようにすべきかしら - } else this.setWsChanged(false); - return writeToFile(saveFilepath, data); + if (saveFilepath === null) { + let filepath = selectSaveFile('xml'); + if (filepath === undefined) { //キャンセル + return undefined; + } else { + setSaveFilepath(filepath); + } //これも保存が成功したら変更するようにすべきかしら + } else this.setWsChanged(false); + return writeToFile(saveFilepath, data); } // その他ファイル保存の一連の動作のラッパ exports.saveFile = (data, ext) => { - let filepath = selectSaveFile(ext); - if (filepath === undefined) { //キャンセル - return undefined; - } - return writeToFile(filepath, data); + let filepath = selectSaveFile(ext); + if (filepath === undefined) { //キャンセル + return undefined; + } + return writeToFile(filepath, data); } // ファイル保存ダイアログ const selectSaveFile = ext => { - let filter; - let defName; - if (ext == 'xml') { - filter = { name: 'xml file', extensions: ['xml'] }; - defName = getUniqueFilepath() + '.xml'; - } else if (ext == 'js') { - filter = { name: 'javascript file', extensions: ['js'] }; - // ワークスペース保存名がある場合、それをベースにファイル名の候補を決める - if (saveFilepath === null) { - defName = getUniqueFilepath() + '.js'; - } else { - let dirname = path.dirname(saveFilepath); - let basename = path.basename(saveFilepath, '.xml'); - defName = path.join(dirname, basename) + '.js'; - } + let filter; + let defName; + if (ext == 'xml') { + filter = { name: 'xml file', extensions: ['xml'] }; + defName = getUniqueFilepath() + '.xml'; + } else if (ext == 'js') { + filter = { name: 'javascript file', extensions: ['js'] }; + // ワークスペース保存名がある場合、それをベースにファイル名の候補を決める + if (saveFilepath === null) { + defName = getUniqueFilepath() + '.js'; } else { - filter = { name: 'text file', extensions: ['txt'] }; + let dirname = path.dirname(saveFilepath); + let basename = path.basename(saveFilepath, '.xml'); + defName = path.join(dirname, basename) + '.js'; } - let filename = dialog.showSaveDialogSync(mainWin, { - title: '保存先を決定してください', - defaultPath: defName, - filters: [filter] - }); - return filename; + } else { + filter = { name: 'text file', extensions: ['txt'] }; + } + let filename = dialog.showSaveDialogSync(mainWin, { + title: '保存先を決定してください', + defaultPath: defName, + filters: [filter] + }); + return filename; } // ファイル書き込み const writeToFile = (filepath, data) => { - try { - fs.writeFileSync(filepath, data); - return true; - } - catch (err) { - return false; - } + try { + fs.writeFileSync(filepath, data); + return true; + } + catch (err) { + return false; + } } // 子プロセス関連 let children = []; // 新しい子プロセスを作成し、配列に保存 exports.addChild = (child) => { - children.push(child); + children.push(child); } // 全ての子プロセスを殺し、配列をクリア exports.killAllChildren = () => { - children.forEach(function( child ) { - child.kill(); - }); - children = []; + children.forEach(function (child) { + child.kill(); + }); + children = []; } // 設定(保存ファイルパスと未保存フラグ)をローカルストレージに保存 exports.savePrefsToLS = () => { - let wc = '0'; - if (wsChanged) wc = '1'; - let o = { 'saveFilepath': saveFilepath, 'wsChanged': wc, 'mascotFilePath': mascotFilePath }; - let s = JSON.stringify(o); - localStorage.setItem("abrage.json", s); + let wc = '0'; + if (wsChanged) wc = '1'; + let o = { 'saveFilepath': saveFilepath, 'wsChanged': wc, 'mascotFilePath': mascotFilePath }; + let s = JSON.stringify(o); + localStorage.setItem("ocoge.json", s); } // 設定(保存ファイルパスと未保存フラグ)をローカルストレージからロード exports.loadPrefsFromLS = () => { - let s = localStorage.getItem("abrage.json"); + let s = localStorage.getItem("ocoge.json"); + if (s !== null) { let o = JSON.parse(s); setSaveFilepath(o.saveFilepath); if (o.wsChanged == '0') this.setWsChanged(false); else this.setWsChanged(true); if (o.mascotFilePath) this.setMascotFilePath(o.mascotFilePath); + } } // マスコット画像パスをプロパティにセット @@ -236,5 +238,5 @@ exports.getMascotFilePath = () => mascotFilePath; // ファイル名にアプリケーションのドキュメントルートまでのパスをつけて返す exports.getDocPath = filename => { - return path.join(appDocRoot, filename); + return path.join(appDocRoot, filename); } diff --git a/scripts/ugj_script.js b/scripts/ugj_script.js index 8ac70e5..3fc0619 100644 --- a/scripts/ugj_script.js +++ b/scripts/ugj_script.js @@ -1,5 +1,5 @@ const testfunc = () => { - elec.openURL('http://ocoge.club'); + elec.openURL('http://ocoge.club'); } //============ User Customize Start =============== @@ -20,48 +20,48 @@ var snippets_color = '90'; // Blockly.Msg.UGJ_SNIPPETS_HUE = 90; var theme = Blockly.Theme.defineTheme('ocoge', { - 'base': Blockly.Themes.Classic, - 'startHats': true, - 'componentStyles': { - 'toolboxBackgroundColour': 'aliceblue', - 'flyoutBackgroundColour': 'lavender', - 'toolboxForegroundColour': 'white', - 'flyoutForegroundColour': 'steelblue' + 'base': Blockly.Themes.Classic, + 'startHats': true, + 'componentStyles': { + 'toolboxBackgroundColour': 'aliceblue', + 'flyoutBackgroundColour': 'lavender', + 'toolboxForegroundColour': 'white', + 'flyoutForegroundColour': 'steelblue' + }, + 'blockStyles': { + 'gpio_blocks': { + "colourPrimary": gpio_color }, - 'blockStyles': { - 'gpio_blocks': { - "colourPrimary": gpio_color - }, - 'multimedia_blocks': { - "colourPrimary": multimedia_color - }, - 'network_blocks': { - "colourPrimary": network_color - }, - 'special_blocks': { - "colourPrimary": special_color - }, - 'snippets_blocks': { - "colourPrimary": snippets_color - } + 'multimedia_blocks': { + "colourPrimary": multimedia_color }, - 'categoryStyles': { - "gpio_category": { - "colour": gpio_color - }, - "multimedia_category": { - "colour": multimedia_color - }, - "network_category": { - "colour": network_color - }, - "special_category": { - "colour": special_color - }, - "snippets_category": { - "colour": snippets_color - } + 'network_blocks': { + "colourPrimary": network_color }, + 'special_blocks': { + "colourPrimary": special_color + }, + 'snippets_blocks': { + "colourPrimary": snippets_color + } + }, + 'categoryStyles': { + "gpio_category": { + "colour": gpio_color + }, + "multimedia_category": { + "colour": multimedia_color + }, + "network_category": { + "colour": network_color + }, + "special_category": { + "colour": special_color + }, + "snippets_category": { + "colour": snippets_color + } + }, }); // Customize messages @@ -73,64 +73,64 @@ Blockly.Msg["TEXT_PRINT_TITLE"] = "ダイアログに %1 を表示"; // Customize Toolbox class CustomCategory extends Blockly.ToolboxCategory { - /** Constructor for a custom category. @override */ - constructor(categoryDef, toolbox, opt_parent) { - super(categoryDef, toolbox, opt_parent); - } - /** @override */ - addColourBorder_(colour) { - this.rowDiv_.style.backgroundColor = colour; - } - /** @override */ - setSelected(isSelected) { - // We do not store the label span on the category, so use getElementsByClassName. - var labelDom = this.rowDiv_.getElementsByClassName('blocklyTreeLabel')[0]; - if (isSelected) { - // Change the background color of the div to white. - this.rowDiv_.style.backgroundColor = 'white'; - // Set the colour of the text to the colour of the category. - labelDom.style.color = this.colour_; - this.iconDom_.style.color = this.colour_; - } else { - // Set the background back to the original colour. - this.rowDiv_.style.backgroundColor = this.colour_; - // Set the text back to white. - labelDom.style.color = 'white'; - this.iconDom_.style.color = 'white'; - } - // This is used for accessibility purposes. - Blockly.utils.aria.setState(/** @type {!Element} */(this.htmlDiv_), - Blockly.utils.aria.State.SELECTED, isSelected); + /** Constructor for a custom category. @override */ + constructor(categoryDef, toolbox, opt_parent) { + super(categoryDef, toolbox, opt_parent); + } + /** @override */ + addColourBorder_(colour) { + this.rowDiv_.style.backgroundColor = colour; + } + /** @override */ + setSelected(isSelected) { + // We do not store the label span on the category, so use getElementsByClassName. + var labelDom = this.rowDiv_.getElementsByClassName('blocklyTreeLabel')[0]; + if (isSelected) { + // Change the background color of the div to white. + this.rowDiv_.style.backgroundColor = 'white'; + // Set the colour of the text to the colour of the category. + labelDom.style.color = this.colour_; + this.iconDom_.style.color = this.colour_; + } else { + // Set the background back to the original colour. + this.rowDiv_.style.backgroundColor = this.colour_; + // Set the text back to white. + labelDom.style.color = 'white'; + this.iconDom_.style.color = 'white'; } + // This is used for accessibility purposes. + Blockly.utils.aria.setState(/** @type {!Element} */(this.htmlDiv_), + Blockly.utils.aria.State.SELECTED, isSelected); + } } Blockly.registry.register( - Blockly.registry.Type.TOOLBOX_ITEM, - Blockly.ToolboxCategory.registrationName, - CustomCategory, true); + Blockly.registry.Type.TOOLBOX_ITEM, + Blockly.ToolboxCategory.registrationName, + CustomCategory, true); //============ User Customize End =============== //背景canvasの準備 const ugj_canvasBgImg = (imgSrc, x, y) => { //x,y == -1: center or middle - let el = document.getElementById('canvas_bg'); - let ctx = el.getContext('2d'); - ctx.fillStyle = 'rgb(255,255,255)'; - ctx.fillRect(0,0,480,360); - let img = new Image(); - img.src = imgSrc; - img.onload = () => { - if (x<0) { //センタリング - let w = img.width; - if (w>=480) x=0; - else x = Math.floor((480 - w) / 2); - } - if (y<0) { //縦中寄せ - let h = img.height; - if (h>=360) y=0; - else y = Math.floor((360 - h) /2); - } - ctx.drawImage(img, x, y); + let el = document.getElementById('canvas_bg'); + let ctx = el.getContext('2d'); + ctx.fillStyle = 'rgb(255,255,255)'; + ctx.fillRect(0, 0, 480, 360); + let img = new Image(); + img.src = imgSrc; + img.onload = () => { + if (x < 0) { //センタリング + let w = img.width; + if (w >= 480) x = 0; + else x = Math.floor((480 - w) / 2); } + if (y < 0) { //縦中寄せ + let h = img.height; + if (h >= 360) y = 0; + else y = Math.floor((360 - h) / 2); + } + ctx.drawImage(img, x, y); + } }; // マスコット // ugj_canvasBgImg("./img/mimmy.png?" + new Date().getTime()); // ミミィ @@ -145,181 +145,183 @@ ugjel_inputBox = document.getElementById('inputBox'); // 入力フィールド // その他のプロパティ ugj_inputEvLstnrID = 0; // 入力フォームの動的イベントリスナの最新のID ugj_sounds = (names => { // サウンドファイルのいろいろの配列の初期化 - let sounds = []; - names.forEach(value => { - let filepath = `./sounds/${value}.wav`; - sounds[value] = { 'file': filepath, 'audio': new Audio(filepath) }; - }); - return sounds; + let sounds = []; + names.forEach(value => { + let filepath = `./sounds/${value}.wav`; + sounds[value] = { 'file': filepath, 'audio': new Audio(filepath) }; + }); + return sounds; })(['meow', 'bounce', 'type_chime', 'type_dink', 'type_tap', 'type_space', 'type_return']); // サウンドファイルのベース名のリスト // メソッド // マスコット選択 const ugj_selectMascot = () => { - let fname = elec.selectMascotFile(); - if (fname) { - ugj_canvasBgImg(fname, -1, -1); - elec.setMascotFilePath(fname); - } + let fname = elec.selectMascotFile(); + if (fname) { + ugj_canvasBgImg(fname, -1, -1); + elec.setMascotFilePath(fname); + } } // サウンド再生 - 連続再生のため、再生開始後すぐにオーディオ要素を再生成する const ugj_soundPlay = soundName => { - ugj_sounds[soundName]['audio'].play(); - ugj_sounds[soundName]['audio'] = new Audio(ugj_sounds[soundName]['file']); + ugj_sounds[soundName]['audio'].play(); + ugj_sounds[soundName]['audio'] = new Audio(ugj_sounds[soundName]['file']); }; // OK,Cancel 2択のダイアログを表示 const ugj_confirm = (title, message, callback) => { - CustomDialog.show(title, message, { - showOkay: true, - onOkay: () => callback(true), - showCancel: true, - onCancel: () => callback(false) - }); + CustomDialog.show(title, message, { + showOkay: true, + onOkay: () => callback(true), + showCancel: true, + onCancel: () => callback(false) + }); }; const ugj_htmlEntities = str =>// HTMLエンティティのエスケープ - String(str).replace(//g, '>').replace(/"/g, '"');//.replace(/&/g, '&').replace(/ /g, ' '); + String(str).replace(//g, '>').replace(/"/g, '"');//.replace(/&/g, '&').replace(/ /g, ' '); // 新規ワークスペース const ugj_newWorkspace = () => { - ugj_confirm('新規ワークスペース', '保存していない内容はすべて破棄されます。よろしいですか?', okey => { - if (okey) { - workspace.clear(); - elec.newFile(); - } - }); + ugj_confirm('新規ワークスペース', '保存していない内容はすべて破棄されます。よろしいですか?', okey => { + if (okey) { + workspace.clear(); + elec.newFile(); + } + }); } // ワークスペースをファイルに保存・読込 const ugj_saveWorkspaceToFile = () => { - let xml = Blockly.Xml.workspaceToDom(workspace); - let xml_text = Blockly.Xml.domToText(xml); - if (elec.saveWsFile(xml_text) === false) { - alert('保存できませんでした。'); - } + let xml = Blockly.Xml.workspaceToDom(workspace); + let xml_text = Blockly.Xml.domToText(xml); + if (elec.saveWsFile(xml_text) === false) { + alert('保存できませんでした。'); + } } const ugj_loadWorkspaceFromFile = () => { - let xml_text = elec.loadWsFile(); - if (xml_text.length > 0) { - let xml = Blockly.Xml.textToDom(xml_text); - Blockly.Xml.domToWorkspace(xml, workspace); - } + let xml_text = elec.loadWsFile(); + if (xml_text.length > 0) { + let xml = Blockly.Xml.textToDom(xml_text); + Blockly.Xml.domToWorkspace(xml, workspace); + } } // ワークスペースを別名で保存 const ugj_saveWorkspaceAs = () => { - elec.newFile(); - ugj_saveWorkspaceToFile(); + elec.newFile(); + ugj_saveWorkspaceToFile(); } // ワークスペースをローカルストレージに保存・読込 const ugj_saveWorkspace = () => { - // Workspace - let xml = Blockly.Xml.workspaceToDom(workspace); - let xml_text = Blockly.Xml.domToText(xml); - localStorage.setItem("abrage.xml", xml_text); + // Workspace + let xml = Blockly.Xml.workspaceToDom(workspace); + let xml_text = Blockly.Xml.domToText(xml); + localStorage.setItem("ocoge.xml", xml_text); } const ugj_loadWorkspace = () => { - // Workspace - let xml_text = localStorage.getItem("abrage.xml"); + // Workspace + let xml_text = localStorage.getItem("ocoge.xml"); + if (xml_text !== null) { if (xml_text.length != 0) { - let xml = Blockly.Xml.textToDom(xml_text); - Blockly.Xml.domToWorkspace(xml, workspace); + let xml = Blockly.Xml.textToDom(xml_text); + Blockly.Xml.domToWorkspace(xml, workspace); } + } } // ワークスペースからコードを生成して必要であれば整形処理をする const ugj_createCode = (args) => { - addAsync = args.async || false; - beautify = args.beautify || false; + addAsync = args.async || false; + beautify = args.beautify || false; - let code = Blockly.JavaScript.workspaceToCode(workspace); + let code = Blockly.JavaScript.workspaceToCode(workspace); - // 追加モジュールのrequire - let requires = [ - ['_gpio_', false, `const pi = require('ocoge_pigpiod');\n`], - ['_axios_', false, `const axios = require('axios');\n`], - ['_sendmail', false, `const nodemailer = require('nodemailer');\n`], - ['_cloudspeech_', false, `const speech = require('@google-cloud/speech');\nconst recorder = require('node-record-lpcm16');\n`], - ['_httpserver', false, `const http = require('http');\n`], - ['_file_', false, `const fs = require('fs');\n`], - // ['_bme280', false, `const BME280 = require('bme280-sensor');`], - ['_dht', false, `const dht = require("node-dht-sensor").promises;`], - ['_socket_', false, `const net = require('net');`] - ]; - let blockArray = workspace.getAllBlocks(); - blockArray.forEach(value => { - for (var i = 0, l = requires.length; i < l; i++) { - if (value.type.indexOf(requires[i][0]) >= 0) requires[i][1] = true; - } - }); + // 追加モジュールのrequire + let requires = [ + ['_gpio_', false, `const pi = require('ocoge_pigpiod');\n`], + ['_axios_', false, `const axios = require('axios');\n`], + ['_sendmail', false, `const nodemailer = require('nodemailer');\n`], + ['_cloudspeech_', false, `const speech = require('@google-cloud/speech');\nconst recorder = require('node-record-lpcm16');\n`], + ['_httpserver', false, `const http = require('http');\n`], + ['_file_', false, `const fs = require('fs');\n`], + // ['_bme280', false, `const BME280 = require('bme280-sensor');`], + ['_dht', false, `const dht = require("node-dht-sensor").promises;`], + ['_socket_', false, `const net = require('net');`] + ]; + let blockArray = workspace.getAllBlocks(); + blockArray.forEach(value => { for (var i = 0, l = requires.length; i < l; i++) { - if (requires[i][1]) code = requires[i][2] + code; + if (value.type.indexOf(requires[i][0]) >= 0) requires[i][1] = true; } + }); + for (var i = 0, l = requires.length; i < l; i++) { + if (requires[i][1]) code = requires[i][2] + code; + } - // await使用のため、必要に応じてコード全体をasync付き即時関数でラップ - if (addAsync) { - code = [ - '(async () => {', - code, - '})();' - ].join('\n'); - } + // await使用のため、必要に応じてコード全体をasync付き即時関数でラップ + if (addAsync) { + code = [ + '(async () => {', + code, + '})();' + ].join('\n'); + } - // コードを綺麗に - if (beautify) code = js_beautify(code, { indent_size: 2 }); + // コードを綺麗に + if (beautify) code = js_beautify(code, { indent_size: 2 }); - return code; + return code; } // ブロックスクリプト実行 const ugj_runCode = () => { - document.activeElement.blur(); //実行ボタンからフォーカスを外す:エンターキー押下が悪さをするため - let code = ugj_createCode({ 'async': true }); - try { - eval(code); - } catch (e) { - alert(e); - } + document.activeElement.blur(); //実行ボタンからフォーカスを外す:エンターキー押下が悪さをするため + let code = ugj_createCode({ 'async': true }); + try { + eval(code); + } catch (e) { + alert(e); + } } // JavaScriptコードをダイアログで表示・保存 // エレメントのオブジェクトとかコールバックとか // 色々この中で完結させてみる const ugj_showCode = () => { - const dialog = document.getElementById('codeDlg'); - const content = document.getElementById('dlgContent'); - const btn_close = document.getElementById('dlgClose'); - const btn_export = document.getElementById('dlgExport'); - const chkbox_cli = document.getElementById('dlgCli'); + const dialog = document.getElementById('codeDlg'); + const content = document.getElementById('dlgContent'); + const btn_close = document.getElementById('dlgClose'); + const btn_export = document.getElementById('dlgExport'); + const chkbox_cli = document.getElementById('dlgCli'); - let code = ugj_createCode({ 'beautify': true }); - code = ugj_htmlEntities(code); - content.innerHTML = PR.prettyPrintOne(code, 'js', true); + let code = ugj_createCode({ 'beautify': true }); + code = ugj_htmlEntities(code); + content.innerHTML = PR.prettyPrintOne(code, 'js', true); - dialog.showModal(); + dialog.showModal(); - const close_cb = () => { - dialog.close(); - btn_close.removeEventListener('click', close_cb); - btn_export.removeEventListener('click', export_cb); + const close_cb = () => { + dialog.close(); + btn_close.removeEventListener('click', close_cb); + btn_export.removeEventListener('click', export_cb); + } + const export_cb = () => { + code = ugj_createCode({ 'async': true, 'beautify': true }); + // blackboardWrite()をconsole.log()に書き換え、 + // document... と ugj_... と elec... をコメントアウト(ブラウザ関連部分の追放という意味では不完全なので注意) + if (chkbox_cli.checked) + code = code.replace(/const appendDiv[^#]*\/\/#/gm, 'const blackboardWrite = text => console.log(text);').replace(/(^(?=.*document.)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*ugj_)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*elec.)[^;]*;)/gm, '/* $1 */'); + if (elec.saveFile(code, 'js') === false) { + alert('保存できませんでした。'); } - const export_cb = () => { - code = ugj_createCode({ 'async': true, 'beautify': true }); - // blackboardWrite()をconsole.log()に書き換え、 - // document... と ugj_... と elec... をコメントアウト(ブラウザ関連部分の追放という意味では不完全なので注意) - if (chkbox_cli.checked) - code = code.replace(/const appendDiv[^#]*\/\/#/gm, 'const blackboardWrite = text => console.log(text);').replace(/(^(?=.*document.)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*ugj_)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*elec.)[^;]*;)/gm, '/* $1 */'); - if (elec.saveFile(code, 'js') === false) { - alert('保存できませんでした。'); - } - close_cb(); - } - btn_close.addEventListener('click', close_cb); - btn_export.addEventListener('click', export_cb); + close_cb(); + } + btn_close.addEventListener('click', close_cb); + btn_export.addEventListener('click', export_cb); } @@ -327,102 +329,102 @@ const ugj_showCode = () => { ugj_fdTimeoutID = null; ugj_fdRecentBox = null; const ugj_fukidashi = (text, sec) => { - // Canvas Context - const context = document.getElementById('canvas').getContext('2d'); - // 吹き出しを消去する関数 - const clearFd = (x, y, w, h) => context.clearRect(x, y, w, h); - // 前回の思い出を忘れる - if (ugj_fdRecentBox !== null) { - clearFd(ugj_fdRecentBox.x, ugj_fdRecentBox.y, ugj_fdRecentBox.w, ugj_fdRecentBox.h); - clearTimeout(ugj_fdTimeoutID); - ugj_fdRecentBox = null; - } + // Canvas Context + const context = document.getElementById('canvas').getContext('2d'); + // 吹き出しを消去する関数 + const clearFd = (x, y, w, h) => context.clearRect(x, y, w, h); + // 前回の思い出を忘れる + if (ugj_fdRecentBox !== null) { + clearFd(ugj_fdRecentBox.x, ugj_fdRecentBox.y, ugj_fdRecentBox.w, ugj_fdRecentBox.h); + clearTimeout(ugj_fdTimeoutID); + ugj_fdRecentBox = null; + } - // 基本設定 - let rtopX = 170; // フキダシ右上 X座標 - let rtopY = 40; // フキダシ右上 Y座標 - let boxWidth = 140; - let padding = 5; - let radius = 5;// 円弧の半径 + // 基本設定 + let rtopX = 170; // フキダシ右上 X座標 + let rtopY = 40; // フキダシ右上 Y座標 + let boxWidth = 140; + let padding = 5; + let radius = 5;// 円弧の半径 - // 吹き出しの背景色 - context.fillStyle = "#b7e6ff"; + // 吹き出しの背景色 + context.fillStyle = "#b7e6ff"; - // テキスト設定 - let limitedWidth = boxWidth - (padding * 2); - let size = 14; - context.font = size + "px ''"; + // テキスト設定 + let limitedWidth = boxWidth - (padding * 2); + let size = 14; + context.font = size + "px ''"; - // テキスト調整 行に分解 - let lineTextList = text.split("\n"); - let newLineTextList = []; - lineTextList.forEach((lineText) => { + // テキスト調整 行に分解 + let lineTextList = text.split("\n"); + let newLineTextList = []; + lineTextList.forEach((lineText) => { + if (context.measureText(lineText).width > limitedWidth) { + characterList = lineText.split("");// 1文字ずつ分割 + let preLineText = ""; + lineText = ""; + characterList.forEach((character) => { + lineText += character; if (context.measureText(lineText).width > limitedWidth) { - characterList = lineText.split("");// 1文字ずつ分割 - let preLineText = ""; - lineText = ""; - characterList.forEach((character) => { - lineText += character; - if (context.measureText(lineText).width > limitedWidth) { - newLineTextList.push(preLineText); - lineText = character; - } - preLineText = lineText; - }); + newLineTextList.push(preLineText); + lineText = character; } - newLineTextList.push(lineText); - }); - let lineLength = newLineTextList.length; - - // 角丸 - let width = boxWidth;// 枠の幅 - let height = (size * lineLength) + (padding * 3); // 枠の高さ - let toRadianCoefficient = Math.PI / 180; // 角度からラジアンへの変換係数 - // 角丸原点(左上座標) - let boxOrigin = { - "x": rtopX - width, - "y": rtopY, + preLineText = lineText; + }); } - // 円弧から円弧までの直線は自動で引かれます、角度は回り方によって変わります。 - // arc(中心x, 中心y, 半径, 開始角度, 終了角度, 反時計回り) - context.beginPath(); - context.arc(boxOrigin.x + radius, boxOrigin.y + radius, radius, 180 * toRadianCoefficient, 270 * toRadianCoefficient, false);// 左上 - context.arc(boxOrigin.x + width - radius, boxOrigin.y + radius, radius, 270 * toRadianCoefficient, 0, false);// 右上 - context.arc(boxOrigin.x + width - radius, boxOrigin.y + height - radius, radius, 0, 90 * toRadianCoefficient, false);// 右下 - context.arc(boxOrigin.x + radius, boxOrigin.y + height - radius, radius, 90 * toRadianCoefficient, 180 * toRadianCoefficient, false);// 左下 - context.closePath(); - context.fill(); + newLineTextList.push(lineText); + }); + let lineLength = newLineTextList.length; - // 矢印(ヒゲ) - let arrow = { - "x": rtopX - width / 2 + 40, - "y": rtopY + height + 10, - "width": 10, - "height": 10, - } - context.beginPath(); - context.moveTo(arrow.x, arrow.y); - context.lineTo(arrow.x, arrow.y - arrow.height); - context.lineTo(arrow.x - arrow.width, arrow.y - arrow.height); - context.fill(); + // 角丸 + let width = boxWidth;// 枠の幅 + let height = (size * lineLength) + (padding * 3); // 枠の高さ + let toRadianCoefficient = Math.PI / 180; // 角度からラジアンへの変換係数 + // 角丸原点(左上座標) + let boxOrigin = { + "x": rtopX - width, + "y": rtopY, + } + // 円弧から円弧までの直線は自動で引かれます、角度は回り方によって変わります。 + // arc(中心x, 中心y, 半径, 開始角度, 終了角度, 反時計回り) + context.beginPath(); + context.arc(boxOrigin.x + radius, boxOrigin.y + radius, radius, 180 * toRadianCoefficient, 270 * toRadianCoefficient, false);// 左上 + context.arc(boxOrigin.x + width - radius, boxOrigin.y + radius, radius, 270 * toRadianCoefficient, 0, false);// 右上 + context.arc(boxOrigin.x + width - radius, boxOrigin.y + height - radius, radius, 0, 90 * toRadianCoefficient, false);// 右下 + context.arc(boxOrigin.x + radius, boxOrigin.y + height - radius, radius, 90 * toRadianCoefficient, 180 * toRadianCoefficient, false);// 左下 + context.closePath(); + context.fill(); - // テキスト描画 - context.fillStyle = "#000000"; - newLineTextList.forEach((lineText, index) => { - context.fillText(lineText, boxOrigin.x + padding, boxOrigin.y + padding + (size * (index + 1))); - }); + // 矢印(ヒゲ) + let arrow = { + "x": rtopX - width / 2 + 40, + "y": rtopY + height + 10, + "width": 10, + "height": 10, + } + context.beginPath(); + context.moveTo(arrow.x, arrow.y); + context.lineTo(arrow.x, arrow.y - arrow.height); + context.lineTo(arrow.x - arrow.width, arrow.y - arrow.height); + context.fill(); - // 描画した吹き出しの位置情報を保存 - ugj_fdRecentBox = { x: boxOrigin.x, y: boxOrigin.y, w: width, h: height + arrow.height }; - // 指定時間後に消去(0以下で自動消去なし) - if (sec > 0) { - ugj_fdTimeoutID = setTimeout(() => { - clearFd(boxOrigin.x, boxOrigin.y, width, height + arrow.height); - }, sec * 1000); - } + // テキスト描画 + context.fillStyle = "#000000"; + newLineTextList.forEach((lineText, index) => { + context.fillText(lineText, boxOrigin.x + padding, boxOrigin.y + padding + (size * (index + 1))); + }); - // return [boxOrigin.x, boxOrigin.y, width, height+arrow.height]; - // https://qiita.com/horikeso/items/95595f379a8dfa63c34a + // 描画した吹き出しの位置情報を保存 + ugj_fdRecentBox = { x: boxOrigin.x, y: boxOrigin.y, w: width, h: height + arrow.height }; + // 指定時間後に消去(0以下で自動消去なし) + if (sec > 0) { + ugj_fdTimeoutID = setTimeout(() => { + clearFd(boxOrigin.x, boxOrigin.y, width, height + arrow.height); + }, sec * 1000); + } + + // return [boxOrigin.x, boxOrigin.y, width, height+arrow.height]; + // https://qiita.com/horikeso/items/95595f379a8dfa63c34a } //===================================== @@ -435,37 +437,37 @@ var blocklyDiv = document.getElementById('blocklyDiv'); // var workspace = Blockly.inject(blocklyDiv, // {toolbox: document.getElementById('toolbox')}); var workspace = Blockly.inject(blocklyDiv, - { - toolbox: document.getElementById('toolbox'), - theme: theme, + { + toolbox: document.getElementById('toolbox'), + theme: theme, - scrollbars: true, - grid: { - spacing: 20, - length: 1, - colour: '#888',//888 - snap: true - }, - zoom: { startScale: 1.0, controls: true }, - trashcan: true, - media: './google-blockly/media/' - }); + scrollbars: true, + grid: { + spacing: 20, + length: 1, + colour: '#888',//888 + snap: true + }, + zoom: { startScale: 1.0, controls: true }, + trashcan: true, + media: './google-blockly/media/' + }); var onresize = function (e) { - // Compute the absolute coordinates and dimensions of blocklyArea. - var element = blocklyArea; - var x = 0; - var y = 0; - do { - x += element.offsetLeft; - y += element.offsetTop; - element = element.offsetParent; - } while (element); - // Position blocklyDiv over blocklyArea. - blocklyDiv.style.left = x + 'px'; - blocklyDiv.style.top = y + 'px'; - blocklyDiv.style.width = blocklyArea.offsetWidth + 'px'; - blocklyDiv.style.height = blocklyArea.offsetHeight + 'px'; - Blockly.svgResize(workspace); + // Compute the absolute coordinates and dimensions of blocklyArea. + var element = blocklyArea; + var x = 0; + var y = 0; + do { + x += element.offsetLeft; + y += element.offsetTop; + element = element.offsetParent; + } while (element); + // Position blocklyDiv over blocklyArea. + blocklyDiv.style.left = x + 'px'; + blocklyDiv.style.top = y + 'px'; + blocklyDiv.style.width = blocklyArea.offsetWidth + 'px'; + blocklyDiv.style.height = blocklyArea.offsetHeight + 'px'; + Blockly.svgResize(workspace); }; window.addEventListener('resize', onresize, false); onresize(); @@ -476,95 +478,95 @@ Blockly.svgResize(workspace); // ワークスペースの未保存の変更のフラグ const ugj_wsUpdateCB = event => { - if (event.type != Blockly.Events.UI) { - elec.setWsChanged(true); - } + if (event.type != Blockly.Events.UI) { + elec.setWsChanged(true); + } } // ウィンドウロード・アンロード時 window.onload = () => { - var menu = document.getElementById('conmenu'); //独自コンテキストメニュー - var area = document.getElementById('dlgContent'); //対象エリア - var body = document.body; //bodyエリア - body.oncontextmenu = () => false; - //右クリック時に独自コンテキストメニューを表示する - area.addEventListener('contextmenu', function (e) { - menu.style.left = e.pageX + 'px'; - menu.style.top = e.pageY + 'px'; - menu.classList.add('on'); - }); - //左クリック時に独自コンテキストメニューを非表示にする - body.addEventListener('click', function () { - if (menu.classList.contains('on')) { - menu.classList.remove('on'); - } - }); + var menu = document.getElementById('conmenu'); //独自コンテキストメニュー + var area = document.getElementById('dlgContent'); //対象エリア + var body = document.body; //bodyエリア + body.oncontextmenu = () => false; + //右クリック時に独自コンテキストメニューを表示する + area.addEventListener('contextmenu', function (e) { + menu.style.left = e.pageX + 'px'; + menu.style.top = e.pageY + 'px'; + menu.classList.add('on'); + }); + //左クリック時に独自コンテキストメニューを非表示にする + body.addEventListener('click', function () { + if (menu.classList.contains('on')) { + menu.classList.remove('on'); + } + }); - // ワークスペースといくつかの環境のオートリストア - ugj_loadWorkspace(); - elec.loadPrefsFromLS(); - setTimeout(() => { // 環境設定のロードが終わってからイベントリスナを作成 - workspace.addChangeListener(ugj_wsUpdateCB); - }, 100); - // 背景canvas - ugj_canvasBgImg(elec.getMascotFilePath(), -1,-1); + // ワークスペースといくつかの環境のオートリストア + ugj_loadWorkspace(); + elec.loadPrefsFromLS(); + setTimeout(() => { // 環境設定のロードが終わってからイベントリスナを作成 + workspace.addChangeListener(ugj_wsUpdateCB); + }, 100); + // 背景canvas + ugj_canvasBgImg(elec.getMascotFilePath(), -1, -1); } window.onbeforeunload = () => { - ugj_saveWorkspace(); - elec.savePrefsToLS(); + ugj_saveWorkspace(); + elec.savePrefsToLS(); } // Electron動作とブラウザ動作を自動で仕分け if (typeof require == 'function') { - // requireが使える = Electron - var elec = require('./scripts/eleclib'); - console.log('elec loaded.'); + // requireが使える = Electron + var elec = require('./scripts/eleclib'); + console.log('elec loaded.'); } else { - //ブラウザ動作 - //requireのダミー - var require = (e) => { - alert(`この機能またはブロック [${e}] は web 版ではご利用になれません。\n詳しくはお問い合わせください。`); - // if (e=='elec' || e=='ocoge' || e=='child_process' || e=='@google-cloud/speech' || e=='nodemailer' || e=='fs' ) - // alert(`この機能またはブロックは web 版ではご利用になれません。\n詳しくはお問い合わせください。`); - // else return; - } - //elecを名前空間で置き換え - var elec = {}; - elec.saveFile = () => require('elec'); - elec.savePrefsToLS = () => { ; } - elec.loadPrefsFromLS = () => { ; } - elec.newFile = () => { ; } - elec.setWsChanged = () => { ; } + //ブラウザ動作 + //requireのダミー + var require = (e) => { + alert(`この機能またはブロック [${e}] は web 版ではご利用になれません。\n詳しくはお問い合わせください。`); + // if (e=='elec' || e=='ocoge' || e=='child_process' || e=='@google-cloud/speech' || e=='nodemailer' || e=='fs' ) + // alert(`この機能またはブロックは web 版ではご利用になれません。\n詳しくはお問い合わせください。`); + // else return; + } + //elecを名前空間で置き換え + var elec = {}; + elec.saveFile = () => require('elec'); + elec.savePrefsToLS = () => { ; } + elec.loadPrefsFromLS = () => { ; } + elec.newFile = () => { ; } + elec.setWsChanged = () => { ; } - // マスコット - elec.getMascotFilePath = () => './img/cogechee.png'; + // マスコット + elec.getMascotFilePath = () => './img/cogechee.png'; - //ワークスペースのダウンロード - elec.saveWsFile = xml_text => { - let blob = new Blob([xml_text], { "type": "text/xml" }); - const downLoadLink = document.createElement("a"); - document.body.appendChild(downLoadLink); - downLoadLink.download = 'workspace.xml'; - downLoadLink.href = URL.createObjectURL(blob); - downLoadLink.click(); - downLoadLink.parentElement.removeChild(downLoadLink); - return true; - } - //ワークスペースのインポート - elec.loadWsFile = () => { - const fileInputEl = document.createElement('input'); - document.body.appendChild(fileInputEl); - fileInputEl.type = 'file'; - fileInputEl.addEventListener('change', ev => { - let reader = new FileReader(); - reader.readAsText(ev.target.files[0]); - reader.addEventListener('load', () => { - let xml = Blockly.Xml.textToDom(reader.result); - Blockly.Xml.domToWorkspace(xml, workspace); - }); - }); - fileInputEl.click(); - fileInputEl.parentElement.removeChild(fileInputEl); - return ''; - } + //ワークスペースのダウンロード + elec.saveWsFile = xml_text => { + let blob = new Blob([xml_text], { "type": "text/xml" }); + const downLoadLink = document.createElement("a"); + document.body.appendChild(downLoadLink); + downLoadLink.download = 'workspace.xml'; + downLoadLink.href = URL.createObjectURL(blob); + downLoadLink.click(); + downLoadLink.parentElement.removeChild(downLoadLink); + return true; + } + //ワークスペースのインポート + elec.loadWsFile = () => { + const fileInputEl = document.createElement('input'); + document.body.appendChild(fileInputEl); + fileInputEl.type = 'file'; + fileInputEl.addEventListener('change', ev => { + let reader = new FileReader(); + reader.readAsText(ev.target.files[0]); + reader.addEventListener('load', () => { + let xml = Blockly.Xml.textToDom(reader.result); + Blockly.Xml.domToWorkspace(xml, workspace); + }); + }); + fileInputEl.click(); + fileInputEl.parentElement.removeChild(fileInputEl); + return ''; + } } \ No newline at end of file