You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1024 lines
44 KiB
1024 lines
44 KiB
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta http-equiv="cache-control" content="max-age=0" /> |
|
<meta http-equiv="cache-control" content="no-store" /> |
|
<meta http-equiv="expires" content="-1" /> |
|
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" /> |
|
<meta http-equiv="pragma" content="no-cache" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
|
<meta charset="utf-8" /> |
|
<script src="lang.js"></script> |
|
<script src="config.js"></script> |
|
<script src="cookie.min.js"></script> |
|
<script src="lang_cookie.js"></script> |
|
<script src="sweetalert2.min.js"></script> |
|
<script src="jquery_3_2_1.min.js"></script> |
|
<script src="fabric_jquery.easyui.min_compressed_on_nvr.js"></script> |
|
<script type="text/javascript" src="jquery.min.js"></script> |
|
<script type="text/javascript" src="jquery.easyui.min.js"></script> |
|
<link rel="stylesheet" type="text/css" href="bootstrap.min.css"> |
|
<link rel="stylesheet" type="text/css" href="easyui.css"> |
|
<link rel="stylesheet" href="css/sweetalert2.min.css"> |
|
|
|
|
|
<link rel="stylesheet" href="codemirror.min.css"> |
|
<script src="codemirror.min.js"></script> |
|
<script src="python.min.js"></script> |
|
|
|
<script> |
|
window.onerror = function (message, url, linenumber) { |
|
console.log('JavaScript error: ' + message + ' on line ' + linenumber + ' for ' + url); |
|
if (url.search("fabric_jquery.easyui.min_compressed_on_nvr.js") >= 1) { |
|
loadScript("fabric_jquery.easyui.min_compressed_on_nvr.js", function () { |
|
}); |
|
} |
|
else if (url.search("jquery_3_2_1.min.js") >= 1) { |
|
loadScript("jquery_3_2_1.min.js", function () { |
|
}); |
|
} |
|
else if (url.search("config.js") >= 1) { |
|
loadScript("config.js", function () { |
|
}); |
|
} |
|
else if (url.search("cookie.min.js") >= 1) { |
|
loadScript("cookie.min.js", function () { |
|
}); |
|
} |
|
else if (url.search("lang.js") >= 1) { |
|
loadScript("lang.js", function () { |
|
}); |
|
} |
|
else if (url.search("lang_cookie.js") >= 1) { |
|
loadScript("lang_cookie.js", function () { |
|
}); |
|
} |
|
else if (url.search("sweetalert2.min.js") >= 1) { |
|
loadScript("sweetalert2.min.js", function () { |
|
}); |
|
} |
|
else if (message.search("slider") >= 1) { |
|
loadScript("fabric_jquery.easyui.min_compressed_on_nvr.js", function () { |
|
}); |
|
} |
|
else if (message.search("cookie") >= 1) { |
|
loadScript("cookie.min.js", function () { |
|
}); |
|
} |
|
else if (url.search("codemirror.min.js") >= 1) { |
|
loadScript("codemirror.min.js", function () { |
|
}); |
|
} |
|
else if (url.search("python.min.js") >= 1) { |
|
loadScript("python.min.js", function () { |
|
}); |
|
} |
|
else { |
|
// # If an unknown error occurs, the page is reorganized |
|
// var replaceUrl = 'http://' + window.location.hostname + ':' + GetUrlPort() + '/Aida/python.html'; |
|
// window.location.replace(replaceUrl); |
|
} |
|
} |
|
|
|
function GetUrlPort() { |
|
var url_href = window.location.href; |
|
console.log("url_href",url_href); |
|
var arr_url = url_href.split(":")[2].split("/")[0]; |
|
return arr_url; |
|
} |
|
if (window.history.replaceState) { |
|
window.history.replaceState(null, null, window.location.href); |
|
} |
|
</script> |
|
<style> |
|
* { |
|
box-sizing: border-box; |
|
} |
|
|
|
body, |
|
html { |
|
height: 100vh; |
|
margin: 0; |
|
font-family: 'Arial', sans-serif; |
|
} |
|
/* 檔案列表的大小 */ |
|
#sidebar { |
|
width: 310px; |
|
height: 200vh; |
|
background-color: #f9f9f9; |
|
padding: 5px; |
|
max-height: calc(100vh - 20px); |
|
} |
|
|
|
@media (max-height: 600px) { |
|
#sidebar { |
|
max-height: 80vh; |
|
} |
|
} |
|
@media (max-height: 400px) { |
|
#sidebar { |
|
max-height: 70vh; |
|
} |
|
} |
|
/*負責裝按鈕的 container*/ |
|
#editor-container { |
|
flex-grow: 1; |
|
display: flex; |
|
flex-direction: column; |
|
height: 100vh; |
|
width: 100%; |
|
background-color: #f9f9f9; |
|
} |
|
/*Program container*/ |
|
#code-editor { |
|
flex-grow: 1; |
|
width: 100%; |
|
height: 100%; |
|
margin-bottom: 10px; |
|
} |
|
/*Program textarea*/ |
|
#code-textarea { |
|
width: 100%; |
|
height: 100%; |
|
font-size: 1vw; |
|
resize: none; |
|
} |
|
/*Messege textarea*/ |
|
#console { |
|
height: 50vh; |
|
padding: 10px; |
|
border: 2px solid #ccc; |
|
font-size: 1vw; |
|
overflow-x: auto; |
|
overflow-y: auto; |
|
flex-grow: 1; |
|
} |
|
#code-editor, #console { |
|
flex-grow: 1; |
|
} |
|
.editor-container { |
|
font-size: 1vw; |
|
font-weight: normal; |
|
margin-left: 15px; |
|
margin-top: 10px; |
|
line-height: normal; |
|
} |
|
.container { |
|
display: flex; |
|
height: 100vh; |
|
} |
|
.console-container { |
|
font-size: 1vw; |
|
font-weight: normal; |
|
margin-left: 15px; |
|
margin-bottom: 5px; |
|
line-height: normal; |
|
} |
|
|
|
</style> |
|
</head> |
|
<body> |
|
<div class="progress" style="background-color:white"> |
|
<div class="progress-bar progress-bar-striped" style="min-width: 20px;visibility:hidden;"></div> |
|
</div> |
|
|
|
<div style="border:1px gray solid;padding:5px;margin:5px 5px 5px 5px;height: 560px;"> |
|
<div id="container" style="height: 100vh; display: flex;"> |
|
<div id="sidebar" style="background-color: #f9f9f9;"> |
|
<div style="padding: 10px;"> |
|
<input type="text" id="search-box" placeholder="Search" |
|
style="width: 108%; height: 40px; border-radius: 5px; border: 1px solid #ccc;margin-right: 0; align-self: start; position: relative; left: -10px;"> |
|
</div> |
|
<div id="file-results"></div> |
|
<div style="display: flex; justify-content: space-between;"> |
|
<button id="uploadfile-button" |
|
style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="Upload File"> |
|
<img src="./images/uploadfile.png" alt="Upload" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
<button id="refreshfile-button" |
|
style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="Refresh"> |
|
<img src="./images/refreshfile.png" alt="refresh" onclick="func_getFileList(null)" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
<button id="addfile-button" |
|
style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="New File"> |
|
<img src="./images/addfile.png" alt="add" onclick="addNewFile()" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
<button id="deletefile-button" |
|
style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="Delete File"> |
|
<img src="./images/deletefile.png" alt="delete" onclick="" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
</div> |
|
<table id="file-data-grid" style="height: 460px;"> |
|
</table> |
|
</div> |
|
<div id="editor-container" style="overflow: auto;"> |
|
<div id="btn" |
|
style="display: flex; justify-content: flex-end; background-color: #f0f0f0; padding: 5px;"> |
|
<button id="run-button" style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="Run"> |
|
<img src="./images/play.png" alt="Run" onclick="" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
<button id="stop-button" style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="Stop" onclick=""> |
|
<img src="./images/stop.png" alt="stop" onclick="" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
<button id="save-button" style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="Save"> |
|
<img src="./images/save.png" alt="Save" onclick="" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
<button id="delete-button" style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="Clear File"> |
|
<img src="./images/delete.png" alt="ClearFile" onclick="" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
<button id="ClearConsole-button" style="color: white; border: none; text-align: center; cursor: pointer;" |
|
title="Clear Console"> |
|
<img src="./images/clear.png" alt="ClearConsole" onclick="" |
|
style="height: 26px; width: 26px; vertical-align: middle; margin-right: 5px;"> |
|
</button> |
|
</div> |
|
<div class="editor-container"> |
|
<label for="code-textarea">Program</label> |
|
</div> |
|
<div id="code-editor" style="overflow: auto;"> |
|
<textarea id="code-textarea" tabindex="0"></textarea> |
|
</div> |
|
|
|
<div class="console-container"> |
|
<label for="console">Message</label> |
|
</div> |
|
<textarea id="console" readonly='true'></textarea> |
|
</div> |
|
</div> |
|
</div> |
|
<script> |
|
var fileArray = []; |
|
var BufferSize = 512; // Buffer 最大 Row 數 |
|
var getPythonBufferIntervalTime = 50; |
|
var getPythonStatusIntervalTime = 50; |
|
var receivedCountThreshold = 37500 / getPythonStatusIntervalTime // 約等於 15 秒 |
|
var dataReceivedCount = 0; |
|
var getPythonBufferSize = 64; |
|
var getBufferStatusTemp = null; |
|
var getBufferSignal = null; |
|
var currentRunningFile = ''; // temp running file name |
|
var g_isPythonRunning = 0; |
|
var pythoncode = "" |
|
var currentFileName = ''; |
|
var intervalId = null; // Ming add 2024/08/22 |
|
var autoScroll = true; // Ming 2024-08-29 |
|
var isUserScrolling = false; // Ming 2024-08-29 |
|
Status_intervalId = null; |
|
var loadingInterval = null; |
|
var dots = 0; |
|
// 取得語系 |
|
func_get_lang_cookie(); |
|
// 修改 window.onload 使用 async/await |
|
window.onload = async function() { |
|
// 確認之前執行狀態 |
|
await func_enablePythonfile(); |
|
// 確認有無回傳的資料 |
|
func_getPythonStatus(); |
|
// 確認是否有 python 文件正在執行 |
|
if (currentRunningFile.endsWith('.py')) |
|
{ |
|
currentFileName = currentRunningFile; |
|
} |
|
else |
|
{ |
|
currentFileName = 'hello.py'; |
|
} |
|
// 加載文件列表並選中 python |
|
func_FileGet(currentFileName); |
|
// 自動加載並顯示 python 的內容 |
|
func_getFileList(currentFileName); |
|
}; |
|
// 初始化 CodeMirror |
|
var editor = CodeMirror.fromTextArea(document.getElementById('code-textarea'), { |
|
lineNumbers: true, |
|
mode: "python", |
|
theme: "default", |
|
lineWrapping: true, |
|
indentUnit: 4, |
|
matchBrackets: true |
|
}); |
|
// 動態調整 CodeMirrir 面板大小 |
|
function resizeEditor() { |
|
editor.setSize("100%", "100%"); |
|
} |
|
// 動態調整 CodeMirrir 字體大小 |
|
editor.getWrapperElement().style.fontSize = '1.0vw'; |
|
resizeEditor(); |
|
window.addEventListener('resize', function() { |
|
resizeEditor(); |
|
}); |
|
// 取得資料夾中的所有檔案名稱 |
|
function func_getFileList(pyFileName) { |
|
//console.log('in get file list'); |
|
$.ajax({ |
|
url: "/python?getfilelist=all", |
|
type: "GET", |
|
beforeSend: function (xmlHttp) { |
|
xmlHttp.setRequestHeader("If-Modified-Since", "0"); |
|
xmlHttp.setRequestHeader("Cache-Control", "no-cache"); |
|
}, |
|
success: function (retdata) { |
|
var data; |
|
|
|
if (typeof retdata === 'string') { |
|
data = JSON.parse(retdata); |
|
} else { |
|
data = retdata; |
|
} |
|
var fileList = data.file_list; |
|
fileArray = Object.values(fileList); // 儲存所有 python 檔名 |
|
|
|
updateDataGrid(fileArray); // 更新顯示的檔案列表 |
|
|
|
if (pyFileName != null) { |
|
selectFileInDataGrid(pyFileName); // 此時 fileList 已包含新文件 |
|
} |
|
}, |
|
error: function (retdata) { |
|
console.warn(retdata); |
|
} |
|
}); |
|
} |
|
// 更新 Files 列表 |
|
function updateDataGrid(files) { |
|
var dataForGrid = files.map(function (filename) { |
|
return { |
|
filename: filename, |
|
lastModified: 'Not available' |
|
}; |
|
}); |
|
$('#file-data-grid').datagrid('loadData', dataForGrid); |
|
} |
|
// 查看 Search box 有無輸入 |
|
document.getElementById('search-box').addEventListener('input', function () { |
|
var searchTerm = this.value.toLowerCase(); // 將輸入的內容轉為小寫 |
|
var filteredFiles = fileArray.filter(function (file) { |
|
return file.toLowerCase().includes(searchTerm); // 根據輸入進行過濾 |
|
}); |
|
updateDataGrid(filteredFiles); |
|
}); |
|
// 取得文件內容 |
|
function func_FileGet(file_name) { |
|
var reqFileNmae = file_name; |
|
|
|
$.ajax({ |
|
url: "/pythonget?file=" + reqFileNmae, |
|
type: "GET", |
|
async: true, |
|
beforeSend: function (xmlHttp) { |
|
xmlHttp.setRequestHeader("If-Modified-Since", "0"); |
|
xmlHttp.setRequestHeader("Cache-Control", "no-cache"); |
|
}, |
|
success: function (retdata) { |
|
editor.setValue(retdata); |
|
}, |
|
error: function (retdata) { |
|
console.warn(retdata); |
|
} |
|
}); |
|
} |
|
|
|
// 儲存文件內容 |
|
function func_FileSave(file_name) { |
|
var pyFileName = file_name; |
|
pythoncode = editor.getValue(); |
|
var encoded = encodeURIComponent(pythoncode); |
|
//console.log("func_FileSave pyFileName ---------- ",pyFileName) |
|
$.ajax({ |
|
url: "/pythonsave?file=" + pyFileName + "&code=" + encoded, |
|
method: "GET", |
|
async: true, |
|
beforeSend: function (xmlHttp) { |
|
xmlHttp.setRequestHeader("If-Modified-Since", "0"); |
|
xmlHttp.setRequestHeader("Cache-Control", "no-cache"); |
|
}, |
|
success: function (response) { |
|
//console.log("response: ", response); |
|
// 如果更新成功,則更新列表 |
|
if (response === "File save successfully") { |
|
$('#console').append("Saving successfully"); |
|
$('#console').append(' '); // 換行 |
|
func_getFileList(pyFileName); |
|
} else { |
|
Swal.fire({ |
|
title: 'Error!', |
|
text: response, |
|
icon: 'error', |
|
confirmButtonText: 'OK' |
|
}); |
|
} |
|
}, |
|
error: function () { |
|
console.warn(retdata); |
|
} |
|
}); |
|
return false; |
|
} |
|
// 更新文件內容 |
|
function func_FileUpload(file_name) { |
|
var pyFileName = file_name; |
|
var encoded = encodeURIComponent(pythoncode); |
|
//console.log("func_FileUpload pyFileName ---------- ",pyFileName) |
|
$.ajax({ |
|
url: "/pythonupload?file=" + pyFileName + "&code=" + encoded, |
|
method: "GET", |
|
async: true, |
|
beforeSend: function (xmlHttp) { |
|
xmlHttp.setRequestHeader("If-Modified-Since", "0"); |
|
xmlHttp.setRequestHeader("Cache-Control", "no-cache"); |
|
}, |
|
success: function (response) { |
|
//console.log("response: ", response); |
|
// 如果更新成功,則更新列表 |
|
if (response === "File uploaded successfully") { |
|
func_getFileList(pyFileName); |
|
} else { |
|
Swal.fire({ |
|
title: 'Error!', |
|
text: response, |
|
icon: 'error', |
|
confirmButtonText: 'OK' |
|
}); |
|
} |
|
}, |
|
error: function () { |
|
console.warn(retdata); |
|
} |
|
}); |
|
return false; |
|
} |
|
// 刪除檔案 |
|
function func_FileDelete(file_name) { |
|
var pyFileNmae = file_name; |
|
$.ajax({ |
|
url: "/pythondelete?file=" + pyFileNmae, |
|
method: "GET", |
|
async: true, |
|
beforeSend: function (xmlHttp) { |
|
xmlHttp.setRequestHeader("If-Modified-Since", "0"); |
|
xmlHttp.setRequestHeader("Cache-Control", "no-cache"); |
|
}, |
|
success: function (retdata) { |
|
// 直接刷新文件列表,無需指定 Index 刪除行 |
|
//console.log("Delete File successfully !!!") |
|
$('#console').append(' '); // 換行 |
|
$('#console').append(retdata.replace(/\n/g, '<br>')); |
|
currentFileName = 'hello.py'; |
|
func_getFileList(currentFileName); // 2024/08/22 Ming add |
|
}, |
|
error: function () { |
|
console.warn(); |
|
} |
|
}); |
|
} |
|
// 停止 Python 檔案 |
|
function func_stopPython(file_name) { |
|
//console.log("func_stopPython"); |
|
currentRunningFile = ''; // |
|
$('#file-data-grid').datagrid('reload'); |
|
var json_data = '{'; |
|
json_data += '"system_setting": {'; |
|
json_data += '"enable_python_file": "' + 'No python running' + '"'; |
|
json_data += '}'; |
|
json_data += '}'; |
|
func_setconfigfile(json_data); |
|
$.ajax({ |
|
url: "/stoppython", |
|
method: "GET", |
|
async: true, |
|
success: function (retdata) { |
|
clearInterval(Status_intervalId); |
|
|
|
$('#console').append(' '); // 換行 |
|
$('#console').append(retdata.replace(/\n/g, '<br>')); |
|
$('#console').append(' '); // 換行 |
|
// 根據 autoScroll 開關自動滾動 Ming 2024-09-13 |
|
if (autoScroll) { |
|
var textarea = $('#console')[0]; |
|
textarea.scrollTop = textarea.scrollHeight; |
|
} |
|
}, |
|
error: function (retdata) { |
|
console.warn(retdata); |
|
} |
|
}); |
|
return false; |
|
} |
|
function func_convertSpecialChars(retdata) { |
|
// 將特殊字元轉換為 Web 可解析的形式 |
|
var result = ''; |
|
for (var i = 0; i < retdata.length; i++) { |
|
const char = retdata[i]; |
|
switch (char) { |
|
case '\b': |
|
// 刪除結果中的前一個字元 |
|
result = result.slice(0, -1); |
|
break; |
|
case '\r': |
|
// 回到該行的開頭,可以將當前行的內容清空 |
|
result = result.replace(/.*$/, ''); // 清空當前行的內容 |
|
break; |
|
case '\n': |
|
result += ' '; // newline for textarea |
|
break; |
|
default: |
|
result += char; // 其他字元保留原樣 |
|
break; |
|
} |
|
} |
|
return result; |
|
} |
|
function func_checkSpecialChar(data) { |
|
data = data.replace(/<br\/>/g, '<br/>'); // Show <br/> |
|
return data.replace(/</g, "<").replace(/>/g, ">"); |
|
} |
|
|
|
// 取得 Python 執行狀態 |
|
function func_getPythonStatus(){ |
|
//console.log("func_getPythonStatus"); |
|
if (Status_intervalId !== null) { |
|
clearInterval(Status_intervalId); |
|
Status_intervalId = null; |
|
} |
|
Status_intervalId = setInterval(function () { |
|
$.ajax({ |
|
url: "/pythongetstatus", |
|
method: "GET", |
|
async: true, |
|
success: function (retdata) { |
|
//console.log("-------------------",currentRunningFile); |
|
updateFileIcon(); |
|
// console.log("GET Status Succeeded."); |
|
// console.log("--------------dataReceivedCount--------------",dataReceivedCount) |
|
// every 15 second to check PythonRunning status |
|
if (dataReceivedCount >= receivedCountThreshold) { |
|
|
|
if (getBufferStatusTemp == 5 && getBufferSignal == 0) |
|
{ |
|
g_isPythonRunning = 0; |
|
clearInterval(Status_intervalId); |
|
Status_intervalId = null; |
|
// 開啟 Run |
|
$("#run-button").prop("disabled", false); |
|
} |
|
dataReceivedCount = 0; |
|
} |
|
try |
|
{ |
|
var existData = $('#console').html(); |
|
//console.log("Get Status: ", retdata); |
|
var displayData = func_convertSpecialChars(retdata); |
|
var data = JSON.parse(displayData); |
|
getBufferStatusTemp = parseInt(data['status']); |
|
getBufferSignal = parseInt(data['bufferSignal']); |
|
//console.log('getBufferStatusTemp',getBufferStatusTemp); |
|
//console.log('getBufferSignal',getBufferSignal); |
|
//console.log("g_isPythonRunning",g_isPythonRunning); |
|
} catch (error) { |
|
console.log(error); |
|
} |
|
if (g_isPythonRunning == 1 || getBufferStatusTemp == 2) |
|
{ |
|
func_getPythonBuffer(); |
|
|
|
} |
|
else if (g_isPythonRunning = 0 || getBufferSignal == 0) // python finish or stop need to clear file |
|
{ |
|
var json_data = '{'; |
|
json_data += '"system_setting": {'; |
|
json_data += '"enable_python_file": "' + 'No python running' + '"'; |
|
json_data += '}'; |
|
json_data += '}'; |
|
func_setconfigfile(json_data); |
|
currentRunningFile = '' |
|
$("#run-button").prop("disabled", false); |
|
} |
|
dataReceivedCount++; |
|
}, |
|
error: function (xhr, textStatus, errorThrown) { |
|
if (textStatus === "timeout") { |
|
console.error("Request timed out."); |
|
} else { |
|
console.error("Failed to fetch data:", errorThrown); |
|
} |
|
clearInterval(Status_intervalId); // 在錯誤情況下清除定時器 |
|
} |
|
}); |
|
}, getPythonStatusIntervalTime); |
|
} |
|
// 取得 Python 結果 |
|
function func_getPythonBuffer() { |
|
//console.log("func_getPythonBuffer() start"); |
|
try { |
|
$.ajax({ |
|
url: "/pythongetbuffer?buffersize=" + getPythonBufferSize.toString(), |
|
method: "GET", |
|
async: true, |
|
success: function (retdata) { |
|
|
|
// console.log("GET request succeeded."); |
|
var existData = $('#console').html(); |
|
// console.log("Get Data: ", retdata); |
|
//console.log("Get Data:", JSON.stringify(retdata)); |
|
// $('#console').append(retdata); |
|
displayData = func_checkSpecialChar(retdata); |
|
//console.log("Get displayData",JSON.stringify(displayData)); |
|
clearInterval(loadingInterval); |
|
$('#console').append(displayData); |
|
|
|
// 根據 autoScroll 開關自動滾動 Ming 2024-08-29 |
|
if (autoScroll) { |
|
var textarea = $('#console')[0]; |
|
textarea.scrollTop = textarea.scrollHeight; |
|
} |
|
|
|
}, |
|
error: function (xhr, textStatus, errorThrown) { |
|
if (textStatus === "timeout") { |
|
console.error("Request timed out."); |
|
} else { |
|
console.error("Failed to fetch data:", errorThrown); |
|
} |
|
|
|
} |
|
}); |
|
|
|
} catch (error) { |
|
console.log(error); |
|
|
|
} |
|
} |
|
|
|
// 更改檔案圖示 |
|
function updateFileIcon() { |
|
var rows = $('#file-data-grid').datagrid('getRows'); // 取得所有行的資料 |
|
rows.forEach(function (row, index) { |
|
var $row = $('#file-data-grid').datagrid('getPanel').find('tr[datagrid-row-index="' + index + '"]'); |
|
if (row.filename.endsWith('.py')) { // 只對 .py 檔案進行操作 |
|
if (row.filename === currentRunningFile) { |
|
// 如果是正在執行的檔案,顯示播放圖示 |
|
$row.find('td[field="filename"] img').attr('src', './images/play.png'); |
|
} else { |
|
// 否則,顯示 py.png 圖示 |
|
$row.find('td[field="filename"] img').attr('src', './images/py.png'); |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function func_runPython(file_name) { |
|
func_getPythonStatus(); |
|
g_isPythonRunning = 1; |
|
currentRunningFile = file_name; |
|
//console.log("currentFileName",currentFileName); |
|
var json_data = '{'; |
|
json_data += '"system_setting": {'; |
|
json_data += '"enable_python_file": "' + currentRunningFile + '"'; |
|
json_data += '}'; |
|
json_data += '}'; |
|
func_setconfigfile(json_data); |
|
//console.log("func_runPython"); |
|
autoScroll = true; |
|
var python_content = editor.getValue(); |
|
var json_data = "lifile_liname=" + file_name; |
|
|
|
//console.log(file_name); |
|
|
|
updateFileIcon(); |
|
// 顯示 "Running" 動態點點效果 |
|
loadingInterval = setInterval(function() { |
|
var loadingText = "Running" + ".".repeat(dots % 4); |
|
$('#console').html(loadingText); |
|
dots++; |
|
}, 500); // 每0.5秒更新一次 |
|
|
|
$.ajax({ |
|
url: "/sendpycode", |
|
method: "POST", |
|
async: true, |
|
data: json_data, |
|
contentType: false, |
|
processData: false, |
|
beforeSend: function (xmlHttp) { |
|
xmlHttp.setRequestHeader("If-Modified-Since", "0"); |
|
xmlHttp.setRequestHeader("Cache-Control", "no-cache"); |
|
}, |
|
success: function (retdata) { |
|
//console.log("[/sendpycode] retdata:", retdata); // 2024/08/21 |
|
// resolve(retdata); |
|
// $('#console').text("Running.....\n"); |
|
g_isPythonRunning = 0; |
|
}, |
|
error: function (retdata) { |
|
console.error("GET request failed: ", retdata); //2024/08/21 |
|
// reject(retdata); |
|
} |
|
}); |
|
return false; |
|
} |
|
|
|
function func_setconfigfile(json_data) { |
|
$.ajax({ |
|
url: "/setconfigfile", |
|
method: "POST", |
|
async: false, |
|
data: json_data, |
|
contentType: false, |
|
processData: false, |
|
beforeSend: function (xmlHttp) { |
|
xmlHttp.setRequestHeader("If-Modified-Since", "0"); |
|
xmlHttp.setRequestHeader("Cache-Control", "no-cache"); |
|
//xmlHttp.setRequestHeader("Authorization", "Basic " + btoa("username:password")); |
|
}, |
|
success: function () { |
|
//console.info(); |
|
}, |
|
error: function () { |
|
console.warn(); |
|
k_index++; |
|
if (k_index <= 94) { |
|
waitSeconds(500); |
|
func_setconfigfile(json_data); |
|
} |
|
} |
|
}); |
|
return false; |
|
} |
|
// 使用 Promise 取得 config.json 中 enable_python_file 的值後返回 |
|
function func_enablePythonfile() { |
|
return new Promise((resolve, reject) => { |
|
$.ajax({ |
|
url: "/getconfigfile?ch=all", |
|
type: "GET", |
|
async: true, |
|
beforeSend: function (xmlHttp) { |
|
xmlHttp.setRequestHeader("If-Modified-Since", "0"); |
|
xmlHttp.setRequestHeader("Cache-Control", "no-cache"); |
|
}, |
|
success: function (retdata) { |
|
var jsonbuf = JSON.parse(retdata); |
|
|
|
if ("enable_python_file" in jsonbuf["system_setting"]) { |
|
currentRunningFile = jsonbuf["system_setting"]["enable_python_file"]; |
|
g_isPythonRunning = 1; |
|
|
|
if (currentRunningFile.endsWith('.py')) { |
|
// 顯示 "Running" 動態點點效果 |
|
|
|
loadingInterval = setInterval(function() { |
|
var loadingText = "Running unfinished python file" + ".".repeat(dots % 4); |
|
$('#console').html(loadingText); |
|
dots++; |
|
}, 500); // 每0.5秒更新一次 |
|
$('#console').append(' '); // 換行 |
|
//console.log("find enable python file !!!!!!!!!!!!!! "); |
|
} |
|
} |
|
resolve(); // 結束後解析 Promise |
|
}, |
|
error: function (retdata) { |
|
console.warn(retdata); |
|
reject(retdata); // 發生錯誤時拒絕 Promise |
|
} |
|
}); |
|
}); |
|
} |
|
// 新建檔案 |
|
function addNewFile() { |
|
var $grid = $('#file-data-grid'); |
|
var rows = $grid.datagrid('getRows'); |
|
|
|
if (rows.length > 0 && rows[0].isNew) { |
|
return; // 如果已有新檔案輸入欄位,則不執行 |
|
} |
|
|
|
// 增加輸入欄位在最上面 |
|
$grid.datagrid('insertRow', { |
|
index: 0, |
|
row: { |
|
filename: '<input type="text" class="new-file-name" placeholder="Only create .py">', |
|
isNew: true |
|
} |
|
}); |
|
|
|
$grid.datagrid('selectRow', 0); |
|
|
|
$('.new-file-name').on('keypress', function (e) { |
|
if (e.which == 13) { // 按下 Enter |
|
var newFileName = $(this).val(); |
|
|
|
// 是否以 .py 結尾 |
|
if (!newFileName.endsWith('.py')) { |
|
alert('Only new Python files (.py) are supported'); |
|
return; |
|
} |
|
// 是否名稱有效 |
|
if (newFileName) { |
|
pythoncode = ""; |
|
func_FileUpload(newFileName); |
|
} |
|
|
|
// 刪除新檔案輸入欄位 |
|
$grid.datagrid('deleteRow', 0); |
|
} |
|
}); |
|
} |
|
|
|
// 自動選擇列表中的某個文件 |
|
function selectFileInDataGrid(fileName) { |
|
var $grid = $('#file-data-grid'); |
|
var rows = $grid.datagrid('getRows'); |
|
for (var i = 0; i < rows.length; i++) { |
|
if (rows[i].filename === fileName) { |
|
$grid.datagrid('selectRow', i); |
|
currentFileName = rows[i].filename; |
|
func_FileGet(currentFileName); |
|
break; |
|
} |
|
} |
|
} |
|
$(document).ready(function () { |
|
// 副檔名圖片 |
|
$('#file-data-grid').datagrid({ |
|
title: 'Files', |
|
columns: [[ |
|
{ |
|
field: 'filename', |
|
title: 'Name', |
|
width: 460, |
|
formatter: function (value, row, index) { |
|
var icon = '<img src="./images/file.png" style="height:16px; width:16px;"/>'; |
|
if (value.endsWith('.py')) { |
|
icon = '<img src="./images/py.png" style="height:16px; width:16px;"/>'; |
|
} else if (value.endsWith('.html')) { |
|
icon = '<img src="./images/html.png" style="height:16px; width:16px;"/>'; |
|
} else if (value.endsWith('.js')) { |
|
icon = '<img src="./images/js.png" style="height:16px; width:16px;"/>'; |
|
} else if (value.endsWith('.bin')) { |
|
icon = '<img src="./images/bin.png" style="height:16px; width:16px;"/>'; |
|
} else if (value.endsWith('.png')) { |
|
icon = '<img src="./images/png.png" style="height:16px; width:16px;"/>'; |
|
} else if (value.endsWith('.jpg')) { |
|
icon = '<img src="./images/jpg.png" style="height:16px; width:16px;"/>'; |
|
} else if (value.endsWith('.json')) { |
|
icon = '<img src="./images/json.png" style="height:16px; width:16px;"/>'; |
|
} else if (value.endsWith('.zip') || value.endsWith('.rar') || value.endsWith('.7z')) { |
|
icon = '<img src="./images/zip.png" style="height:16px; width:16px;"/>'; |
|
} |
|
return icon + ' ' + value; |
|
} |
|
} |
|
]], |
|
singleSelect: true, |
|
fitColumns: false, // This will fit the columns to the grid width |
|
nowrap: true // This will prevent wrapping in a cell and possibly remove the need for horizontal scrolling |
|
}); |
|
// 選擇某檔案時,更新當前文件內容 |
|
$('#file-data-grid').datagrid({ |
|
onClickRow: function (rowIndex, rowData) { |
|
currentFileName = rowData.filename; |
|
func_FileGet(currentFileName); |
|
// func_stopPython(); // Ming 2024-09-27 switch file need to stop python |
|
} |
|
|
|
}); |
|
|
|
// Ctrl + S 保存 |
|
editor.on('keydown', function (instance, event) { |
|
if (event.ctrlKey && event.which === 83) { |
|
event.preventDefault(); |
|
func_FileSave(currentFileName); // 使用現在的文件名稱進行上傳 |
|
} |
|
}); |
|
// Shift + Enter 執行 |
|
editor.on('keydown', function (instance, event) { |
|
if (event.shiftKey && event.which === 13) { |
|
event.preventDefault(); |
|
//console.log("Now Running:", currentFileName); |
|
// 禁用「Run」,防止連續點擊 |
|
$("#run-button").prop("disabled", true); |
|
func_runPython(currentFileName); // 使用現在的文件名稱執行 Python |
|
} |
|
}); |
|
// 設定 Textarea 可使用 Tab |
|
editor.setOption("extraKeys", { |
|
Tab: function(cm) { |
|
cm.replaceSelection(" ", "end"); |
|
} |
|
}); |
|
|
|
$('#console').on('mousedown', function() { |
|
isUserScrolling = true; |
|
}); |
|
|
|
$('#console').on('mouseup mouseleave', function() { |
|
isUserScrolling = false; |
|
}); |
|
// Textarea 自動滾動 Ming 2024-08-29 |
|
$('#console').on('scroll', function() { |
|
var textarea = this; |
|
// 用滑鼠滾動條控制 |
|
if (isUserScrolling) { |
|
if (textarea.scrollTop + textarea.clientHeight < textarea.scrollHeight) { |
|
autoScroll = false; |
|
} else { |
|
autoScroll = true; |
|
} |
|
} |
|
}); |
|
// 按儲存按鈕 |
|
$('#save-button').click(function (event) { |
|
//console.log("Click event triggered on #save-button"); |
|
func_FileSave(currentFileName); // 使用現在的文件名稱進行上傳 |
|
}); |
|
// 按刪除按鈕 |
|
$('#deletefile-button').click(function () { |
|
if (currentFileName) { |
|
Swal.fire({ |
|
title: MESSAGE108 + "<" + currentFileName + ">", |
|
icon: 'warning', |
|
showCancelButton: true, |
|
}).then((result) => { |
|
if (result.isConfirmed) { |
|
func_FileDelete(currentFileName); |
|
} |
|
}); |
|
} |
|
}); |
|
// 上傳按鈕 (只接受 .py 檔案) |
|
$('#uploadfile-button').click(function() { |
|
var fileInput = $('<input type="file" accept=".py">'); |
|
fileInput.click(); |
|
fileInput.on('change', function(e) { |
|
var file = e.target.files[0]; |
|
if (file) { |
|
var reader = new FileReader(); |
|
reader.onload = function(e) { |
|
if (!file.name.endsWith('.py')) { |
|
alert('Only supports uploading Python files (.py)'); |
|
return; |
|
} |
|
pythoncode = e.target.result; |
|
var fileName = file.name; |
|
func_FileUpload(fileName); |
|
}; |
|
reader.readAsText(file); |
|
} |
|
}); |
|
}); |
|
// 按執行按鈕 |
|
$('#run-button').click(function () { |
|
$('#console').append("Running.....\n"); |
|
var startTime = Date.now(); // 記錄開始時間 |
|
// 禁用「Run」,防止連續點擊 |
|
$("#run-button").prop("disabled", true); |
|
func_runPython(currentFileName); // 使用現在文件執行 Python |
|
|
|
// StartGetPythonBuffer(currentFileName) |
|
}); |
|
// 按暫停按鈕 |
|
$('#stop-button').click(function () { |
|
// 開啟 「Run」& Func_funpython |
|
$("#run-button").prop("disabled", false); |
|
func_stopPython(currentFileName) |
|
}); |
|
// 按清除 Console 按鈕 |
|
$('#ClearConsole-button').click(function () { |
|
$('#console').html(''); |
|
}); |
|
// 將程式碼清空並上傳 |
|
$('#delete-button').click(function () { |
|
//console.log(""); |
|
Swal.fire({ |
|
title: MESSAGE109, |
|
icon: 'warning', |
|
showCancelButton: true, |
|
}).then((result) => { |
|
if (result.isConfirmed) { |
|
// 清空 textarea 的内容 |
|
//var codeContent = editor.getValue(); |
|
editor.setValue(""); |
|
func_FileUpload(currentFileName); |
|
} |
|
}); |
|
}); |
|
}); |
|
</script> |
|
</body> |
|
</html> |