From d033d4bc085f93ed46edb80c223395db72d84b4c Mon Sep 17 00:00:00 2001 From: kujirahand Date: Sat, 29 Jun 2024 21:48:36 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE=E5=86=8D?= =?UTF-8?q?=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=81=BF=E3=81=8C=E5=BF=85=E8=A6=81?= =?UTF-8?q?=E3=81=AE=E3=83=90=E3=82=B0=20#95=20jQuery=E3=81=B8=E3=81=AE?= =?UTF-8?q?=E4=BE=9D=E5=AD=98=E3=82=92=E5=B0=91=E3=81=AA=E3=81=8F=20#96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kona3engine/action/edit.inc.php | 62 +++++--- kona3engine/kona3conf.inc.php | 1 + kona3engine/kona3lib.inc.php | 2 +- kona3engine/konawiki_version.inc.php | 2 +- kona3engine/lang/ja-ai_prompt.md | 12 +- kona3engine/resource/ajax_qq.js | 158 +++++++++++++++++++ kona3engine/resource/edit.js | 228 ++++++++++----------------- kona3engine/template/edit.html | 1 + 8 files changed, 302 insertions(+), 164 deletions(-) create mode 100644 kona3engine/resource/ajax_qq.js diff --git a/kona3engine/action/edit.inc.php b/kona3engine/action/edit.inc.php index 841d831..8d05c9b 100644 --- a/kona3engine/action/edit.inc.php +++ b/kona3engine/action/edit.inc.php @@ -140,7 +140,7 @@ function kona3edit_checkEditToken($page, $i_mode) { // check edit_token if (!kona3_checkEditToken($page)) { $label = lang('Edit'); - $edit_token = kona3_getEditToken($page, TRUE); + $edit_token = kona3_getEditToken($page, FALSE); $url = kona3getPageURL($page, 'edit', '', "edit_token=" . $edit_token); $page_html = htmlspecialchars($page, ENT_QUOTES); if ($i_mode == 'form') { @@ -150,7 +150,14 @@ function kona3edit_checkEditToken($page, $i_mode) { "$label - $page_html" ); } else { - kona3_edit_err(lang('Invalid edit token.'), $i_mode); + $postId = intval(kona3param('postId', 0)); + $edit_token = ''; + foreach ($_SESSION as $key => $val) { + if (is_string($val)) { + $edit_token .= "[$key=$val]"; + } + } + kona3_edit_err(lang('Invalid edit token.') . "et=$edit_token", $i_mode, $postId); } exit; } @@ -176,7 +183,7 @@ function edit_command($cmd) { 'WHERE history_id=? AND hash=?', [$history_id, $hash]); if ($r) { - $edit_token = kona3_getEditToken(); + $edit_token = kona3_getEditToken($page, FALSE); $url = kona3getPageURL($page, "edit", "", "edit_token=$edit_token"); return kona3showMessage( 'DELETE History', @@ -278,6 +285,7 @@ function kona3_trywrite(&$txt, &$a_hash, $i_mode, &$result) { $a_hash_frm = kona3param('a_hash', ''); $tags = kona3param('tags', ''); $edit_ext = kona3param('edit_ext', ''); + $postId = intval(kona3param('postId', 0)); // option $fname = kona3getEditFile("{$page}.{$edit_ext}", $ext); $user_id = kona3getUserId(); @@ -291,7 +299,7 @@ function kona3_trywrite(&$txt, &$a_hash, $i_mode, &$result) { // === for FILE === if (file_exists($fname)) { if (!is_writable($fname)) { - kona3_edit_err(lang('Could not write file.'), $i_mode); + kona3_edit_err(lang('Could not write file.'), $i_mode, $postId); return ""; } } else { @@ -313,25 +321,25 @@ function kona3_trywrite(&$txt, &$a_hash, $i_mode, &$result) { // check directories level if ($cnt > $max_level) { if ($max_level == 0) { - kona3_edit_err(lang("Invalid Wiki Name: not allow use '/'"), $i_mode); + kona3_edit_err(lang("Invalid Wiki Name: not allow use '/'"), $i_mode, $postId); exit; } kona3_edit_err( sprintf(lang("Invalid Wiki Name: not allow use '/' over %s times"), $max_level), - $i_mode); + $i_mode, $postId); exit; } // get dir mode $dir_mode = @octdec($kona3conf['chmod_mkdir']); if ($dir_mode == 0) { - kona3_edit_err('Invalid value: chmod_mkdir in config', $i_mode); + kona3_edit_err('Invalid value: chmod_mkdir in config', $i_mode, $postId); exit; } // mkdir $b = @mkdir($dirname, $dir_mode, TRUE); if (!$b) { - kona3_edit_err('mkdir failed.', $i_mode); + kona3_edit_err('mkdir failed.', $i_mode, $postId); exit; } } @@ -341,7 +349,7 @@ function kona3_trywrite(&$txt, &$a_hash, $i_mode, &$result) { $bytes = @file_put_contents($fname, $edit_txt); if ($bytes === FALSE) { $msg = lang('Could not write file.'); - kona3_edit_err($msg, $i_mode); + kona3_edit_err($msg, $i_mode, $postId); $result = FALSE; return $msg; } @@ -357,6 +365,7 @@ function kona3_trywrite(&$txt, &$a_hash, $i_mode, &$result) { echo json_encode(array( 'result' => 'ok', 'a_hash' => kona3getPageHash($edit_txt), + 'postId' => $postId, )); return TRUE; } @@ -450,14 +459,19 @@ function kona3edit_ai() { function kona3edit_ai_load_template() { // read wiki data (ai_prompt) + // read user defined $prompt_file = KONA3_DIR_DATA."/ai_prompt.md"; - $prompt = file_exists($prompt_file) ? file_get_contents($prompt_file) : ''; - if ($prompt == '') { - // read default template - $lang = kona3getLangCode(); - $prompt_file = KONA3_DIR_ENGINE."/lang/{$lang}-ai_prompt.md"; - $prompt = file_get_contents($prompt_file); + $prompt = file_exists($prompt_file) ? @file_get_contents($prompt_file) : ''; + // read system defined + $lang = kona3getLangCode(); + $prompt_file = KONA3_DIR_ENGINE."/lang/{$lang}-ai_prompt.md"; + if (file_exists($prompt_file)) { + $promptSys = file_get_contents($prompt_file); + if ($promptSys != '') { + $prompt .= $promptSys; + } } + // echo json_encode([ 'result' => 'ok', 'message' => $prompt, @@ -470,9 +484,21 @@ function kona3ai_edit_template() $prompt_file = KONA3_DIR_DATA."/ai_prompt.md"; if (!file_exists($prompt_file)) { // copy template - $lang = kona3getLangCode(); - $prompt_file_template = KONA3_DIR_ENGINE."/lang/$lang-ai_prompt.md"; - file_put_contents($prompt_file, file_get_contents($prompt_file_template)); + $template = << $expire_time) { diff --git a/kona3engine/konawiki_version.inc.php b/kona3engine/konawiki_version.inc.php index 695c9fc..7c43f23 100644 --- a/kona3engine/konawiki_version.inc.php +++ b/kona3engine/konawiki_version.inc.php @@ -1,2 +1,2 @@ { + e.addEventListener('click', f); + }; + obj.keydown = (f) => { + e.addEventListener('keydown', f); + }; + obj.change = (f) => { + e.addEventListener('change', f); + }; + obj.attr = (key, val) => { + if (val !== undefined) { + return e.setAttribute(key, val); + } + return e.getAttribute(key, val); + }; + obj.prop = (key, val) => { + if (val !== undefined) { + e[key] = val; + } + return e[key]; + }; + obj.html = (val) => { + if (val !== undefined) { + e.innerHTML = val; + } + return e.innerHTML; + }; + obj.val = (val) => { + if (val !== undefined) { + e.value = val; + } + return e.value; + }; + obj.text = (val) => { + if (val !== undefined) { + e.innerText = val; + } + return e.innerText; + }; + obj.css = (styleName, val) => { + if (val !== undefined) { + e.style[styleName] = val; + } + return e.style[styleName]; + }; + obj.on = (event, f) => { + e.addEventListener(event, f); + if (events[event] === undefined) { + events[event] = []; + } + events[event].push(f); + }; + obj.off = (event, f) => { + if (f !== undefined) { + e.removeEventListener(event, f); + } else { + if (events[event] !== undefined) { + events[event].forEach((f) => { + e.removeEventListener(event, f); + }); + } + } + }; + obj.enabled = (val) => { + if (val !== undefined) { + e.disabled = !val; + } + return !e.disabled; + }; + obj.show = () => { + e.style.display = 'block'; + } + obj.hide = () => { + e.style.display = 'none'; + } + obj.append = (child) => { + e.appendChild(child); + } + // ajax method + obj.post = (url, dataObj) => { + // make FormData + let formData = new FormData(); + if (dataObj instanceof FormData) { + formData = dataObj; + } else { + for (const key in dataObj) { + if (dataObj.hasOwnProperty(key)) { + formData.append(key, dataObj[key]); + } + } + } + // make request + setTimeout(() => { + // fetch + fetch(url, { + method: 'POST', + body: formData, + }) + .then((response) => { + if (!response.ok) { + throw new Error('Network response was not ok.'); + } + return response.text(); + }) + .then((text) => { + if (typeof (text) == 'string') { + try { + jsonObj = JSON.parse(text); + } catch (e) { + jsonObj = { "result": false, "reason": text }; + } + } + obj._done(jsonObj); + }) + .catch((error) => { + obj._fail(error); + }) + }, 1) + return obj; + }; + obj._done = (_f) => { } + obj._fail = (_f) => { } + obj.done = (f) => { obj._done = f; return obj; } + obj.fail = (f) => { obj._fail = f; return obj; } + return obj; +} diff --git a/kona3engine/resource/edit.js b/kona3engine/resource/edit.js index 18005db..b61b69d 100644 --- a/kona3engine/resource/edit.js +++ b/kona3engine/resource/edit.js @@ -9,83 +9,9 @@ var outline_mode = false; var outline_lines = []; var isChanged = false; var timerIdAutosave = 0; -var isWaiting = false; // Ajaxの待ち合わせ中 - -// 簡単なDOM操作の関数群 -function qs(id) { return document.querySelector(id); } -function qsa(id) { return document.querySelectorAll(id); } -function prop(id, key) { - const e = qs(id); - if (!e) { return ''; } - return e.getAttribute(key); -} -function setEnabled(id, enabled) { - let e = id; - if (typeof(id) === 'string') { e = qs(id); } - if (!e) { return false; } - e.disabled = !enabled; -} -function getEnabled(id) { - let e = id; - if (typeof(id) === 'string') { e = qs(id); } - if (!e) { return false; } - return !e.disabled; -} -function qq(id) { - let e = id; - if (typeof(id) === 'string') { - e = document.querySelector(id); - } - if (!e) { - console.warn('qq: not found', id); - return null; - } - const obj = {} - obj.click = (f) => { - e.addEventListener('click', f); - }; - obj.attr = (key, val) => { - if (val !== undefined) { - return e.getAttribute(key); - } - e.setAttribute(key, val); - }; - obj.prop = (key, val) => { - if (val !== undefined) { - e[key] = val; - } - return e[key]; - }; - obj.html = (val) => { - if (val !== undefined) { - e.innerHTML = val; - } - return e.innerHTML; - }; - obj.val = (val) => { - if (val !== undefined) { - e.value = val; - } - return e.value; - }; - obj.css = (styleName, val) => { - if (val !== undefined) { - e.style[styleName] = val; - } - return e.style[styleName]; - }; - obj.on = (event, f) => { - e.addEventListener(event, f); - }; - obj.enabled = (val) => { - if (val !== undefined) { - e.disabled = !val; - } - return !e.disabled; - }; - return obj; -} - +var isProcessingAjax = false; // Ajaxの待ち合わせ中 +var isDebug = false; // このファイルのJSをデバッグする時はtrueにする +var postId = 1; // init window.addEventListener('load', edit_init, false); @@ -96,10 +22,19 @@ href = href.replace('index.php', ''); href = href.replace(/(http|https)\:\/\//, ''); var STORAGE_KEY = 'kona3:' + href; +function elog(message) { + if (isDebug) { + console.log(message); + } +} + function edit_init() { // editor key event const edit_txt = qs('#edit_txt'); edit_txt.addEventListener('keydown', editorKeydownHandler, false); + edit_txt.addEventListener('input', () => { + handleChange(true, 'input'); + }); // set button event qq('#temporarily_save_btn').click(clickTempSaveButton); @@ -121,10 +56,12 @@ function edit_init() { loadAutoSave(); // shortcut - $(window).keydown(function(e) { + qq(window).keydown(function(e) { // shortcut Ctrl+S if ((e.metaKey || e.ctrlKey) && e.keyCode == 83) { - clickTempSaveButton(); + if (isChanged) { + ajaxSave('Shortcut:Ctrl+S'); + } e.preventDefault(); } // shortcut Ctrl+Alt+N @@ -137,6 +74,22 @@ function edit_init() { }); } +// handle editor change event +function handleChange(changed, reason) { + elog(`changed: ${changed} - ${reason}`) + isChanged = changed; + // set #change-info + if (isChanged) { + qq('#change-info').html('[c]'); + use_beforeunload(true); + setButtonsDisabled(false); + } else { + qq('#change-info').html('-'); + use_beforeunload(false); + setButtonsDisabled(true); + } +} + // edit_txt.onkeydown function editorKeydownHandler(event) { const c = event.keyCode; @@ -146,11 +99,6 @@ function editorKeydownHandler(event) { if (c == 13) { // ENTER saveTextToLS(); } - if (!isChanged) { - isChanged = true; - use_beforeunload(true); - setButtonsDisabled(false); - } } // auto save setting @@ -188,14 +136,14 @@ var use_unload_flag = false; function use_beforeunload(b) { if (use_unload_flag == b) return; if (b) { - $(window).on('beforeunload', function() { + qq(window).on('beforeunload', function() { return "Finish editing?"; }); qq('form').on('submit', function() { - $(window).off('beforeunload'); + qq(window).off('beforeunload'); }); } else { - $(window).off('beforeunload'); + qq(window).off('beforeunload'); } use_unload_flag = b; } @@ -213,73 +161,65 @@ function loadTextFromLS() { } function clickTempSaveButton() { - if (isWaiting) { - console.log('save - waiting') - return - } - console.log('save') + console.log('@clickTempSaveButton') saveTextToLS(); - save_ajax(); + ajaxSave('clickTempSaveButton'); } -function save_ajax() { - qq('#temporarily_save_btn').prop('disabled', true); - go_ajax('trywrite'); +// save to server by ajax +function ajaxSave(source) { + ajaxProc('trywrite', source); } + function git_save() { qq('#git_save_btn').prop('disabled', true); - go_ajax('trygit'); + ajaxProc('trygit'); } // Timer function timerAutoSaveOnTime() { - if (isChanged && !isWaiting) { - if (!qq('#temporarily_save_btn').prop('disabled')) { - clickTempSaveButton(); - } + if (isChanged && !isProcessingAjax) { + ajaxSave('timerAutoSaveOnTime'); } } -function go_ajax(a_mode) { - if (isWaiting) { +// Ajax procedure for save +function ajaxProc(a_mode, source) { + if (isProcessingAjax) { console.log("- skip go_ajax:" + a_mode) return; } - isWaiting = true; - var action = qq('#wikiedit form').attr('action'); - var text = qq('#edit_txt').val(); - $.post(action, - { - 'i_mode': 'ajax', - 'a_mode': a_mode, - 'a_hash': qq('#a_hash').val(), - 'edit_txt': text, - 'edit_ext': qq('#edit_ext').val(), - 'edit_token': qq('#edit_token').val(), - 'tags': qq('#tags').val() - }) - .done(function(msg) { - isChanged = false; - isWaiting = false; - // parse to json - if (typeof(msg) == 'string') { - try { - msg = JSON.parse(msg); - } catch (e) { - msg = {"result":false, "reason":msg}; - } - } + qq('#temporarily_save_btn').prop('disabled', true); + + isProcessingAjax = true; + const text = qq('#edit_txt').val(); + const actionUrl = qq('#wikiedit form').attr('action'); + const pid = postId++; + const params = new FormData(); + params.append('i_mode', 'ajax'); + params.append('a_mode', a_mode); + params.append('a_hash', qq('#a_hash').val()); + params.append('edit_txt', text); + params.append('edit_ext', qq('#edit_ext').val()); + params.append('edit_token', qq('#edit_token').val()); + params.append('tags', qq('#tags').val()); + params.append('postId', pid); + // post + console.log(`@@ajaxProc::${pid}::${a_mode}::${source}`) + qq().post(actionUrl, params) + .done(msg => { + elog(`@@done: ${pid}`) // check result - var result = msg["result"]; + const result = msg["result"]; if (result != 'ok') { - console.log(msg); + console.error(`ajaxProc::error::${pid}`, msg); const code = msg['code']; if (code == 'nologin') { console.log('try to login!!') // auto login? kona3tryAutologin(false); setTimeout(() => { - save_ajax(); + ajaxSave('auto login and save'); }, 1000); } qq('#edit_info').html("[error] " + msg['reason']); @@ -287,6 +227,9 @@ function go_ajax(a_mode) { setButtonsDisabled(false); return; } + // ok + handleChange(false, `ajaxProc::done::${pid}`) + isProcessingAjax = false; // count countText(); // set hash @@ -303,8 +246,9 @@ function go_ajax(a_mode) { info.css('color', 'silver'); }, 700); }) - .fail(function(xhr, status, error) { - isWaiting = false; + .fail(function(error) { + console.error('[ajax::fail]', pid, error) + isProcessingAjax = false; qq('#edit_info').html("Sorry request failed." + error); setButtonsDisabled(false); }); @@ -516,7 +460,6 @@ function aiAskClickHandler() { aiInsertText('---'); return; } - console.log('@@@aiAskClickHandler:', text) // test case if (text.substring(0, 3) === '@@@') { aiInsertText(text.substring(3)); @@ -525,7 +468,7 @@ function aiAskClickHandler() { // ajax aiButtonEnabeld(false); var action = qq('#wikiedit form').attr('action'); - $.post(action, + qq().post(action, { 'i_mode': 'ajax', 'edit_token': qq('#edit_token').val(), @@ -539,7 +482,7 @@ function aiAskClickHandler() { const msg = obj['message']; aiInsertText('' + msg); }) - .fail(function (xhr, status, error) { + .fail(function (error) { qq('#edit_info').html("Sorry AI request failed." + error); aiButtonEnabeld(true); }); @@ -575,7 +518,6 @@ function aiBlockCopy(id) { } function aiBlockReplace(id) { - console.log('@aiBlockReplace', id) // extract JSON block let block = qq('#aiBlock' + id).text(); let edit_txt = qq('#edit_txt').text(); @@ -602,7 +544,6 @@ function aiBlockReplace(id) { function loadAITemplate() { const action = qq('#wikiedit form').attr('action'); - console.log('@', action) let params = { 'i_mode': 'ajax', 'edit_token': qq('#edit_token').val(), @@ -610,11 +551,11 @@ function loadAITemplate() { 'a_mode': 'load_template', 'a_hash': qq('#a_hash').val(), } - $.post(action, params) + qq().post(action, params) .done(function (obj) { const messageStr = obj['message']; const selectBox = qq('#ai_template_select'); - const messages = messageStr.split("\n"); + const messages = messageStr.split("\r").join("").split("\n"); const templateData = {}; let key = ''; messages.forEach(function (message) { @@ -623,7 +564,7 @@ function loadAITemplate() { const option = document.createElement("option"); option.text = message; selectBox.append(option); - templateData[key] = '' + templateData[key] = ''; return; } if (message.substring(0, 5) == '-----') { @@ -636,12 +577,13 @@ function loadAITemplate() { const key = selectBox.val(); if (key == '') { return; } const input = qq('#ai_input_text'); - if (templateData[key]) { - input.val(templateData[key]); + const val = templateData[key]; + if (val) { + input.val(val); } }) }) - .fail(function (xhr, status, error) { + .fail(function (error) { qq('#edit_info').html("Sorry AI request failed." + error); aiButtonEnabeld(true); }); diff --git a/kona3engine/template/edit.html b/kona3engine/template/edit.html index 979d37f..7eb2f89 100644 --- a/kona3engine/template/edit.html +++ b/kona3engine/template/edit.html @@ -29,6 +29,7 @@
+ -