[update] tfjs-node が動作しなかったため contextIsolation と nodeIntegration を元の設定(false, true)に戻した。face-apiを最新のtfjs-node上で動作するバージョン(別の作者によるフォーク)に変更

This commit is contained in:
ocogeclub 2021-10-01 23:33:13 +09:00
parent c6957ef715
commit 4018f0c5b7
5 changed files with 58 additions and 700 deletions

View File

@ -313,7 +313,8 @@ const ugj_loadWorkspace = () => {
// Python コードフォーマッタ Black をコール
const ugj_pyBeautify = (code) => {
let formatted;
formatted = window.ocogeapi.child_process.spawnSync('python3', ['-m', 'black', '-'], { input: code }).stdout.toString();
// formatted = window.ocogeapi.child_process.spawnSync('python3', ['-m', 'black', '-'], { input: code }).stdout.toString();
formatted = require('child_process').spawnSync('python3', ['-m', 'black', '-'], { input: code }).stdout.toString();
return formatted;
}

View File

@ -18,10 +18,10 @@ const ugj_const = {
// Electron 動作用
class elUtil {
constructor() {
this.path = window.ocogeapi.path
this.fs = window.ocogeapi.fs
this.ipcRenderer = window.ocogeapi.electron_ipcRenderer
this.shell = window.ocogeapi.electron_shell
this.path = require('path') //window.ocogeapi.path
this.fs = require('fs') //window.ocogeapi.fs
this.ipcRenderer = require('electron').ipcRenderer //window.ocogeapi.electron_ipcRenderer
this.shell = require('electron').shell //window.ocogeapi.electron_shell
this.saveFilepath = null;
this.wsChanged = false;
this.mascotFilePath = this.path.join(ugj_const.mascot_path, ugj_const.mascot_defname);
@ -271,7 +271,8 @@ class brUtil {
}
// Electron 動作 / ブラウザ動作自動判別
const is_el = (typeof window.ocogeapi !== 'undefined')
// const is_el = (typeof window.ocogeapi !== 'undefined')
const is_el = (typeof require === 'function')
// utilクラスのインスタンスを返す
const elUtil_new = () => {
@ -279,23 +280,9 @@ const elUtil_new = () => {
else return new brUtil
}
// "require" for "BLOCK"s
// ブラウザ動作時にはすべてアラートを表示
const require = module_name => {
if (is_el) {
switch (module_name) {
case '@ocogeclub/lgpio':
return window.ocogeapi.lgpio;
case '@ocogeclub/bme280':
return window.ocogeapi.bme280;
case 'fs':
return window.ocogeapi.fs;
case 'path':
return window.ocogeapi.path;
default:
throw new Error(`Unknown module "${module_name}" required.\nStopped.`);
}
} else {
// "require" for web browsers if contextIsolation is false && nodeIntegration is true:
if (!is_el) {
var require = module_name => {
let block;
switch (module_name) {
case '@ocogeclub/lgpio':
@ -313,3 +300,43 @@ const require = module_name => {
throw `ブロック「${block}」は、Web体験版ではご利用になれません。\n詳しくは https://ocoge.club/ をご覧ください。`;
}
}
// "require" for "BLOCK"s if contextIsolation is true
// ブラウザ動作時にはすべてアラートを表示
// const require = module_name => {
// if (is_el) {
// switch (module_name) {
// case '@ocogeclub/lgpio':
// return window.ocogeapi.lgpio;
// case '@ocogeclub/bme280':
// return window.ocogeapi.bme280;
// case 'fs':
// return window.ocogeapi.fs;
// case 'path':
// return window.ocogeapi.path;
// case '@tensorflow/tfjs-node':
// return window.ocogeapi.tfjs_node;
// case '@vladmandic/face-api/dist/face-api.node.js':
// return window.ocogeapi.face_api;
// default:
// throw new Error(`Unknown module "${module_name}" required.\nStopped.`);
// }
// } else {
// let block;
// switch (module_name) {
// case '@ocogeclub/lgpio':
// block = 'GPIO';
// break;
// case '@ocogeclub/bme280':
// block = 'BME280';
// break;
// case 'fs':
// block = 'ファイル';
// break;
// default:
// throw new Error(ugj_const.error_ja_all);
// }
// throw `ブロック「${block}」は、Web体験版ではご利用になれません。\n詳しくは https://ocoge.club/ をご覧ください。`;
// }
// }

View File

@ -16,9 +16,9 @@ function createWindow() {
width: 1280,
height: 940,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
// nodeIntegration: true,
// contextIsolation: false
// preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false
}
})
/** Maximize Window at startup */

View File

@ -1,6 +1,6 @@
const electron = require("electron")
const path = require('path')
// const tf = require('@tensorflow/tfjs-node')
electron.contextBridge.exposeInMainWorld(
"ocogeapi",
{
@ -11,6 +11,9 @@ electron.contextBridge.exposeInMainWorld(
fs: require('fs'),
path: path,
child_process: require('child_process'),
tfjs_node: require('@tensorflow/tfjs-node'),
// tfjs_node: tf,
// face_api: require('@vladmandic/face-api/dist/face-api.node.js'),
lgpio: require('@ocogeclub/lgpio'),
bme280: require('@ocogeclub/bme280'),
// グローバル変数

View File

@ -1,673 +0,0 @@
const testfunc = () => {
elec.openURL('http://ocoge.club');
}
//============ User Customize Start ===============
// カスタムブロックカラー定義
Blockly.HSV_SATURATION = 0.55;
Blockly.HSV_VALUE = 0.75;
var gpio_color = '0';
var multimedia_color = '240';
var network_color = '340';
var special_color = '20';
var snippets_color = '90';
// テーマ
var theme = Blockly.Theme.defineTheme('ocoge', {
'base': Blockly.Themes.Classic,
'startHats': true,
'componentStyles': {
'toolboxBackgroundColour': 'aliceblue',
'flyoutBackgroundColour': 'lavender',
'toolboxForegroundColour': 'white',
'flyoutForegroundColour': 'steelblue'
},
'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
}
},
'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
Blockly.Msg["CONTROLS_IF_MSG_THEN"] = "ならば";
Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"] = "";
Blockly.Msg["MATH_CHANGE_TITLE"] = "変数 %1 を %2 増やす";
Blockly.Msg["VARIABLES_SET"] = "変数 %1 を %2 にする";
// Blockly.Msg["TEXT_PRINT_TITLE"] = "ダイアログに %1 を表示";
Blockly.Msg["LOGIC_BOOLEAN_FALSE"] = "偽";
Blockly.Msg["LOGIC_BOOLEAN_TOOLTIP"] = "真 または 偽 を返します。";
Blockly.Msg["LOGIC_BOOLEAN_TRUE"] = "真";
// ローカライズ対応の準備
Blockly.Msg["UGJ_CONTROL_FOR_TITLE"] = "%1 %2 を %3 から %4 まで %5 ずつ %6 %7 %8";
Blockly.Msg["UGJ_CONTROL_FOR_INDEX"] = "番号";
Blockly.Msg["UGJ_CONTROL_FOR_INCREASE"] = "増やして";
Blockly.Msg["UGJ_CONTROL_FOR_DECREASE"] = "減らして";
Blockly.Msg["UGJ_CONTROL_FOR_TOOLTIP"] = "インデックス番号を決められた数ずつ増やし(減らし)ながら、ステートメントを実行します。";
Blockly.Msg["UGJ_FOREACH_TITLE"] = "リスト %1 の各 %2 について %3 %4";
Blockly.Msg["UGJ_FOREACH_ITEM"] = "項目";
Blockly.Msg["UGJ_FOREACH_TOOLTIP"] = "リストの各項目について、その項目を変数「項目」としてステートメントを実行します。";
Blockly.Msg["GPIOCHIP_OPEN_TITLE"] = "GPIO を使えるようにする";
Blockly.Msg["GPIOCHIP_OPEN_TOOLTIP"] = "GPIOを初期化して接続します。";
Blockly.Msg["GPIOCHIP_CLOSE_TITLE"] = "GPIO の後片付けをする";
Blockly.Msg["GPIOCHIP_CLOSE_TOOLTIP"] = "GPIOとの接続を終了します。";
Blockly.Msg["GPIOCHIP_CLOSE_TITLE"] = "GPIO の後片付けをする";
Blockly.Msg["GPIO_CLAIM_INPUT_TITLE"] = "GPIO %1 を入力モードにして %2";
Blockly.Msg["GPIO_CLAIM_INPUT_TOOLTIP"] = "GPIO端子を入力モードにして、プルアップ・プルダウン・無しを設定します。";
Blockly.Msg["GPIO_CLAIM_OUTPUT_TITLE"] = "GPIO %1 を出力モードにする";
Blockly.Msg["GPIO_CLAIM_OUTPUT_TOOLTIP"] = "GPIO端子のモードを出力に設定します。";
Blockly.Msg["GPIO_READ_TITLE"] = "GPIO %1 の値";
Blockly.Msg["GPIO_READ_TOOLTIP"] = "GPIO端子の値をデジタル値0または1で読み取ります。";
Blockly.Msg["GPIO_WRITE_TITLE"] = "GPIO %1 の値を %2 にする";
Blockly.Msg["GPIO_WRITE_TOOLTIP"] = "GPIO端子の値をデジタル値0または1で出力します。";
Blockly.Msg["TX_SERVO_TITLE"] = "GPIO %1 のサーボモータの回転を %2 にする";
Blockly.Msg["TX_SERVO_TOOLTIP"] = "サーボモータの回転をパルス幅(10002000μsec)までの数値で指定します。";
Blockly.Msg["TX_PWM_TITLE"] = "PWM : GPIO %1 に、パルス周波数 %2 Hz, デューティー比 %3 %%で出力";
Blockly.Msg["TX_PWM_TOOLTIP"] = "パルス周波数をセットして、GPIO端子がPWM出力できるようにします。レンジは100固定です。";
Blockly.Msg["I2C_OPEN_TITLE"] = "アドレス %1 の I2C デバイスを開く";
Blockly.Msg["I2C_OPEN_TOOLTIP"] = "I2C接続されたデバイスとの通信を開始します。一度にオープンできるI2Cデバイスはひとつだけです。";
Blockly.Msg["SERIAL_OPEN_TITLE"] = "シリアルポート %1 を速度 %2 bpsで開く";
Blockly.Msg["SERIAL_OPEN_TOOLTIP"] = "シリアルデバイスとの接続を開きます。";
Blockly.Msg["SERIAL_CLOSE_TITLE"] = "シリアルポートを閉じる";
Blockly.Msg["SERIAL_CLOSE_TOOLTIP"] = "シリアルデバイスとの接続を閉じます。";
Blockly.Msg["SERIAL_WRITE_TITLE"] = "シリアルポートに %1 を送信する";
Blockly.Msg["SERIAL_WRITE_TOOLTIP"] = "シリアル接続されたデバイスにデータを送信します。シリアルポートは開かれていなくてはいけません。";
Blockly.Msg["SERIAL_READ_TITLE"] = "シリアルポートから %1 文字読み込む";
Blockly.Msg["SERIAL_READ_TOOLTIP"] = "オープン済みシリアルポートから、指定のバイト数だけデータを読み込みます。";
Blockly.Msg["I2C_CLOSE_TITLE"] = "I2C デバイスを閉じる";
Blockly.Msg["I2C_CLOSE_TOOLTIP"] = "I2C接続されたデバイスと通信を切断します。";
Blockly.Msg["I2C_WRITE_BYTE_TITLE"] = "デバイスに1バイトデータ %1 を送信";
Blockly.Msg["I2C_WRITE_BYTE_TOOLTIP"] = "i2cデバイスにバイトデータを送信します。0-0xFFの範囲の数字で入力してください。";
Blockly.Msg["I2C_READ_BYTE_TITLE"] = "I2C デバイスから 1 バイト受け取る";
Blockly.Msg["I2C_READ_BYTE_TOOLTIP"] = "オープン済み I2C デバイスからデータを 1 バイト受け取ります。";
Blockly.Msg["I2C_WRITE_BYTE_DATA_TITLE"] = "レジスタ %1 に %2 を書き込む";
Blockly.Msg["I2C_WRITE_BYTE_DATA_TOOLTIP"] = "デバイスの指定されたレジスタに1バイトを書き込みます。";
Blockly.Msg["I2C_READ_BYTE_DATA_TITLE"] = "レジスタ %1 の値";
Blockly.Msg["I2C_READ_BYTE_DATA_TOOLTIP"] = "デバイスの指定されたレジスタから1バイトを読み込みます。";
Blockly.Msg["I2C_READ_DEVICE_TITLE"] = "i2cデバイスから %1 バイト受け取る";
Blockly.Msg["I2C_READ_DEVICE_TOOLTIP"] = "デバイスから指定したバイト数のデータを受け取ります。データが指定の長さより短いこともあります。";
Blockly.Msg["I2C_WRITE_DEVICE_TITLE"] = "i2c デバイスに %1 を送信";
Blockly.Msg["I2C_WRITE_DEVICE_TOOLTIP"] = "i2c デバイスにデータを送信します。";
Blockly.Msg["UGJ_BME280_TITLE"] = "BME280アドレス %1 )の計測値: %2 %3 %4";
Blockly.Msg["UGJ_BME280_TOOLTIP"] = "環境センサーBME280で、気温摂氏、湿度、気圧hPaを計測し、それぞれを変数に代入します。";
Blockly.Msg["UGJ_BME280_READ_TEMP"] = "気温";
Blockly.Msg["UGJ_BME280_READ_HUM"] = "湿度";
Blockly.Msg["UGJ_BME280_READ_PRES"] = "気圧";
Blockly.Msg["UGJ_CODECHAR_TITLE"] = "コード %1 の文字";
Blockly.Msg["UGJ_CODECHAR_TOOLTIP"] = "文字コードを文字に変換します。";
Blockly.Msg["UGJ_CHARCODE_TITLE"] = "%1 の文字コード";
Blockly.Msg["UGJ_CHARCODE_TOOLTIP"] = "入力テキストの1文字目の文字コードを返します。";
Blockly.Msg["UGJ_HEXTODEC_TITLE"] = "0x %1";
Blockly.Msg["UGJ_HEXTODEC_TOOLTIP"] = "16進数を10進数に変換します。";
Blockly.Msg["UGJ_DECTOHEX_TITLE"] = "%1 を16進数に変換";
Blockly.Msg["UGJ_DECTOHEX_TOOLTIP"] = "10進数を16進数に変換します。";
Blockly.Msg["UGJ_CANVAS_INIT_TITLE"] = "キャンバスを表示";
Blockly.Msg["UGJ_CANVAS_INIT_TOOLTIP"] = "キャンバスを表示し、使用できるようにします。";
Blockly.Msg["UGJ_FACEAPI_TITLE"] = "TensorFlowによる顔検出 %1 ランドマークを検出 %2 %3";
Blockly.Msg["UGJ_FACEAPI_TOOLTIP"] = "TensorFlow とFaceAPI をロードし、顔検出をできるようにします。";
Blockly.Msg["UGJ_SLEEP_TITLE"] = "%1 秒待つ";
Blockly.Msg["UGJ_SLEEP_TOOLTIP"] = "指定した秒数だけ処理を中断します。";
// 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);
}
}
Blockly.registry.register(
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);
}
};
// HTML部品のインスタンス - 画面上の必要な部品はすべてここで取得しておく
ugjel_displayArea = document.getElementById('display_area'); // ディスプレイ部
ugjel_blackboard = document.getElementById('blackboard'); // 黒板
ugjel_inputForm = document.getElementById('inputForm'); // 入力フォーム
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;
})(['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);
}
}
// サウンド再生 - 連続再生のため、再生開始後すぐにオーディオ要素を再生成する
const ugj_soundPlay = soundName => {
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)
});
};
const ugj_htmlEntities = str =>// HTMLエンティティのエスケープ
String(str).replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');//.replace(/&/g, '&amp;').replace(/ /g, '&nbsp;');
// 新規ワークスペース
const ugj_newWorkspace = () => {
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('保存できませんでした。');
}
}
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);
}
}
// ワークスペースを別名で保存
const ugj_saveWorkspaceAs = () => {
elec.newFile();
ugj_saveWorkspaceToFile();
}
// ワークスペースをローカルストレージに保存・読込
const ugj_saveWorkspace = () => {
// 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("ocoge.xml");
if (xml_text !== null) {
if (xml_text.length != 0) {
let xml = Blockly.Xml.textToDom(xml_text);
Blockly.Xml.domToWorkspace(xml, workspace);
}
}
}
// // Python コードフォーマッタ YAPF をコール
// const ugj_pyBeautify = (code) => {
// let formatted;
// formatted = require('child_process').spawnSync('python3', ['-m', 'yapf'], { input: code }).stdout.toString();
// return formatted;
// }
// Python コードフォーマッタ Black をコール
const ugj_pyBeautify = (code) => {
let formatted;
formatted = require('child_process').spawnSync('python3', ['-m', 'black', '-'], { input: code }).stdout.toString();
return formatted;
}
// ワークスペースからコードを生成して必要であれば整形処理をする
const ugj_createCode = (args) => {
addAsync = args.async || false;
beautify = args.beautify || false;
prettify = args.prettify || false;
ext = args.ext || 'js';
let code, requires;
if (ext == 'py') { // Python コード出力
try {
code = Blockly.Python.workspaceToCode(workspace);
} catch (e) { // Pythonコードを持たないブロックがある場合
window.alert('Python 非対応のブロックが使用されています。\n' + e.message);
}
code = Blockly.Python.workspaceToCode(workspace);
}
else { // Javascript コード出力
code = Blockly.JavaScript.workspaceToCode(workspace);
}
if (ext == 'py') { //Python
// コードを綺麗に
if (beautify) code = ugj_pyBeautify(code);
} else { // JavaScript
// await使用のため、必要に応じてコード全体をasync付き即時関数でラップ
if (addAsync) {
code = [
'(async () => {',
code,
'})();'
].join('\n');
}
// コードを綺麗に
if (beautify) code = js_beautify(code, { indent_size: 2 });
}
// シンタックスハイライト(HTML化): 先に HTML エンティティをエスケープ
if (prettify) code = PR.prettyPrintOne(ugj_htmlEntities(code), ext, true);
return code;
}
// ブロックスクリプト実行
const ugj_runCode = () => {
document.activeElement.blur(); //実行ボタンからフォーカスを外す:エンターキー押下が悪さをするため
let code = ugj_createCode({ 'async': true });
try {
eval(code);
} catch (e) {
alert(e);
}
}
// コードをダイアログで表示・保存
// エレメントのオブジェクトとかコールバックとか Python対応とか
// 色々この中で完結させてみる
const ugj_showCode = () => {
var ext = 'js';
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_title = document.getElementById('dlgTitle');
const showCode = () =>
content.innerHTML = ugj_createCode({ 'beautify': true, 'ext': ext, 'prettify': true });
showCode();
dialog.showModal();
const close_cb = () => {
dialog.close();
btn_close.removeEventListener('click', close_cb);
btn_export.removeEventListener('click', export_cb);
}
const export_cb = () => {
code = ugj_createCode({ 'ext': ext, 'async': true, 'beautify': true });
// blackboardWrite()とwindow.alert()、fukidashi()をconsole.log()に書き換え、
// document... と ugj_... と elec... をコメントアウト(ブラウザ関連部分の追放という意味では不完全なので注意)
// あと正規表現もいい加減
if (chkbox_cli.checked && ext == 'js')
code = code.replace(/const appendDiv[^#]*\/\/#/gm, 'const blackboardWrite = text => console.log(text);').replace('window.alert', 'console.log').replace(/ugj_fukidashi(.*), \d+(\);)/gm, 'console.log$1$2').replace(/(^(?=.*document.)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*ugj_)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*elec.)[^;]*;)/gm, '/* $1 */');
if (elec.saveFile(code, ext) === false) {
alert('保存できませんでした。');
}
close_cb();
}
btn_close.addEventListener('click', close_cb);
btn_export.addEventListener('click', export_cb);
document.addEventListener('keypress', (ev) => {
if (ev.key == 'p' && ext == 'js') {
ext = 'py';
showCode();
}
});
document.addEventListener('keyup', (ev) => {
if (ev.key == 'p') {
ext = 'js';
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;
}
// 基本設定
let rtopX = 170; // フキダシ右上 X座標
let rtopY = 40; // フキダシ右上 Y座標
let boxWidth = 140;
let padding = 5;
let radius = 5;// 円弧の半径
// 吹き出しの背景色
context.fillStyle = "#b7e6ff";
// テキスト設定
let limitedWidth = boxWidth - (padding * 2);
let size = 14;
context.font = size + "px ''";
// テキスト調整 行に分解
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) {
newLineTextList.push(preLineText);
lineText = character;
}
preLineText = lineText;
});
}
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,
}
// 円弧から円弧までの直線は自動で引かれます、角度は回り方によって変わります。
// 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();
// 矢印(ヒゲ)
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();
// テキスト描画
context.fillStyle = "#000000";
newLineTextList.forEach((lineText, index) => {
context.fillText(lineText, boxOrigin.x + padding, boxOrigin.y + padding + (size * (index + 1)));
});
// 描画した吹き出しの位置情報を保存
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
}
// コードジェネレータモードJavascript/Python切り替え
var isPyMode;
const ugj_pyMode = (checked) => isPyMode = checked;
//=====================================
//======= Blockly GUI codes ===========
// Use in a block or block definition:
// Resizable workspace injection script
var blocklyArea = document.getElementById('blocklyArea');
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,
renderer: 'thrasos',
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);
};
window.addEventListener('resize', onresize, false);
onresize();
Blockly.svgResize(workspace);
//=====================================
//=====================================
// ワークスペースの未保存の変更のフラグ
const ugj_wsUpdateCB = event => {
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');
}
});
// ワークスペースといくつかの環境のオートリストア
ugj_loadWorkspace();
elec.loadPrefsFromLS();
setTimeout(() => { // 環境設定のロードが終わってからイベントリスナを作成
workspace.addChangeListener(ugj_wsUpdateCB);
}, 100);
// 背景canvas
ugj_canvasBgImg(elec.getMascotFilePath(), -1, -1);
}
window.onbeforeunload = () => {
ugj_saveWorkspace();
elec.savePrefsToLS();
elec.killAllChildren();
}
// Electron動作とブラウザ動作を自動で仕分け
if (typeof require == 'function') {
// requireが使える = Electron
var elec = require('./ugj_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 = () => { ; }
// マスコット
elec.getMascotFilePath = () => './img/tamachee.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 '';
}
}