From b3a48ac13953d875455230316c2f88adf2cb0de8 Mon Sep 17 00:00:00 2001 From: "Sha Md. Nawaz Sharif" Date: Wed, 6 Nov 2024 11:37:17 +0600 Subject: [PATCH 01/14] Create gha.dist.yml --- .github/gha.dist.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/gha.dist.yml diff --git a/.github/gha.dist.yml b/.github/gha.dist.yml new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/.github/gha.dist.yml @@ -0,0 +1 @@ + From 4396b0942a5a1b87a5a26fdffa8229bef55cb95f Mon Sep 17 00:00:00 2001 From: "Sha Md. Nawaz Sharif" Date: Wed, 6 Nov 2024 11:44:40 +0600 Subject: [PATCH 02/14] Delete .github directory --- .github/gha.dist.yml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .github/gha.dist.yml diff --git a/.github/gha.dist.yml b/.github/gha.dist.yml deleted file mode 100644 index 8b137891..00000000 --- a/.github/gha.dist.yml +++ /dev/null @@ -1 +0,0 @@ - From 6e0c65e61115278824e1930218f75bd73f783dbb Mon Sep 17 00:00:00 2001 From: "Sha Md. Nawaz Sharif" Date: Wed, 6 Nov 2024 11:48:35 +0600 Subject: [PATCH 03/14] Create ci.yml --- .github/workflows/ci.yml | 122 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..3cdfd061 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,122 @@ +name: Moodle Plugin CI + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-22.04 + + services: + postgres: + image: postgres:13 + env: + POSTGRES_USER: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3 + + mariadb: + image: mariadb:10 + env: + MYSQL_USER: 'root' + MYSQL_ALLOW_EMPTY_PASSWORD: "true" + MYSQL_CHARACTER_SET_SERVER: "utf8mb4" + MYSQL_COLLATION_SERVER: "utf8mb4_unicode_ci" + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3 + + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0', '8.1'] + moodle-branch: ['MOODLE_401_STABLE'] + database: [pgsql, mariadb] + + steps: + - name: Check out repository code + uses: actions/checkout@v4 + with: + path: plugin + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.extensions }} + ini-values: max_input_vars=5000 + # If you are not using code coverage, keep "none". Otherwise, use "pcov" (Moodle 3.10 and up) or "xdebug". + # If you try to use code coverage with "none", it will fallback to phpdbg (which has known problems). + coverage: none + + - name: Initialise moodle-plugin-ci + run: | + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 + echo $(cd ci/bin; pwd) >> $GITHUB_PATH + echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH + sudo locale-gen en_AU.UTF-8 + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV + + - name: Install moodle-plugin-ci + run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 + env: + DB: ${{ matrix.database }} + MOODLE_BRANCH: ${{ matrix.moodle-branch }} + # Uncomment this to run Behat tests using the Moodle App. + # MOODLE_APP: 'true' + + - name: PHP Lint + if: ${{ !cancelled() }} + run: moodle-plugin-ci phplint + + - name: PHP Mess Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpmd + + - name: Moodle Code Checker + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpcs --max-warnings 0 + + - name: Moodle PHPDoc Checker + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpdoc --max-warnings 0 + + - name: Validating + if: ${{ !cancelled() }} + run: moodle-plugin-ci validate + + - name: Check upgrade savepoints + if: ${{ !cancelled() }} + run: moodle-plugin-ci savepoints + + - name: Mustache Lint + if: ${{ !cancelled() }} + run: moodle-plugin-ci mustache + + - name: Grunt + if: ${{ !cancelled() }} + run: moodle-plugin-ci grunt --max-lint-warnings 0 + + - name: PHPUnit tests + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpunit --fail-on-warning + + - name: Behat features + id: behat + if: ${{ !cancelled() }} + run: moodle-plugin-ci behat --profile chrome + + - name: Upload Behat Faildump + if: ${{ failure() && steps.behat.outcome == 'failure' }} + uses: actions/upload-artifact@v4 + with: + name: Behat Faildump (${{ join(matrix.*, ', ') }}) + path: ${{ github.workspace }}/moodledata/behat_dump + retention-days: 7 + if-no-files-found: ignore + + - name: Mark cancelled jobs as failed. + if: ${{ cancelled() }} + run: exit 1 From d7848b0f1e07812d3360e72297e6d2d6895029bb Mon Sep 17 00:00:00 2001 From: "Sha Md. Nawaz Sharif" Date: Wed, 6 Nov 2024 11:51:02 +0600 Subject: [PATCH 04/14] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cdfd061..2d80c7ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: fail-fast: false matrix: php: ['7.4', '8.0', '8.1'] - moodle-branch: ['MOODLE_401_STABLE'] + moodle-branch: ['MOODLE_405_STABLE'] database: [pgsql, mariadb] steps: From 7867335b9bd5136b3805ab4826f8fece65d2f3bd Mon Sep 17 00:00:00 2001 From: "Sha Md. Nawaz Sharif" Date: Wed, 6 Nov 2024 11:52:35 +0600 Subject: [PATCH 05/14] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d80c7ea..dcc931b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.4', '8.0', '8.1'] + php: ['8.1', '7.2', '8.3'] moodle-branch: ['MOODLE_405_STABLE'] database: [pgsql, mariadb] From 31c646b205a753d41c8733b6e53fc9716109b5a5 Mon Sep 17 00:00:00 2001 From: "Sha Md. Nawaz Sharif" Date: Wed, 6 Nov 2024 11:53:43 +0600 Subject: [PATCH 06/14] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcc931b1..2dac55e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.1', '7.2', '8.3'] + php: ['8.1', '8.2', '8.3'] moodle-branch: ['MOODLE_405_STABLE'] database: [pgsql, mariadb] From dbadcb710222f2cd23c05391543e05234844c7b4 Mon Sep 17 00:00:00 2001 From: Tareq-Adnan Date: Thu, 15 Jan 2026 17:55:26 +0600 Subject: [PATCH 07/14] Cursive PDF Annotator plugin Support, some issue fixes --- amd/build/append_pdfannotator.min.js | 10 + amd/build/append_pdfannotator.min.js.map | 1 + amd/build/autosaver.min.js | 2 +- amd/build/autosaver.min.js.map | 2 +- amd/build/document_view.min.js | 2 +- amd/build/document_view.min.js.map | 2 +- amd/build/plugin.min.js | 2 +- amd/build/plugin.min.js.map | 2 +- amd/build/svg_repo.min.js | 2 +- amd/build/svg_repo.min.js.map | 2 +- amd/src/append_pdfannotator.js | 261 +++++++++++++++++++++++ amd/src/autosaver.js | 138 +++++++----- amd/src/document_view.js | 36 +++- amd/src/plugin.js | 3 +- amd/src/svg_repo.js | 60 +++++- classes/constants.php | 3 +- classes/helper.php | 130 +++++++++++ classes/hook_callbacks.php | 2 +- db/services.php | 9 + externallib.php | 68 +++++- styles.css | 6 + version.php | 2 +- 22 files changed, 668 insertions(+), 77 deletions(-) create mode 100644 amd/build/append_pdfannotator.min.js create mode 100644 amd/build/append_pdfannotator.min.js.map create mode 100644 amd/src/append_pdfannotator.js create mode 100644 classes/helper.php diff --git a/amd/build/append_pdfannotator.min.js b/amd/build/append_pdfannotator.min.js new file mode 100644 index 00000000..b6ba7407 --- /dev/null +++ b/amd/build/append_pdfannotator.min.js @@ -0,0 +1,10 @@ +define("tiny_cursive/append_pdfannotator",["exports","core/ajax","tiny_cursive/analytic_button","tiny_cursive/replay_button","tiny_cursive/analytic_events","core/templates","tiny_cursive/replay"],(function(_exports,_ajax,_analytic_button,_replay_button,_analytic_events,_templates,_replay){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +/** + * Module for handling PDF annotator functionality, + * + * @module tiny_cursive/append_pdfannotator + * @copyright 2025 Cursive Technology, Inc. + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_analytic_button=_interopRequireDefault(_analytic_button),_replay_button=_interopRequireDefault(_replay_button),_analytic_events=_interopRequireDefault(_analytic_events),_templates=_interopRequireDefault(_templates),_replay=_interopRequireDefault(_replay);_exports.init=(scoreSetting,comments,hasApiKey,userid)=>{const replayInstances={};window.video_playback=function(mid,filepath){if(""!==filepath){const replay=new _replay.default("content"+mid,filepath,10,!1,"player_"+mid);replayInstances[mid]=replay}else _templates.default.render("tiny_cursive/no_submission").then((html=>(document.getElementById("content"+mid).innerHTML=html,!0))).catch((e=>window.console.error(e)));return!1};let container=document.querySelector(".comment-list-container");const overviewTable=document.querySelector('table[id^="mod-pdfannotator-"]');document.addEventListener("click",(function(e){"commentSubmit"===e.target.id&&(localStorage.removeItem("isEditing"),buttonElement=e.target.value,pendingSubmit=!0);"commentCancel"===e.target.id&&(localStorage.removeItem("isEditing"),pendingSubmit=!1)}));const moduleName=document.body.id.split("-")[2];var pendingSubmit=!1,buttonElement="";if(container){new MutationObserver((()=>{var _container$lastChild;null!=container&&null!==(_container$lastChild=container.lastChild)&&void 0!==_container$lastChild&&_container$lastChild.id&&function(id){if("Save"===buttonElement)return void(pendingSubmit=!1);let resourceId=parseInt(null==id?void 0:id.split("_")[1]);if(resourceId&&pendingSubmit){pendingSubmit=!1,(async(methodname,args)=>{try{await(0,_ajax.call)([{methodname:methodname,args:args}])[0]}catch(error){throw window.console.error("updating Entries:",error),error}})("cursive_update_pdf_annote_id",{cmid:M.cfg.contextInstanceId,userid:M.cfg.userId??0,courseid:M.cfg.courseId,modulename:moduleName,resourceid:resourceId})}}(container.lastChild.id)})).observe(container,{subtree:!0,childList:!0})}if(overviewTable){let newChild=document.createElement("th");newChild.textContent="Analytics",overviewTable.querySelector("thead>tr>th:first-child").insertAdjacentElement("afterend",newChild),function(overviewTable){const rows=overviewTable.querySelectorAll("tbody > tr"),action=new URL(window.location.href).searchParams.get("action");rows.forEach((row=>{var _cols$col,_cols$col2,_cols$col3,_links$link,_userLink;const cols={col1:row.querySelector("td:nth-child(1)"),col2:row.querySelector("td:nth-child(2)"),col3:row.querySelector("td:nth-child(3)")},links={link1:null===(_cols$col=cols.col1)||void 0===_cols$col?void 0:_cols$col.querySelector("a"),link2:null===(_cols$col2=cols.col2)||void 0===_cols$col2?void 0:_cols$col2.querySelector("a"),link3:null===(_cols$col3=cols.col3)||void 0===_cols$col3?void 0:_cols$col3.querySelector("a")},commentId=null!==(_links$link=links.link1)&&void 0!==_links$link&&_links$link.href?new URL(links.link1.href).searchParams.get("commid"):null;let userId=null,userLink=null;switch(action){case"overviewquestions":userLink=links.link2;break;case"overviewanswers":userLink=links.link3;break;default:userId=userid}if(null!==(_userLink=userLink)&&void 0!==_userLink&&_userLink.href)try{userId=new URL(userLink.href).searchParams.get("id")}catch(e){window.console.warn("Error parsing user URL:",e)}!function(userid,resourceid,cmid,place){let args={id:resourceid,modulename:"pdfannotator",cmid:cmid},methodname="cursive_get_forum_comment_link",com=(0,_ajax.call)([{methodname:methodname,args:args}]);com[0].done((function(json){var data=JSON.parse(json),filepath="";data.data.filename&&(filepath=data.data.filename);let analyticButtonDiv=document.createElement("div"),analyticsColumn=document.createElement("td");hasApiKey?analyticButtonDiv.append((0,_analytic_button.default)(data.data.effort_ratio,resourceid)):analyticButtonDiv.append((0,_replay_button.default)(resourceid)),analyticButtonDiv.dataset.region="analytic-div"+userid,analyticsColumn.append(analyticButtonDiv),place.insertAdjacentElement("afterend",analyticsColumn);let myEvents=new _analytic_events.default;var context={tabledata:data.data,formattime:myEvents.formatedTime(data.data),page:scoreSetting,userid:resourceid,apikey:hasApiKey};let authIcon=myEvents.authorshipStatus(data.data.first_file,data.data.score,scoreSetting);myEvents.createModal(resourceid,context,"",replayInstances,authIcon),myEvents.analytics(resourceid,_templates.default,context,"",replayInstances,authIcon),myEvents.checkDiff(resourceid,data.data.file_id,"",replayInstances),myEvents.replyWriting(resourceid,filepath,"",replayInstances)})),com[0].fail((error=>{window.console.error("Error getting cursive config:",error)}))}(userId,commentId,M.cfg.contextInstanceId,cols.col1)}))}(overviewTable)}}})); + +//# sourceMappingURL=append_pdfannotator.min.js.map \ No newline at end of file diff --git a/amd/build/append_pdfannotator.min.js.map b/amd/build/append_pdfannotator.min.js.map new file mode 100644 index 00000000..2e2adf54 --- /dev/null +++ b/amd/build/append_pdfannotator.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"append_pdfannotator.min.js","sources":["../src/append_pdfannotator.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module for handling PDF annotator functionality,\n *\n * @module tiny_cursive/append_pdfannotator\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call} from 'core/ajax';\nimport analyticButton from 'tiny_cursive/analytic_button';\nimport replayButton from 'tiny_cursive/replay_button';\nimport AnalyticEvents from 'tiny_cursive/analytic_events';\nimport templates from 'core/templates';\nimport Replay from 'tiny_cursive/replay';\nexport const init = (scoreSetting, comments, hasApiKey, userid) => {\n const replayInstances = {};\n // eslint-disable-next-line camelcase\n window.video_playback = function(mid, filepath) {\n if (filepath !== '') {\n const replay = new Replay(\n 'content' + mid,\n filepath,\n 10,\n false,\n 'player_' + mid\n );\n replayInstances[mid] = replay;\n } else {\n templates.render('tiny_cursive/no_submission').then(html => {\n document.getElementById('content' + mid).innerHTML = html;\n return true;\n }).catch(e => window.console.error(e));\n }\n return false;\n };\n\n let container = document.querySelector('.comment-list-container');\n const overviewTable = document.querySelector('table[id^=\"mod-pdfannotator-\"]');\n\n document.addEventListener('click', handleSubmit);\n const moduleName = document.body.id.split('-')[2];\n var pendingSubmit = false;\n var buttonElement = \"\";\n\n if (container) {\n const observer = new MutationObserver(() => {\n if (container?.lastChild?.id) {\n extractResourceId(container.lastChild.id);\n }\n });\n\n observer.observe(container, {\n subtree: true,\n childList: true\n });\n }\n\n if (overviewTable) {\n let newChild = document.createElement('th');\n newChild.textContent = 'Analytics';\n let header = overviewTable.querySelector('thead>tr>th:first-child');\n header.insertAdjacentElement('afterend', newChild);\n setReplayButton(overviewTable);\n }\n\n /**\n * Sets up replay buttons and analytics for each row in the overview table\n * @param {HTMLTableElement} overviewTable - The table element containing the overview data\n * @description This function:\n * 1. Gets all rows from the table\n * 2. For each row:\n * - Extracts comment ID and user ID from relevant links\n * - Adds analytics column with replay/analytics buttons\n * - Sets up cursive analytics functionality\n */\n function setReplayButton(overviewTable) {\n const rows = overviewTable.querySelectorAll('tbody > tr');\n const action = new URL(window.location.href).searchParams.get('action');\n\n rows.forEach(row => {\n const cols = {\n col1: row.querySelector('td:nth-child(1)'),\n col2: row.querySelector('td:nth-child(2)'),\n col3: row.querySelector('td:nth-child(3)')\n };\n\n const links = {\n link1: cols.col1?.querySelector('a'),\n link2: cols.col2?.querySelector('a'),\n link3: cols.col3?.querySelector('a')\n };\n\n // Extract comment ID safely\n const commentId = links.link1?.href ?\n new URL(links.link1.href).searchParams.get('commid') : null;\n\n // Extract user ID based on action\n let userId = null;\n let userLink = null;\n\n switch(action) {\n case 'overviewquestions':\n userLink = links.link2;\n break;\n case 'overviewanswers':\n userLink = links.link3;\n break;\n default:\n userId = userid;\n }\n\n if (userLink?.href) {\n try {\n userId = new URL(userLink.href).searchParams.get('id');\n } catch (e) {\n window.console.warn('Error parsing user URL:', e);\n }\n }\n\n getCursiveAnalytics(userId, commentId, M.cfg.contextInstanceId, cols.col1);\n });\n }\n\n /**\n * Handles the submission and cancellation of comments\n * @param {Event} e - The click event object\n * @description When comment is submitted or cancelled:\n * - Removes 'isEditing' flag from localStorage\n * - Sets pendingSubmit flag appropriately (true for submit, false for cancel)\n */\n function handleSubmit(e) {\n if (e.target.id === 'commentSubmit') {\n localStorage.removeItem('isEditing');\n buttonElement = e.target.value;\n pendingSubmit = true;\n }\n if (e.target.id === 'commentCancel') {\n localStorage.removeItem('isEditing');\n pendingSubmit = false;\n }\n }\n\n const updateEntries = async (methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n return response;\n } catch (error) {\n window.console.error('updating Entries:', error);\n throw error;\n }\n };\n\n /**\n * Extracts the resource ID from a comment ID and updates entries if submission is pending\n * @param {string} id - The ID string to extract resource ID from, expected format: 'prefix_number'\n * @description This function:\n * 1. Parses the resource ID from the given ID string\n * 2. If resource ID exists and there's a pending submission:\n * - Resets the pending submission flag\n * - Constructs arguments with context info\n * - Calls updateEntries to process the PDF annotation\n */\n function extractResourceId(id) {\n\n // prevent updating ID while editing a existing entry.\n if (buttonElement === 'Save') {\n\n pendingSubmit = false;\n return;\n }\n\n let resourceId = parseInt(id?.split('_')[1]);\n if (resourceId && pendingSubmit) {\n pendingSubmit = false;\n let args = {\n cmid: M.cfg.contextInstanceId,\n userid: M.cfg.userId ?? 0,\n courseid: M.cfg.courseId,\n modulename: moduleName,\n resourceid: resourceId\n };\n updateEntries('cursive_update_pdf_annote_id', args);\n }\n }\n\n /**\n * Retrieves and displays cursive analytics for a given resource\n * @param {number} userid - The ID of the user\n * @param {number} resourceid - The ID of the resource to get analytics for\n * @param {number} cmid - The course module ID\n * @param {HTMLElement} place - The DOM element where analytics should be placed\n * @description This function:\n * 1. Makes an AJAX call to get forum comment data\n * 2. Creates and inserts analytics/replay buttons\n * 3. Sets up analytics events and modal functionality\n * 4. Handles both API key and non-API key scenarios\n */\n function getCursiveAnalytics(userid, resourceid, cmid, place) {\n let args = {id: resourceid, modulename: \"pdfannotator\", cmid: cmid};\n let methodname = 'cursive_get_forum_comment_link';\n let com = call([{methodname, args}]);\n com[0].done(function(json) {\n var data = JSON.parse(json);\n\n var filepath = '';\n if (data.data.filename) {\n filepath = data.data.filename;\n }\n\n let analyticButtonDiv = document.createElement('div');\n let analyticsColumn = document.createElement('td');\n\n if (!hasApiKey) {\n analyticButtonDiv.append(replayButton(resourceid));\n } else {\n analyticButtonDiv.append(analyticButton(data.data.effort_ratio, resourceid));\n }\n\n analyticButtonDiv.dataset.region = \"analytic-div\" + userid;\n analyticsColumn.append(analyticButtonDiv);\n place.insertAdjacentElement('afterend', analyticsColumn);\n\n let myEvents = new AnalyticEvents();\n var context = {\n tabledata: data.data,\n formattime: myEvents.formatedTime(data.data),\n page: scoreSetting,\n userid: resourceid,\n apikey: hasApiKey\n };\n\n let authIcon = myEvents.authorshipStatus(data.data.first_file, data.data.score, scoreSetting);\n myEvents.createModal(resourceid, context, '', replayInstances, authIcon);\n myEvents.analytics(resourceid, templates, context, '', replayInstances, authIcon);\n myEvents.checkDiff(resourceid, data.data.file_id, '', replayInstances);\n myEvents.replyWriting(resourceid, filepath, '', replayInstances);\n\n });\n com[0].fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n }\n};"],"names":["scoreSetting","comments","hasApiKey","userid","replayInstances","window","video_playback","mid","filepath","replay","Replay","render","then","html","document","getElementById","innerHTML","catch","e","console","error","container","querySelector","overviewTable","addEventListener","target","id","localStorage","removeItem","buttonElement","value","pendingSubmit","moduleName","body","split","MutationObserver","lastChild","_container$lastChild","resourceId","parseInt","async","methodname","args","updateEntries","cmid","M","cfg","contextInstanceId","userId","courseid","courseId","modulename","resourceid","extractResourceId","observe","subtree","childList","newChild","createElement","textContent","insertAdjacentElement","rows","querySelectorAll","action","URL","location","href","searchParams","get","forEach","row","cols","col1","col2","col3","links","link1","_cols$col","link2","_cols$col2","link3","_cols$col3","commentId","userLink","_userLink","warn","place","com","done","json","data","JSON","parse","filename","analyticButtonDiv","analyticsColumn","append","effort_ratio","dataset","region","myEvents","AnalyticEvents","context","tabledata","formattime","formatedTime","page","apikey","authIcon","authorshipStatus","first_file","score","createModal","analytics","templates","checkDiff","file_id","replyWriting","fail","getCursiveAnalytics","setReplayButton"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/autosaver.min.js b/amd/build/autosaver.min.js index 13940655..d582ed30 100644 --- a/amd/build/autosaver.min.js +++ b/amd/build/autosaver.min.js @@ -1,3 +1,3 @@ -define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return{resourceId:resourceId,name:modulename}}(ur,new URL(ur),MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;"assign"!==modulename&&(PASTE_SETTING="cite_source");const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:`
${title}
\n ${titledes}
`,body:``,removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename=`${userid}_${resourceId}_${cmid}_${modulename}_attempt`,"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename=`${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey=`${userid}_${resourceId}_${cmid}_position`;let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename);let originalText=editor.getContent({format:"text"});try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector(`#${elementId}`)&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector(`#${elementId}`)||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId=`tiny_cursive_tooltip${index}`;tooltipText.then((text=>setTooltip(text,document.querySelector(`#${elementId}`),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)(`#${elementId}`).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)(`#${tooltipId}`).css(_common.tooltipCss)})),(0,_jquery.default)(`#${elementId}`).on("mouseleave",(function(){(0,_jquery.default)(`#${tooltipId}`).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector(`#${tooltipId}`)&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); +define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,parm=new URL(ur),modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return checkIsPdfAnnotator(),{resourceId:resourceId,name:modulename}}(ur,parm,MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;"assign"!==modulename&&(PASTE_SETTING="cite_source"),ur.includes("pdfannotator")&&document.addEventListener("click",(e=>{if("dropdown-item comment-edit-a"===e.target.className){let id=e.target.id;resourceId=id.replace("editButton",""),localStorage.setItem("isEditing","1")}"commentSubmit"===e.target.id&&syncData()}));const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:`
${title}
\n ${titledes}
`,body:``,removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename=`${userid}_${resourceId}_${cmid}_${modulename}_attempt`,"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename=`${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey=`${userid}_${resourceId}_${cmid}_position`;let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){checkIsPdfAnnotator();let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename),editor.fire("change");let originalText=editor.getContent({format:"text"});try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector(`#${elementId}`)&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector(`#${elementId}`)||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId=`tiny_cursive_tooltip${index}`;tooltipText.then((text=>setTooltip(text,document.querySelector(`#${elementId}`),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)(`#${elementId}`).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)(`#${tooltipId}`).css(_common.tooltipCss)})),(0,_jquery.default)(`#${elementId}`).on("mouseleave",(function(){(0,_jquery.default)(`#${tooltipId}`).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector(`#${tooltipId}`)&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}function checkIsPdfAnnotator(){ur.includes("pdfannotator")&&(resourceId="id_pdfannotator_content"!==editor.id&&parseInt(localStorage.getItem("isEditing"))?parseInt(null==editor?void 0:editor.id.replace("editarea","")):0)}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); //# sourceMappingURL=autosaver.min.js.map \ No newline at end of file diff --git a/amd/build/autosaver.min.js.map b/amd/build/autosaver.min.js.map index 3282341f..2f7ef148 100644 --- a/amd/build/autosaver.min.js.map +++ b/amd/build/autosaver.min.js.map @@ -1 +1 @@ -{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (modulename !== 'assign') {\n PASTE_SETTING = 'cite_source';\n }\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n let originalText = editor.getContent({format: 'text'});\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","modulesInfo","parm","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","name","getModulesInfo","URL","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","e","preventDefault","syncData","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","document","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","originalText","getContent","format","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","className","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","querySelector","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","addEventListener","setInterval"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (modulename !== 'assign') {\n PASTE_SETTING = 'cite_source';\n }\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","querySelector","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/document_view.min.js b/amd/build/document_view.min.js index 7b04f290..ce7b48b7 100644 --- a/amd/build/document_view.min.js +++ b/amd/build/document_view.min.js @@ -5,6 +5,6 @@ define("tiny_cursive/document_view",["exports","tiny_cursive/svg_repo"],(functio * @module tiny_cursive/document_view * @copyright 2025 Cursive Technology, Inc. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_svg_repo=(obj=_svg_repo)&&obj.__esModule?obj:{default:obj};return _exports.default=class{constructor(User,Rubrics,submission,modulename,editor,quizInfo){this.User=User,this.Rubrics=Rubrics,this.submission=submission,this.module=modulename,this.editor=editor,this.moduleIcon=_svg_repo.default.assignment,this.quizInfo=quizInfo,this.initStrings()}normalMode(){var _this$editor;let id=(null===(_this$editor=this.editor)||void 0===_this$editor?void 0:_this$editor.id)+"_ifr";("assign"===this.module||"quiz"===this.module||"forum"===this.module||"lesson"===this.module)&&this.normalizePage(id)}fullPageMode(){var _this$editor4,_this$editor2;if("assign"===this.module)this.moduleIcon=_svg_repo.default.assignment,this.fullPageModule(null===(_this$editor2=this.editor)||void 0===_this$editor2?void 0:_this$editor2.id);else if("forum"===this.module){var _this$editor3;this.moduleIcon=_svg_repo.default.forum,this.fullPageModule(null===(_this$editor3=this.editor)||void 0===_this$editor3?void 0:_this$editor3.id)}else if("quiz"===this.module&&null!==(_this$editor4=this.editor)&&void 0!==_this$editor4&&_this$editor4.id){var _this$editor5;this.moduleIcon=_svg_repo.default.quiz,this.fullPageModule(null===(_this$editor5=this.editor)||void 0===_this$editor5?void 0:_this$editor5.id)}else if("lesson"===this.module){var _this$editor6;this.moduleIcon=_svg_repo.default.lesson,this.fullPageModule(null===(_this$editor6=this.editor)||void 0===_this$editor6?void 0:_this$editor6.id)}}docSideBar(status){var _this$editor7;const replyId=new URL(window.location.href).searchParams.get("reply"),toggle=document.querySelector("#cursive-fullpagemode-sidebar-toggle"),timelimitBlock=this.getTimerBlock(this.module),headerInfo=this.getSidebarTitle(),progressBar=document.querySelector(".box.progress_bar"),courseName=document.querySelector("#page-navbar > nav > ol > li:nth-child(1) > a"),courseDes=document.querySelector("#intro"),Dates=document.querySelector(".activity-dates");let openDate=null==Dates?void 0:Dates.querySelector("div:nth-child(1)"),dueDate=null==Dates?void 0:Dates.querySelector("div:nth-child(2)");const container=this.create("div");Object.assign(container,{id:"cursive-fullpagemode-sidebar",className:"bg-white h-100 shadow"}),Object.assign(container.style,{width:"300px",overflow:"auto"});const crossBtn=this.create("span");Object.assign(crossBtn,{id:"cursive-collapse-sidebar",className:"btn p-2",innerHTML:_svg_repo.default.close}),crossBtn.addEventListener("click",(()=>{container.style.transition="width 0.3s ease",container.style.width="0",toggle.style.display="flex"})),null==toggle||toggle.addEventListener("click",(function(){toggle.style.display="none",container.style.width="300px"}));const btnWrapper=this.create("div");Object.assign(btnWrapper,{padding:"0 1rem",position:"sticky",top:"0",backgroundColor:"white"}),btnWrapper.append(crossBtn);const header=this.create("div");header.className="border-bottom p-3 bg-light",Object.assign(header.style,{position:"sticky",top:"0"});const headerTitle=this.create("h3");headerTitle.className="mb-3 d-flex align-items-center",headerTitle.textContent=`${headerInfo.title} ${this.details}`,headerTitle.style.fontWeight="600";const headerIcon=document.querySelector(".page-header-image > div");headerIcon&&headerTitle.prepend(headerIcon.cloneNode(!0));let wordCount=this.wordCounter(status);null!=timelimitBlock&&timelimitBlock.textContent?header.append(headerTitle,wordCount,this.timerCountDown(timelimitBlock)):header.append(headerTitle,wordCount);const content=this.create("div");if(content.className="p-3",content.append(this.createBox({bg:"bg-info",titleColor:"text-info",icon:_svg_repo.default.people,title:this.studentInfo,bodyHTML:this.generateStudentInfo(this.User,courseName)})),"lesson"===this.module&&progressBar&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.progress,bodyHTML:progressBar.innerHTML})),courseDes&&""!==(null==courseDes?void 0:courseDes.textContent.trim())&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.getSidebarTitle().title} ${this.description}`,bodyHTML:courseDes.innerHTML})),"forum"===this.module&&replyId){this.checkForumSubject();let replyPost=document.querySelector(`#post-content-${replyId}`);null!=replyPost&&replyPost.textContent.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.replyingto,bodyHTML:replyPost.textContent.trim()}))}if("quiz"===this.module&&null!==(_this$editor7=this.editor)&&void 0!==_this$editor7&&_this$editor7.id){var _this$editor8;let questionId=this.getQuestionId(null===(_this$editor8=this.editor)||void 0===_this$editor8?void 0:_this$editor8.id),question=document.querySelector(`#question-${questionId} .qtext`),intro=atob(this.quizInfo.intro);null!=question&&question.textContent.trim()&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:this.moduleIcon,title:this.answeringto,bodyHTML:question.textContent})),intro&&""!==intro.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.quiz} ${this.description}`,bodyHTML:intro})),Number(this.quizInfo.open)&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(Number(this.quizInfo.open),Number(this.quizInfo.close))}))}return Object.keys(this.Rubrics).length&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.rubrics,bodyHTML:this.generateRubrics(this.Rubrics)})),Dates&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(openDate,dueDate)})),"assign"===this.module&&content.append(this.createBox({bg:"bg-green",titleColor:"text-success",icon:this.moduleIcon,title:this.subStatus,bodyHTML:this.submissionStatus(this.submission)})),container.append(btnWrapper,header,content),container}createBox(_ref){let{bg:bg,titleColor:titleColor,icon:icon,title:title,bodyHTML:bodyHTML}=_ref;const box=this.create("div");box.className=`tiny_cursive-fullpage-card ${bg}`;const heading=this.create("h4");heading.className=`tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`,heading.innerHTML=`${icon} ${title}`;const body=this.create("div");return body.className="tiny_cursive-fullpage-card-body",body.innerHTML=bodyHTML,box.append(heading,body),box}generateRubrics(Rubrics){const wrapper=this.create("div");return Rubrics.forEach((rubric=>{const rubricDiv=this.create("div");rubricDiv.className="tiny_cursive-rubric-card";const title=this.create("h3");title.className="tiny_cursive-rubric-title",title.textContent=rubric.description,rubricDiv.appendChild(title),Object.values(rubric.levels).forEach((level=>{const levelDiv=this.create("div"),score=Number(level.score);levelDiv.className=0===score?"tiny_cursive-rubric-level tiny_cursive-rubric-low":score<=2?"tiny_cursive-rubric-level tiny_cursive-rubric-mid":"tiny_cursive-rubric-level tiny_cursive-rubric-high",levelDiv.textContent=`${level.definition} / ${level.score}`,rubricDiv.appendChild(levelDiv)})),wrapper.appendChild(rubricDiv)})),wrapper.innerHTML}submissionStatus(submission){var _submission$current,_submission$current2;const wrapper=this.create("div"),statusWrapper=this.create("div");statusWrapper.className="tiny_cursive-status-row";const statusName=this.create("span");statusName.textContent=`${this.status}:`;const statusValue=this.create("span"),isNew="new"===(null==submission||null===(_submission$current=submission.current)||void 0===_submission$current?void 0:_submission$current.status);statusValue.textContent=isNew?this.draftnot:this.draft,statusValue.className="tiny_cursive-status-value "+(isNew?"tiny_cursive-status-red":"tiny_cursive-status-green"),statusWrapper.append(statusName,statusValue);const modifiedWrapper=this.create("div");modifiedWrapper.className="tiny_cursive-status-row";const modifiedName=this.create("span");modifiedName.textContent=`${this.lastModified}: `;const modifiedValue=this.create("span");if(null!=submission&&null!==(_submission$current2=submission.current)&&void 0!==_submission$current2&&_submission$current2.timemodified){const date=new Date(1e3*submission.current.timemodified);modifiedValue.textContent=this.formatDate(date)}else modifiedValue.textContent="N/A";modifiedWrapper.append(modifiedName,modifiedValue);const gradeWrapper=this.create("div");gradeWrapper.className="tiny_cursive-status-row";const gradeName=this.create("span");gradeName.textContent=`${this.gradings}: `;const gradeValue=this.create("span");return null!=submission&&submission.grade?gradeValue.textContent=Number(submission.grade.grade)>0?submission.grade.grade:this.gradenot:gradeValue.textContent=this.gradenot,gradeWrapper.append(gradeName,gradeValue),wrapper.append(statusWrapper,gradeWrapper,modifiedWrapper),wrapper.innerHTML}wordCounter(status){const wordCount=this.create("div"),labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");icon.className="me-2",icon.innerHTML=_svg_repo.default.assignment,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.wordCount}:`,value.textContent="0",value.className="text-primary",value.style.fontWeight="600",value.style.fontSize="14px",wordCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2",wordCount.append(labelDiv,value),wordCount.style.fontSize="12px";return new MutationObserver((()=>{const newText=status.textContent.trim();value.textContent=`${newText.replace("words","")}`})).observe(status,{characterData:!0,subtree:!0,childList:!0}),wordCount}timerCountDown(timer){let warningDiv=document.querySelector("#user-notifications > div");if(warningDiv){var _clone$querySelector;let clone=warningDiv.cloneNode(!0);null===(_clone$querySelector=clone.querySelector("button"))||void 0===_clone$querySelector||_clone$querySelector.remove(),this.editor.notificationManager.open({text:clone.textContent,type:"error"})}const timerCount=this.create("div");timerCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2";const labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");if(icon.innerHTML=_svg_repo.default.time,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.timeleft}: }`,value.textContent="00:00:00",value.className=warningDiv?"text-danger":"text-primary",Object.assign(value.style,{fontWeight:"600",fontSize:"14px"}),timerCount.append(labelDiv,value),timerCount.style.fontSize="12px",timer){new MutationObserver((()=>{const newText=timer.textContent.trim();value.textContent=`${newText}`})).observe(timer,{characterData:!0,subtree:!0,childList:!0})}else value.textContent=this.nolimit;return timerCount}generateStudentInfo(user,course){const wrapper=this.create("div"),nameWrapper=this.create("div"),usernameWrapper=this.create("div"),courseWrapper=this.create("div"),nameLabel=this.create("span"),nameValue=this.create("span"),usernameLabel=this.create("span"),usernameValue=this.create("span"),courseLabel=this.create("span"),courseValue=this.create("span");return nameLabel.textContent=`${this.name}`,nameValue.textContent=user.fullname,usernameLabel.textContent=`${this.userename}: `,usernameValue.textContent=user.username,courseLabel.textContent=`${this.course}: `,courseValue.textContent=course.title,nameWrapper.className="d-flex justify-content-between",usernameWrapper.className="d-flex justify-content-between",courseWrapper.className="d-flex justify-content-between",nameWrapper.append(nameLabel,nameValue),usernameWrapper.append(usernameLabel,usernameValue),courseWrapper.append(courseLabel,courseValue),wrapper.append(nameWrapper,usernameWrapper,courseWrapper),wrapper.innerHTML}generateImportantDates(open,due){const wrapper=this.create("div");let openDate=null,dueDate=null;const openedWrapper=this.create("div"),dueWrapper=this.create("div"),remainingWrapper=this.create("div"),openedLabel=this.create("span"),openedValue=this.create("span"),dueLabel=this.create("span"),dueValue=this.create("span"),remainingLabel=this.create("span"),remainingValue=this.create("span");return"quiz"===this.module?(openDate=1e3*open,dueDate=1e3*due):(openDate=this.extractDate(null==open?void 0:open.textContent),dueDate=this.extractDate(null==due?void 0:due.textContent)),openedLabel.textContent=`${this.opened}: `,openedValue.textContent=this.formatDate(openDate?new Date(openDate):null),openedValue.className="text-dark",dueLabel.textContent=`${this.due}: `,dueValue.textContent=this.formatDate(dueDate?new Date(dueDate):null),dueValue.className="text-danger",remainingLabel.textContent=`${this.remaining}: `,remainingValue.textContent=this.calculateDate(dueDate),remainingValue.className="text-danger",openedWrapper.className="d-flex justify-content-between",dueWrapper.className="d-flex justify-content-between",remainingWrapper.className="d-flex align-items-center justify-content-between mt-2 pt-2 border-top",openedWrapper.append(openedLabel,openedValue),dueWrapper.append(dueLabel,dueValue),remainingWrapper.append(remainingLabel,remainingValue),wrapper.append(openedWrapper,dueWrapper,remainingWrapper),wrapper.innerHTML}formatDate(date){if(!date)return"-";return date.toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"numeric",minute:"numeric",hour12:!0})}extractDate(text){if(!text)return"-";const parts=null==text?void 0:text.split(":");return parts.length>1?parts.slice(1).join(":").trim():text.trim()}calculateDate(date){if(!date)return"-";const diffMs=new Date(date)-new Date;if(diffMs<=0)return"Overdue";return`${Math.floor(diffMs/864e5)} days, ${Math.floor(diffMs/36e5%24)} hours`}fullPageModule(module){var _current$contentDocum,_current$contentWindo,_current$contentWindo2,_document$getElementB;let current="quiz"===this.module?document.getElementById(`${module}_ifr`):document.querySelector(`#${module}_ifr`),p1=current.parentElement,p2=p1.parentElement,p4=p2.parentElement.parentElement,statusBar=document.querySelector(".tox-statusbar__right-container > button"),assignName=document.querySelector(".page-context-header"),header=this.create("div"),btn=null;assignName.classList.remove("mb-2"),header.id="tiny_cursive-fullpage-custom-header",Object.assign(header.style,{backgroundColor:"white",display:"flex",justifyContent:"space-between"}),"quiz"===this.module?(btn=document.querySelector("#mod_quiz-next-nav").cloneNode(!0),btn.className="tiny_cursive-fullpage-submit-btn",btn.style.margin=".5rem"):(btn=this.create("input"),btn.className="tiny_cursive-fullpage-submit-btn",btn.value=this.savechanges,btn.type="submit",btn.style.margin=".5rem");const leftSide=this.create("div"),rightSide=this.create("div");let commonStyle={display:"flex",alignItems:"center",margin:"0 1rem"};Object.assign(leftSide.style,commonStyle),rightSide.id="tiny_cursive-fullpage-right-wrapper",Object.assign(rightSide.style,commonStyle),rightSide.appendChild(btn),leftSide.appendChild(assignName.cloneNode(!0)),header.appendChild(leftSide),header.appendChild(rightSide),p4.insertBefore(header,p4.firstChild),p2.style.backgroundColor="#efefef",Object.assign(current.style,{width:"750px",minWidth:"750px",boxShadow:"0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)"}),Object.assign(p1.style,{display:"flex",justifyContent:"center",outline:"none",margin:"2rem 0 0"});const style=this.create("style");style.id="tiny_cursive-fullpage-mode-style",style.textContent="\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }",document.head.appendChild(style);let iframeBody=(null===(_current$contentDocum=current.contentDocument)||void 0===_current$contentDocum?void 0:_current$contentDocum.body)||(null===(_current$contentWindo=current.contentWindow)||void 0===_current$contentWindo||null===(_current$contentWindo2=_current$contentWindo.document)||void 0===_current$contentWindo2?void 0:_current$contentWindo2.body);iframeBody&&(iframeBody.style.padding="0.5in"),p2.style.position="relative",null===(_document$getElementB=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB||_document$getElementB.remove();let toggle=this.create("div");toggle.id="cursive-fullpagemode-sidebar-toggle",toggle.innerHTML=_svg_repo.default.hamburger,p2.appendChild(toggle),p2.appendChild(this.docSideBar(statusBar))}normalizePage(editorId){var _document$getElementB2,_document$getElementB3,_current$contentDocum2,_current$contentWindo3,_current$contentWindo4,_document$head$queryS;null===(_document$getElementB2=document.getElementById("tiny_cursive-fullpage-custom-header"))||void 0===_document$getElementB2||_document$getElementB2.remove(),null===(_document$getElementB3=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB3||_document$getElementB3.remove();let current=document.getElementById(editorId),p1=current.parentElement,p2=p1.parentElement;Object.assign(p2.style,{backgroundColor:"",position:""}),Object.assign(current.style,{width:"",minWidth:"",boxShadow:""}),Object.assign(p1.style,{display:"",justifyContent:"",outline:"",margin:""}),p1.classList.remove("tiny-cursive-editor-container");let iframeBody=(null===(_current$contentDocum2=current.contentDocument)||void 0===_current$contentDocum2?void 0:_current$contentDocum2.body)||(null===(_current$contentWindo3=current.contentWindow)||void 0===_current$contentWindo3||null===(_current$contentWindo4=_current$contentWindo3.document)||void 0===_current$contentWindo4?void 0:_current$contentWindo4.body);iframeBody&&(iframeBody.style.padding="0"),null===(_document$head$queryS=document.head.querySelector("#tiny_cursive-fullpage-mode-style"))||void 0===_document$head$queryS||_document$head$queryS.remove()}checkForumSubject(){const form=document.querySelector("#tiny_cursive-fullpage-right-wrapper > input"),msg=this.subjectnot;form&&form.addEventListener("click",(e=>{const subjectInput=document.getElementById("id_subject");let content=this.editor.getContent().trim();subjectInput&&""!==subjectInput.value.trim()&&""!==content||(e.preventDefault(),e.stopPropagation(),this.editor.windowManager.alert(msg))}))}getSidebarTitle(){const[assign,discus,quiz,lesson]=this.getText("sbTitle");switch(this.module){case"assign":return{title:assign,icon:_svg_repo.default.assignment};case"forum":return{title:discus,icon:_svg_repo.default.forum};case"lesson":return{title:lesson,icon:_svg_repo.default.forum};case"quiz":return{title:quiz,icon:_svg_repo.default.quiz};default:return{title:"Page",icon:_svg_repo.default.quiz}}}getTimerBlock(module){switch(module){case"assign":return document.querySelector("#mod_assign_timelimit_block > div > div");case"forum":return document.querySelector("#mod_forum_timelimit_block");case"lesson":return document.querySelector("#lesson-timer");case"quiz":return document.querySelector("#quiz-time-left");default:return null}}getQuestionId(editoId){try{return editoId&&"string"==typeof editoId?editoId.replace(/^q(\d+):(\d+)_.*$/,"$1-$2"):""}catch(error){return window.console.error("Error getting question ID:",error),""}}initStrings(){[this.details,this.studentInfo,this.progress,this.description,this.replyingto,this.answeringto,this.importantdates,this.rubrics,this.subStatus,this.status,this.draft,this.draftnot,this.lastModified,this.gradings,this.gradenot,this.wordCount,this.timeleft,this.nolimit,this.name,this.userename,this.course,this.opened,this.due,this.overdue,this.remaining,this.savechanges,this.subjectnot]=this.getText("docSideBar")}getText(key){return JSON.parse(localStorage.getItem(key))||[]}create(tag){return document.createElement(tag)}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_svg_repo=(obj=_svg_repo)&&obj.__esModule?obj:{default:obj};return _exports.default=class{constructor(User,Rubrics,submission,modulename,editor,quizInfo){this.User=User,this.Rubrics=Rubrics,this.submission=submission,this.module=modulename,this.editor=editor,this.moduleIcon=_svg_repo.default.assignment,this.quizInfo=quizInfo,this.initStrings()}normalMode(){var _this$editor;let id=(null===(_this$editor=this.editor)||void 0===_this$editor?void 0:_this$editor.id)+"_ifr";("assign"===this.module||"quiz"===this.module||"forum"===this.module||"lesson"===this.module||"pdfannotator"===this.module)&&this.normalizePage(id)}fullPageMode(){var _this$editor4,_this$editor2;if("assign"===this.module)this.moduleIcon=_svg_repo.default.assignment,this.fullPageModule(null===(_this$editor2=this.editor)||void 0===_this$editor2?void 0:_this$editor2.id);else if("forum"===this.module){var _this$editor3;this.moduleIcon=_svg_repo.default.forum,this.fullPageModule(null===(_this$editor3=this.editor)||void 0===_this$editor3?void 0:_this$editor3.id)}else if("quiz"===this.module&&null!==(_this$editor4=this.editor)&&void 0!==_this$editor4&&_this$editor4.id){var _this$editor5;this.moduleIcon=_svg_repo.default.quiz,this.fullPageModule(null===(_this$editor5=this.editor)||void 0===_this$editor5?void 0:_this$editor5.id)}else if("lesson"===this.module){var _this$editor6;this.moduleIcon=_svg_repo.default.lesson,this.fullPageModule(null===(_this$editor6=this.editor)||void 0===_this$editor6?void 0:_this$editor6.id)}else if("pdfannotator"===this.module){var _this$editor7;this.moduleIcon=_svg_repo.default.pdfannotator,this.fullPageModule(null===(_this$editor7=this.editor)||void 0===_this$editor7?void 0:_this$editor7.id)}}docSideBar(status){var _this$editor8;const replyId=new URL(window.location.href).searchParams.get("reply"),toggle=document.querySelector("#cursive-fullpagemode-sidebar-toggle"),timelimitBlock=this.getTimerBlock(this.module),headerInfo=this.getSidebarTitle(),progressBar=document.querySelector(".box.progress_bar"),courseName=document.querySelector("#page-navbar > nav > ol > li:nth-child(1) > a"),courseDes=document.querySelector("#intro"),Dates=document.querySelector(".activity-dates");let openDate=null==Dates?void 0:Dates.querySelector("div:nth-child(1)"),dueDate=null==Dates?void 0:Dates.querySelector("div:nth-child(2)");const container=this.create("div");Object.assign(container,{id:"cursive-fullpagemode-sidebar",className:"bg-white h-100 shadow"}),Object.assign(container.style,{width:"300px",overflow:"auto"});const crossBtn=this.create("span");Object.assign(crossBtn,{id:"cursive-collapse-sidebar",className:"btn p-2",innerHTML:_svg_repo.default.close}),crossBtn.addEventListener("click",(()=>{container.style.transition="width 0.3s ease",container.style.width="0",toggle.style.display="flex"})),null==toggle||toggle.addEventListener("click",(function(){toggle.style.display="none",container.style.width="300px"}));const btnWrapper=this.create("div");Object.assign(btnWrapper,{padding:"0 1rem",position:"sticky",top:"0",backgroundColor:"white"}),btnWrapper.append(crossBtn);const header=this.create("div");header.className="border-bottom p-3 bg-light",Object.assign(header.style,{position:"sticky",top:"0"});const headerTitle=this.create("h3");headerTitle.className="mb-3 d-flex align-items-center",headerTitle.textContent=`${headerInfo.title} ${this.details}`,headerTitle.style.fontWeight="600";const headerIcon=document.querySelector(".page-header-image > div");headerIcon&&headerTitle.prepend(headerIcon.cloneNode(!0));let wordCount=this.wordCounter(status);null!=timelimitBlock&&timelimitBlock.textContent?header.append(headerTitle,wordCount,this.timerCountDown(timelimitBlock)):header.append(headerTitle,wordCount);const content=this.create("div");if(content.className="p-3",content.append(this.createBox({bg:"bg-info",titleColor:"text-info",icon:_svg_repo.default.people,title:this.studentInfo,bodyHTML:this.generateStudentInfo(this.User,courseName)})),"lesson"===this.module&&progressBar&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.progress,bodyHTML:progressBar.innerHTML})),courseDes&&""!==(null==courseDes?void 0:courseDes.textContent.trim())&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.getSidebarTitle().title} ${this.description}`,bodyHTML:courseDes.innerHTML})),"forum"===this.module&&replyId){this.checkForumSubject();let replyPost=document.querySelector(`#post-content-${replyId}`);null!=replyPost&&replyPost.textContent.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.replyingto,bodyHTML:replyPost.textContent.trim()}))}if("quiz"===this.module&&null!==(_this$editor8=this.editor)&&void 0!==_this$editor8&&_this$editor8.id){var _this$editor9;let questionId=this.getQuestionId(null===(_this$editor9=this.editor)||void 0===_this$editor9?void 0:_this$editor9.id),question=document.querySelector(`#question-${questionId} .qtext`),intro=atob(this.quizInfo.intro);null!=question&&question.textContent.trim()&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:this.moduleIcon,title:this.answeringto,bodyHTML:question.textContent})),intro&&""!==intro.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.quiz} ${this.description}`,bodyHTML:intro})),Number(this.quizInfo.open)&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(Number(this.quizInfo.open),Number(this.quizInfo.close))}))}return Object.keys(this.Rubrics).length&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.rubrics,bodyHTML:this.generateRubrics(this.Rubrics)})),Dates&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(openDate,dueDate)})),"assign"===this.module&&content.append(this.createBox({bg:"bg-green",titleColor:"text-success",icon:this.moduleIcon,title:this.subStatus,bodyHTML:this.submissionStatus(this.submission)})),container.append(btnWrapper,header,content),container}createBox(_ref){let{bg:bg,titleColor:titleColor,icon:icon,title:title,bodyHTML:bodyHTML}=_ref;const box=this.create("div");box.className=`tiny_cursive-fullpage-card ${bg}`;const heading=this.create("h4");heading.className=`tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`,heading.innerHTML=`${icon} ${title}`;const body=this.create("div");return body.className="tiny_cursive-fullpage-card-body",body.innerHTML=bodyHTML,box.append(heading,body),box}generateRubrics(Rubrics){const wrapper=this.create("div");return Rubrics.forEach((rubric=>{const rubricDiv=this.create("div");rubricDiv.className="tiny_cursive-rubric-card";const title=this.create("h3");title.className="tiny_cursive-rubric-title",title.textContent=rubric.description,rubricDiv.appendChild(title),Object.values(rubric.levels).forEach((level=>{const levelDiv=this.create("div"),score=Number(level.score);levelDiv.className=0===score?"tiny_cursive-rubric-level tiny_cursive-rubric-low":score<=2?"tiny_cursive-rubric-level tiny_cursive-rubric-mid":"tiny_cursive-rubric-level tiny_cursive-rubric-high",levelDiv.textContent=`${level.definition} / ${level.score}`,rubricDiv.appendChild(levelDiv)})),wrapper.appendChild(rubricDiv)})),wrapper.innerHTML}submissionStatus(submission){var _submission$current,_submission$current2;const wrapper=this.create("div"),statusWrapper=this.create("div");statusWrapper.className="tiny_cursive-status-row";const statusName=this.create("span");statusName.textContent=`${this.status}:`;const statusValue=this.create("span"),isNew="new"===(null==submission||null===(_submission$current=submission.current)||void 0===_submission$current?void 0:_submission$current.status);statusValue.textContent=isNew?this.draftnot:this.draft,statusValue.className="tiny_cursive-status-value "+(isNew?"tiny_cursive-status-red":"tiny_cursive-status-green"),statusWrapper.append(statusName,statusValue);const modifiedWrapper=this.create("div");modifiedWrapper.className="tiny_cursive-status-row";const modifiedName=this.create("span");modifiedName.textContent=`${this.lastModified}: `;const modifiedValue=this.create("span");if(null!=submission&&null!==(_submission$current2=submission.current)&&void 0!==_submission$current2&&_submission$current2.timemodified){const date=new Date(1e3*submission.current.timemodified);modifiedValue.textContent=this.formatDate(date)}else modifiedValue.textContent="N/A";modifiedWrapper.append(modifiedName,modifiedValue);const gradeWrapper=this.create("div");gradeWrapper.className="tiny_cursive-status-row";const gradeName=this.create("span");gradeName.textContent=`${this.gradings}: `;const gradeValue=this.create("span");return null!=submission&&submission.grade?gradeValue.textContent=Number(submission.grade.grade)>0?submission.grade.grade:this.gradenot:gradeValue.textContent=this.gradenot,gradeWrapper.append(gradeName,gradeValue),wrapper.append(statusWrapper,gradeWrapper,modifiedWrapper),wrapper.innerHTML}wordCounter(status){const wordCount=this.create("div"),labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");icon.className="me-2",icon.innerHTML=_svg_repo.default.assignment,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.wordCount}:`,value.textContent="0",value.className="text-primary",value.style.fontWeight="600",value.style.fontSize="14px",wordCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2",wordCount.append(labelDiv,value),wordCount.style.fontSize="12px";return new MutationObserver((()=>{const newText=status.textContent.trim();value.textContent=`${newText.replace("words","")}`})).observe(status,{characterData:!0,subtree:!0,childList:!0}),wordCount}timerCountDown(timer){let warningDiv=document.querySelector("#user-notifications > div");if(warningDiv){var _clone$querySelector;let clone=warningDiv.cloneNode(!0);null===(_clone$querySelector=clone.querySelector("button"))||void 0===_clone$querySelector||_clone$querySelector.remove(),this.editor.notificationManager.open({text:clone.textContent,type:"error"})}const timerCount=this.create("div");timerCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2";const labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");if(icon.innerHTML=_svg_repo.default.time,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.timeleft}:`,value.textContent="00:00:00",value.className=warningDiv?"text-danger":"text-primary",Object.assign(value.style,{fontWeight:"600",fontSize:"14px"}),timerCount.append(labelDiv,value),timerCount.style.fontSize="12px",timer){new MutationObserver((()=>{const newText=timer.textContent.trim();value.textContent=`${newText}`})).observe(timer,{characterData:!0,subtree:!0,childList:!0})}else value.textContent=this.nolimit;return timerCount}generateStudentInfo(user,course){const wrapper=this.create("div"),nameWrapper=this.create("div"),usernameWrapper=this.create("div"),courseWrapper=this.create("div"),nameLabel=this.create("strong"),nameValue=this.create("span"),usernameLabel=this.create("strong"),usernameValue=this.create("span"),courseLabel=this.create("strong"),courseValue=this.create("span");return nameLabel.textContent=`${this.name}`,nameValue.textContent=user.fullname,usernameLabel.textContent=`${this.userename}: `,usernameValue.textContent=user.username,courseLabel.textContent=`${this.course}: `,courseValue.textContent=course.title,usernameLabel.className="cfw-bold me-2",usernameValue.className="cursiveFw-wrap",courseLabel.className="cfw-bold me-2",courseValue.className="cursiveFw-wrap",nameLabel.className="cfw-bold me-2",nameValue.className="cursiveFw-wrap",nameWrapper.append(nameLabel,nameValue),usernameWrapper.append(usernameLabel,usernameValue),courseWrapper.append(courseLabel,courseValue),wrapper.append(nameWrapper,usernameWrapper,courseWrapper),wrapper.innerHTML}generateImportantDates(open,due){const wrapper=this.create("div");let openDate=null,dueDate=null;const openedWrapper=this.create("div"),dueWrapper=this.create("div"),remainingWrapper=this.create("div"),openedLabel=this.create("span"),openedValue=this.create("span"),dueLabel=this.create("span"),dueValue=this.create("span"),remainingLabel=this.create("span"),remainingValue=this.create("span");return"quiz"===this.module?(openDate=1e3*open,dueDate=1e3*due):(openDate=this.extractDate(null==open?void 0:open.textContent),dueDate=this.extractDate(null==due?void 0:due.textContent)),openedLabel.textContent=`${this.opened}: `,openedValue.textContent=this.formatDate(openDate?new Date(openDate):null),openedValue.className="text-dark",dueLabel.textContent=`${this.due}: `,dueValue.textContent=this.formatDate(dueDate?new Date(dueDate):null),dueValue.className="text-danger",remainingLabel.textContent=`${this.remaining}: `,remainingValue.textContent=this.calculateDate(dueDate),remainingValue.className="text-danger",openedWrapper.className="d-flex justify-content-between",dueWrapper.className="d-flex justify-content-between",remainingWrapper.className="d-flex align-items-center justify-content-between mt-2 pt-2 border-top",openedWrapper.append(openedLabel,openedValue),dueWrapper.append(dueLabel,dueValue),remainingWrapper.append(remainingLabel,remainingValue),wrapper.append(openedWrapper,dueWrapper,remainingWrapper),wrapper.innerHTML}formatDate(date){if(!date)return"-";return date.toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"numeric",minute:"numeric",hour12:!0})}extractDate(text){if(!text)return"-";const parts=null==text?void 0:text.split(":");return parts.length>1?parts.slice(1).join(":").trim():text.trim()}calculateDate(date){if(!date)return"-";const diffMs=new Date(date)-new Date;if(diffMs<=0)return"Overdue";return`${Math.floor(diffMs/864e5)} days, ${Math.floor(diffMs/36e5%24)} hours`}fullPageModule(module){var _current$contentDocum,_current$contentWindo,_current$contentWindo2,_document$getElementB;let current="quiz"===this.module?document.getElementById(`${module}_ifr`):document.querySelector(`#${module}_ifr`),p1=current.parentElement,p2=p1.parentElement,p4=p2.parentElement.parentElement,statusBar=document.querySelector(".tox-statusbar__right-container > button"),assignName=document.querySelector(".page-context-header"),header=this.create("div"),btn=null;if(assignName.classList.remove("mb-2"),header.id="tiny_cursive-fullpage-custom-header",Object.assign(header.style,{backgroundColor:"white",display:"flex",justifyContent:"space-between"}),"quiz"===this.module?(btn=document.querySelector("#mod_quiz-next-nav").cloneNode(!0),btn.className="tiny_cursive-fullpage-submit-btn",btn.style.margin=".5rem"):(btn=this.create("input"),btn.className="tiny_cursive-fullpage-submit-btn",btn.value=this.savechanges,btn.type="submit",btn.style.margin=".5rem"),"pdfannotator"===this.module){const style=document.createElement("style");style.id="cursiveForceStyle",style.textContent="\n .path-mod-pdfannotator #comment-wrapper h4,\n .path-mod-pdfannotator #comment-nav {\n margin: 0 !important;\n }\n ",document.head.appendChild(style)}const leftSide=this.create("div"),rightSide=this.create("div");let commonStyle={display:"flex",alignItems:"center",margin:"0 1rem"};Object.assign(leftSide.style,commonStyle),rightSide.id="tiny_cursive-fullpage-right-wrapper",Object.assign(rightSide.style,commonStyle),rightSide.appendChild(btn),leftSide.appendChild(assignName.cloneNode(!0)),header.appendChild(leftSide),header.appendChild(rightSide),p4.insertBefore(header,p4.firstChild),p2.style.backgroundColor="#efefef",Object.assign(current.style,{width:"750px",minWidth:"750px",boxShadow:"0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)"}),Object.assign(p1.style,{display:"flex",justifyContent:"center",outline:"none",margin:"2rem 0 0"});const style=this.create("style");style.id="tiny_cursive-fullpage-mode-style",style.textContent="\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }",document.head.appendChild(style);let iframeBody=(null===(_current$contentDocum=current.contentDocument)||void 0===_current$contentDocum?void 0:_current$contentDocum.body)||(null===(_current$contentWindo=current.contentWindow)||void 0===_current$contentWindo||null===(_current$contentWindo2=_current$contentWindo.document)||void 0===_current$contentWindo2?void 0:_current$contentWindo2.body);iframeBody&&(iframeBody.style.padding="0.5in"),p2.style.position="relative",null===(_document$getElementB=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB||_document$getElementB.remove();let toggle=this.create("div");toggle.id="cursive-fullpagemode-sidebar-toggle",toggle.innerHTML=_svg_repo.default.hamburger,p2.appendChild(toggle),p2.appendChild(this.docSideBar(statusBar))}normalizePage(editorId){var _document$getElementB2,_document$getElementB3,_current$contentDocum2,_current$contentWindo3,_current$contentWindo4,_document$head$queryS,_document$head$queryS2;null===(_document$getElementB2=document.getElementById("tiny_cursive-fullpage-custom-header"))||void 0===_document$getElementB2||_document$getElementB2.remove(),null===(_document$getElementB3=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB3||_document$getElementB3.remove();let current=document.getElementById(editorId),p1=current.parentElement,p2=p1.parentElement;Object.assign(p2.style,{backgroundColor:"",position:""}),Object.assign(current.style,{width:"",minWidth:"",boxShadow:""}),Object.assign(p1.style,{display:"",justifyContent:"",outline:"",margin:""}),p1.classList.remove("tiny-cursive-editor-container");let iframeBody=(null===(_current$contentDocum2=current.contentDocument)||void 0===_current$contentDocum2?void 0:_current$contentDocum2.body)||(null===(_current$contentWindo3=current.contentWindow)||void 0===_current$contentWindo3||null===(_current$contentWindo4=_current$contentWindo3.document)||void 0===_current$contentWindo4?void 0:_current$contentWindo4.body);iframeBody&&(iframeBody.style.padding="0"),null===(_document$head$queryS=document.head.querySelector("#tiny_cursive-fullpage-mode-style"))||void 0===_document$head$queryS||_document$head$queryS.remove(),null===(_document$head$queryS2=document.head.querySelector("#cursiveForceStyle"))||void 0===_document$head$queryS2||_document$head$queryS2.remove()}checkForumSubject(){const form=document.querySelector("#tiny_cursive-fullpage-right-wrapper > input"),msg=this.subjectnot;form&&form.addEventListener("click",(e=>{const subjectInput=document.getElementById("id_subject");let content=this.editor.getContent().trim();subjectInput&&""!==subjectInput.value.trim()&&""!==content||(e.preventDefault(),e.stopPropagation(),this.editor.windowManager.alert(msg))}))}getSidebarTitle(){const[assign,discus,quiz,lesson]=this.getText("sbTitle");switch(this.module){case"assign":return{title:assign,icon:_svg_repo.default.assignment};case"forum":return{title:discus,icon:_svg_repo.default.forum};case"lesson":return{title:lesson,icon:_svg_repo.default.forum};case"quiz":return{title:quiz,icon:_svg_repo.default.quiz};case"pdfannotator":return{title:"PDF Annotation",icon:_svg_repo.default.pdfannotator};default:return{title:"Page",icon:_svg_repo.default.quiz}}}getTimerBlock(module){switch(module){case"assign":return document.querySelector("#mod_assign_timelimit_block > div > div");case"forum":return document.querySelector("#mod_forum_timelimit_block");case"lesson":return document.querySelector("#lesson-timer");case"quiz":return document.querySelector("#quiz-time-left");default:return null}}getQuestionId(editoId){try{return editoId&&"string"==typeof editoId?editoId.replace(/^q(\d+):(\d+)_.*$/,"$1-$2"):""}catch(error){return window.console.error("Error getting question ID:",error),""}}initStrings(){[this.details,this.studentInfo,this.progress,this.description,this.replyingto,this.answeringto,this.importantdates,this.rubrics,this.subStatus,this.status,this.draft,this.draftnot,this.lastModified,this.gradings,this.gradenot,this.wordCount,this.timeleft,this.nolimit,this.name,this.userename,this.course,this.opened,this.due,this.overdue,this.remaining,this.savechanges,this.subjectnot]=this.getText("docSideBar")}getText(key){return JSON.parse(localStorage.getItem(key))||[]}create(tag){return document.createElement(tag)}},_exports.default})); //# sourceMappingURL=document_view.min.js.map \ No newline at end of file diff --git a/amd/build/document_view.min.js.map b/amd/build/document_view.min.js.map index 39104e64..b19d13d7 100644 --- a/amd/build/document_view.min.js.map +++ b/amd/build/document_view.min.js.map @@ -1 +1 @@ -{"version":3,"file":"document_view.min.js","sources":["../src/document_view.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module provides functionality for document view management in the Tiny editor,\n * including full page mode display and sidebar information\n * @module tiny_cursive/document_view\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Icons from 'tiny_cursive/svg_repo';\nexport default class DocumentView {\n\n constructor(User, Rubrics, submission, modulename, editor, quizInfo) {\n this.User = User;\n this.Rubrics = Rubrics;\n this.submission = submission;\n this.module = modulename;\n this.editor = editor;\n this.moduleIcon = Icons.assignment;\n this.quizInfo = quizInfo;\n this.initStrings();\n }\n\n normalMode() {\n let id = this.editor?.id + \"_ifr\";\n if (this.module === 'assign') {\n this.normalizePage(id);\n } else if (this.module === 'quiz') {\n this.normalizePage(id);\n } else if (this.module === 'forum') {\n this.normalizePage(id);\n } else if (this.module === 'lesson') {\n this.normalizePage(id);\n }\n }\n\n fullPageMode() {\n\n if (this.module === 'assign') {\n this.moduleIcon = Icons.assignment;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'forum') {\n this.moduleIcon = Icons.forum;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'quiz' && this.editor?.id) {\n this.moduleIcon = Icons.quiz;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'lesson') {\n this.moduleIcon = Icons.lesson;\n this.fullPageModule(this.editor?.id);\n }\n }\n\n docSideBar(status) {\n\n const url = new URL(window.location.href);\n const replyId = url.searchParams.get(\"reply\");\n const toggle = document.querySelector('#cursive-fullpagemode-sidebar-toggle');\n const timelimitBlock = this.getTimerBlock(this.module);\n const headerInfo = this.getSidebarTitle();\n const progressBar = document.querySelector('.box.progress_bar');\n\n const courseName = document.querySelector('#page-navbar > nav > ol > li:nth-child(1) > a');\n const courseDes = document.querySelector('#intro');\n const Dates = document.querySelector('.activity-dates');\n\n let openDate = Dates?.querySelector('div:nth-child(1)');\n let dueDate = Dates?.querySelector('div:nth-child(2)');\n\n const container = this.create('div');\n Object.assign(container, {\n id: 'cursive-fullpagemode-sidebar',\n className: 'bg-white h-100 shadow'\n });\n Object.assign(container.style, {\n width: '300px',\n overflow: 'auto'\n });\n\n const crossBtn = this.create('span');\n Object.assign(crossBtn, {\n id: 'cursive-collapse-sidebar',\n className: 'btn p-2',\n innerHTML: Icons.close\n });\n\n crossBtn.addEventListener('click', () => {\n container.style.transition = 'width 0.3s ease';\n container.style.width = '0';\n toggle.style.display = 'flex';\n });\n toggle?.addEventListener('click', function() {\n toggle.style.display = 'none';\n container.style.width = '300px';\n });\n\n const btnWrapper = this.create('div');\n Object.assign(btnWrapper, {\n padding: '0 1rem',\n position: 'sticky',\n top: '0',\n backgroundColor: 'white'\n });\n btnWrapper.append(crossBtn);\n\n\n const header = this.create('div');\n header.className = 'border-bottom p-3 bg-light';\n Object.assign(header.style, {\n position: 'sticky',\n top: '0'\n });\n\n const headerTitle = this.create('h3');\n headerTitle.className = 'mb-3 d-flex align-items-center';\n headerTitle.textContent = `${headerInfo.title} ${this.details}`;\n headerTitle.style.fontWeight = '600';\n\n const headerIcon = document.querySelector('.page-header-image > div');\n if (headerIcon) {\n headerTitle.prepend(headerIcon.cloneNode(true));\n }\n\n let wordCount = this.wordCounter(status);\n if (timelimitBlock?.textContent) {\n header.append(headerTitle, wordCount, this.timerCountDown(timelimitBlock));\n } else {\n header.append(headerTitle, wordCount);\n }\n\n const content = this.create('div');\n content.className = 'p-3';\n\n content.append(\n this.createBox({\n bg: 'bg-info',\n titleColor: 'text-info',\n icon: Icons.people,\n title: this.studentInfo,\n bodyHTML: this.generateStudentInfo(this.User, courseName)\n })\n );\n\n if (this.module === 'lesson' && progressBar) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.progress,\n bodyHTML: progressBar.innerHTML\n })\n );\n }\n\n if (courseDes && courseDes?.textContent.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.getSidebarTitle().title} ${this.description}`,\n bodyHTML: courseDes.innerHTML\n })\n );\n }\n\n if (this.module === 'forum' && replyId) {\n this.checkForumSubject();\n let replyPost = document.querySelector(`#post-content-${replyId}`);\n if (replyPost?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.replyingto,\n bodyHTML: replyPost.textContent.trim()\n })\n );\n }\n }\n\n if (this.module === 'quiz' && this.editor?.id) {\n\n let questionId = this.getQuestionId(this.editor?.id);\n let question = document.querySelector(`#question-${questionId} .qtext`);\n let intro = atob(this.quizInfo.intro);\n\n if (question?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.answeringto,\n bodyHTML: question.textContent\n })\n );\n }\n\n if (intro && intro.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.quiz} ${this.description}`,\n bodyHTML: intro\n })\n );\n }\n\n if (Number(this.quizInfo.open)) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(Number(this.quizInfo.open), Number(this.quizInfo.close))\n })\n );\n }\n }\n\n if (Object.keys(this.Rubrics).length) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.rubrics,\n bodyHTML: this.generateRubrics(this.Rubrics)\n })\n );\n }\n\n if (Dates) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(openDate, dueDate)\n })\n );\n }\n if (this.module === 'assign') {\n content.append(\n this.createBox({\n bg: 'bg-green',\n titleColor: 'text-success',\n icon: this.moduleIcon,\n title: this.subStatus,\n bodyHTML: this.submissionStatus(this.submission)\n })\n );\n }\n\n container.append(btnWrapper, header, content);\n return container;\n\n }\n // Helper to create info boxes\n createBox({bg, titleColor, icon, title, bodyHTML}) {\n const box = this.create('div');\n box.className = `tiny_cursive-fullpage-card ${bg}`;\n\n const heading = this.create('h4');\n heading.className = `tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`;\n heading.innerHTML = `${icon} ${title}`;\n\n const body = this.create('div');\n body.className = `tiny_cursive-fullpage-card-body`;\n body.innerHTML = bodyHTML;\n\n box.append(heading, body);\n return box;\n }\n\n generateRubrics(Rubrics) {\n const wrapper = this.create('div');\n\n Rubrics.forEach(rubric => {\n const rubricDiv = this.create('div');\n rubricDiv.className = 'tiny_cursive-rubric-card';\n\n const title = this.create('h3');\n title.className = 'tiny_cursive-rubric-title';\n title.textContent = rubric.description;\n rubricDiv.appendChild(title);\n\n Object.values(rubric.levels).forEach(level => {\n const levelDiv = this.create('div');\n const score = Number(level.score);\n\n // Assign background color class based on score\n if (score === 0) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-low';\n } else if (score <= 2) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-mid';\n } else {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-high';\n }\n\n levelDiv.textContent = `${level.definition} / ${level.score}`;\n rubricDiv.appendChild(levelDiv);\n });\n\n wrapper.appendChild(rubricDiv);\n });\n\n return wrapper.innerHTML;\n }\n\n submissionStatus(submission) {\n const wrapper = this.create('div');\n\n const statusWrapper = this.create('div');\n statusWrapper.className = 'tiny_cursive-status-row';\n\n const statusName = this.create('span');\n statusName.textContent = `${this.status}:`;\n\n const statusValue = this.create('span');\n const isNew = submission?.current?.status === 'new';\n statusValue.textContent = isNew ? this.draftnot : this.draft;\n statusValue.className = `tiny_cursive-status-value ${isNew ? 'tiny_cursive-status-red' : 'tiny_cursive-status-green'}`;\n\n statusWrapper.append(statusName, statusValue);\n\n const modifiedWrapper = this.create('div');\n modifiedWrapper.className = 'tiny_cursive-status-row';\n\n const modifiedName = this.create('span');\n modifiedName.textContent = `${this.lastModified}: `;\n\n const modifiedValue = this.create('span');\n if (submission?.current?.timemodified) {\n const date = new Date(submission.current.timemodified * 1000);\n modifiedValue.textContent = this.formatDate(date);\n } else {\n modifiedValue.textContent = 'N/A';\n }\n modifiedWrapper.append(modifiedName, modifiedValue);\n\n const gradeWrapper = this.create('div');\n gradeWrapper.className = 'tiny_cursive-status-row';\n\n const gradeName = this.create('span');\n gradeName.textContent = `${this.gradings}: `;\n\n const gradeValue = this.create('span');\n\n if (submission?.grade) {\n gradeValue.textContent = Number(submission.grade.grade) > 0\n ? submission.grade.grade\n : this.gradenot;\n } else {\n gradeValue.textContent = this.gradenot;\n }\n\n gradeWrapper.append(gradeName, gradeValue);\n wrapper.append(statusWrapper, gradeWrapper, modifiedWrapper);\n return wrapper.innerHTML;\n }\n\n wordCounter(status) {\n const wordCount = this.create('div');\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n\n icon.className = 'me-2';\n icon.innerHTML = Icons.assignment;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.wordCount}:`;\n value.textContent = '0';\n value.className = 'text-primary';\n value.style.fontWeight = '600';\n value.style.fontSize = '14px';\n\n wordCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n wordCount.append(labelDiv, value);\n wordCount.style.fontSize = '12px';\n\n const observer = new MutationObserver(() => {\n const newText = status.textContent.trim();\n value.textContent = `${newText.replace('words', '')}`;\n });\n\n observer.observe(status, {\n characterData: true,\n subtree: true,\n childList: true\n });\n\n return wordCount;\n }\n\n\n timerCountDown(timer) {\n\n let warningDiv = document.querySelector('#user-notifications > div');\n if (warningDiv) {\n let clone = warningDiv.cloneNode(true);\n clone.querySelector('button')?.remove();\n this.editor.notificationManager.open({\n text: clone.textContent,\n type: 'error'\n });\n }\n\n\n const timerCount = this.create('div');\n timerCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n icon.innerHTML = Icons.time;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.timeleft}: }`;\n value.textContent = '00:00:00';\n value.className = warningDiv ? 'text-danger' : 'text-primary';\n Object.assign(value.style, {\n fontWeight: '600',\n fontSize: '14px'\n });\n\n\n timerCount.append(labelDiv, value);\n timerCount.style.fontSize = '12px';\n if (timer) {\n const observer = new MutationObserver(() => {\n const newText = timer.textContent.trim();\n value.textContent = `${newText}`;\n });\n observer.observe(timer, {\n characterData: true,\n subtree: true,\n childList: true\n });\n } else {\n value.textContent = this.nolimit;\n }\n\n\n return timerCount;\n }\n\n\n generateStudentInfo(user, course) {\n\n const wrapper = this.create('div');\n\n const nameWrapper = this.create('div');\n const usernameWrapper = this.create('div');\n const courseWrapper = this.create('div');\n\n const nameLabel = this.create('span');\n const nameValue = this.create('span');\n const usernameLabel = this.create('span');\n const usernameValue = this.create('span');\n const courseLabel = this.create('span');\n const courseValue = this.create('span');\n\n nameLabel.textContent = `${this.name}`;\n nameValue.textContent = user.fullname;\n\n usernameLabel.textContent = `${this.userename}: `;\n usernameValue.textContent = user.username;\n\n courseLabel.textContent = `${this.course}: `;\n courseValue.textContent = course.title;\n\n nameWrapper.className = 'd-flex justify-content-between';\n usernameWrapper.className = 'd-flex justify-content-between';\n courseWrapper.className = 'd-flex justify-content-between';\n\n nameWrapper.append(nameLabel, nameValue);\n usernameWrapper.append(usernameLabel, usernameValue);\n courseWrapper.append(courseLabel, courseValue);\n\n wrapper.append(nameWrapper, usernameWrapper, courseWrapper);\n\n return wrapper.innerHTML;\n\n }\n\n generateImportantDates(open, due) {\n\n const wrapper = this.create('div');\n let openDate = null;\n let dueDate = null;\n\n const openedWrapper = this.create('div');\n const dueWrapper = this.create('div');\n const remainingWrapper = this.create('div');\n\n const openedLabel = this.create('span');\n const openedValue = this.create('span');\n const dueLabel = this.create('span');\n const dueValue = this.create('span');\n const remainingLabel = this.create('span');\n const remainingValue = this.create('span');\n if (this.module === 'quiz') {\n openDate = open * 1000;\n dueDate = due * 1000;\n } else {\n openDate = this.extractDate(open?.textContent);\n dueDate = this.extractDate(due?.textContent);\n }\n\n openedLabel.textContent = `${this.opened}: `;\n openedValue.textContent = this.formatDate(openDate ? new Date(openDate) : null);\n openedValue.className = 'text-dark';\n\n dueLabel.textContent = `${this.due}: `;\n dueValue.textContent = this.formatDate(dueDate ? new Date(dueDate) : null);\n dueValue.className = 'text-danger';\n\n remainingLabel.textContent = `${this.remaining}: `;\n remainingValue.textContent = this.calculateDate(dueDate);\n remainingValue.className = 'text-danger';\n\n openedWrapper.className = 'd-flex justify-content-between';\n dueWrapper.className = 'd-flex justify-content-between';\n remainingWrapper.className = 'd-flex align-items-center justify-content-between mt-2 pt-2 border-top';\n\n openedWrapper.append(openedLabel, openedValue);\n dueWrapper.append(dueLabel, dueValue);\n remainingWrapper.append(remainingLabel, remainingValue);\n\n wrapper.append(openedWrapper, dueWrapper, remainingWrapper);\n\n return wrapper.innerHTML;\n }\n\n formatDate(date) {\n if (!date) {\n return '-';\n }\n\n let options = {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true};\n return date.toLocaleString('en-US', options);\n }\n\n extractDate(text) {\n if (!text) {\n return '-';\n }\n // Split on first colon and return the right part\n const parts = text?.split(':');\n if (parts.length > 1) {\n return parts.slice(1).join(':').trim();\n }\n\n return text.trim();\n }\n\n\n calculateDate(date) {\n if (!date) {\n return '-';\n }\n const date1 = new Date(date); // Due date (local time)\n const now = new Date(); // Current date/time\n\n // Calculate the difference in milliseconds\n const diffMs = date1 - now;\n\n // Convert to days, hours, minutes\n if (diffMs <= 0) {\n return \"Overdue\";\n } else {\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n const diffHours = Math.floor((diffMs / (1000 * 60 * 60)) % 24);\n\n return `${diffDays} days, ${diffHours} hours`;\n }\n\n }\n\n fullPageModule(module) {\n let current = this.module === 'quiz' ?\n document.getElementById(`${module}_ifr`) : document.querySelector(`#${module}_ifr`);\n\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n let p3 = p2.parentElement;\n let p4 = p3.parentElement;\n\n let statusBar = document.querySelector('.tox-statusbar__right-container > button');\n let assignName = document.querySelector('.page-context-header');\n let header = this.create('div');\n let btn = null;\n\n assignName.classList.remove('mb-2');\n header.id = 'tiny_cursive-fullpage-custom-header';\n Object.assign(header.style, {\n backgroundColor: 'white',\n display: 'flex',\n justifyContent: 'space-between'\n });\n\n if (this.module === 'quiz') {\n btn = document.querySelector('#mod_quiz-next-nav').cloneNode(true);\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.style.margin = '.5rem';\n } else {\n btn = this.create('input');\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.value = this.savechanges;\n btn.type = 'submit';\n btn.style.margin = '.5rem';\n }\n\n\n const leftSide = this.create('div');\n const rightSide = this.create('div');\n let commonStyle = {\n display: 'flex',\n alignItems: 'center',\n margin: '0 1rem'\n };\n\n Object.assign(leftSide.style, commonStyle);\n rightSide.id = 'tiny_cursive-fullpage-right-wrapper';\n Object.assign(rightSide.style, commonStyle);\n\n rightSide.appendChild(btn);\n leftSide.appendChild(assignName.cloneNode(true));\n\n header.appendChild(leftSide);\n header.appendChild(rightSide);\n\n p4.insertBefore(header, p4.firstChild);\n p2.style.backgroundColor = '#efefef';\n Object.assign(current.style, {\n width: '750px',\n minWidth: '750px',\n boxShadow: '0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)'\n });\n\n Object.assign(p1.style, {\n display: 'flex',\n justifyContent: 'center',\n outline: 'none',\n margin: '2rem 0 0'\n });\n const style = this.create('style');\n style.id = 'tiny_cursive-fullpage-mode-style';\n style.textContent = `\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }`;\n document.head.appendChild(style);\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n\n if (iframeBody) {\n iframeBody.style.padding = '0.5in';\n }\n p2.style.position = 'relative';\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let toggle = this.create('div');\n toggle.id = 'cursive-fullpagemode-sidebar-toggle';\n toggle.innerHTML = Icons.hamburger;\n p2.appendChild(toggle);\n p2.appendChild(this.docSideBar(statusBar));\n }\n\n normalizePage(editorId) {\n document.getElementById('tiny_cursive-fullpage-custom-header')?.remove();\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let current = document.getElementById(editorId);\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n\n Object.assign(p2.style, {\n backgroundColor: \"\",\n position: \"\"\n });\n\n Object.assign(current.style, {\n width: '',\n minWidth: '',\n boxShadow: '',\n });\n\n Object.assign(p1.style, {\n display: '',\n justifyContent: '',\n outline: '',\n margin: ''\n });\n\n p1.classList.remove('tiny-cursive-editor-container');\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n if (iframeBody) {\n iframeBody.style.padding = '0';\n }\n document.head.querySelector('#tiny_cursive-fullpage-mode-style')?.remove();\n }\n\n checkForumSubject() {\n const form = document.querySelector('#tiny_cursive-fullpage-right-wrapper > input');\n const msg = this.subjectnot;\n\n if (form) {\n form.addEventListener('click', (e) => {\n const subjectInput = document.getElementById('id_subject');\n let content = this.editor.getContent().trim();\n if (!subjectInput || subjectInput.value.trim() === '' || content === '') {\n e.preventDefault();\n e.stopPropagation();\n this.editor.windowManager.alert(msg);\n }\n });\n }\n }\n\n getSidebarTitle() {\n const [assign, discus, quiz, lesson] = this.getText('sbTitle');\n switch (this.module) {\n case 'assign':\n return {title: assign, icon: Icons.assignment};\n case 'forum':\n return {title: discus, icon: Icons.forum};\n case 'lesson':\n return {title: lesson, icon: Icons.forum};\n case 'quiz':\n return {title: quiz, icon: Icons.quiz};\n default:\n return {title: 'Page', icon: Icons.quiz};\n }\n }\n\n getTimerBlock(module) {\n switch (module) {\n case 'assign':\n return document.querySelector('#mod_assign_timelimit_block > div > div');\n case 'forum':\n return document.querySelector('#mod_forum_timelimit_block');\n case 'lesson':\n return document.querySelector('#lesson-timer');\n case 'quiz':\n return document.querySelector('#quiz-time-left');\n default:\n return null;\n }\n }\n\n getQuestionId(editoId) {\n try {\n if (!editoId || typeof editoId !== 'string') {\n return '';\n }\n return editoId.replace(/^q(\\d+):(\\d+)_.*$/, \"$1-$2\");\n } catch (error) {\n window.console.error('Error getting question ID:', error);\n return '';\n }\n }\n\n initStrings() {\n [\n this.details,\n this.studentInfo,\n this.progress,\n this.description,\n this.replyingto,\n this.answeringto,\n this.importantdates,\n this.rubrics,\n this.subStatus,\n this.status,\n this.draft,\n this.draftnot,\n this.lastModified,\n this.gradings,\n this.gradenot,\n this.wordCount,\n this.timeleft,\n this.nolimit,\n this.name,\n this.userename,\n this.course,\n this.opened,\n this.due,\n this.overdue,\n this.remaining,\n this.savechanges,\n this.subjectnot\n ] = this.getText('docSideBar');\n }\n\n getText(key) {\n return JSON.parse(localStorage.getItem(key)) || [];\n }\n\n create(tag) {\n return document.createElement(tag);\n }\n\n}"],"names":["constructor","User","Rubrics","submission","modulename","editor","quizInfo","module","moduleIcon","Icons","assignment","initStrings","normalMode","id","this","normalizePage","fullPageMode","fullPageModule","_this$editor2","forum","_this$editor3","_this$editor4","quiz","_this$editor5","lesson","_this$editor6","docSideBar","status","replyId","URL","window","location","href","searchParams","get","toggle","document","querySelector","timelimitBlock","getTimerBlock","headerInfo","getSidebarTitle","progressBar","courseName","courseDes","Dates","openDate","dueDate","container","create","Object","assign","className","style","width","overflow","crossBtn","innerHTML","close","addEventListener","transition","display","btnWrapper","padding","position","top","backgroundColor","append","header","headerTitle","textContent","title","details","fontWeight","headerIcon","prepend","cloneNode","wordCount","wordCounter","timerCountDown","content","createBox","bg","titleColor","icon","people","studentInfo","bodyHTML","generateStudentInfo","progress","trim","description","checkForumSubject","replyPost","replyingto","_this$editor7","questionId","getQuestionId","_this$editor8","question","intro","atob","answeringto","Number","open","time","importantdates","generateImportantDates","keys","length","rubrics","generateRubrics","subStatus","submissionStatus","box","heading","body","wrapper","forEach","rubric","rubricDiv","appendChild","values","levels","level","levelDiv","score","definition","statusWrapper","statusName","statusValue","isNew","current","draftnot","draft","modifiedWrapper","modifiedName","lastModified","modifiedValue","_submission$current2","timemodified","date","Date","formatDate","gradeWrapper","gradeName","gradings","gradeValue","grade","gradenot","labelDiv","label","value","fontSize","MutationObserver","newText","replace","observe","characterData","subtree","childList","timer","warningDiv","clone","remove","notificationManager","text","type","timerCount","timeleft","nolimit","user","course","nameWrapper","usernameWrapper","courseWrapper","nameLabel","nameValue","usernameLabel","usernameValue","courseLabel","courseValue","name","fullname","userename","username","due","openedWrapper","dueWrapper","remainingWrapper","openedLabel","openedValue","dueLabel","dueValue","remainingLabel","remainingValue","extractDate","opened","remaining","calculateDate","toLocaleString","year","month","day","hour","minute","hour12","parts","split","slice","join","diffMs","Math","floor","getElementById","p1","parentElement","p2","p4","statusBar","assignName","btn","classList","justifyContent","margin","savechanges","leftSide","rightSide","commonStyle","alignItems","insertBefore","firstChild","minWidth","boxShadow","outline","head","iframeBody","contentDocument","contentWindow","_current$contentWindo","_current$contentWindo2","hamburger","editorId","_current$contentWindo3","_current$contentWindo4","form","msg","subjectnot","e","subjectInput","getContent","preventDefault","stopPropagation","windowManager","alert","discus","getText","editoId","error","console","overdue","key","JSON","parse","localStorage","getItem","tag","createElement"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"document_view.min.js","sources":["../src/document_view.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module provides functionality for document view management in the Tiny editor,\n * including full page mode display and sidebar information\n * @module tiny_cursive/document_view\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Icons from 'tiny_cursive/svg_repo';\nexport default class DocumentView {\n\n constructor(User, Rubrics, submission, modulename, editor, quizInfo) {\n this.User = User;\n this.Rubrics = Rubrics;\n this.submission = submission;\n this.module = modulename;\n this.editor = editor;\n this.moduleIcon = Icons.assignment;\n this.quizInfo = quizInfo;\n this.initStrings();\n }\n\n normalMode() {\n let id = this.editor?.id + \"_ifr\";\n if (this.module === 'assign') {\n this.normalizePage(id);\n } else if (this.module === 'quiz') {\n this.normalizePage(id);\n } else if (this.module === 'forum') {\n this.normalizePage(id);\n } else if (this.module === 'lesson') {\n this.normalizePage(id);\n } else if(this.module === 'pdfannotator') {\n this.normalizePage(id);\n }\n }\n\n fullPageMode() {\n\n if (this.module === 'assign') {\n this.moduleIcon = Icons.assignment;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'forum') {\n this.moduleIcon = Icons.forum;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'quiz' && this.editor?.id) {\n this.moduleIcon = Icons.quiz;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'lesson') {\n this.moduleIcon = Icons.lesson;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'pdfannotator') {\n this.moduleIcon = Icons.pdfannotator;\n this.fullPageModule(this.editor?.id);\n }\n }\n\n docSideBar(status) {\n\n const url = new URL(window.location.href);\n const replyId = url.searchParams.get(\"reply\");\n const toggle = document.querySelector('#cursive-fullpagemode-sidebar-toggle');\n const timelimitBlock = this.getTimerBlock(this.module);\n const headerInfo = this.getSidebarTitle();\n const progressBar = document.querySelector('.box.progress_bar');\n\n const courseName = document.querySelector('#page-navbar > nav > ol > li:nth-child(1) > a');\n const courseDes = document.querySelector('#intro');\n const Dates = document.querySelector('.activity-dates');\n\n let openDate = Dates?.querySelector('div:nth-child(1)');\n let dueDate = Dates?.querySelector('div:nth-child(2)');\n\n const container = this.create('div');\n Object.assign(container, {\n id: 'cursive-fullpagemode-sidebar',\n className: 'bg-white h-100 shadow'\n });\n Object.assign(container.style, {\n width: '300px',\n overflow: 'auto'\n });\n\n const crossBtn = this.create('span');\n Object.assign(crossBtn, {\n id: 'cursive-collapse-sidebar',\n className: 'btn p-2',\n innerHTML: Icons.close\n });\n\n crossBtn.addEventListener('click', () => {\n container.style.transition = 'width 0.3s ease';\n container.style.width = '0';\n toggle.style.display = 'flex';\n });\n toggle?.addEventListener('click', function() {\n toggle.style.display = 'none';\n container.style.width = '300px';\n });\n\n const btnWrapper = this.create('div');\n Object.assign(btnWrapper, {\n padding: '0 1rem',\n position: 'sticky',\n top: '0',\n backgroundColor: 'white'\n });\n btnWrapper.append(crossBtn);\n\n\n const header = this.create('div');\n header.className = 'border-bottom p-3 bg-light';\n Object.assign(header.style, {\n position: 'sticky',\n top: '0'\n });\n\n const headerTitle = this.create('h3');\n headerTitle.className = 'mb-3 d-flex align-items-center';\n headerTitle.textContent = `${headerInfo.title} ${this.details}`;\n headerTitle.style.fontWeight = '600';\n\n const headerIcon = document.querySelector('.page-header-image > div');\n if (headerIcon) {\n headerTitle.prepend(headerIcon.cloneNode(true));\n }\n\n let wordCount = this.wordCounter(status);\n if (timelimitBlock?.textContent) {\n header.append(headerTitle, wordCount, this.timerCountDown(timelimitBlock));\n } else {\n header.append(headerTitle, wordCount);\n }\n\n const content = this.create('div');\n content.className = 'p-3';\n\n content.append(\n this.createBox({\n bg: 'bg-info',\n titleColor: 'text-info',\n icon: Icons.people,\n title: this.studentInfo,\n bodyHTML: this.generateStudentInfo(this.User, courseName)\n })\n );\n\n if (this.module === 'lesson' && progressBar) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.progress,\n bodyHTML: progressBar.innerHTML\n })\n );\n }\n\n if (courseDes && courseDes?.textContent.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.getSidebarTitle().title} ${this.description}`,\n bodyHTML: courseDes.innerHTML\n })\n );\n }\n\n if (this.module === 'forum' && replyId) {\n this.checkForumSubject();\n let replyPost = document.querySelector(`#post-content-${replyId}`);\n if (replyPost?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.replyingto,\n bodyHTML: replyPost.textContent.trim()\n })\n );\n }\n }\n\n if (this.module === 'quiz' && this.editor?.id) {\n\n let questionId = this.getQuestionId(this.editor?.id);\n let question = document.querySelector(`#question-${questionId} .qtext`);\n let intro = atob(this.quizInfo.intro);\n\n if (question?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.answeringto,\n bodyHTML: question.textContent\n })\n );\n }\n\n if (intro && intro.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.quiz} ${this.description}`,\n bodyHTML: intro\n })\n );\n }\n\n if (Number(this.quizInfo.open)) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(Number(this.quizInfo.open), Number(this.quizInfo.close))\n })\n );\n }\n }\n\n if (Object.keys(this.Rubrics).length) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.rubrics,\n bodyHTML: this.generateRubrics(this.Rubrics)\n })\n );\n }\n\n if (Dates) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(openDate, dueDate)\n })\n );\n }\n if (this.module === 'assign') {\n content.append(\n this.createBox({\n bg: 'bg-green',\n titleColor: 'text-success',\n icon: this.moduleIcon,\n title: this.subStatus,\n bodyHTML: this.submissionStatus(this.submission)\n })\n );\n }\n\n container.append(btnWrapper, header, content);\n return container;\n\n }\n // Helper to create info boxes\n createBox({bg, titleColor, icon, title, bodyHTML}) {\n const box = this.create('div');\n box.className = `tiny_cursive-fullpage-card ${bg}`;\n\n const heading = this.create('h4');\n heading.className = `tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`;\n heading.innerHTML = `${icon} ${title}`;\n\n const body = this.create('div');\n body.className = `tiny_cursive-fullpage-card-body`;\n body.innerHTML = bodyHTML;\n\n box.append(heading, body);\n return box;\n }\n\n generateRubrics(Rubrics) {\n const wrapper = this.create('div');\n\n Rubrics.forEach(rubric => {\n const rubricDiv = this.create('div');\n rubricDiv.className = 'tiny_cursive-rubric-card';\n\n const title = this.create('h3');\n title.className = 'tiny_cursive-rubric-title';\n title.textContent = rubric.description;\n rubricDiv.appendChild(title);\n\n Object.values(rubric.levels).forEach(level => {\n const levelDiv = this.create('div');\n const score = Number(level.score);\n\n // Assign background color class based on score\n if (score === 0) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-low';\n } else if (score <= 2) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-mid';\n } else {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-high';\n }\n\n levelDiv.textContent = `${level.definition} / ${level.score}`;\n rubricDiv.appendChild(levelDiv);\n });\n\n wrapper.appendChild(rubricDiv);\n });\n\n return wrapper.innerHTML;\n }\n\n submissionStatus(submission) {\n const wrapper = this.create('div');\n\n const statusWrapper = this.create('div');\n statusWrapper.className = 'tiny_cursive-status-row';\n\n const statusName = this.create('span');\n statusName.textContent = `${this.status}:`;\n\n const statusValue = this.create('span');\n const isNew = submission?.current?.status === 'new';\n statusValue.textContent = isNew ? this.draftnot : this.draft;\n statusValue.className = `tiny_cursive-status-value ${isNew ? 'tiny_cursive-status-red' : 'tiny_cursive-status-green'}`;\n\n statusWrapper.append(statusName, statusValue);\n\n const modifiedWrapper = this.create('div');\n modifiedWrapper.className = 'tiny_cursive-status-row';\n\n const modifiedName = this.create('span');\n modifiedName.textContent = `${this.lastModified}: `;\n\n const modifiedValue = this.create('span');\n if (submission?.current?.timemodified) {\n const date = new Date(submission.current.timemodified * 1000);\n modifiedValue.textContent = this.formatDate(date);\n } else {\n modifiedValue.textContent = 'N/A';\n }\n modifiedWrapper.append(modifiedName, modifiedValue);\n\n const gradeWrapper = this.create('div');\n gradeWrapper.className = 'tiny_cursive-status-row';\n\n const gradeName = this.create('span');\n gradeName.textContent = `${this.gradings}: `;\n\n const gradeValue = this.create('span');\n\n if (submission?.grade) {\n gradeValue.textContent = Number(submission.grade.grade) > 0\n ? submission.grade.grade\n : this.gradenot;\n } else {\n gradeValue.textContent = this.gradenot;\n }\n\n gradeWrapper.append(gradeName, gradeValue);\n wrapper.append(statusWrapper, gradeWrapper, modifiedWrapper);\n return wrapper.innerHTML;\n }\n\n wordCounter(status) {\n const wordCount = this.create('div');\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n\n icon.className = 'me-2';\n icon.innerHTML = Icons.assignment;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.wordCount}:`;\n value.textContent = '0';\n value.className = 'text-primary';\n value.style.fontWeight = '600';\n value.style.fontSize = '14px';\n\n wordCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n wordCount.append(labelDiv, value);\n wordCount.style.fontSize = '12px';\n\n const observer = new MutationObserver(() => {\n const newText = status.textContent.trim();\n value.textContent = `${newText.replace('words', '')}`;\n });\n\n observer.observe(status, {\n characterData: true,\n subtree: true,\n childList: true\n });\n\n return wordCount;\n }\n\n\n timerCountDown(timer) {\n\n let warningDiv = document.querySelector('#user-notifications > div');\n if (warningDiv) {\n let clone = warningDiv.cloneNode(true);\n clone.querySelector('button')?.remove();\n this.editor.notificationManager.open({\n text: clone.textContent,\n type: 'error'\n });\n }\n\n\n const timerCount = this.create('div');\n timerCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n icon.innerHTML = Icons.time;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.timeleft}:`;\n value.textContent = '00:00:00';\n value.className = warningDiv ? 'text-danger' : 'text-primary';\n Object.assign(value.style, {\n fontWeight: '600',\n fontSize: '14px'\n });\n\n\n timerCount.append(labelDiv, value);\n timerCount.style.fontSize = '12px';\n if (timer) {\n const observer = new MutationObserver(() => {\n const newText = timer.textContent.trim();\n value.textContent = `${newText}`;\n });\n observer.observe(timer, {\n characterData: true,\n subtree: true,\n childList: true\n });\n } else {\n value.textContent = this.nolimit;\n }\n\n\n return timerCount;\n }\n\n\n generateStudentInfo(user, course) {\n\n const wrapper = this.create('div');\n\n const nameWrapper = this.create('div');\n const usernameWrapper = this.create('div');\n const courseWrapper = this.create('div');\n\n const nameLabel = this.create('strong');\n const nameValue = this.create('span');\n const usernameLabel = this.create('strong');\n const usernameValue = this.create('span');\n const courseLabel = this.create('strong');\n const courseValue = this.create('span');\n\n nameLabel.textContent = `${this.name}`;\n nameValue.textContent = user.fullname;\n\n usernameLabel.textContent = `${this.userename}: `;\n usernameValue.textContent = user.username;\n\n courseLabel.textContent = `${this.course}: `;\n courseValue.textContent = course.title;\n\n usernameLabel.className = 'cfw-bold me-2';\n usernameValue.className = 'cursiveFw-wrap';\n courseLabel.className = 'cfw-bold me-2';\n courseValue.className = 'cursiveFw-wrap';\n nameLabel.className = 'cfw-bold me-2';\n nameValue.className = 'cursiveFw-wrap';\n\n // nameWrapper.className = 'd-flex justify-content-between';\n // usernameWrapper.className = 'd-flex justify-content-between';\n // courseWrapper.className = 'd-flex justify-content-between';\n\n nameWrapper.append(nameLabel, nameValue);\n usernameWrapper.append(usernameLabel, usernameValue);\n courseWrapper.append(courseLabel, courseValue);\n\n wrapper.append(nameWrapper, usernameWrapper, courseWrapper);\n\n return wrapper.innerHTML;\n\n }\n\n generateImportantDates(open, due) {\n\n const wrapper = this.create('div');\n let openDate = null;\n let dueDate = null;\n\n const openedWrapper = this.create('div');\n const dueWrapper = this.create('div');\n const remainingWrapper = this.create('div');\n\n const openedLabel = this.create('span');\n const openedValue = this.create('span');\n const dueLabel = this.create('span');\n const dueValue = this.create('span');\n const remainingLabel = this.create('span');\n const remainingValue = this.create('span');\n if (this.module === 'quiz') {\n openDate = open * 1000;\n dueDate = due * 1000;\n } else {\n openDate = this.extractDate(open?.textContent);\n dueDate = this.extractDate(due?.textContent);\n }\n\n openedLabel.textContent = `${this.opened}: `;\n openedValue.textContent = this.formatDate(openDate ? new Date(openDate) : null);\n openedValue.className = 'text-dark';\n\n dueLabel.textContent = `${this.due}: `;\n dueValue.textContent = this.formatDate(dueDate ? new Date(dueDate) : null);\n dueValue.className = 'text-danger';\n\n remainingLabel.textContent = `${this.remaining}: `;\n remainingValue.textContent = this.calculateDate(dueDate);\n remainingValue.className = 'text-danger';\n\n openedWrapper.className = 'd-flex justify-content-between';\n dueWrapper.className = 'd-flex justify-content-between';\n remainingWrapper.className = 'd-flex align-items-center justify-content-between mt-2 pt-2 border-top';\n\n openedWrapper.append(openedLabel, openedValue);\n dueWrapper.append(dueLabel, dueValue);\n remainingWrapper.append(remainingLabel, remainingValue);\n\n wrapper.append(openedWrapper, dueWrapper, remainingWrapper);\n\n return wrapper.innerHTML;\n }\n\n formatDate(date) {\n if (!date) {\n return '-';\n }\n\n let options = {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true};\n return date.toLocaleString('en-US', options);\n }\n\n extractDate(text) {\n if (!text) {\n return '-';\n }\n // Split on first colon and return the right part\n const parts = text?.split(':');\n if (parts.length > 1) {\n return parts.slice(1).join(':').trim();\n }\n\n return text.trim();\n }\n\n\n calculateDate(date) {\n if (!date) {\n return '-';\n }\n const date1 = new Date(date); // Due date (local time)\n const now = new Date(); // Current date/time\n\n // Calculate the difference in milliseconds\n const diffMs = date1 - now;\n\n // Convert to days, hours, minutes\n if (diffMs <= 0) {\n return \"Overdue\";\n } else {\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n const diffHours = Math.floor((diffMs / (1000 * 60 * 60)) % 24);\n\n return `${diffDays} days, ${diffHours} hours`;\n }\n\n }\n\n fullPageModule(module) {\n let current = this.module === 'quiz' ?\n document.getElementById(`${module}_ifr`) : document.querySelector(`#${module}_ifr`);\n\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n let p3 = p2.parentElement;\n let p4 = p3.parentElement;\n\n let statusBar = document.querySelector('.tox-statusbar__right-container > button');\n let assignName = document.querySelector('.page-context-header');\n let header = this.create('div');\n let btn = null;\n\n assignName.classList.remove('mb-2');\n header.id = 'tiny_cursive-fullpage-custom-header';\n Object.assign(header.style, {\n backgroundColor: 'white',\n display: 'flex',\n justifyContent: 'space-between'\n });\n\n if (this.module === 'quiz') {\n btn = document.querySelector('#mod_quiz-next-nav').cloneNode(true);\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.style.margin = '.5rem';\n } else {\n btn = this.create('input');\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.value = this.savechanges;\n btn.type = 'submit';\n btn.style.margin = '.5rem';\n }\n\n if (this.module === 'pdfannotator') {\n const style = document.createElement('style');\n style.id = 'cursiveForceStyle';\n style.textContent = `\n .path-mod-pdfannotator #comment-wrapper h4,\n .path-mod-pdfannotator #comment-nav {\n margin: 0 !important;\n }\n `;\n document.head.appendChild(style);\n }\n\n\n\n const leftSide = this.create('div');\n const rightSide = this.create('div');\n let commonStyle = {\n display: 'flex',\n alignItems: 'center',\n margin: '0 1rem'\n };\n\n Object.assign(leftSide.style, commonStyle);\n rightSide.id = 'tiny_cursive-fullpage-right-wrapper';\n Object.assign(rightSide.style, commonStyle);\n\n rightSide.appendChild(btn);\n leftSide.appendChild(assignName.cloneNode(true));\n\n header.appendChild(leftSide);\n header.appendChild(rightSide);\n\n p4.insertBefore(header, p4.firstChild);\n p2.style.backgroundColor = '#efefef';\n Object.assign(current.style, {\n width: '750px',\n minWidth: '750px',\n boxShadow: '0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)'\n });\n\n Object.assign(p1.style, {\n display: 'flex',\n justifyContent: 'center',\n outline: 'none',\n margin: '2rem 0 0'\n });\n const style = this.create('style');\n style.id = 'tiny_cursive-fullpage-mode-style';\n style.textContent = `\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }`;\n document.head.appendChild(style);\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n\n if (iframeBody) {\n iframeBody.style.padding = '0.5in';\n }\n p2.style.position = 'relative';\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let toggle = this.create('div');\n toggle.id = 'cursive-fullpagemode-sidebar-toggle';\n toggle.innerHTML = Icons.hamburger;\n p2.appendChild(toggle);\n p2.appendChild(this.docSideBar(statusBar));\n }\n\n normalizePage(editorId) {\n document.getElementById('tiny_cursive-fullpage-custom-header')?.remove();\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let current = document.getElementById(editorId);\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n\n Object.assign(p2.style, {\n backgroundColor: \"\",\n position: \"\"\n });\n\n Object.assign(current.style, {\n width: '',\n minWidth: '',\n boxShadow: '',\n });\n\n Object.assign(p1.style, {\n display: '',\n justifyContent: '',\n outline: '',\n margin: ''\n });\n\n p1.classList.remove('tiny-cursive-editor-container');\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n if (iframeBody) {\n iframeBody.style.padding = '0';\n }\n document.head.querySelector('#tiny_cursive-fullpage-mode-style')?.remove();\n document.head.querySelector('#cursiveForceStyle')?.remove();\n }\n\n checkForumSubject() {\n const form = document.querySelector('#tiny_cursive-fullpage-right-wrapper > input');\n const msg = this.subjectnot;\n\n if (form) {\n form.addEventListener('click', (e) => {\n const subjectInput = document.getElementById('id_subject');\n let content = this.editor.getContent().trim();\n if (!subjectInput || subjectInput.value.trim() === '' || content === '') {\n e.preventDefault();\n e.stopPropagation();\n this.editor.windowManager.alert(msg);\n }\n });\n }\n }\n\n getSidebarTitle() {\n const [assign, discus, quiz, lesson] = this.getText('sbTitle');\n switch (this.module) {\n case 'assign':\n return {title: assign, icon: Icons.assignment};\n case 'forum':\n return {title: discus, icon: Icons.forum};\n case 'lesson':\n return {title: lesson, icon: Icons.forum};\n case 'quiz':\n return {title: quiz, icon: Icons.quiz};\n case 'pdfannotator':\n return {title: 'PDF Annotation', icon: Icons.pdfannotator};\n default:\n return {title: 'Page', icon: Icons.quiz};\n }\n }\n\n getTimerBlock(module) {\n switch (module) {\n case 'assign':\n return document.querySelector('#mod_assign_timelimit_block > div > div');\n case 'forum':\n return document.querySelector('#mod_forum_timelimit_block');\n case 'lesson':\n return document.querySelector('#lesson-timer');\n case 'quiz':\n return document.querySelector('#quiz-time-left');\n default:\n return null;\n }\n }\n\n getQuestionId(editoId) {\n try {\n if (!editoId || typeof editoId !== 'string') {\n return '';\n }\n return editoId.replace(/^q(\\d+):(\\d+)_.*$/, \"$1-$2\");\n } catch (error) {\n window.console.error('Error getting question ID:', error);\n return '';\n }\n }\n\n initStrings() {\n [\n this.details,\n this.studentInfo,\n this.progress,\n this.description,\n this.replyingto,\n this.answeringto,\n this.importantdates,\n this.rubrics,\n this.subStatus,\n this.status,\n this.draft,\n this.draftnot,\n this.lastModified,\n this.gradings,\n this.gradenot,\n this.wordCount,\n this.timeleft,\n this.nolimit,\n this.name,\n this.userename,\n this.course,\n this.opened,\n this.due,\n this.overdue,\n this.remaining,\n this.savechanges,\n this.subjectnot\n ] = this.getText('docSideBar');\n }\n\n getText(key) {\n return JSON.parse(localStorage.getItem(key)) || [];\n }\n\n create(tag) {\n return document.createElement(tag);\n }\n\n}"],"names":["constructor","User","Rubrics","submission","modulename","editor","quizInfo","module","moduleIcon","Icons","assignment","initStrings","normalMode","id","this","normalizePage","fullPageMode","fullPageModule","_this$editor2","forum","_this$editor3","_this$editor4","quiz","_this$editor5","lesson","_this$editor6","pdfannotator","_this$editor7","docSideBar","status","replyId","URL","window","location","href","searchParams","get","toggle","document","querySelector","timelimitBlock","getTimerBlock","headerInfo","getSidebarTitle","progressBar","courseName","courseDes","Dates","openDate","dueDate","container","create","Object","assign","className","style","width","overflow","crossBtn","innerHTML","close","addEventListener","transition","display","btnWrapper","padding","position","top","backgroundColor","append","header","headerTitle","textContent","title","details","fontWeight","headerIcon","prepend","cloneNode","wordCount","wordCounter","timerCountDown","content","createBox","bg","titleColor","icon","people","studentInfo","bodyHTML","generateStudentInfo","progress","trim","description","checkForumSubject","replyPost","replyingto","_this$editor8","questionId","getQuestionId","_this$editor9","question","intro","atob","answeringto","Number","open","time","importantdates","generateImportantDates","keys","length","rubrics","generateRubrics","subStatus","submissionStatus","box","heading","body","wrapper","forEach","rubric","rubricDiv","appendChild","values","levels","level","levelDiv","score","definition","statusWrapper","statusName","statusValue","isNew","current","draftnot","draft","modifiedWrapper","modifiedName","lastModified","modifiedValue","_submission$current2","timemodified","date","Date","formatDate","gradeWrapper","gradeName","gradings","gradeValue","grade","gradenot","labelDiv","label","value","fontSize","MutationObserver","newText","replace","observe","characterData","subtree","childList","timer","warningDiv","clone","remove","notificationManager","text","type","timerCount","timeleft","nolimit","user","course","nameWrapper","usernameWrapper","courseWrapper","nameLabel","nameValue","usernameLabel","usernameValue","courseLabel","courseValue","name","fullname","userename","username","due","openedWrapper","dueWrapper","remainingWrapper","openedLabel","openedValue","dueLabel","dueValue","remainingLabel","remainingValue","extractDate","opened","remaining","calculateDate","toLocaleString","year","month","day","hour","minute","hour12","parts","split","slice","join","diffMs","Math","floor","getElementById","p1","parentElement","p2","p4","statusBar","assignName","btn","classList","justifyContent","margin","savechanges","createElement","head","leftSide","rightSide","commonStyle","alignItems","insertBefore","firstChild","minWidth","boxShadow","outline","iframeBody","contentDocument","contentWindow","_current$contentWindo","_current$contentWindo2","hamburger","editorId","_current$contentWindo3","_current$contentWindo4","form","msg","subjectnot","e","subjectInput","getContent","preventDefault","stopPropagation","windowManager","alert","discus","getText","editoId","error","console","overdue","key","JSON","parse","localStorage","getItem","tag"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/plugin.min.js b/amd/build/plugin.min.js index cbbc0ab8..02dbc830 100644 --- a/amd/build/plugin.min.js +++ b/amd/build/plugin.min.js @@ -1,3 +1,3 @@ -define("tiny_cursive/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./autosaver","core/ajax"],(function(_exports,_loader,_utils,_common,Autosaver,_ajax){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Autosaver=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Autosaver),_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};var _default=new Promise(((resolve,reject)=>{const page=["page-mod-assign-editsubmission","page-mod-quiz-attempt","page-mod-forum-view","page-mod-forum-post","page-mod-lesson-view"];Promise.all([(0,_loader.getTinyMCE)(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]).then((_ref=>{let[tinyMCE,pluginMetadata]=_ref;return tinyMCE.PluginManager.add(_common.pluginName,(editor=>(_ajax.default.call([{methodname:"cursive_get_config",args:{courseid:M.cfg.courseId,cmid:M.cfg.contextInstanceId}}])[0].done((data=>{data.status&&page.includes(document.body.id)&&data.mod_state&&Autosaver.register(editor,data.sync_interval,data.userid,data.apikey_status,JSON.parse(data.plugins),JSON.parse(data.rubrics),JSON.parse(data.submission),JSON.parse(data.quizinfo),data.pastesetting)})).fail((error=>{window.console.error("Error getting cursive config:",error)})),pluginMetadata))),resolve(_common.pluginName)})).catch((error=>{reject(error)}))}));return _exports.default=_default,_exports.default})); +define("tiny_cursive/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./autosaver","core/ajax"],(function(_exports,_loader,_utils,_common,Autosaver,_ajax){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Autosaver=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Autosaver),_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};var _default=new Promise(((resolve,reject)=>{const page=["page-mod-assign-editsubmission","page-mod-quiz-attempt","page-mod-forum-view","page-mod-forum-post","page-mod-lesson-view","page-mod-pdfannotator-view"];Promise.all([(0,_loader.getTinyMCE)(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]).then((_ref=>{let[tinyMCE,pluginMetadata]=_ref;return tinyMCE.PluginManager.add(_common.pluginName,(editor=>(_ajax.default.call([{methodname:"cursive_get_config",args:{courseid:M.cfg.courseId,cmid:M.cfg.contextInstanceId}}])[0].done((data=>{data.status&&page.includes(document.body.id)&&data.mod_state&&Autosaver.register(editor,data.sync_interval,data.userid,data.apikey_status,JSON.parse(data.plugins),JSON.parse(data.rubrics),JSON.parse(data.submission),JSON.parse(data.quizinfo),data.pastesetting)})).fail((error=>{window.console.error("Error getting cursive config:",error)})),pluginMetadata))),resolve(_common.pluginName)})).catch((error=>{reject(error)}))}));return _exports.default=_default,_exports.default})); //# sourceMappingURL=plugin.min.js.map \ No newline at end of file diff --git a/amd/build/plugin.min.js.map b/amd/build/plugin.min.js.map index 73faa28f..30e6bff0 100644 --- a/amd/build/plugin.min.js.map +++ b/amd/build/plugin.min.js.map @@ -1 +1 @@ -{"version":3,"file":"plugin.min.js","sources":["../src/plugin.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/plugin\n * @category TinyMCE Editor\n * @copyright 2025 CTI \n * @author Brain Station 23 \n */\n\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\nimport {component, pluginName} from './common';\nimport * as Autosaver from './autosaver';\nimport getConfig from 'core/ajax';\n\nexport default new Promise((resolve, reject) => {\n const page = [\n 'page-mod-assign-editsubmission',\n 'page-mod-quiz-attempt',\n 'page-mod-forum-view',\n 'page-mod-forum-post',\n 'page-mod-lesson-view']; // 'page-mod-oublog-editpost' excluded\n\n Promise.all([\n getTinyMCE(),\n getPluginMetadata(component, pluginName),\n ])\n .then(([tinyMCE, pluginMetadata]) => {\n tinyMCE.PluginManager.add(pluginName, (editor) => {\n\n getConfig.call([{\n methodname: \"cursive_get_config\",\n args: {courseid: M.cfg.courseId, cmid: M.cfg.contextInstanceId}\n }])[0].done((data) => {\n if (data.status && page.includes(document.body.id) && data.mod_state) {\n\n Autosaver.register(\n editor,\n data.sync_interval,\n data.userid,\n data.apikey_status,\n JSON.parse(data.plugins),\n JSON.parse(data.rubrics),\n JSON.parse(data.submission),\n JSON.parse(data.quizinfo),\n data.pastesetting\n );\n }\n }).fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n\n return pluginMetadata;\n });\n return resolve(pluginName);\n })\n .catch((error) => {\n reject(error);\n });\n});\n"],"names":["Promise","resolve","reject","page","all","component","pluginName","then","_ref","tinyMCE","pluginMetadata","PluginManager","add","editor","call","methodname","args","courseid","M","cfg","courseId","cmid","contextInstanceId","done","data","status","includes","document","body","id","mod_state","Autosaver","register","sync_interval","userid","apikey_status","JSON","parse","plugins","rubrics","submission","quizinfo","pastesetting","fail","error","window","console","catch"],"mappings":"gwCA4Be,IAAIA,SAAQ,CAACC,QAASC,gBAC3BC,KAAO,CACT,iCACA,wBACA,sBACA,sBACA,wBAEJH,QAAQI,IAAI,EACR,yBACA,4BAAkBC,kBAAWC,sBAE5BC,MAAKC,WAAEC,QAASC,4BACbD,QAAQE,cAAcC,IAAIN,oBAAaO,uBAEzBC,KAAK,CAAC,CACZC,WAAY,qBACZC,KAAM,CAACC,SAAUC,EAAEC,IAAIC,SAAUC,KAAMH,EAAEC,IAAIG,sBAC7C,GAAGC,MAAMC,OACLA,KAAKC,QAAUtB,KAAKuB,SAASC,SAASC,KAAKC,KAAOL,KAAKM,WAEvDC,UAAUC,SACNnB,OACAW,KAAKS,cACLT,KAAKU,OACLV,KAAKW,cACLC,KAAKC,MAAMb,KAAKc,SAChBF,KAAKC,MAAMb,KAAKe,SAChBH,KAAKC,MAAMb,KAAKgB,YAChBJ,KAAKC,MAAMb,KAAKiB,UAChBjB,KAAKkB,iBAGdC,MAAMC,QACLC,OAAOC,QAAQF,MAAM,gCAAiCA,UAGnDlC,kBAEJT,QAAQK,uBAElByC,OAAOH,QACJ1C,OAAO0C"} \ No newline at end of file +{"version":3,"file":"plugin.min.js","sources":["../src/plugin.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/plugin\n * @category TinyMCE Editor\n * @copyright 2025 CTI \n * @author Brain Station 23 \n */\n\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\nimport {component, pluginName} from './common';\nimport * as Autosaver from './autosaver';\nimport getConfig from 'core/ajax';\n\nexport default new Promise((resolve, reject) => {\n const page = [\n 'page-mod-assign-editsubmission',\n 'page-mod-quiz-attempt',\n 'page-mod-forum-view',\n 'page-mod-forum-post',\n 'page-mod-lesson-view',\n 'page-mod-pdfannotator-view']; // 'page-mod-oublog-editpost' excluded\n\n Promise.all([\n getTinyMCE(),\n getPluginMetadata(component, pluginName),\n ])\n .then(([tinyMCE, pluginMetadata]) => {\n tinyMCE.PluginManager.add(pluginName, (editor) => {\n\n getConfig.call([{\n methodname: \"cursive_get_config\",\n args: {courseid: M.cfg.courseId, cmid: M.cfg.contextInstanceId}\n }])[0].done((data) => {\n if (data.status && page.includes(document.body.id) && data.mod_state) {\n\n Autosaver.register(\n editor,\n data.sync_interval,\n data.userid,\n data.apikey_status,\n JSON.parse(data.plugins),\n JSON.parse(data.rubrics),\n JSON.parse(data.submission),\n JSON.parse(data.quizinfo),\n data.pastesetting\n );\n }\n }).fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n\n return pluginMetadata;\n });\n return resolve(pluginName);\n })\n .catch((error) => {\n reject(error);\n });\n});\n"],"names":["Promise","resolve","reject","page","all","component","pluginName","then","_ref","tinyMCE","pluginMetadata","PluginManager","add","editor","call","methodname","args","courseid","M","cfg","courseId","cmid","contextInstanceId","done","data","status","includes","document","body","id","mod_state","Autosaver","register","sync_interval","userid","apikey_status","JSON","parse","plugins","rubrics","submission","quizinfo","pastesetting","fail","error","window","console","catch"],"mappings":"gwCA4Be,IAAIA,SAAQ,CAACC,QAASC,gBAC3BC,KAAO,CACT,iCACA,wBACA,sBACA,sBACA,uBACA,8BAEJH,QAAQI,IAAI,EACR,yBACA,4BAAkBC,kBAAWC,sBAE5BC,MAAKC,WAAEC,QAASC,4BACbD,QAAQE,cAAcC,IAAIN,oBAAaO,uBAEzBC,KAAK,CAAC,CACZC,WAAY,qBACZC,KAAM,CAACC,SAAUC,EAAEC,IAAIC,SAAUC,KAAMH,EAAEC,IAAIG,sBAC7C,GAAGC,MAAMC,OACLA,KAAKC,QAAUtB,KAAKuB,SAASC,SAASC,KAAKC,KAAOL,KAAKM,WAEvDC,UAAUC,SACNnB,OACAW,KAAKS,cACLT,KAAKU,OACLV,KAAKW,cACLC,KAAKC,MAAMb,KAAKc,SAChBF,KAAKC,MAAMb,KAAKe,SAChBH,KAAKC,MAAMb,KAAKgB,YAChBJ,KAAKC,MAAMb,KAAKiB,UAChBjB,KAAKkB,iBAGdC,MAAMC,QACLC,OAAOC,QAAQF,MAAM,gCAAiCA,UAGnDlC,kBAEJT,QAAQK,uBAElByC,OAAOH,QACJ1C,OAAO0C"} \ No newline at end of file diff --git a/amd/build/svg_repo.min.js b/amd/build/svg_repo.min.js index ef22ae26..e4901924 100644 --- a/amd/build/svg_repo.min.js +++ b/amd/build/svg_repo.min.js @@ -6,6 +6,6 @@ define("tiny_cursive/svg_repo",["exports"],(function(_exports){Object.defineProp * @copyright 2025 Cursive Technology, Inc. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -var _default={people:'\n \n \n \n \n ',assignment:'\n \n \n \n ',time:'\n \n \n ',offline:'',forum:'',close:'\n \n ',hamburger:'\n \n ',quiz:'',cloudSave:'',lesson:''};return _exports.default=_default,_exports.default})); +var _default={people:'\n \n \n \n \n ',assignment:'\n \n \n \n ',time:'\n \n \n ',offline:'',forum:'',close:'\n \n ',hamburger:'\n \n ',quiz:'',cloudSave:'',lesson:'',pdfannotator:'\n \n \n \n \n \n \n \n \n \n \n \n \n \n '};return _exports.default=_default,_exports.default})); //# sourceMappingURL=svg_repo.min.js.map \ No newline at end of file diff --git a/amd/build/svg_repo.min.js.map b/amd/build/svg_repo.min.js.map index 5b3ccebe..505312fd 100644 --- a/amd/build/svg_repo.min.js.map +++ b/amd/build/svg_repo.min.js.map @@ -1 +1 @@ -{"version":3,"file":"svg_repo.min.js","sources":["../src/svg_repo.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * SVG repository for icons used in the Curs\n *\n * @module tiny_cursive/svg_repo\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n people: `\n \n \n \n \n `,\n assignment: `\n \n \n \n `,\n time: `\n \n \n `,\n offline: ``,\n forum: ``,\n close: `\n \n `,\n hamburger: `\n \n `,\n quiz: ``,\n cloudSave: ``,\n lesson: ``\n};\n"],"names":["people","assignment","time","offline","forum","close","hamburger","quiz","cloudSave","lesson"],"mappings":";;;;;;;;aAuBe,CACbA,OAAS,ijBASTC,WAAa,siBAQbC,KAAO,qaAOPC,QAAU,qgBAMVC,MAAQ,mtDAeNC,MAAQ,85BAQRC,UAAY,6jBAKZC,KAAO,ksFAuBPC,UAAY,qzBAOZC,OAAS"} \ No newline at end of file +{"version":3,"file":"svg_repo.min.js","sources":["../src/svg_repo.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * SVG repository for icons used in the Curs\n *\n * @module tiny_cursive/svg_repo\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n people: `\n \n \n \n \n `,\n assignment: `\n \n \n \n `,\n time: `\n \n \n `,\n offline: ``,\n forum: ``,\n close: `\n \n `,\n hamburger: `\n \n `,\n quiz: ``,\n cloudSave: ``,\n lesson: ``,\n pdfannotator: `\n \n \n \n \n \n \n \n \n \n \n \n \n \n `\n};\n"],"names":["people","assignment","time","offline","forum","close","hamburger","quiz","cloudSave","lesson","pdfannotator"],"mappings":";;;;;;;;aAuBe,CACXA,OAAS,ijBASTC,WAAa,siBAQbC,KAAO,qaAOPC,QAAU,qgBAMVC,MAAQ,mtDAeRC,MAAQ,85BAQRC,UAAY,6jBAKZC,KAAO,ksFAuBPC,UAAY,qzBAOZC,OAAS,4/EAqBTC,aAAe"} \ No newline at end of file diff --git a/amd/src/append_pdfannotator.js b/amd/src/append_pdfannotator.js new file mode 100644 index 00000000..9980a1f8 --- /dev/null +++ b/amd/src/append_pdfannotator.js @@ -0,0 +1,261 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Module for handling PDF annotator functionality, + * + * @module tiny_cursive/append_pdfannotator + * @copyright 2025 Cursive Technology, Inc. + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import {call} from 'core/ajax'; +import analyticButton from 'tiny_cursive/analytic_button'; +import replayButton from 'tiny_cursive/replay_button'; +import AnalyticEvents from 'tiny_cursive/analytic_events'; +import templates from 'core/templates'; +import Replay from 'tiny_cursive/replay'; +export const init = (scoreSetting, comments, hasApiKey, userid) => { + const replayInstances = {}; + // eslint-disable-next-line camelcase + window.video_playback = function(mid, filepath) { + if (filepath !== '') { + const replay = new Replay( + 'content' + mid, + filepath, + 10, + false, + 'player_' + mid + ); + replayInstances[mid] = replay; + } else { + templates.render('tiny_cursive/no_submission').then(html => { + document.getElementById('content' + mid).innerHTML = html; + return true; + }).catch(e => window.console.error(e)); + } + return false; + }; + + let container = document.querySelector('.comment-list-container'); + const overviewTable = document.querySelector('table[id^="mod-pdfannotator-"]'); + + document.addEventListener('click', handleSubmit); + const moduleName = document.body.id.split('-')[2]; + var pendingSubmit = false; + var buttonElement = ""; + + if (container) { + const observer = new MutationObserver(() => { + if (container?.lastChild?.id) { + extractResourceId(container.lastChild.id); + } + }); + + observer.observe(container, { + subtree: true, + childList: true + }); + } + + if (overviewTable) { + let newChild = document.createElement('th'); + newChild.textContent = 'Analytics'; + let header = overviewTable.querySelector('thead>tr>th:first-child'); + header.insertAdjacentElement('afterend', newChild); + setReplayButton(overviewTable); + } + + /** + * Sets up replay buttons and analytics for each row in the overview table + * @param {HTMLTableElement} overviewTable - The table element containing the overview data + * @description This function: + * 1. Gets all rows from the table + * 2. For each row: + * - Extracts comment ID and user ID from relevant links + * - Adds analytics column with replay/analytics buttons + * - Sets up cursive analytics functionality + */ + function setReplayButton(overviewTable) { + const rows = overviewTable.querySelectorAll('tbody > tr'); + const action = new URL(window.location.href).searchParams.get('action'); + + rows.forEach(row => { + const cols = { + col1: row.querySelector('td:nth-child(1)'), + col2: row.querySelector('td:nth-child(2)'), + col3: row.querySelector('td:nth-child(3)') + }; + + const links = { + link1: cols.col1?.querySelector('a'), + link2: cols.col2?.querySelector('a'), + link3: cols.col3?.querySelector('a') + }; + + // Extract comment ID safely + const commentId = links.link1?.href ? + new URL(links.link1.href).searchParams.get('commid') : null; + + // Extract user ID based on action + let userId = null; + let userLink = null; + + switch(action) { + case 'overviewquestions': + userLink = links.link2; + break; + case 'overviewanswers': + userLink = links.link3; + break; + default: + userId = userid; + } + + if (userLink?.href) { + try { + userId = new URL(userLink.href).searchParams.get('id'); + } catch (e) { + window.console.warn('Error parsing user URL:', e); + } + } + + getCursiveAnalytics(userId, commentId, M.cfg.contextInstanceId, cols.col1); + }); + } + + /** + * Handles the submission and cancellation of comments + * @param {Event} e - The click event object + * @description When comment is submitted or cancelled: + * - Removes 'isEditing' flag from localStorage + * - Sets pendingSubmit flag appropriately (true for submit, false for cancel) + */ + function handleSubmit(e) { + if (e.target.id === 'commentSubmit') { + localStorage.removeItem('isEditing'); + buttonElement = e.target.value; + pendingSubmit = true; + } + if (e.target.id === 'commentCancel') { + localStorage.removeItem('isEditing'); + pendingSubmit = false; + } + } + + const updateEntries = async (methodname, args) => { + try { + const response = await call([{ + methodname, + args, + }])[0]; + return response; + } catch (error) { + window.console.error('updating Entries:', error); + throw error; + } + }; + + /** + * Extracts the resource ID from a comment ID and updates entries if submission is pending + * @param {string} id - The ID string to extract resource ID from, expected format: 'prefix_number' + * @description This function: + * 1. Parses the resource ID from the given ID string + * 2. If resource ID exists and there's a pending submission: + * - Resets the pending submission flag + * - Constructs arguments with context info + * - Calls updateEntries to process the PDF annotation + */ + function extractResourceId(id) { + + // prevent updating ID while editing a existing entry. + if (buttonElement === 'Save') { + + pendingSubmit = false; + return; + } + + let resourceId = parseInt(id?.split('_')[1]); + if (resourceId && pendingSubmit) { + pendingSubmit = false; + let args = { + cmid: M.cfg.contextInstanceId, + userid: M.cfg.userId ?? 0, + courseid: M.cfg.courseId, + modulename: moduleName, + resourceid: resourceId + }; + updateEntries('cursive_update_pdf_annote_id', args); + } + } + + /** + * Retrieves and displays cursive analytics for a given resource + * @param {number} userid - The ID of the user + * @param {number} resourceid - The ID of the resource to get analytics for + * @param {number} cmid - The course module ID + * @param {HTMLElement} place - The DOM element where analytics should be placed + * @description This function: + * 1. Makes an AJAX call to get forum comment data + * 2. Creates and inserts analytics/replay buttons + * 3. Sets up analytics events and modal functionality + * 4. Handles both API key and non-API key scenarios + */ + function getCursiveAnalytics(userid, resourceid, cmid, place) { + let args = {id: resourceid, modulename: "pdfannotator", cmid: cmid}; + let methodname = 'cursive_get_forum_comment_link'; + let com = call([{methodname, args}]); + com[0].done(function(json) { + var data = JSON.parse(json); + + var filepath = ''; + if (data.data.filename) { + filepath = data.data.filename; + } + + let analyticButtonDiv = document.createElement('div'); + let analyticsColumn = document.createElement('td'); + + if (!hasApiKey) { + analyticButtonDiv.append(replayButton(resourceid)); + } else { + analyticButtonDiv.append(analyticButton(data.data.effort_ratio, resourceid)); + } + + analyticButtonDiv.dataset.region = "analytic-div" + userid; + analyticsColumn.append(analyticButtonDiv); + place.insertAdjacentElement('afterend', analyticsColumn); + + let myEvents = new AnalyticEvents(); + var context = { + tabledata: data.data, + formattime: myEvents.formatedTime(data.data), + page: scoreSetting, + userid: resourceid, + apikey: hasApiKey + }; + + let authIcon = myEvents.authorshipStatus(data.data.first_file, data.data.score, scoreSetting); + myEvents.createModal(resourceid, context, '', replayInstances, authIcon); + myEvents.analytics(resourceid, templates, context, '', replayInstances, authIcon); + myEvents.checkDiff(resourceid, data.data.file_id, '', replayInstances); + myEvents.replyWriting(resourceid, filepath, '', replayInstances); + + }); + com[0].fail((error) => { + window.console.error('Error getting cursive config:', error); + }); + } +}; \ No newline at end of file diff --git a/amd/src/autosaver.js b/amd/src/autosaver.js index a3948532..614c6372 100644 --- a/amd/src/autosaver.js +++ b/amd/src/autosaver.js @@ -63,6 +63,20 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, if (modulename !== 'assign') { PASTE_SETTING = 'cite_source'; } + + if (ur.includes('pdfannotator')) { + document.addEventListener('click', e => { + if (e.target.className === "dropdown-item comment-edit-a") { + let id = e.target.id; + resourceId = id.replace('editButton', ''); + localStorage.setItem('isEditing', '1'); + } + if (e.target.id === 'commentSubmit') { + syncData(); + } + }); + } + const postOne = async(methodname, args) => { try { const response = await call([{ @@ -299,7 +313,7 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, editor.rePosition = position.rePosition; sendKeyEvent("keyDown", editor); }); - editor.on('Cut', () => { + editor.on('Cut', () => { const selectedContent = editor.selection.getContent({format: 'text'}); localStorage.setItem('lastCopyCutContent', selectedContent.trim()); }); @@ -465,75 +479,75 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, */ function getCaretPosition(skip = false) { try { - if (!editor || !editor.selection) { + if (!editor || !editor.selection) { return {caretPosition: 0, rePosition: 0}; - } - - const range = editor.selection.getRng(); - const body = editor.getBody(); - - // Create a range from start of document to current caret - const preCaretRange = range.cloneRange(); - preCaretRange.selectNodeContents(body); - preCaretRange.setEnd(range.endContainer, range.endOffset); - - const fragment = preCaretRange.cloneContents(); - const tempDiv = document.createElement('div'); - tempDiv.appendChild(fragment); - let textBeforeCursor = tempDiv.innerText || ''; - - const endContainer = range.endContainer; - const endOffset = range.endOffset; - - if (endOffset === 0 && - endContainer.nodeType === Node.ELEMENT_NODE && - editor.dom.isBlock(endContainer) && - endContainer.previousSibling) { - textBeforeCursor += '\n'; - } - const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li'); - let emptyBlockCount = 0; - blockElements.forEach(block => { + } + + const range = editor.selection.getRng(); + const body = editor.getBody(); + + // Create a range from start of document to current caret + const preCaretRange = range.cloneRange(); + preCaretRange.selectNodeContents(body); + preCaretRange.setEnd(range.endContainer, range.endOffset); + + const fragment = preCaretRange.cloneContents(); + const tempDiv = document.createElement('div'); + tempDiv.appendChild(fragment); + let textBeforeCursor = tempDiv.innerText || ''; + + const endContainer = range.endContainer; + const endOffset = range.endOffset; + + if (endOffset === 0 && + endContainer.nodeType === Node.ELEMENT_NODE && + editor.dom.isBlock(endContainer) && + endContainer.previousSibling) { + textBeforeCursor += '\n'; + } + const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li'); + let emptyBlockCount = 0; + blockElements.forEach(block => { const text = block.innerText || block.textContent || ''; if (text.trim() === '' && block.childNodes.length === 1 && block.childNodes[0].nodeName === 'BR') { - emptyBlockCount++; + emptyBlockCount++; } - }); + }); - // Add newlines for empty blocks (these represent Enter presses that created empty lines) - if (emptyBlockCount > 0) { + // Add newlines for empty blocks (these represent Enter presses that created empty lines) + if (emptyBlockCount > 0) { textBeforeCursor += '\n'.repeat(emptyBlockCount); - } + } - const absolutePosition = textBeforeCursor.length; + const absolutePosition = textBeforeCursor.length; - if (skip) { + if (skip) { return { - caretPosition: lastCaretPos, - rePosition: absolutePosition + caretPosition: lastCaretPos, + rePosition: absolutePosition }; - } - // Increment sequential caretPosition - const storageKey = `${userid}_${resourceId}_${cmid}_position`; - let storedPos = parseInt(sessionStorage.getItem(storageKey), 10); - if (isNaN(storedPos)) { + } + // Increment sequential caretPosition + const storageKey = `${userid}_${resourceId}_${cmid}_position`; + let storedPos = parseInt(sessionStorage.getItem(storageKey), 10); + if (isNaN(storedPos)) { storedPos = 0; - } - storedPos++; - lastCaretPos = storedPos; - sessionStorage.setItem(storageKey, storedPos); + } + storedPos++; + lastCaretPos = storedPos; + sessionStorage.setItem(storageKey, storedPos); - return { + return { caretPosition: storedPos, rePosition: absolutePosition - }; + }; } catch (e) { window.console.warn('Error getting caret position:', e); return {caretPosition: lastCaretPos || 1, rePosition: 0}; } - } + } /** @@ -545,13 +559,14 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, * @throws {Error} Logs error to console if data submission fails */ async function syncData() { - + checkIsPdfAnnotator(); let data = localStorage.getItem(filename); if (!data || data.length === 0) { return; } else { localStorage.removeItem(filename); + editor.fire('change'); let originalText = editor.getContent({format: 'text'}); try { Autosave.updateSavingState('saving'); @@ -771,6 +786,7 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, */ function getModulesInfo(ur, parm, MODULES) { fetchStrings(); + if (!MODULES.some(module => ur.includes(module))) { return false; } @@ -797,6 +813,8 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, } } + checkIsPdfAnnotator(); + return {resourceId: resourceId, name: modulename}; } @@ -856,6 +874,24 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, } + /** + * Checks if the current page is a PDF annotator and updates the resourceId accordingly + * @function checkIsPdfAnnotator + * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state: + * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true): + * Sets resourceId to the annotation ID extracted from editor.id + * - Otherwise: Sets resourceId to 0 + */ + function checkIsPdfAnnotator() { + if (ur.includes('pdfannotator')) { + if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) { + resourceId = parseInt(editor?.id.replace('editarea', '')); + } else { + resourceId = 0; + } + } + } + window.addEventListener('unload', () => { syncData(); }); diff --git a/amd/src/document_view.js b/amd/src/document_view.js index 74b67d89..f469c26e 100644 --- a/amd/src/document_view.js +++ b/amd/src/document_view.js @@ -45,6 +45,8 @@ export default class DocumentView { this.normalizePage(id); } else if (this.module === 'lesson') { this.normalizePage(id); + } else if(this.module === 'pdfannotator') { + this.normalizePage(id); } } @@ -62,6 +64,9 @@ export default class DocumentView { } else if (this.module === 'lesson') { this.moduleIcon = Icons.lesson; this.fullPageModule(this.editor?.id); + } else if (this.module === 'pdfannotator') { + this.moduleIcon = Icons.pdfannotator; + this.fullPageModule(this.editor?.id); } } @@ -444,7 +449,7 @@ export default class DocumentView { labelDiv.appendChild(icon); labelDiv.append(label); - label.textContent = `${this.timeleft}: }`; + label.textContent = `${this.timeleft}:`; value.textContent = '00:00:00'; value.className = warningDiv ? 'text-danger' : 'text-primary'; Object.assign(value.style, { @@ -482,11 +487,11 @@ export default class DocumentView { const usernameWrapper = this.create('div'); const courseWrapper = this.create('div'); - const nameLabel = this.create('span'); + const nameLabel = this.create('strong'); const nameValue = this.create('span'); - const usernameLabel = this.create('span'); + const usernameLabel = this.create('strong'); const usernameValue = this.create('span'); - const courseLabel = this.create('span'); + const courseLabel = this.create('strong'); const courseValue = this.create('span'); nameLabel.textContent = `${this.name}`; @@ -498,9 +503,12 @@ export default class DocumentView { courseLabel.textContent = `${this.course}: `; courseValue.textContent = course.title; - nameWrapper.className = 'd-flex justify-content-between'; - usernameWrapper.className = 'd-flex justify-content-between'; - courseWrapper.className = 'd-flex justify-content-between'; + usernameLabel.className = 'cfw-bold me-2'; + usernameValue.className = 'cursiveFw-wrap'; + courseLabel.className = 'cfw-bold me-2'; + courseValue.className = 'cursiveFw-wrap'; + nameLabel.className = 'cfw-bold me-2'; + nameValue.className = 'cursiveFw-wrap'; nameWrapper.append(nameLabel, nameValue); usernameWrapper.append(usernameLabel, usernameValue); @@ -640,6 +648,17 @@ export default class DocumentView { btn.style.margin = '.5rem'; } + if (this.module === 'pdfannotator') { + const style = document.createElement('style'); + style.id = 'cursiveForceStyle'; + style.textContent = ` + .path-mod-pdfannotator #comment-wrapper h4, + .path-mod-pdfannotator #comment-nav { + margin: 0 !important; + } + `; + document.head.appendChild(style); + } const leftSide = this.create('div'); const rightSide = this.create('div'); @@ -729,6 +748,7 @@ export default class DocumentView { iframeBody.style.padding = '0'; } document.head.querySelector('#tiny_cursive-fullpage-mode-style')?.remove(); + document.head.querySelector('#cursiveForceStyle')?.remove(); } checkForumSubject() { @@ -759,6 +779,8 @@ export default class DocumentView { return {title: lesson, icon: Icons.forum}; case 'quiz': return {title: quiz, icon: Icons.quiz}; + case 'pdfannotator': + return {title: 'PDF Annotation', icon: Icons.pdfannotator}; default: return {title: 'Page', icon: Icons.quiz}; } diff --git a/amd/src/plugin.js b/amd/src/plugin.js index 8e3a0cfc..0ef0aef3 100644 --- a/amd/src/plugin.js +++ b/amd/src/plugin.js @@ -32,7 +32,8 @@ export default new Promise((resolve, reject) => { 'page-mod-quiz-attempt', 'page-mod-forum-view', 'page-mod-forum-post', - 'page-mod-lesson-view']; // 'page-mod-oublog-editpost' excluded + 'page-mod-lesson-view', + 'page-mod-pdfannotator-view']; // 'page-mod-oublog-editpost' excluded Promise.all([ getTinyMCE(), diff --git a/amd/src/svg_repo.js b/amd/src/svg_repo.js index 18a0ef79..8c56956b 100644 --- a/amd/src/svg_repo.js +++ b/amd/src/svg_repo.js @@ -22,7 +22,7 @@ */ export default { - people: ` @@ -31,7 +31,7 @@ export default { `, - assignment: ` @@ -39,20 +39,20 @@ export default { `, - time: ` `, - offline: ``, - forum: `` + 15.0007Z" fill="#212529"/>`, + pdfannotator: ` + + + + + + + + + + + + + + ` }; diff --git a/classes/constants.php b/classes/constants.php index 41584e99..c03270e6 100644 --- a/classes/constants.php +++ b/classes/constants.php @@ -34,7 +34,7 @@ class constants { * Array of supported activity module names. * const array NAMES List of module names where cursive can be used */ - public const NAMES = ["assign", "forum", "quiz", "lesson"]; // Excluded oublog. + public const NAMES = ["assign", "forum", "quiz", "lesson", 'pdfannotator']; // Excluded oublog. /** * Array mapping module names to their corresponding rubric areas. * Used to identify the correct rubric area for different module types. @@ -58,6 +58,7 @@ class constants { 'page-mod-quiz-review' => ['show_url_in_quiz_detail', 'quiz'], 'page-course-view-participants' => ['append_participants_table', 'course'], 'page-mod-lesson-essay' => ['append_lesson_grade_table', 'lesson'], + 'page-mod-pdfannotator-view' => ['append_pdfannotator', 'pdfannotator'], ]; diff --git a/classes/helper.php b/classes/helper.php new file mode 100644 index 00000000..7bc0a7ca --- /dev/null +++ b/classes/helper.php @@ -0,0 +1,130 @@ +. + +namespace tiny_cursive; + +/** + * Class helper + * + * @package tiny_cursive + * @copyright 2026 Cursive Technology, Inc. + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class helper { + /** + * Updates resource IDs for both comments and cursive files + * + * @param array $data Array containing userid, modulename, courseid, cmid and resourceid + * @return void + * @throws \dml_exception + */ + public static function update_resource_id($data) { + self::update_comment($data); + self::update_cursive_files($data); + } + + /** + * Tiny cursive plugin update comment observer. + * + * @param \core\event\base $event The event object + * @throws \dml_exception + */ + public static function update_comment($data) { + global $DB; + + $table = 'tiny_cursive_comments'; + $conditions = [ + "userid" => $data['userid'], + "modulename" => $data['modulename'], + 'resourceid' => 0, + 'courseid' => $data['courseid'], + 'cmid' => $data['cmid'], + ]; + + $recs = $DB->get_records($table, $conditions); + if ($recs) { + self::update_records($recs, $table, $data['resourceid']); + } + // Update autosave content as well. + $conditions['modulename'] = $data['modulename'] . "_autosave"; + self::update_autosaved_content($conditions, $table, $data['resourceid']); + } + + /** + * Tiny cursive plugin update cursive files observer. + * + * @param \core\event\base $event The event object + * @return void + * @throws \dml_exception + */ + public static function update_cursive_files($data) { + global $DB; + + $table = 'tiny_cursive_files'; + $conditions = [ + "userid" => $data['userid'], + "modulename" => $data['modulename'], + 'resourceid' => 0, + 'courseid' => $data['courseid'], + 'cmid' => $data['cmid'], + ]; + $recs = $DB->get_records($table, $conditions); + if ($recs) { + $fname = $data['userid'] . '_' . $data['resourceid'] . '_' . $data['cmid'] . '_attempt' . '.json'; + self::update_records($recs, $table, $data['resourceid'], $fname); + } + } + + /** + * Update autosaved content records. + * + * @param array $conditions The conditions to find records to update + * @param string $table The database table name + * @param array $data The event data containing user, course and context info + * @param int $postid The post ID to update the records with + * @return void + * @throws \dml_exception + */ + public static function update_autosaved_content($conditions, $table, $postid) { + global $DB; + $recs = $DB->get_records($table, $conditions); + if ($recs) { + self::update_records($recs, $table, $postid); + } + } + + /** + * Updates records in the database with new resource ID and optionally a new filename + * + * @param array $recs Array of records to update + * @param string $table Database table name + * @param int $id New resource ID to set + * @param string|null $name Optional new filename to set + * @return void + * @throws \dml_exception + */ + private static function update_records($recs, $table, $id, $name = null) { + global $DB; + + foreach ($recs as $rec) { + $rec->resourceid = $id; + if ($name) { + $rec->filename = $name; + } + $DB->update_record($table, $rec, true); + } + } +} diff --git a/classes/hook_callbacks.php b/classes/hook_callbacks.php index d1f74a2a..317ccb9f 100644 --- a/classes/hook_callbacks.php +++ b/classes/hook_callbacks.php @@ -88,7 +88,7 @@ public static function before_footer_html_generation(before_footer_html_generati $PAGE->requires->js_call_amd( "tiny_cursive/" . constants::BODY_IDS[$PAGE->bodyid][0], 'init', - [constants::confidence_threshold(), constants::show_comments(), constants::has_api_key()], + [constants::confidence_threshold(), constants::show_comments(), constants::has_api_key(), $USER->id], ); } } diff --git a/db/services.php b/db/services.php index f9f477ce..04134454 100644 --- a/db/services.php +++ b/db/services.php @@ -230,6 +230,15 @@ 'ajax' => true, 'capabilities' => 'tiny/cursive:view', ], + 'cursive_update_pdf_annote_id' => [ + 'classname' => 'cursive_json_func_data', + 'methodname' => 'update_pdf_annote_id', + 'classpath' => '/lib/editor/tiny/plugins/cursive/externallib.php', + 'description' => 'update resourceid for pdf annote analytics file', + 'type' => 'write', + 'ajax' => true, + 'capabilities' => 'tiny/cursive:view', + ], ]; // We define the services to install as pre-build services. diff --git a/externallib.php b/externallib.php index 90870c6a..80d74390 100644 --- a/externallib.php +++ b/externallib.php @@ -30,6 +30,7 @@ use core_external\external_single_structure; use core_external\external_value; use tiny_cursive\constants; +use tiny_cursive\helper; defined('MOODLE_INTERNAL') || die; @@ -1658,7 +1659,7 @@ public static function write_local_to_json( ], ); - if ($params['resourceId'] == 0 && $params['modulename'] !== 'forum' && $params['modulename'] !== 'oublog') { + if ($params['resourceId'] == 0 && $params['modulename'] !== 'forum' && $params['modulename'] !== 'oublog' && $params['modulename'] !== 'pdfannotator') { // For Quiz and Assignment there is no resourceid that's why cmid is resourceid. $params['resourceId'] = $params['cmid']; } @@ -2159,4 +2160,69 @@ public static function get_autosave_content($id, $modulename, $cmid, $editorid = public static function get_autosave_content_returns() { return new external_value(PARAM_TEXT, 'autosave content'); } + + + /** + * Returns the parameters definition for update_pdf_annote_id external function. + * + * @return external_function_parameters Parameters definition containing: + * - resourceid (int): Optional ID parameter + * - modulename (string): Optional module name parameter + * - cmid (int): Optional course module ID parameter + * - userid (int): Optional user ID parameter + * - courseid (int): Optional course ID parameter + */ + public static function update_pdf_annote_id_parameters() { + return new external_function_parameters([ + 'cmid' => new external_value(PARAM_INT, 'cmid', VALUE_DEFAULT, 0), + 'userid' => new external_value(PARAM_INT, 'userid', VALUE_DEFAULT, 0), + 'courseid' => new external_value(PARAM_INT, 'courseid', VALUE_DEFAULT, 0), + 'modulename' => new external_value(PARAM_TEXT, 'modulename', VALUE_DEFAULT, ''), + 'resourceid' => new external_value(PARAM_INT, 'pdf annote comment id', VALUE_DEFAULT, 0), + ]); + } + + /** + * Updates the PDF annotation ID for a comment record + * + * @param int $resourceid The PDF annotation comment ID + * @param string $modulename The name of the module + * @param int $cmid The course module ID + * @param int $userid The user ID (defaults to current user) + * @param int $courseid The course ID + * @return bool + * @throws coding_exception + * @throws dml_exception + * @throws invalid_parameter_exception + * @throws moodle_exception + * @throws required_capability_exception + */ + public static function update_pdf_annote_id($cmid, $userid, $courseid, $modulename, $resourceid) { + $params = self::validate_parameters( + self::update_pdf_annote_id_parameters(), + [ + 'cmid' => $cmid, + 'userid' => $userid, + 'courseid' => $courseid, + 'modulename' => $modulename, + 'resourceid' => $resourceid, + ], + ); + + $context = context_module::instance($params['cmid']); + self::validate_context($context); + require_capability("tiny/cursive:writingreport", $context); + + helper::update_resource_id($params); + return true; + } + + /** + * Returns description of update_pdf_annote_id return value + * + * @return external_value Returns a boolean parameter indicating if the update was successful + */ + public static function update_pdf_annote_id_returns() { + return new external_value(PARAM_BOOL, 'update pdf annote id'); + } } diff --git a/styles.css b/styles.css index d35d914b..4ed34912 100644 --- a/styles.css +++ b/styles.css @@ -1918,4 +1918,10 @@ body.tox-fullscreen .tiny-cursive-modal .modal-dialog .close { .tiny_cursive-card-price span { font-size: 16px; font-weight: 600; +} +.cfw-bold { + font-weight: 600 !important; +} +.cursiveFw-wrap { + word-wrap: break-word; } \ No newline at end of file diff --git a/version.php b/version.php index 1881a399..47e23930 100644 --- a/version.php +++ b/version.php @@ -29,6 +29,6 @@ $plugin->component = 'tiny_cursive'; $plugin->release = '2.1.2'; -$plugin->version = 2025121900; +$plugin->version = 2025121901; $plugin->requires = 2022041912; $plugin->maturity = MATURITY_STABLE; From d20f467a0c7cc4e25ac1d0ae90df90edf54912a0 Mon Sep 17 00:00:00 2001 From: Shahriar075 Date: Fri, 16 Jan 2026 11:10:38 +0600 Subject: [PATCH 08/14] Feedback resolved for paste block feature --- amd/build/analytic_events.min.js | 2 +- amd/build/analytic_events.min.js.map | 2 +- amd/build/analytic_modal.min.js | 9 +-------- amd/build/analytic_modal.min.js.map | 2 +- amd/build/append_lesson_grade_table.min.js | 2 +- amd/build/append_lesson_grade_table.min.js.map | 2 +- amd/build/autosaver.min.js | 2 +- amd/build/autosaver.min.js.map | 2 +- amd/build/common.min.js | 2 +- amd/build/common.min.js.map | 2 +- amd/build/cursive_autosave.min.js | 9 +-------- amd/build/cursive_autosave.min.js.map | 2 +- amd/build/document_view.min.js | 2 +- amd/build/document_view.min.js.map | 2 +- amd/build/replay.min.js | 2 +- amd/build/replay.min.js.map | 2 +- amd/build/scatter_chart.min.js | 2 +- amd/build/scatter_chart.min.js.map | 2 +- amd/build/svg_repo.min.js | 10 +--------- amd/build/svg_repo.min.js.map | 2 +- amd/build/texteditor.min.js | 2 +- amd/build/texteditor.min.js.map | 2 +- amd/build/token_approve.min.js | 2 +- amd/build/token_approve.min.js.map | 2 +- amd/src/autosaver.js | 3 --- lib.php | 2 +- 26 files changed, 25 insertions(+), 50 deletions(-) diff --git a/amd/build/analytic_events.min.js b/amd/build/analytic_events.min.js index 7a33c868..a771d356 100644 --- a/amd/build/analytic_events.min.js +++ b/amd/build/analytic_events.min.js @@ -7,6 +7,6 @@ define("tiny_cursive/analytic_events",["exports","./analytic_modal","core/ajax", * @module tiny_cursive/analytic_events * @copyright 2024 CTI * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_analytic_modal=_interopRequireDefault(_analytic_modal),_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates);return _exports.default=class{constructor(){(0,_str.get_string)("notenoughtinfo","tiny_cursive").then((str=>(localStorage.setItem("notenoughtinfo",str),str))).catch((error=>window.console.log(error)))}createModal(userid,context){let questionid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",replayInstances=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,authIcon=arguments.length>4?arguments[4]:void 0;const self=this;(0,_jquery.default)("#analytics"+userid+questionid).on("click",(function(e){e.preventDefault();const isReplayButton=(0,_jquery.default)(this).find(".tiny_cursive-replay-button").length>0;_analytic_modal.default.create({templateContext:context}).then((modal=>{(0,_jquery.default)("#content"+userid+" .tiny_cursive_table tbody tr:first-child td:nth-child(2)").html(authIcon),modal.show(),isReplayButton&&setTimeout((()=>{(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active");const replayTab=(0,_jquery.default)("#rep"+userid+questionid);replayTab.length&&(replayTab.trigger("click"),replayTab.addClass("active"))}),50);let moreBtn=(0,_jquery.default)("body #more"+userid+questionid);return moreBtn.length>0&&((0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)("#analytic"+userid+questionid).prop("disabled",!0),(0,_jquery.default)("#diff"+userid+questionid).prop("disabled",!0),(0,_jquery.default)("#analytic"+userid+questionid).css({"background-color":"rgba(168, 168, 168, 0.133)",cursor:"not-allowed"}),(0,_jquery.default)("#diff"+userid+questionid).css({"background-color":"rgba(168, 168, 168, 0.133)",cursor:"not-allowed"}),moreBtn.on("click",(function(e){e.preventDefault(),self.learnMore((0,_jquery.default)(this),context,userid,questionid,replayInstances)}))),!0})).catch((error=>{window.console.error("Failed to create modal:",error)}))}))}analytics(userid,templates,context){let questionid=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",replayInstances=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,authIcon=arguments.length>5?arguments[5]:void 0;(0,_jquery.default)("body").on("click","#analytic"+userid+questionid,(function(e){(0,_jquery.default)("#rep"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#quality"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#content"+userid).attr("data-label","analytics"),(0,_jquery.default)("#player_"+userid+questionid).css({display:"none"}),(0,_jquery.default)("#content"+userid).removeClass("tiny_cursive_outputElement").addClass("tiny_cursive").attr("data-label","analytics"),e.preventDefault(),(0,_jquery.default)("#content"+userid).html((0,_jquery.default)("
").addClass("d-flex justify-content-center my-5").append((0,_jquery.default)("
").addClass("tiny_cursive-loader"))),replayInstances&&replayInstances[userid]&&replayInstances[userid].stopReplay(),(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)(this).addClass("active"),templates.render("tiny_cursive/analytics_table",context).then((function(html){return(0,_jquery.default)("#content"+userid).html(html),(0,_jquery.default)("#content"+userid+" .tiny_cursive_table tbody tr:first-child td:nth-child(2)").html(authIcon),!0})).fail((function(error){window.console.error("Failed to render template:",error)}))}))}checkDiff(userid,fileid){let questionid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",replayInstances=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;const nodata=document.createElement("p");nodata.classList.add("tiny_cursive_nopayload","bg-light"),(0,_str.get_string)("nopaylod","tiny_cursive").then((str=>(nodata.textContent=str,!0))).catch((error=>window.console.log(error))),(0,_jquery.default)("body").on("click","#diff"+userid+questionid,(function(e){if((0,_jquery.default)("#rep"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#quality"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#content"+userid).attr("data-label","diff"),(0,_jquery.default)("#player_"+userid+questionid).css({display:"none"}),(0,_jquery.default)("#content"+userid).removeClass("tiny_cursive_outputElement").addClass("tiny_cursive").attr("data-label","diff"),e.preventDefault(),(0,_jquery.default)("#content"+userid).html((0,_jquery.default)("
").addClass("d-flex justify-content-center my-5").append((0,_jquery.default)("
").addClass("tiny_cursive-loader"))),(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)(this).addClass("active"),replayInstances&&replayInstances[userid]&&replayInstances[userid].stopReplay(),!fileid)throw(0,_jquery.default)("#content"+userid).html(nodata),new Error("Missing file id or Difference Content not received yet");(0,_ajax.call)([{methodname:"cursive_get_writing_differences",args:{fileid:fileid}}])[0].done((response=>{let responsedata=JSON.parse(response.data);if(responsedata){let submittedText=atob(responsedata.submitted_text);(0,_str.get_strings)([{key:"original_text",component:"tiny_cursive"},{key:"editspastesai",component:"tiny_cursive"}]).done((strings=>{const originalTextString=strings[0],editsPastesAIString=strings[1],commentBox=(0,_jquery.default)('
');var pasteCountDiv=(0,_jquery.default)("
");(0,_str.get_string)("pastecount","tiny_cursive").then((str=>(pasteCountDiv.append("
"+str+" : "+responsedata.commentscount+"
"),!0))).catch((error=>window.console.log(error)));var commentsDiv=(0,_jquery.default)('
');(0,_str.get_string)("comments","tiny_cursive").then((str=>(commentsDiv.append(""+str+""),!0))).catch((error=>window.console.error(error)));var commentsList=(0,_jquery.default)("
");let comments=responsedata.comments;for(let index in comments){var commentDiv=(0,_jquery.default)('
').text(comments[index].usercomment);commentsList.append(commentDiv)}commentBox.append(pasteCountDiv).append(commentsDiv).append(commentsList);const $legend=(0,_jquery.default)('
'),$attributedItem=(0,_jquery.default)("
",{class:"tiny_cursive-legend-item"}),$attributedBox=(0,_jquery.default)("
",{class:"tiny_cursive-box attributed"}),$attributedText=(0,_jquery.default)("").text(originalTextString);$attributedItem.append($attributedBox).append($attributedText);const $unattributedItem=(0,_jquery.default)("
",{class:"tiny_cursive-legend-item"}),$unattributedBox=(0,_jquery.default)("
",{class:"tiny_cursive-box tiny_cursive_added"}),$unattributedText=(0,_jquery.default)("").text(editsPastesAIString);$unattributedItem.append($unattributedBox).append($unattributedText),$legend.append($attributedItem).append($unattributedItem);let contents=(0,_jquery.default)("
").addClass("tiny_cursive-comparison-content"),textBlock2=(0,_jquery.default)("
").addClass("tiny_cursive-text-block").append((0,_jquery.default)("
").attr("id","tiny_cursive-reconstructed_text").html(JSON.parse(submittedText)));contents.append(commentBox,$legend,textBlock2),(0,_jquery.default)("#content"+userid).html(contents)})).fail((error=>{window.console.error("Failed to load language strings:",error),(0,_jquery.default)("#content"+userid).html(nodata)}))}else(0,_jquery.default)("#content"+userid).html(nodata)})).fail((error=>{throw(0,_jquery.default)("#content"+userid).html(nodata),new Error("Error loading JSON file: "+error.message)}))}))}replyWriting(userid,filepath){let questionid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",replayInstances=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;(0,_jquery.default)("body").on("click","#rep"+userid+questionid,(function(e){filepath&&((0,_jquery.default)("#replayControls_"+userid+questionid).removeClass("d-none"),(0,_jquery.default)("#content"+userid).addClass("tiny_cursive_outputElement")),(0,_jquery.default)(this).prop("disabled",!0),(0,_jquery.default)("#quality"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#content"+userid).attr("data-label","replay"),(0,_jquery.default)("#player_"+userid+questionid).css({display:"block","padding-right":"8px"}),e.preventDefault(),(0,_jquery.default)("#content"+userid).html((0,_jquery.default)("
").addClass("d-flex justify-content-center my-5").append((0,_jquery.default)("
").addClass("tiny_cursive-loader"))),(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)(this).addClass("active"),replayInstances&&replayInstances[userid]&&replayInstances[userid].stopReplay(),questionid?video_playback(userid,filepath,questionid):video_playback(userid,filepath)}))}learnMore(moreBtn,context,userid,questionid,replayInstances){(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),moreBtn.addClass("active"),(0,_jquery.default)("#rep"+userid+questionid).prop("disabled",!1),replayInstances&&replayInstances[userid]&&replayInstances[userid].stopReplay(),(0,_jquery.default)("#content"+userid+questionid).removeClass("tiny_cursive_outputElement"),(0,_jquery.default)("#replayControls_"+userid+questionid).addClass("d-none"),_templates.default.render("tiny_cursive/learn_more",context).then((function(html){return(0,_jquery.default)("#content"+userid+questionid).html(html),!0})).fail((function(error){window.console.error("Failed to render template:",error)}))}formatedTime(data){if(data.total_time_seconds){let totalTimeSeconds=data.total_time_seconds;return`${Math.floor(totalTimeSeconds/3600).toString().padStart(2,0)}h ${Math.floor(totalTimeSeconds%3600/60).toString().padStart(2,0)}m ${(totalTimeSeconds%60).toString().padStart(2,0)}s`}return"0h 0m 0s"}authorshipStatus(firstFile,score,scoreSetting){var icon="fa fa-circle-o",color="font-size:32px;color:black";return score=parseFloat(score),firstFile?(icon="fa fa-solid fa-info-circle",color="font-size:32px;color:#000000"):score>=scoreSetting&&(icon="fa fa-check-circle",color="font-size:32px;color:green"),score").addClass(icon).attr("style",color).attr("title",localStorage.getItem("notenoughtinfo"))):(0,_jquery.default)("").addClass(icon).attr("style",color)}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_analytic_modal=_interopRequireDefault(_analytic_modal),_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates);return _exports.default=class{constructor(){(0,_str.get_string)("notenoughtinfo","tiny_cursive").then((str=>(localStorage.setItem("notenoughtinfo",str),str))).catch((error=>window.console.log(error)))}createModal(userid,context){let questionid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",replayInstances=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,authIcon=arguments.length>4?arguments[4]:void 0;const self=this;(0,_jquery.default)("#analytics"+userid+questionid).on("click",(function(e){e.preventDefault();const isReplayButton=(0,_jquery.default)(this).find(".tiny_cursive-replay-button").length>0;_analytic_modal.default.create({templateContext:context}).then((modal=>{(0,_jquery.default)("#content"+userid+" .tiny_cursive_table tbody tr:first-child td:nth-child(2)").html(authIcon),modal.show(),isReplayButton&&setTimeout((()=>{(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active");const replayTab=(0,_jquery.default)("#rep"+userid+questionid);replayTab.length&&(replayTab.trigger("click"),replayTab.addClass("active"))}),50);let moreBtn=(0,_jquery.default)("body #more"+userid+questionid);return moreBtn.length>0&&((0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)("#analytic"+userid+questionid).prop("disabled",!0),(0,_jquery.default)("#diff"+userid+questionid).prop("disabled",!0),(0,_jquery.default)("#analytic"+userid+questionid).css({"background-color":"rgba(168, 168, 168, 0.133)",cursor:"not-allowed"}),(0,_jquery.default)("#diff"+userid+questionid).css({"background-color":"rgba(168, 168, 168, 0.133)",cursor:"not-allowed"}),moreBtn.on("click",(function(e){e.preventDefault(),self.learnMore((0,_jquery.default)(this),context,userid,questionid,replayInstances)}))),!0})).catch((error=>{window.console.error("Failed to create modal:",error)}))}))}analytics(userid,templates,context){let questionid=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",replayInstances=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,authIcon=arguments.length>5?arguments[5]:void 0;(0,_jquery.default)("body").on("click","#analytic"+userid+questionid,(function(e){(0,_jquery.default)("#rep"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#quality"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#content"+userid).attr("data-label","analytics"),(0,_jquery.default)("#player_"+userid+questionid).css({display:"none"}),(0,_jquery.default)("#content"+userid).removeClass("tiny_cursive_outputElement").addClass("tiny_cursive").attr("data-label","analytics"),e.preventDefault(),(0,_jquery.default)("#content"+userid).html((0,_jquery.default)("
").addClass("d-flex justify-content-center my-5").append((0,_jquery.default)("
").addClass("tiny_cursive-loader"))),replayInstances&&replayInstances[userid]&&replayInstances[userid].stopReplay(),(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)(this).addClass("active"),templates.render("tiny_cursive/analytics_table",context).then((function(html){return(0,_jquery.default)("#content"+userid).html(html),(0,_jquery.default)("#content"+userid+" .tiny_cursive_table tbody tr:first-child td:nth-child(2)").html(authIcon),!0})).fail((function(error){window.console.error("Failed to render template:",error)}))}))}checkDiff(userid,fileid){let questionid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",replayInstances=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;const nodata=document.createElement("p");nodata.classList.add("tiny_cursive_nopayload","bg-light"),(0,_str.get_string)("nopaylod","tiny_cursive").then((str=>(nodata.textContent=str,!0))).catch((error=>window.console.log(error))),(0,_jquery.default)("body").on("click","#diff"+userid+questionid,(function(e){if((0,_jquery.default)("#rep"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#quality"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#content"+userid).attr("data-label","diff"),(0,_jquery.default)("#player_"+userid+questionid).css({display:"none"}),(0,_jquery.default)("#content"+userid).removeClass("tiny_cursive_outputElement").addClass("tiny_cursive").attr("data-label","diff"),e.preventDefault(),(0,_jquery.default)("#content"+userid).html((0,_jquery.default)("
").addClass("d-flex justify-content-center my-5").append((0,_jquery.default)("
").addClass("tiny_cursive-loader"))),(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)(this).addClass("active"),replayInstances&&replayInstances[userid]&&replayInstances[userid].stopReplay(),!fileid)throw(0,_jquery.default)("#content"+userid).html(nodata),new Error("Missing file id or Difference Content not received yet");(0,_ajax.call)([{methodname:"cursive_get_writing_differences",args:{fileid:fileid}}])[0].done((response=>{let responsedata=JSON.parse(response.data);if(responsedata){let submittedText=atob(responsedata.submitted_text);(0,_str.get_strings)([{key:"original_text",component:"tiny_cursive"},{key:"editspastesai",component:"tiny_cursive"}]).done((strings=>{const originalTextString=strings[0],editsPastesAIString=strings[1],commentBox=(0,_jquery.default)('
');var pasteCountDiv=(0,_jquery.default)("
");(0,_str.get_string)("pastecount","tiny_cursive").then((str=>(pasteCountDiv.append("
"+str+" : "+responsedata.commentscount+"
"),!0))).catch((error=>window.console.log(error)));var commentsDiv=(0,_jquery.default)('
');(0,_str.get_string)("comments","tiny_cursive").then((str=>(commentsDiv.append(""+str+""),!0))).catch((error=>window.console.error(error)));var commentsList=(0,_jquery.default)("
");let comments=responsedata.comments;for(let index in comments){var commentDiv=(0,_jquery.default)('
').text(comments[index].usercomment);commentsList.append(commentDiv)}commentBox.append(pasteCountDiv).append(commentsDiv).append(commentsList);const $legend=(0,_jquery.default)('
'),$attributedItem=(0,_jquery.default)("
",{class:"tiny_cursive-legend-item"}),$attributedBox=(0,_jquery.default)("
",{class:"tiny_cursive-box attributed"}),$attributedText=(0,_jquery.default)("").text(originalTextString);$attributedItem.append($attributedBox).append($attributedText);const $unattributedItem=(0,_jquery.default)("
",{class:"tiny_cursive-legend-item"}),$unattributedBox=(0,_jquery.default)("
",{class:"tiny_cursive-box tiny_cursive_added"}),$unattributedText=(0,_jquery.default)("").text(editsPastesAIString);$unattributedItem.append($unattributedBox).append($unattributedText),$legend.append($attributedItem).append($unattributedItem);let contents=(0,_jquery.default)("
").addClass("tiny_cursive-comparison-content"),textBlock2=(0,_jquery.default)("
").addClass("tiny_cursive-text-block").append((0,_jquery.default)("
").attr("id","tiny_cursive-reconstructed_text").html(JSON.parse(submittedText)));contents.append(commentBox,$legend,textBlock2),(0,_jquery.default)("#content"+userid).html(contents)})).fail((error=>{window.console.error("Failed to load language strings:",error),(0,_jquery.default)("#content"+userid).html(nodata)}))}else(0,_jquery.default)("#content"+userid).html(nodata)})).fail((error=>{throw(0,_jquery.default)("#content"+userid).html(nodata),new Error("Error loading JSON file: "+error.message)}))}))}replyWriting(userid,filepath){let questionid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",replayInstances=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;(0,_jquery.default)("body").on("click","#rep"+userid+questionid,(function(e){filepath&&((0,_jquery.default)("#replayControls_"+userid+questionid).removeClass("d-none"),(0,_jquery.default)("#content"+userid).addClass("tiny_cursive_outputElement")),(0,_jquery.default)(this).prop("disabled",!0),(0,_jquery.default)("#quality"+userid+questionid).prop("disabled",!1),(0,_jquery.default)("#content"+userid).attr("data-label","replay"),(0,_jquery.default)("#player_"+userid+questionid).css({display:"block","padding-right":"8px"}),e.preventDefault(),(0,_jquery.default)("#content"+userid).html((0,_jquery.default)("
").addClass("d-flex justify-content-center my-5").append((0,_jquery.default)("
").addClass("tiny_cursive-loader"))),(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)(this).addClass("active"),replayInstances&&replayInstances[userid]&&replayInstances[userid].stopReplay(),questionid?video_playback(userid,filepath,questionid):video_playback(userid,filepath)}))}learnMore(moreBtn,context,userid,questionid,replayInstances){(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),moreBtn.addClass("active"),(0,_jquery.default)("#rep"+userid+questionid).prop("disabled",!1),replayInstances&&replayInstances[userid]&&replayInstances[userid].stopReplay(),(0,_jquery.default)("#content"+userid+questionid).removeClass("tiny_cursive_outputElement"),(0,_jquery.default)("#replayControls_"+userid+questionid).addClass("d-none"),_templates.default.render("tiny_cursive/learn_more",context).then((function(html){return(0,_jquery.default)("#content"+userid+questionid).html(html),!0})).fail((function(error){window.console.error("Failed to render template:",error)}))}formatedTime(data){if(data.total_time_seconds){let totalTimeSeconds=data.total_time_seconds,hours=Math.floor(totalTimeSeconds/3600).toString().padStart(2,0),minutes=Math.floor(totalTimeSeconds%3600/60).toString().padStart(2,0),seconds=(totalTimeSeconds%60).toString().padStart(2,0);return"".concat(hours,"h ").concat(minutes,"m ").concat(seconds,"s")}return"0h 0m 0s"}authorshipStatus(firstFile,score,scoreSetting){var icon="fa fa-circle-o",color="font-size:32px;color:black";return score=parseFloat(score),firstFile?(icon="fa fa-solid fa-info-circle",color="font-size:32px;color:#000000"):score>=scoreSetting&&(icon="fa fa-check-circle",color="font-size:32px;color:green"),score").addClass(icon).attr("style",color).attr("title",localStorage.getItem("notenoughtinfo"))):(0,_jquery.default)("").addClass(icon).attr("style",color)}},_exports.default})); //# sourceMappingURL=analytic_events.min.js.map \ No newline at end of file diff --git a/amd/build/analytic_events.min.js.map b/amd/build/analytic_events.min.js.map index 321173da..536eeb19 100644 --- a/amd/build/analytic_events.min.js.map +++ b/amd/build/analytic_events.min.js.map @@ -1 +1 @@ -{"version":3,"file":"analytic_events.min.js","sources":["../src/analytic_events.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module for handling analytics events in the Tiny Cursive plugin.\n * Provides functionality for displaying analytics data, replaying writing,\n * checking differences and showing quality metrics.\n *\n * @module tiny_cursive/analytic_events\n * @copyright 2024 CTI \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport myModal from \"./analytic_modal\";\nimport {call as getContent} from \"core/ajax\";\nimport $ from 'jquery';\nimport {get_string as getString} from 'core/str';\nimport {get_strings as getStrings} from 'core/str';\nimport template from 'core/templates';\n\nexport default class AnalyticEvents {\n\n constructor() {\n getString('notenoughtinfo', 'tiny_cursive').then(str => {\n localStorage.setItem('notenoughtinfo', str);\n return str;\n }).catch(error => window.console.log(error));\n }\n\n createModal(userid, context, questionid = '', replayInstances = null, authIcon) {\n const self = this;\n $('#analytics' + userid + questionid).on('click', function(e) {\n e.preventDefault();\n\n const isReplayButton = $(this).find('.tiny_cursive-replay-button').length > 0;\n // Create Moodle modal\n myModal.create({templateContext: context}).then(modal => {\n $('#content' + userid + ' .tiny_cursive_table tbody tr:first-child td:nth-child(2)').html(authIcon);\n modal.show();\n\n if (isReplayButton) {\n setTimeout(() => {\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n\n const replayTab = $('#rep' + userid + questionid);\n if (replayTab.length) {\n replayTab.trigger('click');\n replayTab.addClass('active');\n }\n }, 50);\n }\n\n let moreBtn = $('body #more' + userid + questionid);\n if (moreBtn.length > 0) {\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $('#analytic' + userid + questionid).prop('disabled', true);\n $('#diff' + userid + questionid).prop('disabled', true);\n $('#analytic' + userid + questionid).css({\n 'background-color': 'rgba(168, 168, 168, 0.133)',\n 'cursor': 'not-allowed'\n });\n $('#diff' + userid + questionid).css({\n 'background-color': 'rgba(168, 168, 168, 0.133)',\n 'cursor': 'not-allowed'\n });\n moreBtn.on('click', function(e) {\n e.preventDefault();\n self.learnMore($(this), context, userid, questionid, replayInstances);\n });\n }\n\n return true;\n }).catch(error => {\n window.console.error(\"Failed to create modal:\", error);\n });\n\n });\n }\n\n analytics(userid, templates, context, questionid = '', replayInstances = null, authIcon) {\n\n $('body').on('click', '#analytic' + userid + questionid, function(e) {\n $('#rep' + userid + questionid).prop('disabled', false);\n $('#quality' + userid + questionid).prop('disabled', false);\n $('#content' + userid).attr('data-label', 'analytics');\n $('#player_' + userid + questionid).css({'display': 'none'});\n $('#content' + userid).removeClass('tiny_cursive_outputElement')\n .addClass('tiny_cursive').attr('data-label', 'analytics');\n e.preventDefault();\n $('#content' + userid).html($('
').addClass('d-flex justify-content-center my-5')\n .append($('
').addClass('tiny_cursive-loader')));\n if (replayInstances && replayInstances[userid]) {\n replayInstances[userid].stopReplay();\n }\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $(this).addClass('active'); // Add 'active' class to the clicked element\n\n templates.render('tiny_cursive/analytics_table', context).then(function(html) {\n $('#content' + userid).html(html);\n $('#content' + userid + ' .tiny_cursive_table tbody tr:first-child td:nth-child(2)').html(authIcon);\n return true;\n }).fail(function(error) {\n window.console.error(\"Failed to render template:\", error);\n });\n });\n }\n\n checkDiff(userid, fileid, questionid = '', replayInstances = null) {\n const nodata = document.createElement('p');\n nodata.classList.add('tiny_cursive_nopayload', 'bg-light');\n getString('nopaylod', 'tiny_cursive').then(str => {\n nodata.textContent = str;\n return true;\n }).catch(error => window.console.log(error));\n $('body').on('click', '#diff' + userid + questionid, function(e) {\n $('#rep' + userid + questionid).prop('disabled', false);\n $('#quality' + userid + questionid).prop('disabled', false);\n $('#content' + userid).attr('data-label', 'diff');\n $('#player_' + userid + questionid).css({\n 'display': 'none'\n });\n $('#content' + userid).removeClass('tiny_cursive_outputElement').addClass('tiny_cursive').attr('data-label', 'diff');\n e.preventDefault();\n $('#content' + userid).html($('
').addClass('d-flex justify-content-center my-5')\n .append($('
').addClass('tiny_cursive-loader')));\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $(this).addClass('active');\n if (replayInstances && replayInstances[userid]) {\n replayInstances[userid].stopReplay();\n }\n if (!fileid) {\n $('#content' + userid).html(nodata);\n throw new Error('Missing file id or Difference Content not received yet');\n }\n getContent([{\n methodname: 'cursive_get_writing_differences',\n args: {fileid: fileid},\n }])[0].done(response => {\n let responsedata = JSON.parse(response.data);\n if (responsedata) {\n let submittedText = atob(responsedata.submitted_text);\n\n // Fetch the dynamic strings.\n getStrings([\n {key: 'original_text', component: 'tiny_cursive'},\n {key: 'editspastesai', component: 'tiny_cursive'}\n ]).done(strings => {\n const originalTextString = strings[0];\n const editsPastesAIString = strings[1];\n\n const commentBox = $('
');\n var pasteCountDiv = $('
');\n getString('pastecount', 'tiny_cursive').then(str => {\n pasteCountDiv.append('
' + str + ' : ' + responsedata.commentscount + '
');\n return true;\n }).catch(error => window.console.log(error));\n\n var commentsDiv = $('
');\n getString('comments', 'tiny_cursive').then(str => {\n commentsDiv.append('' + str + '');\n return true;\n }).catch(error => window.console.error(error));\n\n var commentsList = $('
');\n\n let comments = responsedata.comments;\n for (let index in comments) {\n var commentDiv = $(`
`).text(comments[index].usercomment);\n commentsList.append(commentDiv);\n }\n commentBox.append(pasteCountDiv).append(commentsDiv).append(commentsList);\n\n const $legend = $('
');\n\n // Create the first legend item\n const $attributedItem = $('
', {\"class\": \"tiny_cursive-legend-item\"});\n const $attributedBox = $('
', {\"class\": \"tiny_cursive-box attributed\"});\n const $attributedText = $('').text(originalTextString);\n $attributedItem.append($attributedBox).append($attributedText);\n\n // Create the second legend item\n const $unattributedItem = $('
', {\"class\": 'tiny_cursive-legend-item'});\n const $unattributedBox = $('
', {\"class\": 'tiny_cursive-box tiny_cursive_added'});\n const $unattributedText = $('').text(editsPastesAIString);\n $unattributedItem.append($unattributedBox).append($unattributedText);\n\n // Append the legend items to the legend container.\n $legend.append($attributedItem).append($unattributedItem);\n\n let contents = $('
').addClass('tiny_cursive-comparison-content');\n let textBlock2 = $('
').addClass('tiny_cursive-text-block').append(\n $('
').attr('id', 'tiny_cursive-reconstructed_text').html(JSON.parse(submittedText))\n );\n\n contents.append(commentBox, $legend, textBlock2);\n $('#content' + userid).html(contents); // Update content.\n }).fail(error => {\n window.console.error(\"Failed to load language strings:\", error);\n $('#content' + userid).html(nodata);\n });\n } else {\n $('#content' + userid).html(nodata);\n }\n }).fail(error => {\n $('#content' + userid).html(nodata);\n throw new Error('Error loading JSON file: ' + error.message);\n });\n });\n }\n\n replyWriting(userid, filepath, questionid = '', replayInstances = null) {\n $('body').on('click', '#rep' + userid + questionid, function(e) {\n\n if (filepath) {\n $('#replayControls_' + userid + questionid).removeClass('d-none');\n $('#content' + userid).addClass('tiny_cursive_outputElement');\n }\n\n $(this).prop('disabled', true);\n $('#quality' + userid + questionid).prop('disabled', false);\n $('#content' + userid).attr('data-label', 'replay');\n $('#player_' + userid + questionid).css({\n 'display': 'block',\n 'padding-right': '8px'\n });\n e.preventDefault();\n $('#content' + userid).html($('
').addClass('d-flex justify-content-center my-5')\n .append($('
').addClass('tiny_cursive-loader')));\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $(this).addClass('active'); // Add 'active' class to the clicked element\n if (replayInstances && replayInstances[userid]) {\n replayInstances[userid].stopReplay();\n }\n if (questionid) {\n // eslint-disable-next-line\n video_playback(userid, filepath, questionid);\n } else {\n // eslint-disable-next-line\n video_playback(userid, filepath);\n }\n });\n }\n\n learnMore(moreBtn, context, userid, questionid, replayInstances) {\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n moreBtn.addClass('active');\n $('#rep' + userid + questionid).prop('disabled', false);\n if (replayInstances && replayInstances[userid]) {\n replayInstances[userid].stopReplay();\n }\n $('#content' + userid + questionid).removeClass('tiny_cursive_outputElement');\n $('#replayControls_' + userid + questionid).addClass('d-none');\n template.render('tiny_cursive/learn_more', context).then(function(html) {\n $('#content' + userid + questionid).html(html);\n return true;\n }).fail(function(error) {\n window.console.error(\"Failed to render template:\", error);\n });\n }\n\n formatedTime(data) {\n if (data.total_time_seconds) {\n let totalTimeSeconds = data.total_time_seconds;\n let hours = Math.floor(totalTimeSeconds / 3600).toString().padStart(2, 0);\n let minutes = Math.floor((totalTimeSeconds % 3600) / 60).toString().padStart(2, 0);\n let seconds = (totalTimeSeconds % 60).toString().padStart(2, 0);\n return `${hours}h ${minutes}m ${seconds}s`;\n } else {\n return \"0h 0m 0s\";\n }\n }\n\n authorshipStatus(firstFile, score, scoreSetting) {\n var icon = 'fa fa-circle-o';\n var color = 'font-size:32px;color:black';\n score = parseFloat(score);\n\n if (firstFile) {\n icon = 'fa fa-solid fa-info-circle';\n color = 'font-size:32px;color:#000000';\n } else if (score >= scoreSetting) {\n icon = 'fa fa-check-circle';\n color = 'font-size:32px;color:green';\n }\n if (score < scoreSetting) {\n icon = 'fa fa-question-circle';\n color = 'font-size:32px;color:#A9A9A9';\n return $('').addClass(icon).attr('style', color).attr('title', localStorage.getItem('notenoughtinfo'));\n } else {\n return $('').addClass(icon).attr('style', color);\n }\n\n }\n}\n"],"names":["constructor","then","str","localStorage","setItem","catch","error","window","console","log","createModal","userid","context","questionid","replayInstances","authIcon","self","this","on","e","preventDefault","isReplayButton","find","length","create","templateContext","modal","html","show","setTimeout","removeClass","replayTab","trigger","addClass","moreBtn","prop","css","learnMore","analytics","templates","attr","append","stopReplay","render","fail","checkDiff","fileid","nodata","document","createElement","classList","add","textContent","Error","methodname","args","done","response","responsedata","JSON","parse","data","submittedText","atob","submitted_text","key","component","strings","originalTextString","editsPastesAIString","commentBox","pasteCountDiv","commentscount","commentsDiv","commentsList","comments","index","commentDiv","text","usercomment","$legend","$attributedItem","$attributedBox","$attributedText","$unattributedItem","$unattributedBox","$unattributedText","contents","textBlock2","message","replyWriting","filepath","video_playback","formatedTime","total_time_seconds","totalTimeSeconds","Math","floor","toString","padStart","authorshipStatus","firstFile","score","scoreSetting","icon","color","parseFloat","getItem"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"analytic_events.min.js","sources":["../src/analytic_events.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module for handling analytics events in the Tiny Cursive plugin.\n * Provides functionality for displaying analytics data, replaying writing,\n * checking differences and showing quality metrics.\n *\n * @module tiny_cursive/analytic_events\n * @copyright 2024 CTI \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport myModal from \"./analytic_modal\";\nimport {call as getContent} from \"core/ajax\";\nimport $ from 'jquery';\nimport {get_string as getString} from 'core/str';\nimport {get_strings as getStrings} from 'core/str';\nimport template from 'core/templates';\n\nexport default class AnalyticEvents {\n\n constructor() {\n getString('notenoughtinfo', 'tiny_cursive').then(str => {\n localStorage.setItem('notenoughtinfo', str);\n return str;\n }).catch(error => window.console.log(error));\n }\n\n createModal(userid, context, questionid = '', replayInstances = null, authIcon) {\n const self = this;\n $('#analytics' + userid + questionid).on('click', function(e) {\n e.preventDefault();\n\n const isReplayButton = $(this).find('.tiny_cursive-replay-button').length > 0;\n // Create Moodle modal\n myModal.create({templateContext: context}).then(modal => {\n $('#content' + userid + ' .tiny_cursive_table tbody tr:first-child td:nth-child(2)').html(authIcon);\n modal.show();\n\n if (isReplayButton) {\n setTimeout(() => {\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n\n const replayTab = $('#rep' + userid + questionid);\n if (replayTab.length) {\n replayTab.trigger('click');\n replayTab.addClass('active');\n }\n }, 50);\n }\n\n let moreBtn = $('body #more' + userid + questionid);\n if (moreBtn.length > 0) {\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $('#analytic' + userid + questionid).prop('disabled', true);\n $('#diff' + userid + questionid).prop('disabled', true);\n $('#analytic' + userid + questionid).css({\n 'background-color': 'rgba(168, 168, 168, 0.133)',\n 'cursor': 'not-allowed'\n });\n $('#diff' + userid + questionid).css({\n 'background-color': 'rgba(168, 168, 168, 0.133)',\n 'cursor': 'not-allowed'\n });\n moreBtn.on('click', function(e) {\n e.preventDefault();\n self.learnMore($(this), context, userid, questionid, replayInstances);\n });\n }\n\n return true;\n }).catch(error => {\n window.console.error(\"Failed to create modal:\", error);\n });\n\n });\n }\n\n analytics(userid, templates, context, questionid = '', replayInstances = null, authIcon) {\n\n $('body').on('click', '#analytic' + userid + questionid, function(e) {\n $('#rep' + userid + questionid).prop('disabled', false);\n $('#quality' + userid + questionid).prop('disabled', false);\n $('#content' + userid).attr('data-label', 'analytics');\n $('#player_' + userid + questionid).css({'display': 'none'});\n $('#content' + userid).removeClass('tiny_cursive_outputElement')\n .addClass('tiny_cursive').attr('data-label', 'analytics');\n e.preventDefault();\n $('#content' + userid).html($('
').addClass('d-flex justify-content-center my-5')\n .append($('
').addClass('tiny_cursive-loader')));\n if (replayInstances && replayInstances[userid]) {\n replayInstances[userid].stopReplay();\n }\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $(this).addClass('active'); // Add 'active' class to the clicked element\n\n templates.render('tiny_cursive/analytics_table', context).then(function(html) {\n $('#content' + userid).html(html);\n $('#content' + userid + ' .tiny_cursive_table tbody tr:first-child td:nth-child(2)').html(authIcon);\n return true;\n }).fail(function(error) {\n window.console.error(\"Failed to render template:\", error);\n });\n });\n }\n\n checkDiff(userid, fileid, questionid = '', replayInstances = null) {\n const nodata = document.createElement('p');\n nodata.classList.add('tiny_cursive_nopayload', 'bg-light');\n getString('nopaylod', 'tiny_cursive').then(str => {\n nodata.textContent = str;\n return true;\n }).catch(error => window.console.log(error));\n $('body').on('click', '#diff' + userid + questionid, function(e) {\n $('#rep' + userid + questionid).prop('disabled', false);\n $('#quality' + userid + questionid).prop('disabled', false);\n $('#content' + userid).attr('data-label', 'diff');\n $('#player_' + userid + questionid).css({\n 'display': 'none'\n });\n $('#content' + userid).removeClass('tiny_cursive_outputElement').addClass('tiny_cursive').attr('data-label', 'diff');\n e.preventDefault();\n $('#content' + userid).html($('
').addClass('d-flex justify-content-center my-5')\n .append($('
').addClass('tiny_cursive-loader')));\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $(this).addClass('active');\n if (replayInstances && replayInstances[userid]) {\n replayInstances[userid].stopReplay();\n }\n if (!fileid) {\n $('#content' + userid).html(nodata);\n throw new Error('Missing file id or Difference Content not received yet');\n }\n getContent([{\n methodname: 'cursive_get_writing_differences',\n args: {fileid: fileid},\n }])[0].done(response => {\n let responsedata = JSON.parse(response.data);\n if (responsedata) {\n let submittedText = atob(responsedata.submitted_text);\n\n // Fetch the dynamic strings.\n getStrings([\n {key: 'original_text', component: 'tiny_cursive'},\n {key: 'editspastesai', component: 'tiny_cursive'}\n ]).done(strings => {\n const originalTextString = strings[0];\n const editsPastesAIString = strings[1];\n\n const commentBox = $('
');\n var pasteCountDiv = $('
');\n getString('pastecount', 'tiny_cursive').then(str => {\n pasteCountDiv.append('
' + str + ' : ' + responsedata.commentscount + '
');\n return true;\n }).catch(error => window.console.log(error));\n\n var commentsDiv = $('
');\n getString('comments', 'tiny_cursive').then(str => {\n commentsDiv.append('' + str + '');\n return true;\n }).catch(error => window.console.error(error));\n\n var commentsList = $('
');\n\n let comments = responsedata.comments;\n for (let index in comments) {\n var commentDiv = $(`
`).text(comments[index].usercomment);\n commentsList.append(commentDiv);\n }\n commentBox.append(pasteCountDiv).append(commentsDiv).append(commentsList);\n\n const $legend = $('
');\n\n // Create the first legend item\n const $attributedItem = $('
', {\"class\": \"tiny_cursive-legend-item\"});\n const $attributedBox = $('
', {\"class\": \"tiny_cursive-box attributed\"});\n const $attributedText = $('').text(originalTextString);\n $attributedItem.append($attributedBox).append($attributedText);\n\n // Create the second legend item\n const $unattributedItem = $('
', {\"class\": 'tiny_cursive-legend-item'});\n const $unattributedBox = $('
', {\"class\": 'tiny_cursive-box tiny_cursive_added'});\n const $unattributedText = $('').text(editsPastesAIString);\n $unattributedItem.append($unattributedBox).append($unattributedText);\n\n // Append the legend items to the legend container.\n $legend.append($attributedItem).append($unattributedItem);\n\n let contents = $('
').addClass('tiny_cursive-comparison-content');\n let textBlock2 = $('
').addClass('tiny_cursive-text-block').append(\n $('
').attr('id', 'tiny_cursive-reconstructed_text').html(JSON.parse(submittedText))\n );\n\n contents.append(commentBox, $legend, textBlock2);\n $('#content' + userid).html(contents); // Update content.\n }).fail(error => {\n window.console.error(\"Failed to load language strings:\", error);\n $('#content' + userid).html(nodata);\n });\n } else {\n $('#content' + userid).html(nodata);\n }\n }).fail(error => {\n $('#content' + userid).html(nodata);\n throw new Error('Error loading JSON file: ' + error.message);\n });\n });\n }\n\n replyWriting(userid, filepath, questionid = '', replayInstances = null) {\n $('body').on('click', '#rep' + userid + questionid, function(e) {\n\n if (filepath) {\n $('#replayControls_' + userid + questionid).removeClass('d-none');\n $('#content' + userid).addClass('tiny_cursive_outputElement');\n }\n\n $(this).prop('disabled', true);\n $('#quality' + userid + questionid).prop('disabled', false);\n $('#content' + userid).attr('data-label', 'replay');\n $('#player_' + userid + questionid).css({\n 'display': 'block',\n 'padding-right': '8px'\n });\n e.preventDefault();\n $('#content' + userid).html($('
').addClass('d-flex justify-content-center my-5')\n .append($('
').addClass('tiny_cursive-loader')));\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $(this).addClass('active'); // Add 'active' class to the clicked element\n if (replayInstances && replayInstances[userid]) {\n replayInstances[userid].stopReplay();\n }\n if (questionid) {\n // eslint-disable-next-line\n video_playback(userid, filepath, questionid);\n } else {\n // eslint-disable-next-line\n video_playback(userid, filepath);\n }\n });\n }\n\n learnMore(moreBtn, context, userid, questionid, replayInstances) {\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n moreBtn.addClass('active');\n $('#rep' + userid + questionid).prop('disabled', false);\n if (replayInstances && replayInstances[userid]) {\n replayInstances[userid].stopReplay();\n }\n $('#content' + userid + questionid).removeClass('tiny_cursive_outputElement');\n $('#replayControls_' + userid + questionid).addClass('d-none');\n template.render('tiny_cursive/learn_more', context).then(function(html) {\n $('#content' + userid + questionid).html(html);\n return true;\n }).fail(function(error) {\n window.console.error(\"Failed to render template:\", error);\n });\n }\n\n formatedTime(data) {\n if (data.total_time_seconds) {\n let totalTimeSeconds = data.total_time_seconds;\n let hours = Math.floor(totalTimeSeconds / 3600).toString().padStart(2, 0);\n let minutes = Math.floor((totalTimeSeconds % 3600) / 60).toString().padStart(2, 0);\n let seconds = (totalTimeSeconds % 60).toString().padStart(2, 0);\n return `${hours}h ${minutes}m ${seconds}s`;\n } else {\n return \"0h 0m 0s\";\n }\n }\n\n authorshipStatus(firstFile, score, scoreSetting) {\n var icon = 'fa fa-circle-o';\n var color = 'font-size:32px;color:black';\n score = parseFloat(score);\n\n if (firstFile) {\n icon = 'fa fa-solid fa-info-circle';\n color = 'font-size:32px;color:#000000';\n } else if (score >= scoreSetting) {\n icon = 'fa fa-check-circle';\n color = 'font-size:32px;color:green';\n }\n if (score < scoreSetting) {\n icon = 'fa fa-question-circle';\n color = 'font-size:32px;color:#A9A9A9';\n return $('').addClass(icon).attr('style', color).attr('title', localStorage.getItem('notenoughtinfo'));\n } else {\n return $('').addClass(icon).attr('style', color);\n }\n\n }\n}\n"],"names":["constructor","then","str","localStorage","setItem","catch","error","window","console","log","createModal","userid","context","questionid","replayInstances","authIcon","self","this","on","e","preventDefault","isReplayButton","find","length","create","templateContext","modal","html","show","setTimeout","removeClass","replayTab","trigger","addClass","moreBtn","prop","css","learnMore","analytics","templates","attr","append","stopReplay","render","fail","checkDiff","fileid","nodata","document","createElement","classList","add","textContent","Error","methodname","args","done","response","responsedata","JSON","parse","data","submittedText","atob","submitted_text","key","component","strings","originalTextString","editsPastesAIString","commentBox","pasteCountDiv","commentscount","commentsDiv","commentsList","comments","index","commentDiv","text","usercomment","$legend","$attributedItem","$attributedBox","$attributedText","$unattributedItem","$unattributedBox","$unattributedText","contents","textBlock2","message","replyWriting","filepath","video_playback","formatedTime","total_time_seconds","totalTimeSeconds","hours","Math","floor","toString","padStart","minutes","seconds","authorshipStatus","firstFile","score","scoreSetting","icon","color","parseFloat","getItem"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/analytic_modal.min.js b/amd/build/analytic_modal.min.js index b0b20b2d..7444b4d6 100644 --- a/amd/build/analytic_modal.min.js +++ b/amd/build/analytic_modal.min.js @@ -1,10 +1,3 @@ -define("tiny_cursive/analytic_modal",["exports","core/modal","jquery"],(function(_exports,_modal,_jquery){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} -/** - * This module defines a custom modal for analytics. - * - * @module tiny_cursive/analytic_modal - * @copyright 2024 CTI - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=_interopRequireDefault(_modal),_jquery=_interopRequireDefault(_jquery);class MyModal extends _modal.default{static TYPE="tiny_cursive/analytics_modal";static TEMPLATE="tiny_cursive/analytics_modal";configure(modalConfig){modalConfig.show=!0,modalConfig.removeOnClose=!0,modalConfig.backdrop=!0,super.configure(modalConfig)}show(){super.show();const root=this.getRoot();root.find(".modal-header").remove(),root.find(".modal-content").css({"border-radius":"30px"}).addClass("shadow-none border-none"),root.find(".modal-body").css({padding:"0","border-radius":"30px",overflow:"auto","scrollbar-width":"none"}),root.find(".modal-dialog").css({"max-width":"800px","background-color":"transparent"}),root.find("#analytic-close").on("click",(()=>{this.destroy()})),root.on("click",(e=>{(0,_jquery.default)(e.target).hasClass("modal")&&this.destroy()}))}}return _exports.default=MyModal,_exports.default})); +define("tiny_cursive/analytic_modal",["exports","core/modal","jquery"],(function(_exports,_modal,_jquery){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=_interopRequireDefault(_modal),_jquery=_interopRequireDefault(_jquery);class MyModal extends _modal.default{configure(modalConfig){modalConfig.show=!0,modalConfig.removeOnClose=!0,modalConfig.backdrop=!0,super.configure(modalConfig)}show(){super.show();const root=this.getRoot();root.find(".modal-header").remove(),root.find(".modal-content").css({"border-radius":"30px"}).addClass("shadow-none border-none"),root.find(".modal-body").css({padding:"0","border-radius":"30px",overflow:"auto","scrollbar-width":"none"}),root.find(".modal-dialog").css({"max-width":"800px","background-color":"transparent"}),root.find("#analytic-close").on("click",(()=>{this.destroy()})),root.on("click",(e=>{(0,_jquery.default)(e.target).hasClass("modal")&&this.destroy()}))}}return _exports.default=MyModal,_defineProperty(MyModal,"TYPE","tiny_cursive/analytics_modal"),_defineProperty(MyModal,"TEMPLATE","tiny_cursive/analytics_modal"),_exports.default})); //# sourceMappingURL=analytic_modal.min.js.map \ No newline at end of file diff --git a/amd/build/analytic_modal.min.js.map b/amd/build/analytic_modal.min.js.map index 068d5e6b..0e95843c 100644 --- a/amd/build/analytic_modal.min.js.map +++ b/amd/build/analytic_modal.min.js.map @@ -1 +1 @@ -{"version":3,"file":"analytic_modal.min.js","sources":["../src/analytic_modal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module defines a custom modal for analytics.\n *\n * @module tiny_cursive/analytic_modal\n * @copyright 2024 CTI \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport $ from 'jquery';\nexport default class MyModal extends Modal {\n static TYPE = \"tiny_cursive/analytics_modal\";\n static TEMPLATE = \"tiny_cursive/analytics_modal\";\n\n configure(modalConfig) {\n // Show this modal on instantiation.\n modalConfig.show = true;\n\n // Remove from the DOM on close.\n modalConfig.removeOnClose = true;\n modalConfig.backdrop = true;\n\n // Call the parent configure method.\n super.configure(modalConfig);\n }\n\n // Override the parent show method to add custom behavior.\n show() {\n super.show();\n\n const root = this.getRoot();\n\n\n // Hide the default modal header.\n root.find('.modal-header').remove();\n\n root.find('.modal-content').css({\n 'border-radius': '30px'\n }).addClass('shadow-none border-none');\n // Remove padding from the modal content.\n root.find('.modal-body').css({\n 'padding': '0',\n 'border-radius': '30px',\n 'overflow': 'auto',\n 'scrollbar-width': 'none'\n });\n root.find('.modal-dialog').css({\n 'max-width': '800px',\n 'background-color': 'transparent'\n });\n\n // Ensure modal closes on 'analytic-close' button click.\n root.find('#analytic-close').on('click', () => {\n this.destroy();\n });\n\n // Ensure modal closes on backdrop click.\n root.on('click', (e) => {\n if ($(e.target).hasClass('modal')) {\n this.destroy();\n }\n });\n }\n}\n"],"names":["MyModal","Modal","configure","modalConfig","show","removeOnClose","backdrop","root","this","getRoot","find","remove","css","addClass","on","destroy","e","target","hasClass"],"mappings":";;;;;;;yKAyBqBA,gBAAgBC,2BACnB,+CACI,+BAElBC,UAAUC,aAENA,YAAYC,MAAO,EAGnBD,YAAYE,eAAgB,EAC5BF,YAAYG,UAAW,QAGjBJ,UAAUC,aAIpBC,aACUA,aAEAG,KAAOC,KAAKC,UAIlBF,KAAKG,KAAK,iBAAiBC,SAE3BJ,KAAKG,KAAK,kBAAkBE,IAAI,iBACX,SAClBC,SAAS,2BAEZN,KAAKG,KAAK,eAAeE,IAAI,SACd,oBACM,gBACL,yBACO,SAEvBL,KAAKG,KAAK,iBAAiBE,IAAI,aACd,2BACO,gBAIxBL,KAAKG,KAAK,mBAAmBI,GAAG,SAAS,UAChCC,aAITR,KAAKO,GAAG,SAAUE,KACV,mBAAEA,EAAEC,QAAQC,SAAS,eAChBH"} \ No newline at end of file +{"version":3,"file":"analytic_modal.min.js","sources":["../src/analytic_modal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module defines a custom modal for analytics.\n *\n * @module tiny_cursive/analytic_modal\n * @copyright 2024 CTI \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport $ from 'jquery';\nexport default class MyModal extends Modal {\n static TYPE = \"tiny_cursive/analytics_modal\";\n static TEMPLATE = \"tiny_cursive/analytics_modal\";\n\n configure(modalConfig) {\n // Show this modal on instantiation.\n modalConfig.show = true;\n\n // Remove from the DOM on close.\n modalConfig.removeOnClose = true;\n modalConfig.backdrop = true;\n\n // Call the parent configure method.\n super.configure(modalConfig);\n }\n\n // Override the parent show method to add custom behavior.\n show() {\n super.show();\n\n const root = this.getRoot();\n\n\n // Hide the default modal header.\n root.find('.modal-header').remove();\n\n root.find('.modal-content').css({\n 'border-radius': '30px'\n }).addClass('shadow-none border-none');\n // Remove padding from the modal content.\n root.find('.modal-body').css({\n 'padding': '0',\n 'border-radius': '30px',\n 'overflow': 'auto',\n 'scrollbar-width': 'none'\n });\n root.find('.modal-dialog').css({\n 'max-width': '800px',\n 'background-color': 'transparent'\n });\n\n // Ensure modal closes on 'analytic-close' button click.\n root.find('#analytic-close').on('click', () => {\n this.destroy();\n });\n\n // Ensure modal closes on backdrop click.\n root.on('click', (e) => {\n if ($(e.target).hasClass('modal')) {\n this.destroy();\n }\n });\n }\n}\n"],"names":["MyModal","Modal","configure","modalConfig","show","removeOnClose","backdrop","root","this","getRoot","find","remove","css","addClass","on","destroy","e","target","hasClass"],"mappings":"ogBAyBqBA,gBAAgBC,eAIjCC,UAAUC,aAENA,YAAYC,MAAO,EAGnBD,YAAYE,eAAgB,EAC5BF,YAAYG,UAAW,QAGjBJ,UAAUC,aAIpBC,aACUA,aAEAG,KAAOC,KAAKC,UAIlBF,KAAKG,KAAK,iBAAiBC,SAE3BJ,KAAKG,KAAK,kBAAkBE,IAAI,iBACX,SAClBC,SAAS,2BAEZN,KAAKG,KAAK,eAAeE,IAAI,SACd,oBACM,gBACL,yBACO,SAEvBL,KAAKG,KAAK,iBAAiBE,IAAI,aACd,2BACO,gBAIxBL,KAAKG,KAAK,mBAAmBI,GAAG,SAAS,UAChCC,aAITR,KAAKO,GAAG,SAAUE,KACV,mBAAEA,EAAEC,QAAQC,SAAS,eAChBH,8DAjDAf,eACH,gDADGA,mBAEC"} \ No newline at end of file diff --git a/amd/build/append_lesson_grade_table.min.js b/amd/build/append_lesson_grade_table.min.js index 5f481a66..52a4b6ae 100644 --- a/amd/build/append_lesson_grade_table.min.js +++ b/amd/build/append_lesson_grade_table.min.js @@ -7,6 +7,6 @@ define("tiny_cursive/append_lesson_grade_table",["exports","./replay","jquery"," * @module tiny_cursive/append_lesson_grade_table * @copyright 2025 CTI * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_replay=_interopRequireDefault(_replay),_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_analytic_events=_interopRequireDefault(_analytic_events),_analytic_button=_interopRequireDefault(_analytic_button),_replay_button=_interopRequireDefault(_replay_button),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str);_exports.init=(scoreSetting,showcomment,hasApiKey)=>{const replayInstances={};window.video_playback=function(mid,filepath){if(""!==filepath){const replay=new _replay.default("content"+mid,filepath,10,!1,"player_"+mid);replayInstances[mid]=replay}else _templates.default.render("tiny_cursive/no_submission").then((html=>((0,_jquery.default)("#content"+mid).html(html),!0))).catch((e=>window.console.error(e)));return!1};var cmid=M.cfg.contextInstanceId,emailLink=(0,_jquery.default)('#page-content div[role="main"] td.lastcol a'),headcolumn=(0,_jquery.default)('#region-main div[role="main"] table thead tr');let url=new URL(window.location.href),mode=url.searchParams.get("mode"),user=url.searchParams.get("user");function analytics(userid,cmid,$emailLink,grade){let args={id:userid,modulename:"lesson",cmid:cmid},com=(0,_ajax.call)([{methodname:"cursive_get_lesson_submission_data",args:args}]);com[0].done((function(json){var data=JSON.parse(json),filepath="";data.res.filename&&(filepath=data.res.filename);let analyticButtonDiv=document.createElement("div"),analyticsColumn=document.createElement("td");hasApiKey?analyticButtonDiv.append((0,_analytic_button.default)(data.res.effort_ratio,userid)):(0,_jquery.default)(analyticButtonDiv).html((0,_replay_button.default)(userid)),analyticButtonDiv.dataset.region="analytic-div"+userid,analyticsColumn.append(analyticButtonDiv),grade?(analyticButtonDiv.classList.add("w-100"),(0,_jquery.default)("#fitem_id_response_editor .felement").prepend(analyticButtonDiv)):$emailLink.closest("tr").find("td:eq(1)").after(analyticsColumn);let myEvents=new _analytic_events.default;var context={tabledata:data.res,formattime:myEvents.formatedTime(data.res),page:scoreSetting,userid:userid,apikey:hasApiKey};let authIcon=myEvents.authorshipStatus(data.res.first_file,data.res.score,scoreSetting);myEvents.createModal(userid,context,"",replayInstances,authIcon),myEvents.analytics(userid,_templates.default,context,"",replayInstances,authIcon),myEvents.checkDiff(userid,data.res.file_id,"",replayInstances),myEvents.replyWriting(userid,filepath,"",replayInstances)})),com[0].fail((error=>{window.console.error("Error getting cursive config:",error)}))}Str.get_string("analytics","tiny_cursive").then((strs=>(headcolumn.each((function(){(0,_jquery.default)(this).find("th:eq(1)").after(`${strs}`)})),!0))).catch((e=>window.console.error(e))),mode&&"grade"===mode&&analytics(user,cmid,"",!0),emailLink.each((function(){let href=(0,_jquery.default)(this).attr("href");const $emailLink=(0,_jquery.default)(this);let userid=0;href&&(userid=parseInt(new URLSearchParams(href.split("?")[1]).get("userid")),userid?((0,_jquery.default)("#region-main").on("click","table tbody tr td.cell.c1 a",(function(e){e.preventDefault();const link=e.target.href,url=new URL(link);url.searchParams.append("user",userid),window.location.href=url.toString()})),analytics(userid,cmid,$emailLink,!1)):$emailLink.closest("tr").find("td:eq(1)").after(""))}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_replay=_interopRequireDefault(_replay),_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_analytic_events=_interopRequireDefault(_analytic_events),_analytic_button=_interopRequireDefault(_analytic_button),_replay_button=_interopRequireDefault(_replay_button),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str);_exports.init=(scoreSetting,showcomment,hasApiKey)=>{const replayInstances={};window.video_playback=function(mid,filepath){if(""!==filepath){const replay=new _replay.default("content"+mid,filepath,10,!1,"player_"+mid);replayInstances[mid]=replay}else _templates.default.render("tiny_cursive/no_submission").then((html=>((0,_jquery.default)("#content"+mid).html(html),!0))).catch((e=>window.console.error(e)));return!1};var cmid=M.cfg.contextInstanceId,emailLink=(0,_jquery.default)('#page-content div[role="main"] td.lastcol a'),headcolumn=(0,_jquery.default)('#region-main div[role="main"] table thead tr');let url=new URL(window.location.href),mode=url.searchParams.get("mode"),user=url.searchParams.get("user");function analytics(userid,cmid,$emailLink,grade){let args={id:userid,modulename:"lesson",cmid:cmid},com=(0,_ajax.call)([{methodname:"cursive_get_lesson_submission_data",args:args}]);com[0].done((function(json){var data=JSON.parse(json),filepath="";data.res.filename&&(filepath=data.res.filename);let analyticButtonDiv=document.createElement("div"),analyticsColumn=document.createElement("td");hasApiKey?analyticButtonDiv.append((0,_analytic_button.default)(data.res.effort_ratio,userid)):(0,_jquery.default)(analyticButtonDiv).html((0,_replay_button.default)(userid)),analyticButtonDiv.dataset.region="analytic-div"+userid,analyticsColumn.append(analyticButtonDiv),grade?(analyticButtonDiv.classList.add("w-100"),(0,_jquery.default)("#fitem_id_response_editor .felement").prepend(analyticButtonDiv)):$emailLink.closest("tr").find("td:eq(1)").after(analyticsColumn);let myEvents=new _analytic_events.default;var context={tabledata:data.res,formattime:myEvents.formatedTime(data.res),page:scoreSetting,userid:userid,apikey:hasApiKey};let authIcon=myEvents.authorshipStatus(data.res.first_file,data.res.score,scoreSetting);myEvents.createModal(userid,context,"",replayInstances,authIcon),myEvents.analytics(userid,_templates.default,context,"",replayInstances,authIcon),myEvents.checkDiff(userid,data.res.file_id,"",replayInstances),myEvents.replyWriting(userid,filepath,"",replayInstances)})),com[0].fail((error=>{window.console.error("Error getting cursive config:",error)}))}Str.get_string("analytics","tiny_cursive").then((strs=>(headcolumn.each((function(){(0,_jquery.default)(this).find("th:eq(1)").after(''.concat(strs,""))})),!0))).catch((e=>window.console.error(e))),mode&&"grade"===mode&&analytics(user,cmid,"",!0),emailLink.each((function(){let href=(0,_jquery.default)(this).attr("href");const $emailLink=(0,_jquery.default)(this);let userid=0;href&&(userid=parseInt(new URLSearchParams(href.split("?")[1]).get("userid")),userid?((0,_jquery.default)("#region-main").on("click","table tbody tr td.cell.c1 a",(function(e){e.preventDefault();const link=e.target.href,url=new URL(link);url.searchParams.append("user",userid),window.location.href=url.toString()})),analytics(userid,cmid,$emailLink,!1)):$emailLink.closest("tr").find("td:eq(1)").after(""))}))}})); //# sourceMappingURL=append_lesson_grade_table.min.js.map \ No newline at end of file diff --git a/amd/build/append_lesson_grade_table.min.js.map b/amd/build/append_lesson_grade_table.min.js.map index ac5fede6..6ad80de5 100644 --- a/amd/build/append_lesson_grade_table.min.js.map +++ b/amd/build/append_lesson_grade_table.min.js.map @@ -1 +1 @@ -{"version":3,"file":"append_lesson_grade_table.min.js","sources":["../src/append_lesson_grade_table.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module to append analytics and grade table data for lesson submissions\n * Handles the display of analytics, replay functionality and grade information\n * for lesson submissions in the Moodle gradebook interface\n *\n * @module tiny_cursive/append_lesson_grade_table\n * @copyright 2025 CTI \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Replay from './replay';\nimport $ from 'jquery';\nimport {call as getData} from 'core/ajax';\nimport templates from 'core/templates';\nimport AnalyticEvents from './analytic_events';\nimport analyticButton from './analytic_button';\nimport replayButton from './replay_button';\nimport * as Str from 'core/str';\n\nexport const init = (scoreSetting, showcomment, hasApiKey) => {\n const replayInstances = {};\n // eslint-disable-next-line camelcase\n window.video_playback = function(mid, filepath) {\n if (filepath !== '') {\n const replay = new Replay(\n 'content' + mid,\n filepath,\n 10,\n false,\n 'player_' + mid\n );\n replayInstances[mid] = replay;\n } else {\n templates.render('tiny_cursive/no_submission').then(html => {\n $('#content' + mid).html(html);\n return true;\n }).catch(e => window.console.error(e));\n }\n return false;\n\n };\n\n\n var cmid = M.cfg.contextInstanceId;\n var emailLink = $('#page-content div[role=\"main\"] td.lastcol a');\n var headcolumn = $('#region-main div[role=\"main\"] table thead tr');\n let url = new URL(window.location.href);\n let mode = url.searchParams.get('mode');\n let user = url.searchParams.get('user');\n\n Str.get_string('analytics', 'tiny_cursive').then((strs) => {\n headcolumn.each(function() {\n $(this).find('th:eq(1)').after(`${strs}`);\n });\n return true;\n }).catch(e => window.console.error(e));\n\n if (mode && mode === \"grade\") {\n analytics(user, cmid, \"\", true);\n }\n\n emailLink.each(function() {\n let href = $(this).attr('href');\n const $emailLink = $(this);\n let userid = 0;\n if (href) {\n userid = parseInt(new URLSearchParams(href.split('?')[1]).get('userid'));\n if (!userid) {\n $emailLink.closest('tr').find('td:eq(1)').after(\"\"); // For aligning the table column\n } else {\n\n $('#region-main').on('click', 'table tbody tr td.cell.c1 a', function(e) {\n e.preventDefault();\n const link = e.target.href;\n const url = new URL(link);\n url.searchParams.append('user', userid);\n window.location.href = url.toString();\n });\n\n analytics(userid, cmid, $emailLink, false);\n }\n }\n });\n\n /**\n * Fetches and displays analytics data for lesson submissions\n * @param {number} userid - The ID of the user whose analytics to fetch\n * @param {number} cmid - The course module ID\n * @param {jQuery|string} $emailLink - jQuery object of email link or empty string\n * @param {boolean} grade - Whether this is being called from grade view\n */\n function analytics(userid, cmid, $emailLink, grade) {\n\n let args = {id: userid, modulename: \"lesson\", cmid: cmid};\n let methodname = 'cursive_get_lesson_submission_data';\n let com = getData([{methodname, args}]);\n com[0].done(function(json) {\n var data = JSON.parse(json);\n var filepath = '';\n if (data.res.filename) {\n filepath = data.res.filename;\n }\n\n let analyticButtonDiv = document.createElement('div');\n let analyticsColumn = document.createElement('td');\n\n if (!hasApiKey) {\n $(analyticButtonDiv).html(replayButton(userid));\n } else {\n analyticButtonDiv.append(analyticButton(data.res.effort_ratio, userid));\n }\n\n analyticButtonDiv.dataset.region = \"analytic-div\" + userid;\n analyticsColumn.append(analyticButtonDiv);\n if (grade) {\n analyticButtonDiv.classList.add('w-100');\n $('#fitem_id_response_editor .felement').prepend(analyticButtonDiv);\n } else {\n $emailLink.closest('tr').find('td:eq(1)').after(analyticsColumn);\n }\n\n\n let myEvents = new AnalyticEvents();\n var context = {\n tabledata: data.res,\n formattime: myEvents.formatedTime(data.res),\n page: scoreSetting,\n userid: userid,\n apikey: hasApiKey\n };\n\n let authIcon = myEvents.authorshipStatus(data.res.first_file, data.res.score, scoreSetting);\n myEvents.createModal(userid, context, '', replayInstances, authIcon);\n myEvents.analytics(userid, templates, context, '', replayInstances, authIcon);\n myEvents.checkDiff(userid, data.res.file_id, '', replayInstances);\n myEvents.replyWriting(userid, filepath, '', replayInstances);\n\n });\n com[0].fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n }\n};"],"names":["scoreSetting","showcomment","hasApiKey","replayInstances","window","video_playback","mid","filepath","replay","Replay","render","then","html","catch","e","console","error","cmid","M","cfg","contextInstanceId","emailLink","headcolumn","url","URL","location","href","mode","searchParams","get","user","analytics","userid","$emailLink","grade","args","id","modulename","com","methodname","done","json","data","JSON","parse","res","filename","analyticButtonDiv","document","createElement","analyticsColumn","append","effort_ratio","dataset","region","classList","add","prepend","closest","find","after","myEvents","AnalyticEvents","context","tabledata","formattime","formatedTime","page","apikey","authIcon","authorshipStatus","first_file","score","createModal","templates","checkDiff","file_id","replyWriting","fail","Str","get_string","strs","each","this","attr","parseInt","URLSearchParams","split","on","preventDefault","link","target","toString"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"append_lesson_grade_table.min.js","sources":["../src/append_lesson_grade_table.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module to append analytics and grade table data for lesson submissions\n * Handles the display of analytics, replay functionality and grade information\n * for lesson submissions in the Moodle gradebook interface\n *\n * @module tiny_cursive/append_lesson_grade_table\n * @copyright 2025 CTI \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Replay from './replay';\nimport $ from 'jquery';\nimport {call as getData} from 'core/ajax';\nimport templates from 'core/templates';\nimport AnalyticEvents from './analytic_events';\nimport analyticButton from './analytic_button';\nimport replayButton from './replay_button';\nimport * as Str from 'core/str';\n\nexport const init = (scoreSetting, showcomment, hasApiKey) => {\n const replayInstances = {};\n // eslint-disable-next-line camelcase\n window.video_playback = function(mid, filepath) {\n if (filepath !== '') {\n const replay = new Replay(\n 'content' + mid,\n filepath,\n 10,\n false,\n 'player_' + mid\n );\n replayInstances[mid] = replay;\n } else {\n templates.render('tiny_cursive/no_submission').then(html => {\n $('#content' + mid).html(html);\n return true;\n }).catch(e => window.console.error(e));\n }\n return false;\n\n };\n\n\n var cmid = M.cfg.contextInstanceId;\n var emailLink = $('#page-content div[role=\"main\"] td.lastcol a');\n var headcolumn = $('#region-main div[role=\"main\"] table thead tr');\n let url = new URL(window.location.href);\n let mode = url.searchParams.get('mode');\n let user = url.searchParams.get('user');\n\n Str.get_string('analytics', 'tiny_cursive').then((strs) => {\n headcolumn.each(function() {\n $(this).find('th:eq(1)').after(`${strs}`);\n });\n return true;\n }).catch(e => window.console.error(e));\n\n if (mode && mode === \"grade\") {\n analytics(user, cmid, \"\", true);\n }\n\n emailLink.each(function() {\n let href = $(this).attr('href');\n const $emailLink = $(this);\n let userid = 0;\n if (href) {\n userid = parseInt(new URLSearchParams(href.split('?')[1]).get('userid'));\n if (!userid) {\n $emailLink.closest('tr').find('td:eq(1)').after(\"\"); // For aligning the table column\n } else {\n\n $('#region-main').on('click', 'table tbody tr td.cell.c1 a', function(e) {\n e.preventDefault();\n const link = e.target.href;\n const url = new URL(link);\n url.searchParams.append('user', userid);\n window.location.href = url.toString();\n });\n\n analytics(userid, cmid, $emailLink, false);\n }\n }\n });\n\n /**\n * Fetches and displays analytics data for lesson submissions\n * @param {number} userid - The ID of the user whose analytics to fetch\n * @param {number} cmid - The course module ID\n * @param {jQuery|string} $emailLink - jQuery object of email link or empty string\n * @param {boolean} grade - Whether this is being called from grade view\n */\n function analytics(userid, cmid, $emailLink, grade) {\n\n let args = {id: userid, modulename: \"lesson\", cmid: cmid};\n let methodname = 'cursive_get_lesson_submission_data';\n let com = getData([{methodname, args}]);\n com[0].done(function(json) {\n var data = JSON.parse(json);\n var filepath = '';\n if (data.res.filename) {\n filepath = data.res.filename;\n }\n\n let analyticButtonDiv = document.createElement('div');\n let analyticsColumn = document.createElement('td');\n\n if (!hasApiKey) {\n $(analyticButtonDiv).html(replayButton(userid));\n } else {\n analyticButtonDiv.append(analyticButton(data.res.effort_ratio, userid));\n }\n\n analyticButtonDiv.dataset.region = \"analytic-div\" + userid;\n analyticsColumn.append(analyticButtonDiv);\n if (grade) {\n analyticButtonDiv.classList.add('w-100');\n $('#fitem_id_response_editor .felement').prepend(analyticButtonDiv);\n } else {\n $emailLink.closest('tr').find('td:eq(1)').after(analyticsColumn);\n }\n\n\n let myEvents = new AnalyticEvents();\n var context = {\n tabledata: data.res,\n formattime: myEvents.formatedTime(data.res),\n page: scoreSetting,\n userid: userid,\n apikey: hasApiKey\n };\n\n let authIcon = myEvents.authorshipStatus(data.res.first_file, data.res.score, scoreSetting);\n myEvents.createModal(userid, context, '', replayInstances, authIcon);\n myEvents.analytics(userid, templates, context, '', replayInstances, authIcon);\n myEvents.checkDiff(userid, data.res.file_id, '', replayInstances);\n myEvents.replyWriting(userid, filepath, '', replayInstances);\n\n });\n com[0].fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n }\n};"],"names":["scoreSetting","showcomment","hasApiKey","replayInstances","window","video_playback","mid","filepath","replay","Replay","render","then","html","catch","e","console","error","cmid","M","cfg","contextInstanceId","emailLink","headcolumn","url","URL","location","href","mode","searchParams","get","user","analytics","userid","$emailLink","grade","args","id","modulename","com","methodname","done","json","data","JSON","parse","res","filename","analyticButtonDiv","document","createElement","analyticsColumn","append","effort_ratio","dataset","region","classList","add","prepend","closest","find","after","myEvents","AnalyticEvents","context","tabledata","formattime","formatedTime","page","apikey","authIcon","authorshipStatus","first_file","score","createModal","templates","checkDiff","file_id","replyWriting","fail","Str","get_string","strs","each","this","attr","parseInt","URLSearchParams","split","on","preventDefault","link","target","toString"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/autosaver.min.js b/amd/build/autosaver.min.js index 13940655..045da664 100644 --- a/amd/build/autosaver.min.js +++ b/amd/build/autosaver.min.js @@ -1,3 +1,3 @@ -define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return{resourceId:resourceId,name:modulename}}(ur,new URL(ur),MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;"assign"!==modulename&&(PASTE_SETTING="cite_source");const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:`
${title}
\n ${titledes}
`,body:``,removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename=`${userid}_${resourceId}_${cmid}_${modulename}_attempt`,"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename=`${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey=`${userid}_${resourceId}_${cmid}_position`;let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename);let originalText=editor.getContent({format:"text"});try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector(`#${elementId}`)&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector(`#${elementId}`)||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId=`tiny_cursive_tooltip${index}`;tooltipText.then((text=>setTooltip(text,document.querySelector(`#${elementId}`),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)(`#${elementId}`).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)(`#${tooltipId}`).css(_common.tooltipCss)})),(0,_jquery.default)(`#${elementId}`).on("mouseleave",(function(){(0,_jquery.default)(`#${tooltipId}`).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector(`#${tooltipId}`)&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); +define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return{resourceId:resourceId,name:modulename}}(ur,new URL(ur),MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:'
'.concat(title,'
\n ').concat(titledes,"
"),body:''),removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename="".concat(userid,"_").concat(resourceId,"_").concat(cmid,"_").concat(modulename,"_attempt"),"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename="".concat(userid,"_").concat(resourceId,"_").concat(cmid,"_").concat(questionid,"_").concat(modulename,"_attempt")),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey="".concat(userid,"_").concat(resourceId,"_").concat(cmid,"_position");let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename);let originalText=editor.getContent({format:"text"});try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector("#".concat(elementId))&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector("#".concat(elementId))||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId="tiny_cursive_tooltip".concat(index);tooltipText.then((text=>setTooltip(text,document.querySelector("#".concat(elementId)),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)("#".concat(elementId)).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)("#".concat(tooltipId)).css(_common.tooltipCss)})),(0,_jquery.default)("#".concat(elementId)).on("mouseleave",(function(){(0,_jquery.default)("#".concat(tooltipId)).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector("#".concat(tooltipId))&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); //# sourceMappingURL=autosaver.min.js.map \ No newline at end of file diff --git a/amd/build/autosaver.min.js.map b/amd/build/autosaver.min.js.map index 3282341f..52f813c1 100644 --- a/amd/build/autosaver.min.js.map +++ b/amd/build/autosaver.min.js.map @@ -1 +1 @@ -{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (modulename !== 'assign') {\n PASTE_SETTING = 'cite_source';\n }\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n let originalText = editor.getContent({format: 'text'});\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","modulesInfo","parm","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","name","getModulesInfo","URL","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","e","preventDefault","syncData","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","document","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","originalText","getContent","format","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","className","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","querySelector","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","addEventListener","setInterval"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n let originalText = editor.getContent({format: 'text'});\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","modulesInfo","parm","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","name","getModulesInfo","URL","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","e","preventDefault","syncData","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","document","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","originalText","getContent","format","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","className","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","querySelector","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","addEventListener","setInterval"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/common.min.js b/amd/build/common.min.js index c2b8ed1c..408c6660 100644 --- a/amd/build/common.min.js +++ b/amd/build/common.min.js @@ -1,3 +1,3 @@ -define("tiny_cursive/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var _default={component:"tiny_cursive",pluginName:"tiny_cursive/plugin",iconUrl:M.util.image_url("cursive","tiny_cursive"),iconSaving:M.util.image_url("rotate","tiny_cursive"),iconGrayUrl:M.util.image_url("cursive_gray","tiny_cursive"),tooltipCss:{display:"block",position:"absolute",transform:"translateX(-100%)",backgroundColor:"white",color:"black",border:"1px solid #ccc",marginBottom:"6px",padding:"10px",textAlign:"justify",minWidth:"200px",borderRadius:"1px",pointerEvents:"none",zIndex:1e4}};return _exports.default=_default,_exports.default})); +define("tiny_cursive/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var _default={component:"tiny_cursive",pluginName:"".concat("tiny_cursive","/plugin"),iconUrl:M.util.image_url("cursive","tiny_cursive"),iconSaving:M.util.image_url("rotate","tiny_cursive"),iconGrayUrl:M.util.image_url("cursive_gray","tiny_cursive"),tooltipCss:{display:"block",position:"absolute",transform:"translateX(-100%)",backgroundColor:"white",color:"black",border:"1px solid #ccc",marginBottom:"6px",padding:"10px",textAlign:"justify",minWidth:"200px",borderRadius:"1px",pointerEvents:"none",zIndex:1e4}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=common.min.js.map \ No newline at end of file diff --git a/amd/build/common.min.js.map b/amd/build/common.min.js.map index ddc7a5f5..a8da292b 100644 --- a/amd/build/common.min.js.map +++ b/amd/build/common.min.js.map @@ -1 +1 @@ -{"version":3,"file":"common.min.js","sources":["../src/common.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/common\n * @category TinyMCE Editor\n * @copyright 2025 CTI \n * @author Brain Station 23 \n */\n\n\nconst component = 'tiny_cursive';\n\nexport default {\n component,\n pluginName: `${component}/plugin`,\n iconUrl: M.util.image_url('cursive', 'tiny_cursive'),\n iconSaving: M.util.image_url('rotate', 'tiny_cursive'),\n iconGrayUrl: M.util.image_url('cursive_gray', 'tiny_cursive'),\n tooltipCss: {\n display: 'block',\n position: 'absolute',\n transform: 'translateX(-100%)',\n backgroundColor: 'white',\n color: 'black',\n border: '1px solid #ccc',\n marginBottom: '6px',\n padding: '10px',\n textAlign: 'justify',\n minWidth: '200px',\n borderRadius: '1px',\n pointerEvents: 'none',\n zIndex: 10000\n }\n};\n"],"names":["component","pluginName","iconUrl","M","util","image_url","iconSaving","iconGrayUrl","tooltipCss","display","position","transform","backgroundColor","color","border","marginBottom","padding","textAlign","minWidth","borderRadius","pointerEvents","zIndex"],"mappings":"0JAyBe,CACXA,UAHc,eAIdC,WAAa,sBACbC,QAASC,EAAEC,KAAKC,UAAU,UAAW,gBACrCC,WAAYH,EAAEC,KAAKC,UAAU,SAAU,gBACvCE,YAAaJ,EAAEC,KAAKC,UAAU,eAAgB,gBAC9CG,WAAY,CACRC,QAAS,QACTC,SAAU,WACVC,UAAW,oBACXC,gBAAiB,QACjBC,MAAO,QACPC,OAAQ,iBACRC,aAAc,MACdC,QAAS,OACTC,UAAW,UACXC,SAAU,QACVC,aAAc,MACdC,cAAe,OACfC,OAAQ"} \ No newline at end of file +{"version":3,"file":"common.min.js","sources":["../src/common.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/common\n * @category TinyMCE Editor\n * @copyright 2025 CTI \n * @author Brain Station 23 \n */\n\n\nconst component = 'tiny_cursive';\n\nexport default {\n component,\n pluginName: `${component}/plugin`,\n iconUrl: M.util.image_url('cursive', 'tiny_cursive'),\n iconSaving: M.util.image_url('rotate', 'tiny_cursive'),\n iconGrayUrl: M.util.image_url('cursive_gray', 'tiny_cursive'),\n tooltipCss: {\n display: 'block',\n position: 'absolute',\n transform: 'translateX(-100%)',\n backgroundColor: 'white',\n color: 'black',\n border: '1px solid #ccc',\n marginBottom: '6px',\n padding: '10px',\n textAlign: 'justify',\n minWidth: '200px',\n borderRadius: '1px',\n pointerEvents: 'none',\n zIndex: 10000\n }\n};\n"],"names":["component","pluginName","iconUrl","M","util","image_url","iconSaving","iconGrayUrl","tooltipCss","display","position","transform","backgroundColor","color","border","marginBottom","padding","textAlign","minWidth","borderRadius","pointerEvents","zIndex"],"mappings":"0JAyBe,CACXA,UAHc,eAIdC,qBAJc,0BAKdC,QAASC,EAAEC,KAAKC,UAAU,UAAW,gBACrCC,WAAYH,EAAEC,KAAKC,UAAU,SAAU,gBACvCE,YAAaJ,EAAEC,KAAKC,UAAU,eAAgB,gBAC9CG,WAAY,CACRC,QAAS,QACTC,SAAU,WACVC,UAAW,oBACXC,gBAAiB,QACjBC,MAAO,QACPC,OAAQ,iBACRC,aAAc,MACdC,QAAS,OACTC,UAAW,UACXC,SAAU,QACVC,aAAc,MACdC,cAAe,OACfC,OAAQ"} \ No newline at end of file diff --git a/amd/build/cursive_autosave.min.js b/amd/build/cursive_autosave.min.js index 2ff57d50..17a4fbb5 100644 --- a/amd/build/cursive_autosave.min.js +++ b/amd/build/cursive_autosave.min.js @@ -1,10 +1,3 @@ -define("tiny_cursive/cursive_autosave",["exports","core/templates","core/ajax","tiny_cursive/svg_repo","core/str"],(function(_exports,_templates,_ajax,_svg_repo,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} -/** - * TODO describe module cursive_autosave - * - * @module tiny_cursive/cursive_autosave - * @copyright 2025 Cursive Technology, Inc. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_svg_repo=_interopRequireDefault(_svg_repo);class CursiveAutosave{static instance=null;constructor(editor,rightWrapper,modules,isFullScreen){if(CursiveAutosave.instance)return CursiveAutosave.instance;this.editor=editor,this.module=modules,this.savingState="",this.rightWrapper=rightWrapper,this.isFullScreen=isFullScreen,this.fetchSavedContent=this.fetchSavedContent.bind(this),this.handleEscapeKey=this.handleEscapeKey.bind(this),this._savingTimer=null,CursiveAutosave.instance=this,this.fetchStrings()}static getInstance(editor,rightWrapper,modules,isFullScreen){this.instance||(this.instance=new CursiveAutosave(editor,rightWrapper,modules,isFullScreen)),this.instance.isFullScreen=isFullScreen;return("quiz"===modules.modulename?document.querySelector(`#tiny_cursive_savingState${modules.questionid}`):document.querySelector("#tiny_cursive_savingState"))||this.instance.init(),this.instance}init(){const stateWrapper=this.cursiveSavingState(this.savingState);stateWrapper.classList.add("tiny_cursive_savingState","btn"),"quiz"===this.module.modulename?stateWrapper.id=`tiny_cursive_savingState${this.module.questionid}`:stateWrapper.id="tiny_cursive_savingState",this.rightWrapper.prepend(stateWrapper),stateWrapper.addEventListener("click",this.fetchSavedContent)}destroy(){CursiveAutosave.instance=null}static destroyInstance(){this.instance&&(this.instance.destroy(),this.instance=null)}cursiveSavingState(state){let wrapperDiv=document.createElement("div"),textSpan=document.createElement("span"),button=document.createElement("button"),iconSpan=document.createElement("span");return button.style.padding=".3rem",textSpan.style.fontSize="0.75rem",textSpan.style.color="gray","quiz"===this.module.modulename?(iconSpan.id=`CursiveCloudIcon${this.module.questionid}`,textSpan.id=`CursiveStateText${this.module.questionid}`):(iconSpan.id="CursiveCloudIcon",textSpan.id="CursiveStateText"),state&&(textSpan.textContent=this.getStateText(state),iconSpan.innerHTML=this.getStateIcon(state)),wrapperDiv.style.verticalAlign="middle",wrapperDiv.appendChild(iconSpan),wrapperDiv.appendChild(textSpan),button.appendChild(wrapperDiv),button}static updateSavingState(state){const instance=this.instance;instance.savingState=state;let stateWrapper=null;stateWrapper="quiz"===instance.module.modulename?document.querySelector(`#tiny_cursive_savingState${instance.module.questionid}`):document.querySelector("#tiny_cursive_savingState");let iconSpan="",stateTextEl="";stateWrapper&&("quiz"===instance.module.modulename?(iconSpan=stateWrapper.querySelector(`#CursiveCloudIcon${instance.module.questionid}`),stateTextEl=stateWrapper.querySelector(`#CursiveStateText${instance.module.questionid}`)):(iconSpan=stateWrapper.querySelector("#CursiveCloudIcon"),stateTextEl=stateWrapper.querySelector("#CursiveStateText")),stateTextEl&&iconSpan&&(stateTextEl.textContent=instance.getStateText(state),iconSpan.innerHTML=instance.getStateIcon(state)),instance._savingTimer&&clearTimeout(instance._savingTimer),"saved"===state&&stateTextEl&&(instance._savingTimer=setTimeout((()=>{stateTextEl.textContent=""}),5e3)))}getStateText(state){const[saving,saved,offline]=this.getText("state");switch(state){case"saving":return saving;case"saved":return saved;case"offline":return offline;default:return""}}getStateIcon(state){switch(state){case"saving":case"saved":return _svg_repo.default.cloudSave;case"offline":return"data:image/svg+xml;base64,"+btoa(_svg_repo.default.offline);default:return""}}async fetchSavedContent(e){var _dropdown$classList,_this$editor;e.preventDefault();let dropdown=document.querySelector("#savedDropdown");if(null==dropdown||null===(_dropdown$classList=dropdown.classList)||void 0===_dropdown$classList?void 0:_dropdown$classList.contains("show"))return void this.closeSavedDropdown();let editorWrapper=null;editorWrapper="quiz"===this.module.modulename?document.querySelector(`#tiny_cursive_savingState${this.module.questionid}`):document.querySelector("#tiny_cursive_savingState");let args={id:this.module.resourceId,cmid:this.module.cmid,modulename:`${this.module.modulename}_autosave`,editorid:null===(_this$editor=this.editor)||void 0===_this$editor?void 0:_this$editor.id,userid:this.module.userid,courseid:this.module.courseid};(0,_ajax.call)([{methodname:"cursive_get_autosave_content",args:args}])[0].done((data=>{let context={comments:JSON.parse(data)};Object.values(context.comments).forEach((content=>{content.time=this.timeAgo(content.timemodified)})),this.renderCommentList(context,editorWrapper)})).fail((error=>{this.throwWarning("fullmodeerrorr",this.editor),window.console.error("Error fetching saved content:",error)}))}toggleSavedDropdown(){var _dropdown$classList2;const dropdown=document.querySelector("#savedDropdown");(null==dropdown||null===(_dropdown$classList2=dropdown.classList)||void 0===_dropdown$classList2?void 0:_dropdown$classList2.contains("show"))?this.closeSavedDropdown():this.openSavedDropdown()}openSavedDropdown(){document.querySelector("#savedDropdown").classList.add("show"),document.removeEventListener("keydown",this.handleEscapeKey),document.addEventListener("keydown",this.handleEscapeKey)}closeSavedDropdown(){const dropdown=document.querySelector("#savedDropdown");dropdown&&(dropdown.classList.remove("show"),dropdown.remove(),document.removeEventListener("keydown",this.handleEscapeKey))}handleEscapeKey(event){"Escape"===event.key&&this.closeSavedDropdown()}timeAgo(unixTime){const seconds=Math.floor(Date.now()/1e3)-unixTime;if(seconds<5)return"just now";if(seconds<60)return`${seconds} sec ago`;const minutes=Math.floor(seconds/60);if(minutes<60)return`${minutes} min ago`;const hours=Math.floor(minutes/60);if(hours<24)return`${hours} hour${hours>1?"s":""} ago`;const days=Math.floor(hours/24);if(days<7)return`${days} day${days>1?"s":""} ago`;const weeks=Math.floor(days/7);if(weeks<4)return`${weeks} week${weeks>1?"s":""} ago`;const months=Math.floor(days/30);if(months<12)return`${months} month${months>1?"s":""} ago`;const years=Math.floor(days/365);return`${years} year${years>1?"s":""} ago`}renderCommentList(context,editorWrapper){_templates.default.render("tiny_cursive/saved_content",context).then((html=>{editorWrapper.style.position="relative";const tempDiv=document.createElement("div");if(tempDiv.innerHTML=html.trim(),tempDiv.id="savedDropdown",tempDiv.classList.add("tiny_cursive-saved-dropdown"),!tempDiv)return window.console.error("Saved content template rendered empty or invalid HTML."),!1;let existingPanel=document.querySelector("#savedDropdown");return existingPanel||(editorWrapper.appendChild(tempDiv),existingPanel=tempDiv),existingPanel.classList.toggle("active"),this.openSavedDropdown(),this.insertSavedItems(this.editor),!0})).catch((error=>window.console.error(error)))}fetchStrings(){localStorage.getItem("state")||Promise.all([(0,_str.get_string)("saving","tiny_cursive"),(0,_str.get_string)("saved","tiny_cursive"),(0,_str.get_string)("offline","tiny_cursive")]).then((function(strings){return localStorage.setItem("state",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}throwWarning(str,editor){(0,_str.get_string)(str,"tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))}getText(key){return JSON.parse(localStorage.getItem(key))}insertSavedItems(editor){document.querySelectorAll(".tiny_cursive-item-preview").forEach((element=>{element.addEventListener("click",(function(){editor.insertContent(" "+this.textContent)}))}))}}return _exports.default=CursiveAutosave,_exports.default})); +define("tiny_cursive/cursive_autosave",["exports","core/templates","core/ajax","tiny_cursive/svg_repo","core/str"],(function(_exports,_templates,_ajax,_svg_repo,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_svg_repo=_interopRequireDefault(_svg_repo);class CursiveAutosave{constructor(editor,rightWrapper,modules,isFullScreen){if(CursiveAutosave.instance)return CursiveAutosave.instance;this.editor=editor,this.module=modules,this.savingState="",this.rightWrapper=rightWrapper,this.isFullScreen=isFullScreen,this.fetchSavedContent=this.fetchSavedContent.bind(this),this.handleEscapeKey=this.handleEscapeKey.bind(this),this._savingTimer=null,CursiveAutosave.instance=this,this.fetchStrings()}static getInstance(editor,rightWrapper,modules,isFullScreen){this.instance||(this.instance=new CursiveAutosave(editor,rightWrapper,modules,isFullScreen)),this.instance.isFullScreen=isFullScreen;return("quiz"===modules.modulename?document.querySelector("#tiny_cursive_savingState".concat(modules.questionid)):document.querySelector("#tiny_cursive_savingState"))||this.instance.init(),this.instance}init(){const stateWrapper=this.cursiveSavingState(this.savingState);stateWrapper.classList.add("tiny_cursive_savingState","btn"),"quiz"===this.module.modulename?stateWrapper.id="tiny_cursive_savingState".concat(this.module.questionid):stateWrapper.id="tiny_cursive_savingState",this.rightWrapper.prepend(stateWrapper),stateWrapper.addEventListener("click",this.fetchSavedContent)}destroy(){CursiveAutosave.instance=null}static destroyInstance(){this.instance&&(this.instance.destroy(),this.instance=null)}cursiveSavingState(state){let wrapperDiv=document.createElement("div"),textSpan=document.createElement("span"),button=document.createElement("button"),iconSpan=document.createElement("span");return button.style.padding=".3rem",textSpan.style.fontSize="0.75rem",textSpan.style.color="gray","quiz"===this.module.modulename?(iconSpan.id="CursiveCloudIcon".concat(this.module.questionid),textSpan.id="CursiveStateText".concat(this.module.questionid)):(iconSpan.id="CursiveCloudIcon",textSpan.id="CursiveStateText"),state&&(textSpan.textContent=this.getStateText(state),iconSpan.innerHTML=this.getStateIcon(state)),wrapperDiv.style.verticalAlign="middle",wrapperDiv.appendChild(iconSpan),wrapperDiv.appendChild(textSpan),button.appendChild(wrapperDiv),button}static updateSavingState(state){const instance=this.instance;instance.savingState=state;let stateWrapper=null;stateWrapper="quiz"===instance.module.modulename?document.querySelector("#tiny_cursive_savingState".concat(instance.module.questionid)):document.querySelector("#tiny_cursive_savingState");let iconSpan="",stateTextEl="";stateWrapper&&("quiz"===instance.module.modulename?(iconSpan=stateWrapper.querySelector("#CursiveCloudIcon".concat(instance.module.questionid)),stateTextEl=stateWrapper.querySelector("#CursiveStateText".concat(instance.module.questionid))):(iconSpan=stateWrapper.querySelector("#CursiveCloudIcon"),stateTextEl=stateWrapper.querySelector("#CursiveStateText")),stateTextEl&&iconSpan&&(stateTextEl.textContent=instance.getStateText(state),iconSpan.innerHTML=instance.getStateIcon(state)),instance._savingTimer&&clearTimeout(instance._savingTimer),"saved"===state&&stateTextEl&&(instance._savingTimer=setTimeout((()=>{stateTextEl.textContent=""}),5e3)))}getStateText(state){const[saving,saved,offline]=this.getText("state");switch(state){case"saving":return saving;case"saved":return saved;case"offline":return offline;default:return""}}getStateIcon(state){switch(state){case"saving":case"saved":return _svg_repo.default.cloudSave;case"offline":return"data:image/svg+xml;base64,"+btoa(_svg_repo.default.offline);default:return""}}async fetchSavedContent(e){var _dropdown$classList,_this$editor;e.preventDefault();let dropdown=document.querySelector("#savedDropdown");if(null==dropdown||null===(_dropdown$classList=dropdown.classList)||void 0===_dropdown$classList?void 0:_dropdown$classList.contains("show"))return void this.closeSavedDropdown();let editorWrapper=null;editorWrapper="quiz"===this.module.modulename?document.querySelector("#tiny_cursive_savingState".concat(this.module.questionid)):document.querySelector("#tiny_cursive_savingState");let args={id:this.module.resourceId,cmid:this.module.cmid,modulename:"".concat(this.module.modulename,"_autosave"),editorid:null===(_this$editor=this.editor)||void 0===_this$editor?void 0:_this$editor.id,userid:this.module.userid,courseid:this.module.courseid};(0,_ajax.call)([{methodname:"cursive_get_autosave_content",args:args}])[0].done((data=>{let context={comments:JSON.parse(data)};Object.values(context.comments).forEach((content=>{content.time=this.timeAgo(content.timemodified)})),this.renderCommentList(context,editorWrapper)})).fail((error=>{this.throwWarning("fullmodeerrorr",this.editor),window.console.error("Error fetching saved content:",error)}))}toggleSavedDropdown(){var _dropdown$classList2;const dropdown=document.querySelector("#savedDropdown");(null==dropdown||null===(_dropdown$classList2=dropdown.classList)||void 0===_dropdown$classList2?void 0:_dropdown$classList2.contains("show"))?this.closeSavedDropdown():this.openSavedDropdown()}openSavedDropdown(){document.querySelector("#savedDropdown").classList.add("show"),document.removeEventListener("keydown",this.handleEscapeKey),document.addEventListener("keydown",this.handleEscapeKey)}closeSavedDropdown(){const dropdown=document.querySelector("#savedDropdown");dropdown&&(dropdown.classList.remove("show"),dropdown.remove(),document.removeEventListener("keydown",this.handleEscapeKey))}handleEscapeKey(event){"Escape"===event.key&&this.closeSavedDropdown()}timeAgo(unixTime){const seconds=Math.floor(Date.now()/1e3)-unixTime;if(seconds<5)return"just now";if(seconds<60)return"".concat(seconds," sec ago");const minutes=Math.floor(seconds/60);if(minutes<60)return"".concat(minutes," min ago");const hours=Math.floor(minutes/60);if(hours<24)return"".concat(hours," hour").concat(hours>1?"s":""," ago");const days=Math.floor(hours/24);if(days<7)return"".concat(days," day").concat(days>1?"s":""," ago");const weeks=Math.floor(days/7);if(weeks<4)return"".concat(weeks," week").concat(weeks>1?"s":""," ago");const months=Math.floor(days/30);if(months<12)return"".concat(months," month").concat(months>1?"s":""," ago");const years=Math.floor(days/365);return"".concat(years," year").concat(years>1?"s":""," ago")}renderCommentList(context,editorWrapper){_templates.default.render("tiny_cursive/saved_content",context).then((html=>{editorWrapper.style.position="relative";const tempDiv=document.createElement("div");if(tempDiv.innerHTML=html.trim(),tempDiv.id="savedDropdown",tempDiv.classList.add("tiny_cursive-saved-dropdown"),!tempDiv)return window.console.error("Saved content template rendered empty or invalid HTML."),!1;let existingPanel=document.querySelector("#savedDropdown");return existingPanel||(editorWrapper.appendChild(tempDiv),existingPanel=tempDiv),existingPanel.classList.toggle("active"),this.openSavedDropdown(),this.insertSavedItems(this.editor),!0})).catch((error=>window.console.error(error)))}fetchStrings(){localStorage.getItem("state")||Promise.all([(0,_str.get_string)("saving","tiny_cursive"),(0,_str.get_string)("saved","tiny_cursive"),(0,_str.get_string)("offline","tiny_cursive")]).then((function(strings){return localStorage.setItem("state",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}throwWarning(str,editor){(0,_str.get_string)(str,"tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))}getText(key){return JSON.parse(localStorage.getItem(key))}insertSavedItems(editor){document.querySelectorAll(".tiny_cursive-item-preview").forEach((element=>{element.addEventListener("click",(function(){editor.insertContent(" "+this.textContent)}))}))}}var obj,key,value;return _exports.default=CursiveAutosave,value=null,(key="instance")in(obj=CursiveAutosave)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,_exports.default})); //# sourceMappingURL=cursive_autosave.min.js.map \ No newline at end of file diff --git a/amd/build/cursive_autosave.min.js.map b/amd/build/cursive_autosave.min.js.map index f0ea83bb..56db2bab 100644 --- a/amd/build/cursive_autosave.min.js.map +++ b/amd/build/cursive_autosave.min.js.map @@ -1 +1 @@ -{"version":3,"file":"cursive_autosave.min.js","sources":["../src/cursive_autosave.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * TODO describe module cursive_autosave\n *\n * @module tiny_cursive/cursive_autosave\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport templates from 'core/templates';\nimport {call} from 'core/ajax';\nimport Icons from 'tiny_cursive/svg_repo';\nimport {get_string as getString} from 'core/str';\n\nexport default class CursiveAutosave {\n\n static instance = null;\n\n\n constructor(editor, rightWrapper, modules, isFullScreen) {\n if (CursiveAutosave.instance) {\n return CursiveAutosave.instance;\n }\n\n this.editor = editor;\n this.module = modules;\n this.savingState = '';\n this.rightWrapper = rightWrapper;\n this.isFullScreen = isFullScreen;\n // Bind methods that will be used as event listener\n this.fetchSavedContent = this.fetchSavedContent.bind(this);\n this.handleEscapeKey = this.handleEscapeKey.bind(this);\n this._savingTimer = null;\n CursiveAutosave.instance = this;\n this.fetchStrings();\n }\n\n static getInstance(editor, rightWrapper, modules, isFullScreen) {\n if (!this.instance) {\n this.instance = new CursiveAutosave(editor, rightWrapper, modules, isFullScreen);\n }\n this.instance.isFullScreen = isFullScreen;\n const hasState = modules.modulename === 'quiz'\n ? document.querySelector(`#tiny_cursive_savingState${modules.questionid}`)\n : document.querySelector('#tiny_cursive_savingState');\n if (!hasState) {\n this.instance.init();\n }\n\n return this.instance;\n }\n\n init() {\n const stateWrapper = this.cursiveSavingState(this.savingState);\n stateWrapper.classList.add('tiny_cursive_savingState', 'btn');\n if (this.module.modulename === 'quiz') {\n stateWrapper.id = `tiny_cursive_savingState${this.module.questionid}`;\n } else {\n stateWrapper.id = 'tiny_cursive_savingState';\n }\n\n this.rightWrapper.prepend(stateWrapper);\n stateWrapper.addEventListener('click', this.fetchSavedContent);\n }\n\n destroy() {\n CursiveAutosave.instance = null;\n }\n\n static destroyInstance() {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n }\n\n /**\n * Creates a wrapper div containing an icon and text to display the saving state\n * @param {string} state - The current saving state ('saving', 'saved', or 'offline')\n * @returns {HTMLElement} A div element containing the state icon and text\n * @description Creates and returns a div element with an icon and text span to show the current saving state.\n * The icon and text are updated based on the provided state parameter.\n */\n cursiveSavingState(state) {\n let wrapperDiv = document.createElement('div');\n let textSpan = document.createElement('span');\n let button = document.createElement('button');\n let iconSpan = document.createElement('span');\n\n button.style.padding = '.3rem';\n textSpan.style.fontSize = '0.75rem';\n textSpan.style.color = 'gray';\n\n if (this.module.modulename === 'quiz') {\n iconSpan.id = `CursiveCloudIcon${this.module.questionid}`;\n textSpan.id = `CursiveStateText${this.module.questionid}`;\n } else {\n iconSpan.id = 'CursiveCloudIcon';\n textSpan.id = 'CursiveStateText';\n }\n if (state) {\n textSpan.textContent = this.getStateText(state);\n iconSpan.innerHTML = this.getStateIcon(state);\n }\n\n wrapperDiv.style.verticalAlign = 'middle';\n\n wrapperDiv.appendChild(iconSpan);\n wrapperDiv.appendChild(textSpan);\n button.appendChild(wrapperDiv);\n\n return button;\n }\n\n /**\n * Updates the saving state icon and text in the editor\n * @param {string} state - The state to update to ('saving', 'saved', or 'offline')\n * @description Updates the global saving state and modifies the UI elements to reflect the new state\n */\n static updateSavingState(state) {\n const instance = this.instance;\n instance.savingState = state;\n let stateWrapper = null;\n if (instance.module.modulename === 'quiz') {\n stateWrapper = document.querySelector(`#tiny_cursive_savingState${instance.module.questionid}`);\n } else {\n stateWrapper = document.querySelector('#tiny_cursive_savingState');\n }\n\n let iconSpan = '';\n let stateTextEl = '';\n\n if (!stateWrapper) {\n return;\n }\n\n if (instance.module.modulename === 'quiz') {\n iconSpan = stateWrapper.querySelector(`#CursiveCloudIcon${instance.module.questionid}`);\n stateTextEl = stateWrapper.querySelector(`#CursiveStateText${instance.module.questionid}`);\n } else {\n iconSpan = stateWrapper.querySelector('#CursiveCloudIcon');\n stateTextEl = stateWrapper.querySelector('#CursiveStateText');\n }\n\n if (stateTextEl && iconSpan) {\n stateTextEl.textContent = instance.getStateText(state);\n iconSpan.innerHTML = instance.getStateIcon(state);\n }\n\n\n if (instance._savingTimer) {\n clearTimeout(instance._savingTimer);\n }\n\n if (state === 'saved' && stateTextEl) {\n instance._savingTimer = setTimeout(() => {\n stateTextEl.textContent = '';\n }, 5000);\n }\n }\n\n /**\n * Gets the display text for a given saving state\n * @param {string} state - The state to get text for ('saving', 'saved', or 'offline')\n * @returns {string} The text to display for the given state\n * @description Returns appropriate text label based on the current saving state\n */\n getStateText(state) {\n const [saving, saved, offline] = this.getText('state');\n switch (state) {\n case 'saving': return saving;\n case 'saved': return saved;\n case 'offline': return offline;\n default: return '';\n }\n }\n /**\n * Gets the icon URL for a given saving state\n * @param {string} state - The state to get icon for ('saving', 'saved', or 'offline')\n * @returns {string} The URL of the icon image for the given state\n * @description Returns appropriate icon URL based on the current saving state\n */\n getStateIcon(state) {\n switch (state) {\n case 'saving': return Icons.cloudSave;\n case 'saved': return Icons.cloudSave;\n case 'offline': return 'data:image/svg+xml;base64,' + btoa(Icons.offline);\n default: return '';\n }\n }\n\n /**\n * Fetches and displays saved content in a dropdown\n * @async\n * @param {Event} e - The event object\n * @description Handles fetching and displaying saved content when the save state button is clicked.\n * If the dropdown is already visible, it will be closed. Otherwise it will fetch saved content\n * from the server (or use cached content if available) and display it in a dropdown panel.\n * @throws {Error} Logs error to console if fetching content fails\n */\n async fetchSavedContent(e) {\n e.preventDefault();\n\n let dropdown = document.querySelector('#savedDropdown');\n let isVisible = dropdown?.classList?.contains('show');\n\n if (isVisible) {\n this.closeSavedDropdown();\n return;\n }\n let editorWrapper = null;\n if (this.module.modulename === 'quiz') {\n editorWrapper = document.querySelector(`#tiny_cursive_savingState${this.module.questionid}`);\n } else {\n editorWrapper = document.querySelector('#tiny_cursive_savingState');\n }\n\n let args = {\n id: this.module.resourceId,\n cmid: this.module.cmid,\n modulename: `${this.module.modulename}_autosave`,\n editorid: this.editor?.id,\n userid: this.module.userid,\n courseid: this.module.courseid\n };\n\n call([{\n methodname: \"cursive_get_autosave_content\",\n args: args\n }])[0].done((data) => {\n let context = {comments: JSON.parse(data)};\n Object.values(context.comments).forEach(content => {\n content.time = this.timeAgo(content.timemodified);\n });\n this.renderCommentList(context, editorWrapper);\n\n }).fail((error) => {\n this.throwWarning('fullmodeerrorr', this.editor);\n window.console.error('Error fetching saved content:', error);\n });\n }\n\n /**\n * Toggles the visibility of the saved content dropdown\n * @description Checks if the saved content dropdown is currently visible and either closes or opens it accordingly.\n * If visible, calls closeSavedDropdown(). If hidden, calls openSavedDropdown().\n */\n toggleSavedDropdown() {\n const dropdown = document.querySelector('#savedDropdown');\n const isVisible = dropdown?.classList?.contains('show');\n\n if (isVisible) {\n this.closeSavedDropdown();\n } else {\n this.openSavedDropdown();\n }\n }\n\n /**\n * Opens the saved content dropdown panel\n * @description Shows the saved content dropdown by adding the 'show' class and sets up an event listener\n * for the Escape key to allow closing the dropdown. This is called when toggling the dropdown open.\n */\n openSavedDropdown() {\n const dropdown = document.querySelector('#savedDropdown');\n dropdown.classList.add('show');\n\n // Add event listener to close on Escape key\n document.removeEventListener('keydown', this.handleEscapeKey);\n document.addEventListener('keydown', this.handleEscapeKey);\n }\n\n /**\n * Closes the saved content dropdown panel\n * @description Removes the 'show' class from the dropdown to hide it, removes the dropdown element from the DOM,\n * and removes the Escape key event listener. This is called when toggling the dropdown closed or when\n * clicking outside the dropdown.\n */\n closeSavedDropdown() {\n const dropdown = document.querySelector('#savedDropdown');\n if (dropdown) {\n dropdown.classList.remove('show');\n dropdown.remove();\n document.removeEventListener('keydown', this.handleEscapeKey);\n }\n }\n\n /**\n * Handles the Escape key press event for closing the saved content dropdown\n * @param {KeyboardEvent} event - The keyboard event object\n * @description Event handler that checks if the Escape key was pressed and closes the saved content dropdown if it was\n */\n handleEscapeKey(event) {\n if (event.key === 'Escape') {\n this.closeSavedDropdown();\n }\n }\n\n timeAgo(unixTime) {\n const seconds = Math.floor(Date.now() / 1000) - unixTime;\n\n if (seconds < 5) {\n return \"just now\";\n }\n if (seconds < 60) {\n return `${seconds} sec ago`;\n }\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) {\n return `${minutes} min ago`;\n }\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours} hour${hours > 1 ? \"s\" : \"\"} ago`;\n }\n\n const days = Math.floor(hours / 24);\n if (days < 7) {\n return `${days} day${days > 1 ? \"s\" : \"\"} ago`;\n }\n\n const weeks = Math.floor(days / 7);\n if (weeks < 4) {\n return `${weeks} week${weeks > 1 ? \"s\" : \"\"} ago`;\n }\n\n const months = Math.floor(days / 30);\n if (months < 12) {\n return `${months} month${months > 1 ? \"s\" : \"\"} ago`;\n }\n\n const years = Math.floor(days / 365);\n return `${years} year${years > 1 ? \"s\" : \"\"} ago`;\n }\n\n\n /**\n * Renders the saved content dropdown list using a template\n * @param {Object} context - The context object containing saved comments data to render\n * @param {HTMLElement} editorWrapper - The wrapper element to attach the dropdown to\n * @description Renders the saved content dropdown using the tiny_cursive/saved_content template.\n * Creates and positions the dropdown relative to the editor wrapper element.\n * Handles toggling visibility and caching of the saved content.\n * @throws {Error} Logs error to console if template rendering fails\n */\n renderCommentList(context, editorWrapper) {\n templates.render('tiny_cursive/saved_content', context).then(html => {\n editorWrapper.style.position = 'relative';\n\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = html.trim();\n tempDiv.id = 'savedDropdown';\n tempDiv.classList.add('tiny_cursive-saved-dropdown');\n\n if (!tempDiv) {\n window.console.error(\"Saved content template rendered empty or invalid HTML.\");\n return false;\n }\n\n // Add to DOM if not already added\n let existingPanel = document.querySelector('#savedDropdown');\n\n if (!existingPanel) {\n editorWrapper.appendChild(tempDiv);\n existingPanel = tempDiv;\n }\n\n // Toggle visibility\n existingPanel.classList.toggle('active');\n this.openSavedDropdown();\n\n this.insertSavedItems(this.editor);\n\n return true;\n\n }).catch(error => window.console.error(error));\n }\n\n fetchStrings() {\n if (!localStorage.getItem('state')) {\n\n Promise.all([\n getString('saving', 'tiny_cursive'),\n getString('saved', 'tiny_cursive'),\n getString('offline', 'tiny_cursive')\n ]).then(function(strings) {\n return localStorage.setItem('state', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n }\n\n throwWarning(str, editor) {\n getString(str, 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n\n getText(key) {\n return JSON.parse(localStorage.getItem(key));\n }\n\n /**\n * Adds click event listeners to saved content items to insert them into the editor\n * @description Finds all elements with class 'tiny_cursive-item-preview' and adds click handlers that will\n * insert the element's text content into the editor when clicked. The text is inserted with\n * a leading space.\n * @param {Object} editor - The TinyMCE editor instance\n * @returns {void}\n */\n insertSavedItems(editor) {\n const items = document.querySelectorAll('.tiny_cursive-item-preview');\n items.forEach(element => {\n element.addEventListener('click', function() {\n editor.insertContent(\" \" + this.textContent);\n });\n });\n }\n\n}"],"names":["CursiveAutosave","constructor","editor","rightWrapper","modules","isFullScreen","instance","module","savingState","fetchSavedContent","this","bind","handleEscapeKey","_savingTimer","fetchStrings","modulename","document","querySelector","questionid","init","stateWrapper","cursiveSavingState","classList","add","id","prepend","addEventListener","destroy","state","wrapperDiv","createElement","textSpan","button","iconSpan","style","padding","fontSize","color","textContent","getStateText","innerHTML","getStateIcon","verticalAlign","appendChild","stateTextEl","clearTimeout","setTimeout","saving","saved","offline","getText","Icons","cloudSave","btoa","e","preventDefault","dropdown","_dropdown$classList","contains","closeSavedDropdown","editorWrapper","args","resourceId","cmid","editorid","_this$editor","userid","courseid","methodname","done","data","context","comments","JSON","parse","Object","values","forEach","content","time","timeAgo","timemodified","renderCommentList","fail","error","throwWarning","window","console","toggleSavedDropdown","_dropdown$classList2","openSavedDropdown","removeEventListener","remove","event","key","unixTime","seconds","Math","floor","Date","now","minutes","hours","days","weeks","months","years","render","then","html","position","tempDiv","trim","existingPanel","toggle","insertSavedItems","catch","localStorage","getItem","Promise","all","strings","setItem","stringify","str","windowManager","alert","querySelectorAll","element","insertContent"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"cursive_autosave.min.js","sources":["../src/cursive_autosave.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * TODO describe module cursive_autosave\n *\n * @module tiny_cursive/cursive_autosave\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport templates from 'core/templates';\nimport {call} from 'core/ajax';\nimport Icons from 'tiny_cursive/svg_repo';\nimport {get_string as getString} from 'core/str';\n\nexport default class CursiveAutosave {\n\n static instance = null;\n\n\n constructor(editor, rightWrapper, modules, isFullScreen) {\n if (CursiveAutosave.instance) {\n return CursiveAutosave.instance;\n }\n\n this.editor = editor;\n this.module = modules;\n this.savingState = '';\n this.rightWrapper = rightWrapper;\n this.isFullScreen = isFullScreen;\n // Bind methods that will be used as event listener\n this.fetchSavedContent = this.fetchSavedContent.bind(this);\n this.handleEscapeKey = this.handleEscapeKey.bind(this);\n this._savingTimer = null;\n CursiveAutosave.instance = this;\n this.fetchStrings();\n }\n\n static getInstance(editor, rightWrapper, modules, isFullScreen) {\n if (!this.instance) {\n this.instance = new CursiveAutosave(editor, rightWrapper, modules, isFullScreen);\n }\n this.instance.isFullScreen = isFullScreen;\n const hasState = modules.modulename === 'quiz'\n ? document.querySelector(`#tiny_cursive_savingState${modules.questionid}`)\n : document.querySelector('#tiny_cursive_savingState');\n if (!hasState) {\n this.instance.init();\n }\n\n return this.instance;\n }\n\n init() {\n const stateWrapper = this.cursiveSavingState(this.savingState);\n stateWrapper.classList.add('tiny_cursive_savingState', 'btn');\n if (this.module.modulename === 'quiz') {\n stateWrapper.id = `tiny_cursive_savingState${this.module.questionid}`;\n } else {\n stateWrapper.id = 'tiny_cursive_savingState';\n }\n\n this.rightWrapper.prepend(stateWrapper);\n stateWrapper.addEventListener('click', this.fetchSavedContent);\n }\n\n destroy() {\n CursiveAutosave.instance = null;\n }\n\n static destroyInstance() {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n }\n\n /**\n * Creates a wrapper div containing an icon and text to display the saving state\n * @param {string} state - The current saving state ('saving', 'saved', or 'offline')\n * @returns {HTMLElement} A div element containing the state icon and text\n * @description Creates and returns a div element with an icon and text span to show the current saving state.\n * The icon and text are updated based on the provided state parameter.\n */\n cursiveSavingState(state) {\n let wrapperDiv = document.createElement('div');\n let textSpan = document.createElement('span');\n let button = document.createElement('button');\n let iconSpan = document.createElement('span');\n\n button.style.padding = '.3rem';\n textSpan.style.fontSize = '0.75rem';\n textSpan.style.color = 'gray';\n\n if (this.module.modulename === 'quiz') {\n iconSpan.id = `CursiveCloudIcon${this.module.questionid}`;\n textSpan.id = `CursiveStateText${this.module.questionid}`;\n } else {\n iconSpan.id = 'CursiveCloudIcon';\n textSpan.id = 'CursiveStateText';\n }\n if (state) {\n textSpan.textContent = this.getStateText(state);\n iconSpan.innerHTML = this.getStateIcon(state);\n }\n\n wrapperDiv.style.verticalAlign = 'middle';\n\n wrapperDiv.appendChild(iconSpan);\n wrapperDiv.appendChild(textSpan);\n button.appendChild(wrapperDiv);\n\n return button;\n }\n\n /**\n * Updates the saving state icon and text in the editor\n * @param {string} state - The state to update to ('saving', 'saved', or 'offline')\n * @description Updates the global saving state and modifies the UI elements to reflect the new state\n */\n static updateSavingState(state) {\n const instance = this.instance;\n instance.savingState = state;\n let stateWrapper = null;\n if (instance.module.modulename === 'quiz') {\n stateWrapper = document.querySelector(`#tiny_cursive_savingState${instance.module.questionid}`);\n } else {\n stateWrapper = document.querySelector('#tiny_cursive_savingState');\n }\n\n let iconSpan = '';\n let stateTextEl = '';\n\n if (!stateWrapper) {\n return;\n }\n\n if (instance.module.modulename === 'quiz') {\n iconSpan = stateWrapper.querySelector(`#CursiveCloudIcon${instance.module.questionid}`);\n stateTextEl = stateWrapper.querySelector(`#CursiveStateText${instance.module.questionid}`);\n } else {\n iconSpan = stateWrapper.querySelector('#CursiveCloudIcon');\n stateTextEl = stateWrapper.querySelector('#CursiveStateText');\n }\n\n if (stateTextEl && iconSpan) {\n stateTextEl.textContent = instance.getStateText(state);\n iconSpan.innerHTML = instance.getStateIcon(state);\n }\n\n\n if (instance._savingTimer) {\n clearTimeout(instance._savingTimer);\n }\n\n if (state === 'saved' && stateTextEl) {\n instance._savingTimer = setTimeout(() => {\n stateTextEl.textContent = '';\n }, 5000);\n }\n }\n\n /**\n * Gets the display text for a given saving state\n * @param {string} state - The state to get text for ('saving', 'saved', or 'offline')\n * @returns {string} The text to display for the given state\n * @description Returns appropriate text label based on the current saving state\n */\n getStateText(state) {\n const [saving, saved, offline] = this.getText('state');\n switch (state) {\n case 'saving': return saving;\n case 'saved': return saved;\n case 'offline': return offline;\n default: return '';\n }\n }\n /**\n * Gets the icon URL for a given saving state\n * @param {string} state - The state to get icon for ('saving', 'saved', or 'offline')\n * @returns {string} The URL of the icon image for the given state\n * @description Returns appropriate icon URL based on the current saving state\n */\n getStateIcon(state) {\n switch (state) {\n case 'saving': return Icons.cloudSave;\n case 'saved': return Icons.cloudSave;\n case 'offline': return 'data:image/svg+xml;base64,' + btoa(Icons.offline);\n default: return '';\n }\n }\n\n /**\n * Fetches and displays saved content in a dropdown\n * @async\n * @param {Event} e - The event object\n * @description Handles fetching and displaying saved content when the save state button is clicked.\n * If the dropdown is already visible, it will be closed. Otherwise it will fetch saved content\n * from the server (or use cached content if available) and display it in a dropdown panel.\n * @throws {Error} Logs error to console if fetching content fails\n */\n async fetchSavedContent(e) {\n e.preventDefault();\n\n let dropdown = document.querySelector('#savedDropdown');\n let isVisible = dropdown?.classList?.contains('show');\n\n if (isVisible) {\n this.closeSavedDropdown();\n return;\n }\n let editorWrapper = null;\n if (this.module.modulename === 'quiz') {\n editorWrapper = document.querySelector(`#tiny_cursive_savingState${this.module.questionid}`);\n } else {\n editorWrapper = document.querySelector('#tiny_cursive_savingState');\n }\n\n let args = {\n id: this.module.resourceId,\n cmid: this.module.cmid,\n modulename: `${this.module.modulename}_autosave`,\n editorid: this.editor?.id,\n userid: this.module.userid,\n courseid: this.module.courseid\n };\n\n call([{\n methodname: \"cursive_get_autosave_content\",\n args: args\n }])[0].done((data) => {\n let context = {comments: JSON.parse(data)};\n Object.values(context.comments).forEach(content => {\n content.time = this.timeAgo(content.timemodified);\n });\n this.renderCommentList(context, editorWrapper);\n\n }).fail((error) => {\n this.throwWarning('fullmodeerrorr', this.editor);\n window.console.error('Error fetching saved content:', error);\n });\n }\n\n /**\n * Toggles the visibility of the saved content dropdown\n * @description Checks if the saved content dropdown is currently visible and either closes or opens it accordingly.\n * If visible, calls closeSavedDropdown(). If hidden, calls openSavedDropdown().\n */\n toggleSavedDropdown() {\n const dropdown = document.querySelector('#savedDropdown');\n const isVisible = dropdown?.classList?.contains('show');\n\n if (isVisible) {\n this.closeSavedDropdown();\n } else {\n this.openSavedDropdown();\n }\n }\n\n /**\n * Opens the saved content dropdown panel\n * @description Shows the saved content dropdown by adding the 'show' class and sets up an event listener\n * for the Escape key to allow closing the dropdown. This is called when toggling the dropdown open.\n */\n openSavedDropdown() {\n const dropdown = document.querySelector('#savedDropdown');\n dropdown.classList.add('show');\n\n // Add event listener to close on Escape key\n document.removeEventListener('keydown', this.handleEscapeKey);\n document.addEventListener('keydown', this.handleEscapeKey);\n }\n\n /**\n * Closes the saved content dropdown panel\n * @description Removes the 'show' class from the dropdown to hide it, removes the dropdown element from the DOM,\n * and removes the Escape key event listener. This is called when toggling the dropdown closed or when\n * clicking outside the dropdown.\n */\n closeSavedDropdown() {\n const dropdown = document.querySelector('#savedDropdown');\n if (dropdown) {\n dropdown.classList.remove('show');\n dropdown.remove();\n document.removeEventListener('keydown', this.handleEscapeKey);\n }\n }\n\n /**\n * Handles the Escape key press event for closing the saved content dropdown\n * @param {KeyboardEvent} event - The keyboard event object\n * @description Event handler that checks if the Escape key was pressed and closes the saved content dropdown if it was\n */\n handleEscapeKey(event) {\n if (event.key === 'Escape') {\n this.closeSavedDropdown();\n }\n }\n\n timeAgo(unixTime) {\n const seconds = Math.floor(Date.now() / 1000) - unixTime;\n\n if (seconds < 5) {\n return \"just now\";\n }\n if (seconds < 60) {\n return `${seconds} sec ago`;\n }\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) {\n return `${minutes} min ago`;\n }\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours} hour${hours > 1 ? \"s\" : \"\"} ago`;\n }\n\n const days = Math.floor(hours / 24);\n if (days < 7) {\n return `${days} day${days > 1 ? \"s\" : \"\"} ago`;\n }\n\n const weeks = Math.floor(days / 7);\n if (weeks < 4) {\n return `${weeks} week${weeks > 1 ? \"s\" : \"\"} ago`;\n }\n\n const months = Math.floor(days / 30);\n if (months < 12) {\n return `${months} month${months > 1 ? \"s\" : \"\"} ago`;\n }\n\n const years = Math.floor(days / 365);\n return `${years} year${years > 1 ? \"s\" : \"\"} ago`;\n }\n\n\n /**\n * Renders the saved content dropdown list using a template\n * @param {Object} context - The context object containing saved comments data to render\n * @param {HTMLElement} editorWrapper - The wrapper element to attach the dropdown to\n * @description Renders the saved content dropdown using the tiny_cursive/saved_content template.\n * Creates and positions the dropdown relative to the editor wrapper element.\n * Handles toggling visibility and caching of the saved content.\n * @throws {Error} Logs error to console if template rendering fails\n */\n renderCommentList(context, editorWrapper) {\n templates.render('tiny_cursive/saved_content', context).then(html => {\n editorWrapper.style.position = 'relative';\n\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = html.trim();\n tempDiv.id = 'savedDropdown';\n tempDiv.classList.add('tiny_cursive-saved-dropdown');\n\n if (!tempDiv) {\n window.console.error(\"Saved content template rendered empty or invalid HTML.\");\n return false;\n }\n\n // Add to DOM if not already added\n let existingPanel = document.querySelector('#savedDropdown');\n\n if (!existingPanel) {\n editorWrapper.appendChild(tempDiv);\n existingPanel = tempDiv;\n }\n\n // Toggle visibility\n existingPanel.classList.toggle('active');\n this.openSavedDropdown();\n\n this.insertSavedItems(this.editor);\n\n return true;\n\n }).catch(error => window.console.error(error));\n }\n\n fetchStrings() {\n if (!localStorage.getItem('state')) {\n\n Promise.all([\n getString('saving', 'tiny_cursive'),\n getString('saved', 'tiny_cursive'),\n getString('offline', 'tiny_cursive')\n ]).then(function(strings) {\n return localStorage.setItem('state', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n }\n\n throwWarning(str, editor) {\n getString(str, 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n\n getText(key) {\n return JSON.parse(localStorage.getItem(key));\n }\n\n /**\n * Adds click event listeners to saved content items to insert them into the editor\n * @description Finds all elements with class 'tiny_cursive-item-preview' and adds click handlers that will\n * insert the element's text content into the editor when clicked. The text is inserted with\n * a leading space.\n * @param {Object} editor - The TinyMCE editor instance\n * @returns {void}\n */\n insertSavedItems(editor) {\n const items = document.querySelectorAll('.tiny_cursive-item-preview');\n items.forEach(element => {\n element.addEventListener('click', function() {\n editor.insertContent(\" \" + this.textContent);\n });\n });\n }\n\n}"],"names":["CursiveAutosave","constructor","editor","rightWrapper","modules","isFullScreen","instance","module","savingState","fetchSavedContent","this","bind","handleEscapeKey","_savingTimer","fetchStrings","modulename","document","querySelector","questionid","init","stateWrapper","cursiveSavingState","classList","add","id","prepend","addEventListener","destroy","state","wrapperDiv","createElement","textSpan","button","iconSpan","style","padding","fontSize","color","textContent","getStateText","innerHTML","getStateIcon","verticalAlign","appendChild","stateTextEl","clearTimeout","setTimeout","saving","saved","offline","getText","Icons","cloudSave","btoa","e","preventDefault","dropdown","_dropdown$classList","contains","closeSavedDropdown","editorWrapper","args","resourceId","cmid","editorid","_this$editor","userid","courseid","methodname","done","data","context","comments","JSON","parse","Object","values","forEach","content","time","timeAgo","timemodified","renderCommentList","fail","error","throwWarning","window","console","toggleSavedDropdown","_dropdown$classList2","openSavedDropdown","removeEventListener","remove","event","key","unixTime","seconds","Math","floor","Date","now","minutes","hours","days","weeks","months","years","render","then","html","position","tempDiv","trim","existingPanel","toggle","insertSavedItems","catch","localStorage","getItem","Promise","all","strings","setItem","stringify","str","windowManager","alert","querySelectorAll","element","insertContent"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/document_view.min.js b/amd/build/document_view.min.js index 7b04f290..40ae35f3 100644 --- a/amd/build/document_view.min.js +++ b/amd/build/document_view.min.js @@ -5,6 +5,6 @@ define("tiny_cursive/document_view",["exports","tiny_cursive/svg_repo"],(functio * @module tiny_cursive/document_view * @copyright 2025 Cursive Technology, Inc. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_svg_repo=(obj=_svg_repo)&&obj.__esModule?obj:{default:obj};return _exports.default=class{constructor(User,Rubrics,submission,modulename,editor,quizInfo){this.User=User,this.Rubrics=Rubrics,this.submission=submission,this.module=modulename,this.editor=editor,this.moduleIcon=_svg_repo.default.assignment,this.quizInfo=quizInfo,this.initStrings()}normalMode(){var _this$editor;let id=(null===(_this$editor=this.editor)||void 0===_this$editor?void 0:_this$editor.id)+"_ifr";("assign"===this.module||"quiz"===this.module||"forum"===this.module||"lesson"===this.module)&&this.normalizePage(id)}fullPageMode(){var _this$editor4,_this$editor2;if("assign"===this.module)this.moduleIcon=_svg_repo.default.assignment,this.fullPageModule(null===(_this$editor2=this.editor)||void 0===_this$editor2?void 0:_this$editor2.id);else if("forum"===this.module){var _this$editor3;this.moduleIcon=_svg_repo.default.forum,this.fullPageModule(null===(_this$editor3=this.editor)||void 0===_this$editor3?void 0:_this$editor3.id)}else if("quiz"===this.module&&null!==(_this$editor4=this.editor)&&void 0!==_this$editor4&&_this$editor4.id){var _this$editor5;this.moduleIcon=_svg_repo.default.quiz,this.fullPageModule(null===(_this$editor5=this.editor)||void 0===_this$editor5?void 0:_this$editor5.id)}else if("lesson"===this.module){var _this$editor6;this.moduleIcon=_svg_repo.default.lesson,this.fullPageModule(null===(_this$editor6=this.editor)||void 0===_this$editor6?void 0:_this$editor6.id)}}docSideBar(status){var _this$editor7;const replyId=new URL(window.location.href).searchParams.get("reply"),toggle=document.querySelector("#cursive-fullpagemode-sidebar-toggle"),timelimitBlock=this.getTimerBlock(this.module),headerInfo=this.getSidebarTitle(),progressBar=document.querySelector(".box.progress_bar"),courseName=document.querySelector("#page-navbar > nav > ol > li:nth-child(1) > a"),courseDes=document.querySelector("#intro"),Dates=document.querySelector(".activity-dates");let openDate=null==Dates?void 0:Dates.querySelector("div:nth-child(1)"),dueDate=null==Dates?void 0:Dates.querySelector("div:nth-child(2)");const container=this.create("div");Object.assign(container,{id:"cursive-fullpagemode-sidebar",className:"bg-white h-100 shadow"}),Object.assign(container.style,{width:"300px",overflow:"auto"});const crossBtn=this.create("span");Object.assign(crossBtn,{id:"cursive-collapse-sidebar",className:"btn p-2",innerHTML:_svg_repo.default.close}),crossBtn.addEventListener("click",(()=>{container.style.transition="width 0.3s ease",container.style.width="0",toggle.style.display="flex"})),null==toggle||toggle.addEventListener("click",(function(){toggle.style.display="none",container.style.width="300px"}));const btnWrapper=this.create("div");Object.assign(btnWrapper,{padding:"0 1rem",position:"sticky",top:"0",backgroundColor:"white"}),btnWrapper.append(crossBtn);const header=this.create("div");header.className="border-bottom p-3 bg-light",Object.assign(header.style,{position:"sticky",top:"0"});const headerTitle=this.create("h3");headerTitle.className="mb-3 d-flex align-items-center",headerTitle.textContent=`${headerInfo.title} ${this.details}`,headerTitle.style.fontWeight="600";const headerIcon=document.querySelector(".page-header-image > div");headerIcon&&headerTitle.prepend(headerIcon.cloneNode(!0));let wordCount=this.wordCounter(status);null!=timelimitBlock&&timelimitBlock.textContent?header.append(headerTitle,wordCount,this.timerCountDown(timelimitBlock)):header.append(headerTitle,wordCount);const content=this.create("div");if(content.className="p-3",content.append(this.createBox({bg:"bg-info",titleColor:"text-info",icon:_svg_repo.default.people,title:this.studentInfo,bodyHTML:this.generateStudentInfo(this.User,courseName)})),"lesson"===this.module&&progressBar&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.progress,bodyHTML:progressBar.innerHTML})),courseDes&&""!==(null==courseDes?void 0:courseDes.textContent.trim())&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.getSidebarTitle().title} ${this.description}`,bodyHTML:courseDes.innerHTML})),"forum"===this.module&&replyId){this.checkForumSubject();let replyPost=document.querySelector(`#post-content-${replyId}`);null!=replyPost&&replyPost.textContent.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.replyingto,bodyHTML:replyPost.textContent.trim()}))}if("quiz"===this.module&&null!==(_this$editor7=this.editor)&&void 0!==_this$editor7&&_this$editor7.id){var _this$editor8;let questionId=this.getQuestionId(null===(_this$editor8=this.editor)||void 0===_this$editor8?void 0:_this$editor8.id),question=document.querySelector(`#question-${questionId} .qtext`),intro=atob(this.quizInfo.intro);null!=question&&question.textContent.trim()&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:this.moduleIcon,title:this.answeringto,bodyHTML:question.textContent})),intro&&""!==intro.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.quiz} ${this.description}`,bodyHTML:intro})),Number(this.quizInfo.open)&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(Number(this.quizInfo.open),Number(this.quizInfo.close))}))}return Object.keys(this.Rubrics).length&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.rubrics,bodyHTML:this.generateRubrics(this.Rubrics)})),Dates&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(openDate,dueDate)})),"assign"===this.module&&content.append(this.createBox({bg:"bg-green",titleColor:"text-success",icon:this.moduleIcon,title:this.subStatus,bodyHTML:this.submissionStatus(this.submission)})),container.append(btnWrapper,header,content),container}createBox(_ref){let{bg:bg,titleColor:titleColor,icon:icon,title:title,bodyHTML:bodyHTML}=_ref;const box=this.create("div");box.className=`tiny_cursive-fullpage-card ${bg}`;const heading=this.create("h4");heading.className=`tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`,heading.innerHTML=`${icon} ${title}`;const body=this.create("div");return body.className="tiny_cursive-fullpage-card-body",body.innerHTML=bodyHTML,box.append(heading,body),box}generateRubrics(Rubrics){const wrapper=this.create("div");return Rubrics.forEach((rubric=>{const rubricDiv=this.create("div");rubricDiv.className="tiny_cursive-rubric-card";const title=this.create("h3");title.className="tiny_cursive-rubric-title",title.textContent=rubric.description,rubricDiv.appendChild(title),Object.values(rubric.levels).forEach((level=>{const levelDiv=this.create("div"),score=Number(level.score);levelDiv.className=0===score?"tiny_cursive-rubric-level tiny_cursive-rubric-low":score<=2?"tiny_cursive-rubric-level tiny_cursive-rubric-mid":"tiny_cursive-rubric-level tiny_cursive-rubric-high",levelDiv.textContent=`${level.definition} / ${level.score}`,rubricDiv.appendChild(levelDiv)})),wrapper.appendChild(rubricDiv)})),wrapper.innerHTML}submissionStatus(submission){var _submission$current,_submission$current2;const wrapper=this.create("div"),statusWrapper=this.create("div");statusWrapper.className="tiny_cursive-status-row";const statusName=this.create("span");statusName.textContent=`${this.status}:`;const statusValue=this.create("span"),isNew="new"===(null==submission||null===(_submission$current=submission.current)||void 0===_submission$current?void 0:_submission$current.status);statusValue.textContent=isNew?this.draftnot:this.draft,statusValue.className="tiny_cursive-status-value "+(isNew?"tiny_cursive-status-red":"tiny_cursive-status-green"),statusWrapper.append(statusName,statusValue);const modifiedWrapper=this.create("div");modifiedWrapper.className="tiny_cursive-status-row";const modifiedName=this.create("span");modifiedName.textContent=`${this.lastModified}: `;const modifiedValue=this.create("span");if(null!=submission&&null!==(_submission$current2=submission.current)&&void 0!==_submission$current2&&_submission$current2.timemodified){const date=new Date(1e3*submission.current.timemodified);modifiedValue.textContent=this.formatDate(date)}else modifiedValue.textContent="N/A";modifiedWrapper.append(modifiedName,modifiedValue);const gradeWrapper=this.create("div");gradeWrapper.className="tiny_cursive-status-row";const gradeName=this.create("span");gradeName.textContent=`${this.gradings}: `;const gradeValue=this.create("span");return null!=submission&&submission.grade?gradeValue.textContent=Number(submission.grade.grade)>0?submission.grade.grade:this.gradenot:gradeValue.textContent=this.gradenot,gradeWrapper.append(gradeName,gradeValue),wrapper.append(statusWrapper,gradeWrapper,modifiedWrapper),wrapper.innerHTML}wordCounter(status){const wordCount=this.create("div"),labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");icon.className="me-2",icon.innerHTML=_svg_repo.default.assignment,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.wordCount}:`,value.textContent="0",value.className="text-primary",value.style.fontWeight="600",value.style.fontSize="14px",wordCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2",wordCount.append(labelDiv,value),wordCount.style.fontSize="12px";return new MutationObserver((()=>{const newText=status.textContent.trim();value.textContent=`${newText.replace("words","")}`})).observe(status,{characterData:!0,subtree:!0,childList:!0}),wordCount}timerCountDown(timer){let warningDiv=document.querySelector("#user-notifications > div");if(warningDiv){var _clone$querySelector;let clone=warningDiv.cloneNode(!0);null===(_clone$querySelector=clone.querySelector("button"))||void 0===_clone$querySelector||_clone$querySelector.remove(),this.editor.notificationManager.open({text:clone.textContent,type:"error"})}const timerCount=this.create("div");timerCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2";const labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");if(icon.innerHTML=_svg_repo.default.time,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.timeleft}: }`,value.textContent="00:00:00",value.className=warningDiv?"text-danger":"text-primary",Object.assign(value.style,{fontWeight:"600",fontSize:"14px"}),timerCount.append(labelDiv,value),timerCount.style.fontSize="12px",timer){new MutationObserver((()=>{const newText=timer.textContent.trim();value.textContent=`${newText}`})).observe(timer,{characterData:!0,subtree:!0,childList:!0})}else value.textContent=this.nolimit;return timerCount}generateStudentInfo(user,course){const wrapper=this.create("div"),nameWrapper=this.create("div"),usernameWrapper=this.create("div"),courseWrapper=this.create("div"),nameLabel=this.create("span"),nameValue=this.create("span"),usernameLabel=this.create("span"),usernameValue=this.create("span"),courseLabel=this.create("span"),courseValue=this.create("span");return nameLabel.textContent=`${this.name}`,nameValue.textContent=user.fullname,usernameLabel.textContent=`${this.userename}: `,usernameValue.textContent=user.username,courseLabel.textContent=`${this.course}: `,courseValue.textContent=course.title,nameWrapper.className="d-flex justify-content-between",usernameWrapper.className="d-flex justify-content-between",courseWrapper.className="d-flex justify-content-between",nameWrapper.append(nameLabel,nameValue),usernameWrapper.append(usernameLabel,usernameValue),courseWrapper.append(courseLabel,courseValue),wrapper.append(nameWrapper,usernameWrapper,courseWrapper),wrapper.innerHTML}generateImportantDates(open,due){const wrapper=this.create("div");let openDate=null,dueDate=null;const openedWrapper=this.create("div"),dueWrapper=this.create("div"),remainingWrapper=this.create("div"),openedLabel=this.create("span"),openedValue=this.create("span"),dueLabel=this.create("span"),dueValue=this.create("span"),remainingLabel=this.create("span"),remainingValue=this.create("span");return"quiz"===this.module?(openDate=1e3*open,dueDate=1e3*due):(openDate=this.extractDate(null==open?void 0:open.textContent),dueDate=this.extractDate(null==due?void 0:due.textContent)),openedLabel.textContent=`${this.opened}: `,openedValue.textContent=this.formatDate(openDate?new Date(openDate):null),openedValue.className="text-dark",dueLabel.textContent=`${this.due}: `,dueValue.textContent=this.formatDate(dueDate?new Date(dueDate):null),dueValue.className="text-danger",remainingLabel.textContent=`${this.remaining}: `,remainingValue.textContent=this.calculateDate(dueDate),remainingValue.className="text-danger",openedWrapper.className="d-flex justify-content-between",dueWrapper.className="d-flex justify-content-between",remainingWrapper.className="d-flex align-items-center justify-content-between mt-2 pt-2 border-top",openedWrapper.append(openedLabel,openedValue),dueWrapper.append(dueLabel,dueValue),remainingWrapper.append(remainingLabel,remainingValue),wrapper.append(openedWrapper,dueWrapper,remainingWrapper),wrapper.innerHTML}formatDate(date){if(!date)return"-";return date.toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"numeric",minute:"numeric",hour12:!0})}extractDate(text){if(!text)return"-";const parts=null==text?void 0:text.split(":");return parts.length>1?parts.slice(1).join(":").trim():text.trim()}calculateDate(date){if(!date)return"-";const diffMs=new Date(date)-new Date;if(diffMs<=0)return"Overdue";return`${Math.floor(diffMs/864e5)} days, ${Math.floor(diffMs/36e5%24)} hours`}fullPageModule(module){var _current$contentDocum,_current$contentWindo,_current$contentWindo2,_document$getElementB;let current="quiz"===this.module?document.getElementById(`${module}_ifr`):document.querySelector(`#${module}_ifr`),p1=current.parentElement,p2=p1.parentElement,p4=p2.parentElement.parentElement,statusBar=document.querySelector(".tox-statusbar__right-container > button"),assignName=document.querySelector(".page-context-header"),header=this.create("div"),btn=null;assignName.classList.remove("mb-2"),header.id="tiny_cursive-fullpage-custom-header",Object.assign(header.style,{backgroundColor:"white",display:"flex",justifyContent:"space-between"}),"quiz"===this.module?(btn=document.querySelector("#mod_quiz-next-nav").cloneNode(!0),btn.className="tiny_cursive-fullpage-submit-btn",btn.style.margin=".5rem"):(btn=this.create("input"),btn.className="tiny_cursive-fullpage-submit-btn",btn.value=this.savechanges,btn.type="submit",btn.style.margin=".5rem");const leftSide=this.create("div"),rightSide=this.create("div");let commonStyle={display:"flex",alignItems:"center",margin:"0 1rem"};Object.assign(leftSide.style,commonStyle),rightSide.id="tiny_cursive-fullpage-right-wrapper",Object.assign(rightSide.style,commonStyle),rightSide.appendChild(btn),leftSide.appendChild(assignName.cloneNode(!0)),header.appendChild(leftSide),header.appendChild(rightSide),p4.insertBefore(header,p4.firstChild),p2.style.backgroundColor="#efefef",Object.assign(current.style,{width:"750px",minWidth:"750px",boxShadow:"0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)"}),Object.assign(p1.style,{display:"flex",justifyContent:"center",outline:"none",margin:"2rem 0 0"});const style=this.create("style");style.id="tiny_cursive-fullpage-mode-style",style.textContent="\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }",document.head.appendChild(style);let iframeBody=(null===(_current$contentDocum=current.contentDocument)||void 0===_current$contentDocum?void 0:_current$contentDocum.body)||(null===(_current$contentWindo=current.contentWindow)||void 0===_current$contentWindo||null===(_current$contentWindo2=_current$contentWindo.document)||void 0===_current$contentWindo2?void 0:_current$contentWindo2.body);iframeBody&&(iframeBody.style.padding="0.5in"),p2.style.position="relative",null===(_document$getElementB=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB||_document$getElementB.remove();let toggle=this.create("div");toggle.id="cursive-fullpagemode-sidebar-toggle",toggle.innerHTML=_svg_repo.default.hamburger,p2.appendChild(toggle),p2.appendChild(this.docSideBar(statusBar))}normalizePage(editorId){var _document$getElementB2,_document$getElementB3,_current$contentDocum2,_current$contentWindo3,_current$contentWindo4,_document$head$queryS;null===(_document$getElementB2=document.getElementById("tiny_cursive-fullpage-custom-header"))||void 0===_document$getElementB2||_document$getElementB2.remove(),null===(_document$getElementB3=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB3||_document$getElementB3.remove();let current=document.getElementById(editorId),p1=current.parentElement,p2=p1.parentElement;Object.assign(p2.style,{backgroundColor:"",position:""}),Object.assign(current.style,{width:"",minWidth:"",boxShadow:""}),Object.assign(p1.style,{display:"",justifyContent:"",outline:"",margin:""}),p1.classList.remove("tiny-cursive-editor-container");let iframeBody=(null===(_current$contentDocum2=current.contentDocument)||void 0===_current$contentDocum2?void 0:_current$contentDocum2.body)||(null===(_current$contentWindo3=current.contentWindow)||void 0===_current$contentWindo3||null===(_current$contentWindo4=_current$contentWindo3.document)||void 0===_current$contentWindo4?void 0:_current$contentWindo4.body);iframeBody&&(iframeBody.style.padding="0"),null===(_document$head$queryS=document.head.querySelector("#tiny_cursive-fullpage-mode-style"))||void 0===_document$head$queryS||_document$head$queryS.remove()}checkForumSubject(){const form=document.querySelector("#tiny_cursive-fullpage-right-wrapper > input"),msg=this.subjectnot;form&&form.addEventListener("click",(e=>{const subjectInput=document.getElementById("id_subject");let content=this.editor.getContent().trim();subjectInput&&""!==subjectInput.value.trim()&&""!==content||(e.preventDefault(),e.stopPropagation(),this.editor.windowManager.alert(msg))}))}getSidebarTitle(){const[assign,discus,quiz,lesson]=this.getText("sbTitle");switch(this.module){case"assign":return{title:assign,icon:_svg_repo.default.assignment};case"forum":return{title:discus,icon:_svg_repo.default.forum};case"lesson":return{title:lesson,icon:_svg_repo.default.forum};case"quiz":return{title:quiz,icon:_svg_repo.default.quiz};default:return{title:"Page",icon:_svg_repo.default.quiz}}}getTimerBlock(module){switch(module){case"assign":return document.querySelector("#mod_assign_timelimit_block > div > div");case"forum":return document.querySelector("#mod_forum_timelimit_block");case"lesson":return document.querySelector("#lesson-timer");case"quiz":return document.querySelector("#quiz-time-left");default:return null}}getQuestionId(editoId){try{return editoId&&"string"==typeof editoId?editoId.replace(/^q(\d+):(\d+)_.*$/,"$1-$2"):""}catch(error){return window.console.error("Error getting question ID:",error),""}}initStrings(){[this.details,this.studentInfo,this.progress,this.description,this.replyingto,this.answeringto,this.importantdates,this.rubrics,this.subStatus,this.status,this.draft,this.draftnot,this.lastModified,this.gradings,this.gradenot,this.wordCount,this.timeleft,this.nolimit,this.name,this.userename,this.course,this.opened,this.due,this.overdue,this.remaining,this.savechanges,this.subjectnot]=this.getText("docSideBar")}getText(key){return JSON.parse(localStorage.getItem(key))||[]}create(tag){return document.createElement(tag)}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_svg_repo=(obj=_svg_repo)&&obj.__esModule?obj:{default:obj};return _exports.default=class{constructor(User,Rubrics,submission,modulename,editor,quizInfo){this.User=User,this.Rubrics=Rubrics,this.submission=submission,this.module=modulename,this.editor=editor,this.moduleIcon=_svg_repo.default.assignment,this.quizInfo=quizInfo,this.initStrings()}normalMode(){var _this$editor;let id=(null===(_this$editor=this.editor)||void 0===_this$editor?void 0:_this$editor.id)+"_ifr";("assign"===this.module||"quiz"===this.module||"forum"===this.module||"lesson"===this.module)&&this.normalizePage(id)}fullPageMode(){var _this$editor4,_this$editor2;if("assign"===this.module)this.moduleIcon=_svg_repo.default.assignment,this.fullPageModule(null===(_this$editor2=this.editor)||void 0===_this$editor2?void 0:_this$editor2.id);else if("forum"===this.module){var _this$editor3;this.moduleIcon=_svg_repo.default.forum,this.fullPageModule(null===(_this$editor3=this.editor)||void 0===_this$editor3?void 0:_this$editor3.id)}else if("quiz"===this.module&&null!==(_this$editor4=this.editor)&&void 0!==_this$editor4&&_this$editor4.id){var _this$editor5;this.moduleIcon=_svg_repo.default.quiz,this.fullPageModule(null===(_this$editor5=this.editor)||void 0===_this$editor5?void 0:_this$editor5.id)}else if("lesson"===this.module){var _this$editor6;this.moduleIcon=_svg_repo.default.lesson,this.fullPageModule(null===(_this$editor6=this.editor)||void 0===_this$editor6?void 0:_this$editor6.id)}}docSideBar(status){var _this$editor7;const replyId=new URL(window.location.href).searchParams.get("reply"),toggle=document.querySelector("#cursive-fullpagemode-sidebar-toggle"),timelimitBlock=this.getTimerBlock(this.module),headerInfo=this.getSidebarTitle(),progressBar=document.querySelector(".box.progress_bar"),courseName=document.querySelector("#page-navbar > nav > ol > li:nth-child(1) > a"),courseDes=document.querySelector("#intro"),Dates=document.querySelector(".activity-dates");let openDate=null==Dates?void 0:Dates.querySelector("div:nth-child(1)"),dueDate=null==Dates?void 0:Dates.querySelector("div:nth-child(2)");const container=this.create("div");Object.assign(container,{id:"cursive-fullpagemode-sidebar",className:"bg-white h-100 shadow"}),Object.assign(container.style,{width:"300px",overflow:"auto"});const crossBtn=this.create("span");Object.assign(crossBtn,{id:"cursive-collapse-sidebar",className:"btn p-2",innerHTML:_svg_repo.default.close}),crossBtn.addEventListener("click",(()=>{container.style.transition="width 0.3s ease",container.style.width="0",toggle.style.display="flex"})),null==toggle||toggle.addEventListener("click",(function(){toggle.style.display="none",container.style.width="300px"}));const btnWrapper=this.create("div");Object.assign(btnWrapper,{padding:"0 1rem",position:"sticky",top:"0",backgroundColor:"white"}),btnWrapper.append(crossBtn);const header=this.create("div");header.className="border-bottom p-3 bg-light",Object.assign(header.style,{position:"sticky",top:"0"});const headerTitle=this.create("h3");headerTitle.className="mb-3 d-flex align-items-center",headerTitle.textContent="".concat(headerInfo.title," ").concat(this.details),headerTitle.style.fontWeight="600";const headerIcon=document.querySelector(".page-header-image > div");headerIcon&&headerTitle.prepend(headerIcon.cloneNode(!0));let wordCount=this.wordCounter(status);null!=timelimitBlock&&timelimitBlock.textContent?header.append(headerTitle,wordCount,this.timerCountDown(timelimitBlock)):header.append(headerTitle,wordCount);const content=this.create("div");if(content.className="p-3",content.append(this.createBox({bg:"bg-info",titleColor:"text-info",icon:_svg_repo.default.people,title:this.studentInfo,bodyHTML:this.generateStudentInfo(this.User,courseName)})),"lesson"===this.module&&progressBar&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.progress,bodyHTML:progressBar.innerHTML})),courseDes&&""!==(null==courseDes?void 0:courseDes.textContent.trim())&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:"".concat(this.getSidebarTitle().title," ").concat(this.description),bodyHTML:courseDes.innerHTML})),"forum"===this.module&&replyId){this.checkForumSubject();let replyPost=document.querySelector("#post-content-".concat(replyId));null!=replyPost&&replyPost.textContent.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.replyingto,bodyHTML:replyPost.textContent.trim()}))}if("quiz"===this.module&&null!==(_this$editor7=this.editor)&&void 0!==_this$editor7&&_this$editor7.id){var _this$editor8;let questionId=this.getQuestionId(null===(_this$editor8=this.editor)||void 0===_this$editor8?void 0:_this$editor8.id),question=document.querySelector("#question-".concat(questionId," .qtext")),intro=atob(this.quizInfo.intro);null!=question&&question.textContent.trim()&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:this.moduleIcon,title:this.answeringto,bodyHTML:question.textContent})),intro&&""!==intro.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:"".concat(this.quiz," ").concat(this.description),bodyHTML:intro})),Number(this.quizInfo.open)&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(Number(this.quizInfo.open),Number(this.quizInfo.close))}))}return Object.keys(this.Rubrics).length&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.rubrics,bodyHTML:this.generateRubrics(this.Rubrics)})),Dates&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(openDate,dueDate)})),"assign"===this.module&&content.append(this.createBox({bg:"bg-green",titleColor:"text-success",icon:this.moduleIcon,title:this.subStatus,bodyHTML:this.submissionStatus(this.submission)})),container.append(btnWrapper,header,content),container}createBox(_ref){let{bg:bg,titleColor:titleColor,icon:icon,title:title,bodyHTML:bodyHTML}=_ref;const box=this.create("div");box.className="tiny_cursive-fullpage-card ".concat(bg);const heading=this.create("h4");heading.className="tiny_cursive-fullpage-card-header ".concat(titleColor," d-flex align-items-center"),heading.innerHTML="".concat(icon," ").concat(title);const body=this.create("div");return body.className="tiny_cursive-fullpage-card-body",body.innerHTML=bodyHTML,box.append(heading,body),box}generateRubrics(Rubrics){const wrapper=this.create("div");return Rubrics.forEach((rubric=>{const rubricDiv=this.create("div");rubricDiv.className="tiny_cursive-rubric-card";const title=this.create("h3");title.className="tiny_cursive-rubric-title",title.textContent=rubric.description,rubricDiv.appendChild(title),Object.values(rubric.levels).forEach((level=>{const levelDiv=this.create("div"),score=Number(level.score);levelDiv.className=0===score?"tiny_cursive-rubric-level tiny_cursive-rubric-low":score<=2?"tiny_cursive-rubric-level tiny_cursive-rubric-mid":"tiny_cursive-rubric-level tiny_cursive-rubric-high",levelDiv.textContent="".concat(level.definition," / ").concat(level.score),rubricDiv.appendChild(levelDiv)})),wrapper.appendChild(rubricDiv)})),wrapper.innerHTML}submissionStatus(submission){var _submission$current,_submission$current2;const wrapper=this.create("div"),statusWrapper=this.create("div");statusWrapper.className="tiny_cursive-status-row";const statusName=this.create("span");statusName.textContent="".concat(this.status,":");const statusValue=this.create("span"),isNew="new"===(null==submission||null===(_submission$current=submission.current)||void 0===_submission$current?void 0:_submission$current.status);statusValue.textContent=isNew?this.draftnot:this.draft,statusValue.className="tiny_cursive-status-value ".concat(isNew?"tiny_cursive-status-red":"tiny_cursive-status-green"),statusWrapper.append(statusName,statusValue);const modifiedWrapper=this.create("div");modifiedWrapper.className="tiny_cursive-status-row";const modifiedName=this.create("span");modifiedName.textContent="".concat(this.lastModified,": ");const modifiedValue=this.create("span");if(null!=submission&&null!==(_submission$current2=submission.current)&&void 0!==_submission$current2&&_submission$current2.timemodified){const date=new Date(1e3*submission.current.timemodified);modifiedValue.textContent=this.formatDate(date)}else modifiedValue.textContent="N/A";modifiedWrapper.append(modifiedName,modifiedValue);const gradeWrapper=this.create("div");gradeWrapper.className="tiny_cursive-status-row";const gradeName=this.create("span");gradeName.textContent="".concat(this.gradings,": ");const gradeValue=this.create("span");return null!=submission&&submission.grade?gradeValue.textContent=Number(submission.grade.grade)>0?submission.grade.grade:this.gradenot:gradeValue.textContent=this.gradenot,gradeWrapper.append(gradeName,gradeValue),wrapper.append(statusWrapper,gradeWrapper,modifiedWrapper),wrapper.innerHTML}wordCounter(status){const wordCount=this.create("div"),labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");icon.className="me-2",icon.innerHTML=_svg_repo.default.assignment,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent="".concat(this.wordCount,":"),value.textContent="0",value.className="text-primary",value.style.fontWeight="600",value.style.fontSize="14px",wordCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2",wordCount.append(labelDiv,value),wordCount.style.fontSize="12px";return new MutationObserver((()=>{const newText=status.textContent.trim();value.textContent="".concat(newText.replace("words",""))})).observe(status,{characterData:!0,subtree:!0,childList:!0}),wordCount}timerCountDown(timer){let warningDiv=document.querySelector("#user-notifications > div");if(warningDiv){var _clone$querySelector;let clone=warningDiv.cloneNode(!0);null===(_clone$querySelector=clone.querySelector("button"))||void 0===_clone$querySelector||_clone$querySelector.remove(),this.editor.notificationManager.open({text:clone.textContent,type:"error"})}const timerCount=this.create("div");timerCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2";const labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");if(icon.innerHTML=_svg_repo.default.time,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent="".concat(this.timeleft,": }"),value.textContent="00:00:00",value.className=warningDiv?"text-danger":"text-primary",Object.assign(value.style,{fontWeight:"600",fontSize:"14px"}),timerCount.append(labelDiv,value),timerCount.style.fontSize="12px",timer){new MutationObserver((()=>{const newText=timer.textContent.trim();value.textContent="".concat(newText)})).observe(timer,{characterData:!0,subtree:!0,childList:!0})}else value.textContent=this.nolimit;return timerCount}generateStudentInfo(user,course){const wrapper=this.create("div"),nameWrapper=this.create("div"),usernameWrapper=this.create("div"),courseWrapper=this.create("div"),nameLabel=this.create("span"),nameValue=this.create("span"),usernameLabel=this.create("span"),usernameValue=this.create("span"),courseLabel=this.create("span"),courseValue=this.create("span");return nameLabel.textContent="".concat(this.name),nameValue.textContent=user.fullname,usernameLabel.textContent="".concat(this.userename,": "),usernameValue.textContent=user.username,courseLabel.textContent="".concat(this.course,": "),courseValue.textContent=course.title,nameWrapper.className="d-flex justify-content-between",usernameWrapper.className="d-flex justify-content-between",courseWrapper.className="d-flex justify-content-between",nameWrapper.append(nameLabel,nameValue),usernameWrapper.append(usernameLabel,usernameValue),courseWrapper.append(courseLabel,courseValue),wrapper.append(nameWrapper,usernameWrapper,courseWrapper),wrapper.innerHTML}generateImportantDates(open,due){const wrapper=this.create("div");let openDate=null,dueDate=null;const openedWrapper=this.create("div"),dueWrapper=this.create("div"),remainingWrapper=this.create("div"),openedLabel=this.create("span"),openedValue=this.create("span"),dueLabel=this.create("span"),dueValue=this.create("span"),remainingLabel=this.create("span"),remainingValue=this.create("span");return"quiz"===this.module?(openDate=1e3*open,dueDate=1e3*due):(openDate=this.extractDate(null==open?void 0:open.textContent),dueDate=this.extractDate(null==due?void 0:due.textContent)),openedLabel.textContent="".concat(this.opened,": "),openedValue.textContent=this.formatDate(openDate?new Date(openDate):null),openedValue.className="text-dark",dueLabel.textContent="".concat(this.due,": "),dueValue.textContent=this.formatDate(dueDate?new Date(dueDate):null),dueValue.className="text-danger",remainingLabel.textContent="".concat(this.remaining,": "),remainingValue.textContent=this.calculateDate(dueDate),remainingValue.className="text-danger",openedWrapper.className="d-flex justify-content-between",dueWrapper.className="d-flex justify-content-between",remainingWrapper.className="d-flex align-items-center justify-content-between mt-2 pt-2 border-top",openedWrapper.append(openedLabel,openedValue),dueWrapper.append(dueLabel,dueValue),remainingWrapper.append(remainingLabel,remainingValue),wrapper.append(openedWrapper,dueWrapper,remainingWrapper),wrapper.innerHTML}formatDate(date){if(!date)return"-";return date.toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"numeric",minute:"numeric",hour12:!0})}extractDate(text){if(!text)return"-";const parts=null==text?void 0:text.split(":");return parts.length>1?parts.slice(1).join(":").trim():text.trim()}calculateDate(date){if(!date)return"-";const diffMs=new Date(date)-new Date;if(diffMs<=0)return"Overdue";{const diffDays=Math.floor(diffMs/864e5),diffHours=Math.floor(diffMs/36e5%24);return"".concat(diffDays," days, ").concat(diffHours," hours")}}fullPageModule(module){var _current$contentDocum,_current$contentWindo,_current$contentWindo2,_document$getElementB;let current="quiz"===this.module?document.getElementById("".concat(module,"_ifr")):document.querySelector("#".concat(module,"_ifr")),p1=current.parentElement,p2=p1.parentElement,p4=p2.parentElement.parentElement,statusBar=document.querySelector(".tox-statusbar__right-container > button"),assignName=document.querySelector(".page-context-header"),header=this.create("div"),btn=null;assignName.classList.remove("mb-2"),header.id="tiny_cursive-fullpage-custom-header",Object.assign(header.style,{backgroundColor:"white",display:"flex",justifyContent:"space-between"}),"quiz"===this.module?(btn=document.querySelector("#mod_quiz-next-nav").cloneNode(!0),btn.className="tiny_cursive-fullpage-submit-btn",btn.style.margin=".5rem"):(btn=this.create("input"),btn.className="tiny_cursive-fullpage-submit-btn",btn.value=this.savechanges,btn.type="submit",btn.style.margin=".5rem");const leftSide=this.create("div"),rightSide=this.create("div");let commonStyle={display:"flex",alignItems:"center",margin:"0 1rem"};Object.assign(leftSide.style,commonStyle),rightSide.id="tiny_cursive-fullpage-right-wrapper",Object.assign(rightSide.style,commonStyle),rightSide.appendChild(btn),leftSide.appendChild(assignName.cloneNode(!0)),header.appendChild(leftSide),header.appendChild(rightSide),p4.insertBefore(header,p4.firstChild),p2.style.backgroundColor="#efefef",Object.assign(current.style,{width:"750px",minWidth:"750px",boxShadow:"0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)"}),Object.assign(p1.style,{display:"flex",justifyContent:"center",outline:"none",margin:"2rem 0 0"});const style=this.create("style");style.id="tiny_cursive-fullpage-mode-style",style.textContent="\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }",document.head.appendChild(style);let iframeBody=(null===(_current$contentDocum=current.contentDocument)||void 0===_current$contentDocum?void 0:_current$contentDocum.body)||(null===(_current$contentWindo=current.contentWindow)||void 0===_current$contentWindo||null===(_current$contentWindo2=_current$contentWindo.document)||void 0===_current$contentWindo2?void 0:_current$contentWindo2.body);iframeBody&&(iframeBody.style.padding="0.5in"),p2.style.position="relative",null===(_document$getElementB=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB||_document$getElementB.remove();let toggle=this.create("div");toggle.id="cursive-fullpagemode-sidebar-toggle",toggle.innerHTML=_svg_repo.default.hamburger,p2.appendChild(toggle),p2.appendChild(this.docSideBar(statusBar))}normalizePage(editorId){var _document$getElementB2,_document$getElementB3,_current$contentDocum2,_current$contentWindo3,_current$contentWindo4,_document$head$queryS;null===(_document$getElementB2=document.getElementById("tiny_cursive-fullpage-custom-header"))||void 0===_document$getElementB2||_document$getElementB2.remove(),null===(_document$getElementB3=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB3||_document$getElementB3.remove();let current=document.getElementById(editorId),p1=current.parentElement,p2=p1.parentElement;Object.assign(p2.style,{backgroundColor:"",position:""}),Object.assign(current.style,{width:"",minWidth:"",boxShadow:""}),Object.assign(p1.style,{display:"",justifyContent:"",outline:"",margin:""}),p1.classList.remove("tiny-cursive-editor-container");let iframeBody=(null===(_current$contentDocum2=current.contentDocument)||void 0===_current$contentDocum2?void 0:_current$contentDocum2.body)||(null===(_current$contentWindo3=current.contentWindow)||void 0===_current$contentWindo3||null===(_current$contentWindo4=_current$contentWindo3.document)||void 0===_current$contentWindo4?void 0:_current$contentWindo4.body);iframeBody&&(iframeBody.style.padding="0"),null===(_document$head$queryS=document.head.querySelector("#tiny_cursive-fullpage-mode-style"))||void 0===_document$head$queryS||_document$head$queryS.remove()}checkForumSubject(){const form=document.querySelector("#tiny_cursive-fullpage-right-wrapper > input"),msg=this.subjectnot;form&&form.addEventListener("click",(e=>{const subjectInput=document.getElementById("id_subject");let content=this.editor.getContent().trim();subjectInput&&""!==subjectInput.value.trim()&&""!==content||(e.preventDefault(),e.stopPropagation(),this.editor.windowManager.alert(msg))}))}getSidebarTitle(){const[assign,discus,quiz,lesson]=this.getText("sbTitle");switch(this.module){case"assign":return{title:assign,icon:_svg_repo.default.assignment};case"forum":return{title:discus,icon:_svg_repo.default.forum};case"lesson":return{title:lesson,icon:_svg_repo.default.forum};case"quiz":return{title:quiz,icon:_svg_repo.default.quiz};default:return{title:"Page",icon:_svg_repo.default.quiz}}}getTimerBlock(module){switch(module){case"assign":return document.querySelector("#mod_assign_timelimit_block > div > div");case"forum":return document.querySelector("#mod_forum_timelimit_block");case"lesson":return document.querySelector("#lesson-timer");case"quiz":return document.querySelector("#quiz-time-left");default:return null}}getQuestionId(editoId){try{return editoId&&"string"==typeof editoId?editoId.replace(/^q(\d+):(\d+)_.*$/,"$1-$2"):""}catch(error){return window.console.error("Error getting question ID:",error),""}}initStrings(){[this.details,this.studentInfo,this.progress,this.description,this.replyingto,this.answeringto,this.importantdates,this.rubrics,this.subStatus,this.status,this.draft,this.draftnot,this.lastModified,this.gradings,this.gradenot,this.wordCount,this.timeleft,this.nolimit,this.name,this.userename,this.course,this.opened,this.due,this.overdue,this.remaining,this.savechanges,this.subjectnot]=this.getText("docSideBar")}getText(key){return JSON.parse(localStorage.getItem(key))||[]}create(tag){return document.createElement(tag)}},_exports.default})); //# sourceMappingURL=document_view.min.js.map \ No newline at end of file diff --git a/amd/build/document_view.min.js.map b/amd/build/document_view.min.js.map index 39104e64..58382050 100644 --- a/amd/build/document_view.min.js.map +++ b/amd/build/document_view.min.js.map @@ -1 +1 @@ -{"version":3,"file":"document_view.min.js","sources":["../src/document_view.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module provides functionality for document view management in the Tiny editor,\n * including full page mode display and sidebar information\n * @module tiny_cursive/document_view\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Icons from 'tiny_cursive/svg_repo';\nexport default class DocumentView {\n\n constructor(User, Rubrics, submission, modulename, editor, quizInfo) {\n this.User = User;\n this.Rubrics = Rubrics;\n this.submission = submission;\n this.module = modulename;\n this.editor = editor;\n this.moduleIcon = Icons.assignment;\n this.quizInfo = quizInfo;\n this.initStrings();\n }\n\n normalMode() {\n let id = this.editor?.id + \"_ifr\";\n if (this.module === 'assign') {\n this.normalizePage(id);\n } else if (this.module === 'quiz') {\n this.normalizePage(id);\n } else if (this.module === 'forum') {\n this.normalizePage(id);\n } else if (this.module === 'lesson') {\n this.normalizePage(id);\n }\n }\n\n fullPageMode() {\n\n if (this.module === 'assign') {\n this.moduleIcon = Icons.assignment;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'forum') {\n this.moduleIcon = Icons.forum;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'quiz' && this.editor?.id) {\n this.moduleIcon = Icons.quiz;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'lesson') {\n this.moduleIcon = Icons.lesson;\n this.fullPageModule(this.editor?.id);\n }\n }\n\n docSideBar(status) {\n\n const url = new URL(window.location.href);\n const replyId = url.searchParams.get(\"reply\");\n const toggle = document.querySelector('#cursive-fullpagemode-sidebar-toggle');\n const timelimitBlock = this.getTimerBlock(this.module);\n const headerInfo = this.getSidebarTitle();\n const progressBar = document.querySelector('.box.progress_bar');\n\n const courseName = document.querySelector('#page-navbar > nav > ol > li:nth-child(1) > a');\n const courseDes = document.querySelector('#intro');\n const Dates = document.querySelector('.activity-dates');\n\n let openDate = Dates?.querySelector('div:nth-child(1)');\n let dueDate = Dates?.querySelector('div:nth-child(2)');\n\n const container = this.create('div');\n Object.assign(container, {\n id: 'cursive-fullpagemode-sidebar',\n className: 'bg-white h-100 shadow'\n });\n Object.assign(container.style, {\n width: '300px',\n overflow: 'auto'\n });\n\n const crossBtn = this.create('span');\n Object.assign(crossBtn, {\n id: 'cursive-collapse-sidebar',\n className: 'btn p-2',\n innerHTML: Icons.close\n });\n\n crossBtn.addEventListener('click', () => {\n container.style.transition = 'width 0.3s ease';\n container.style.width = '0';\n toggle.style.display = 'flex';\n });\n toggle?.addEventListener('click', function() {\n toggle.style.display = 'none';\n container.style.width = '300px';\n });\n\n const btnWrapper = this.create('div');\n Object.assign(btnWrapper, {\n padding: '0 1rem',\n position: 'sticky',\n top: '0',\n backgroundColor: 'white'\n });\n btnWrapper.append(crossBtn);\n\n\n const header = this.create('div');\n header.className = 'border-bottom p-3 bg-light';\n Object.assign(header.style, {\n position: 'sticky',\n top: '0'\n });\n\n const headerTitle = this.create('h3');\n headerTitle.className = 'mb-3 d-flex align-items-center';\n headerTitle.textContent = `${headerInfo.title} ${this.details}`;\n headerTitle.style.fontWeight = '600';\n\n const headerIcon = document.querySelector('.page-header-image > div');\n if (headerIcon) {\n headerTitle.prepend(headerIcon.cloneNode(true));\n }\n\n let wordCount = this.wordCounter(status);\n if (timelimitBlock?.textContent) {\n header.append(headerTitle, wordCount, this.timerCountDown(timelimitBlock));\n } else {\n header.append(headerTitle, wordCount);\n }\n\n const content = this.create('div');\n content.className = 'p-3';\n\n content.append(\n this.createBox({\n bg: 'bg-info',\n titleColor: 'text-info',\n icon: Icons.people,\n title: this.studentInfo,\n bodyHTML: this.generateStudentInfo(this.User, courseName)\n })\n );\n\n if (this.module === 'lesson' && progressBar) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.progress,\n bodyHTML: progressBar.innerHTML\n })\n );\n }\n\n if (courseDes && courseDes?.textContent.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.getSidebarTitle().title} ${this.description}`,\n bodyHTML: courseDes.innerHTML\n })\n );\n }\n\n if (this.module === 'forum' && replyId) {\n this.checkForumSubject();\n let replyPost = document.querySelector(`#post-content-${replyId}`);\n if (replyPost?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.replyingto,\n bodyHTML: replyPost.textContent.trim()\n })\n );\n }\n }\n\n if (this.module === 'quiz' && this.editor?.id) {\n\n let questionId = this.getQuestionId(this.editor?.id);\n let question = document.querySelector(`#question-${questionId} .qtext`);\n let intro = atob(this.quizInfo.intro);\n\n if (question?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.answeringto,\n bodyHTML: question.textContent\n })\n );\n }\n\n if (intro && intro.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.quiz} ${this.description}`,\n bodyHTML: intro\n })\n );\n }\n\n if (Number(this.quizInfo.open)) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(Number(this.quizInfo.open), Number(this.quizInfo.close))\n })\n );\n }\n }\n\n if (Object.keys(this.Rubrics).length) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.rubrics,\n bodyHTML: this.generateRubrics(this.Rubrics)\n })\n );\n }\n\n if (Dates) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(openDate, dueDate)\n })\n );\n }\n if (this.module === 'assign') {\n content.append(\n this.createBox({\n bg: 'bg-green',\n titleColor: 'text-success',\n icon: this.moduleIcon,\n title: this.subStatus,\n bodyHTML: this.submissionStatus(this.submission)\n })\n );\n }\n\n container.append(btnWrapper, header, content);\n return container;\n\n }\n // Helper to create info boxes\n createBox({bg, titleColor, icon, title, bodyHTML}) {\n const box = this.create('div');\n box.className = `tiny_cursive-fullpage-card ${bg}`;\n\n const heading = this.create('h4');\n heading.className = `tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`;\n heading.innerHTML = `${icon} ${title}`;\n\n const body = this.create('div');\n body.className = `tiny_cursive-fullpage-card-body`;\n body.innerHTML = bodyHTML;\n\n box.append(heading, body);\n return box;\n }\n\n generateRubrics(Rubrics) {\n const wrapper = this.create('div');\n\n Rubrics.forEach(rubric => {\n const rubricDiv = this.create('div');\n rubricDiv.className = 'tiny_cursive-rubric-card';\n\n const title = this.create('h3');\n title.className = 'tiny_cursive-rubric-title';\n title.textContent = rubric.description;\n rubricDiv.appendChild(title);\n\n Object.values(rubric.levels).forEach(level => {\n const levelDiv = this.create('div');\n const score = Number(level.score);\n\n // Assign background color class based on score\n if (score === 0) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-low';\n } else if (score <= 2) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-mid';\n } else {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-high';\n }\n\n levelDiv.textContent = `${level.definition} / ${level.score}`;\n rubricDiv.appendChild(levelDiv);\n });\n\n wrapper.appendChild(rubricDiv);\n });\n\n return wrapper.innerHTML;\n }\n\n submissionStatus(submission) {\n const wrapper = this.create('div');\n\n const statusWrapper = this.create('div');\n statusWrapper.className = 'tiny_cursive-status-row';\n\n const statusName = this.create('span');\n statusName.textContent = `${this.status}:`;\n\n const statusValue = this.create('span');\n const isNew = submission?.current?.status === 'new';\n statusValue.textContent = isNew ? this.draftnot : this.draft;\n statusValue.className = `tiny_cursive-status-value ${isNew ? 'tiny_cursive-status-red' : 'tiny_cursive-status-green'}`;\n\n statusWrapper.append(statusName, statusValue);\n\n const modifiedWrapper = this.create('div');\n modifiedWrapper.className = 'tiny_cursive-status-row';\n\n const modifiedName = this.create('span');\n modifiedName.textContent = `${this.lastModified}: `;\n\n const modifiedValue = this.create('span');\n if (submission?.current?.timemodified) {\n const date = new Date(submission.current.timemodified * 1000);\n modifiedValue.textContent = this.formatDate(date);\n } else {\n modifiedValue.textContent = 'N/A';\n }\n modifiedWrapper.append(modifiedName, modifiedValue);\n\n const gradeWrapper = this.create('div');\n gradeWrapper.className = 'tiny_cursive-status-row';\n\n const gradeName = this.create('span');\n gradeName.textContent = `${this.gradings}: `;\n\n const gradeValue = this.create('span');\n\n if (submission?.grade) {\n gradeValue.textContent = Number(submission.grade.grade) > 0\n ? submission.grade.grade\n : this.gradenot;\n } else {\n gradeValue.textContent = this.gradenot;\n }\n\n gradeWrapper.append(gradeName, gradeValue);\n wrapper.append(statusWrapper, gradeWrapper, modifiedWrapper);\n return wrapper.innerHTML;\n }\n\n wordCounter(status) {\n const wordCount = this.create('div');\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n\n icon.className = 'me-2';\n icon.innerHTML = Icons.assignment;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.wordCount}:`;\n value.textContent = '0';\n value.className = 'text-primary';\n value.style.fontWeight = '600';\n value.style.fontSize = '14px';\n\n wordCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n wordCount.append(labelDiv, value);\n wordCount.style.fontSize = '12px';\n\n const observer = new MutationObserver(() => {\n const newText = status.textContent.trim();\n value.textContent = `${newText.replace('words', '')}`;\n });\n\n observer.observe(status, {\n characterData: true,\n subtree: true,\n childList: true\n });\n\n return wordCount;\n }\n\n\n timerCountDown(timer) {\n\n let warningDiv = document.querySelector('#user-notifications > div');\n if (warningDiv) {\n let clone = warningDiv.cloneNode(true);\n clone.querySelector('button')?.remove();\n this.editor.notificationManager.open({\n text: clone.textContent,\n type: 'error'\n });\n }\n\n\n const timerCount = this.create('div');\n timerCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n icon.innerHTML = Icons.time;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.timeleft}: }`;\n value.textContent = '00:00:00';\n value.className = warningDiv ? 'text-danger' : 'text-primary';\n Object.assign(value.style, {\n fontWeight: '600',\n fontSize: '14px'\n });\n\n\n timerCount.append(labelDiv, value);\n timerCount.style.fontSize = '12px';\n if (timer) {\n const observer = new MutationObserver(() => {\n const newText = timer.textContent.trim();\n value.textContent = `${newText}`;\n });\n observer.observe(timer, {\n characterData: true,\n subtree: true,\n childList: true\n });\n } else {\n value.textContent = this.nolimit;\n }\n\n\n return timerCount;\n }\n\n\n generateStudentInfo(user, course) {\n\n const wrapper = this.create('div');\n\n const nameWrapper = this.create('div');\n const usernameWrapper = this.create('div');\n const courseWrapper = this.create('div');\n\n const nameLabel = this.create('span');\n const nameValue = this.create('span');\n const usernameLabel = this.create('span');\n const usernameValue = this.create('span');\n const courseLabel = this.create('span');\n const courseValue = this.create('span');\n\n nameLabel.textContent = `${this.name}`;\n nameValue.textContent = user.fullname;\n\n usernameLabel.textContent = `${this.userename}: `;\n usernameValue.textContent = user.username;\n\n courseLabel.textContent = `${this.course}: `;\n courseValue.textContent = course.title;\n\n nameWrapper.className = 'd-flex justify-content-between';\n usernameWrapper.className = 'd-flex justify-content-between';\n courseWrapper.className = 'd-flex justify-content-between';\n\n nameWrapper.append(nameLabel, nameValue);\n usernameWrapper.append(usernameLabel, usernameValue);\n courseWrapper.append(courseLabel, courseValue);\n\n wrapper.append(nameWrapper, usernameWrapper, courseWrapper);\n\n return wrapper.innerHTML;\n\n }\n\n generateImportantDates(open, due) {\n\n const wrapper = this.create('div');\n let openDate = null;\n let dueDate = null;\n\n const openedWrapper = this.create('div');\n const dueWrapper = this.create('div');\n const remainingWrapper = this.create('div');\n\n const openedLabel = this.create('span');\n const openedValue = this.create('span');\n const dueLabel = this.create('span');\n const dueValue = this.create('span');\n const remainingLabel = this.create('span');\n const remainingValue = this.create('span');\n if (this.module === 'quiz') {\n openDate = open * 1000;\n dueDate = due * 1000;\n } else {\n openDate = this.extractDate(open?.textContent);\n dueDate = this.extractDate(due?.textContent);\n }\n\n openedLabel.textContent = `${this.opened}: `;\n openedValue.textContent = this.formatDate(openDate ? new Date(openDate) : null);\n openedValue.className = 'text-dark';\n\n dueLabel.textContent = `${this.due}: `;\n dueValue.textContent = this.formatDate(dueDate ? new Date(dueDate) : null);\n dueValue.className = 'text-danger';\n\n remainingLabel.textContent = `${this.remaining}: `;\n remainingValue.textContent = this.calculateDate(dueDate);\n remainingValue.className = 'text-danger';\n\n openedWrapper.className = 'd-flex justify-content-between';\n dueWrapper.className = 'd-flex justify-content-between';\n remainingWrapper.className = 'd-flex align-items-center justify-content-between mt-2 pt-2 border-top';\n\n openedWrapper.append(openedLabel, openedValue);\n dueWrapper.append(dueLabel, dueValue);\n remainingWrapper.append(remainingLabel, remainingValue);\n\n wrapper.append(openedWrapper, dueWrapper, remainingWrapper);\n\n return wrapper.innerHTML;\n }\n\n formatDate(date) {\n if (!date) {\n return '-';\n }\n\n let options = {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true};\n return date.toLocaleString('en-US', options);\n }\n\n extractDate(text) {\n if (!text) {\n return '-';\n }\n // Split on first colon and return the right part\n const parts = text?.split(':');\n if (parts.length > 1) {\n return parts.slice(1).join(':').trim();\n }\n\n return text.trim();\n }\n\n\n calculateDate(date) {\n if (!date) {\n return '-';\n }\n const date1 = new Date(date); // Due date (local time)\n const now = new Date(); // Current date/time\n\n // Calculate the difference in milliseconds\n const diffMs = date1 - now;\n\n // Convert to days, hours, minutes\n if (diffMs <= 0) {\n return \"Overdue\";\n } else {\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n const diffHours = Math.floor((diffMs / (1000 * 60 * 60)) % 24);\n\n return `${diffDays} days, ${diffHours} hours`;\n }\n\n }\n\n fullPageModule(module) {\n let current = this.module === 'quiz' ?\n document.getElementById(`${module}_ifr`) : document.querySelector(`#${module}_ifr`);\n\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n let p3 = p2.parentElement;\n let p4 = p3.parentElement;\n\n let statusBar = document.querySelector('.tox-statusbar__right-container > button');\n let assignName = document.querySelector('.page-context-header');\n let header = this.create('div');\n let btn = null;\n\n assignName.classList.remove('mb-2');\n header.id = 'tiny_cursive-fullpage-custom-header';\n Object.assign(header.style, {\n backgroundColor: 'white',\n display: 'flex',\n justifyContent: 'space-between'\n });\n\n if (this.module === 'quiz') {\n btn = document.querySelector('#mod_quiz-next-nav').cloneNode(true);\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.style.margin = '.5rem';\n } else {\n btn = this.create('input');\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.value = this.savechanges;\n btn.type = 'submit';\n btn.style.margin = '.5rem';\n }\n\n\n const leftSide = this.create('div');\n const rightSide = this.create('div');\n let commonStyle = {\n display: 'flex',\n alignItems: 'center',\n margin: '0 1rem'\n };\n\n Object.assign(leftSide.style, commonStyle);\n rightSide.id = 'tiny_cursive-fullpage-right-wrapper';\n Object.assign(rightSide.style, commonStyle);\n\n rightSide.appendChild(btn);\n leftSide.appendChild(assignName.cloneNode(true));\n\n header.appendChild(leftSide);\n header.appendChild(rightSide);\n\n p4.insertBefore(header, p4.firstChild);\n p2.style.backgroundColor = '#efefef';\n Object.assign(current.style, {\n width: '750px',\n minWidth: '750px',\n boxShadow: '0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)'\n });\n\n Object.assign(p1.style, {\n display: 'flex',\n justifyContent: 'center',\n outline: 'none',\n margin: '2rem 0 0'\n });\n const style = this.create('style');\n style.id = 'tiny_cursive-fullpage-mode-style';\n style.textContent = `\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }`;\n document.head.appendChild(style);\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n\n if (iframeBody) {\n iframeBody.style.padding = '0.5in';\n }\n p2.style.position = 'relative';\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let toggle = this.create('div');\n toggle.id = 'cursive-fullpagemode-sidebar-toggle';\n toggle.innerHTML = Icons.hamburger;\n p2.appendChild(toggle);\n p2.appendChild(this.docSideBar(statusBar));\n }\n\n normalizePage(editorId) {\n document.getElementById('tiny_cursive-fullpage-custom-header')?.remove();\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let current = document.getElementById(editorId);\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n\n Object.assign(p2.style, {\n backgroundColor: \"\",\n position: \"\"\n });\n\n Object.assign(current.style, {\n width: '',\n minWidth: '',\n boxShadow: '',\n });\n\n Object.assign(p1.style, {\n display: '',\n justifyContent: '',\n outline: '',\n margin: ''\n });\n\n p1.classList.remove('tiny-cursive-editor-container');\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n if (iframeBody) {\n iframeBody.style.padding = '0';\n }\n document.head.querySelector('#tiny_cursive-fullpage-mode-style')?.remove();\n }\n\n checkForumSubject() {\n const form = document.querySelector('#tiny_cursive-fullpage-right-wrapper > input');\n const msg = this.subjectnot;\n\n if (form) {\n form.addEventListener('click', (e) => {\n const subjectInput = document.getElementById('id_subject');\n let content = this.editor.getContent().trim();\n if (!subjectInput || subjectInput.value.trim() === '' || content === '') {\n e.preventDefault();\n e.stopPropagation();\n this.editor.windowManager.alert(msg);\n }\n });\n }\n }\n\n getSidebarTitle() {\n const [assign, discus, quiz, lesson] = this.getText('sbTitle');\n switch (this.module) {\n case 'assign':\n return {title: assign, icon: Icons.assignment};\n case 'forum':\n return {title: discus, icon: Icons.forum};\n case 'lesson':\n return {title: lesson, icon: Icons.forum};\n case 'quiz':\n return {title: quiz, icon: Icons.quiz};\n default:\n return {title: 'Page', icon: Icons.quiz};\n }\n }\n\n getTimerBlock(module) {\n switch (module) {\n case 'assign':\n return document.querySelector('#mod_assign_timelimit_block > div > div');\n case 'forum':\n return document.querySelector('#mod_forum_timelimit_block');\n case 'lesson':\n return document.querySelector('#lesson-timer');\n case 'quiz':\n return document.querySelector('#quiz-time-left');\n default:\n return null;\n }\n }\n\n getQuestionId(editoId) {\n try {\n if (!editoId || typeof editoId !== 'string') {\n return '';\n }\n return editoId.replace(/^q(\\d+):(\\d+)_.*$/, \"$1-$2\");\n } catch (error) {\n window.console.error('Error getting question ID:', error);\n return '';\n }\n }\n\n initStrings() {\n [\n this.details,\n this.studentInfo,\n this.progress,\n this.description,\n this.replyingto,\n this.answeringto,\n this.importantdates,\n this.rubrics,\n this.subStatus,\n this.status,\n this.draft,\n this.draftnot,\n this.lastModified,\n this.gradings,\n this.gradenot,\n this.wordCount,\n this.timeleft,\n this.nolimit,\n this.name,\n this.userename,\n this.course,\n this.opened,\n this.due,\n this.overdue,\n this.remaining,\n this.savechanges,\n this.subjectnot\n ] = this.getText('docSideBar');\n }\n\n getText(key) {\n return JSON.parse(localStorage.getItem(key)) || [];\n }\n\n create(tag) {\n return document.createElement(tag);\n }\n\n}"],"names":["constructor","User","Rubrics","submission","modulename","editor","quizInfo","module","moduleIcon","Icons","assignment","initStrings","normalMode","id","this","normalizePage","fullPageMode","fullPageModule","_this$editor2","forum","_this$editor3","_this$editor4","quiz","_this$editor5","lesson","_this$editor6","docSideBar","status","replyId","URL","window","location","href","searchParams","get","toggle","document","querySelector","timelimitBlock","getTimerBlock","headerInfo","getSidebarTitle","progressBar","courseName","courseDes","Dates","openDate","dueDate","container","create","Object","assign","className","style","width","overflow","crossBtn","innerHTML","close","addEventListener","transition","display","btnWrapper","padding","position","top","backgroundColor","append","header","headerTitle","textContent","title","details","fontWeight","headerIcon","prepend","cloneNode","wordCount","wordCounter","timerCountDown","content","createBox","bg","titleColor","icon","people","studentInfo","bodyHTML","generateStudentInfo","progress","trim","description","checkForumSubject","replyPost","replyingto","_this$editor7","questionId","getQuestionId","_this$editor8","question","intro","atob","answeringto","Number","open","time","importantdates","generateImportantDates","keys","length","rubrics","generateRubrics","subStatus","submissionStatus","box","heading","body","wrapper","forEach","rubric","rubricDiv","appendChild","values","levels","level","levelDiv","score","definition","statusWrapper","statusName","statusValue","isNew","current","draftnot","draft","modifiedWrapper","modifiedName","lastModified","modifiedValue","_submission$current2","timemodified","date","Date","formatDate","gradeWrapper","gradeName","gradings","gradeValue","grade","gradenot","labelDiv","label","value","fontSize","MutationObserver","newText","replace","observe","characterData","subtree","childList","timer","warningDiv","clone","remove","notificationManager","text","type","timerCount","timeleft","nolimit","user","course","nameWrapper","usernameWrapper","courseWrapper","nameLabel","nameValue","usernameLabel","usernameValue","courseLabel","courseValue","name","fullname","userename","username","due","openedWrapper","dueWrapper","remainingWrapper","openedLabel","openedValue","dueLabel","dueValue","remainingLabel","remainingValue","extractDate","opened","remaining","calculateDate","toLocaleString","year","month","day","hour","minute","hour12","parts","split","slice","join","diffMs","Math","floor","getElementById","p1","parentElement","p2","p4","statusBar","assignName","btn","classList","justifyContent","margin","savechanges","leftSide","rightSide","commonStyle","alignItems","insertBefore","firstChild","minWidth","boxShadow","outline","head","iframeBody","contentDocument","contentWindow","_current$contentWindo","_current$contentWindo2","hamburger","editorId","_current$contentWindo3","_current$contentWindo4","form","msg","subjectnot","e","subjectInput","getContent","preventDefault","stopPropagation","windowManager","alert","discus","getText","editoId","error","console","overdue","key","JSON","parse","localStorage","getItem","tag","createElement"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"document_view.min.js","sources":["../src/document_view.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module provides functionality for document view management in the Tiny editor,\n * including full page mode display and sidebar information\n * @module tiny_cursive/document_view\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Icons from 'tiny_cursive/svg_repo';\nexport default class DocumentView {\n\n constructor(User, Rubrics, submission, modulename, editor, quizInfo) {\n this.User = User;\n this.Rubrics = Rubrics;\n this.submission = submission;\n this.module = modulename;\n this.editor = editor;\n this.moduleIcon = Icons.assignment;\n this.quizInfo = quizInfo;\n this.initStrings();\n }\n\n normalMode() {\n let id = this.editor?.id + \"_ifr\";\n if (this.module === 'assign') {\n this.normalizePage(id);\n } else if (this.module === 'quiz') {\n this.normalizePage(id);\n } else if (this.module === 'forum') {\n this.normalizePage(id);\n } else if (this.module === 'lesson') {\n this.normalizePage(id);\n }\n }\n\n fullPageMode() {\n\n if (this.module === 'assign') {\n this.moduleIcon = Icons.assignment;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'forum') {\n this.moduleIcon = Icons.forum;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'quiz' && this.editor?.id) {\n this.moduleIcon = Icons.quiz;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'lesson') {\n this.moduleIcon = Icons.lesson;\n this.fullPageModule(this.editor?.id);\n }\n }\n\n docSideBar(status) {\n\n const url = new URL(window.location.href);\n const replyId = url.searchParams.get(\"reply\");\n const toggle = document.querySelector('#cursive-fullpagemode-sidebar-toggle');\n const timelimitBlock = this.getTimerBlock(this.module);\n const headerInfo = this.getSidebarTitle();\n const progressBar = document.querySelector('.box.progress_bar');\n\n const courseName = document.querySelector('#page-navbar > nav > ol > li:nth-child(1) > a');\n const courseDes = document.querySelector('#intro');\n const Dates = document.querySelector('.activity-dates');\n\n let openDate = Dates?.querySelector('div:nth-child(1)');\n let dueDate = Dates?.querySelector('div:nth-child(2)');\n\n const container = this.create('div');\n Object.assign(container, {\n id: 'cursive-fullpagemode-sidebar',\n className: 'bg-white h-100 shadow'\n });\n Object.assign(container.style, {\n width: '300px',\n overflow: 'auto'\n });\n\n const crossBtn = this.create('span');\n Object.assign(crossBtn, {\n id: 'cursive-collapse-sidebar',\n className: 'btn p-2',\n innerHTML: Icons.close\n });\n\n crossBtn.addEventListener('click', () => {\n container.style.transition = 'width 0.3s ease';\n container.style.width = '0';\n toggle.style.display = 'flex';\n });\n toggle?.addEventListener('click', function() {\n toggle.style.display = 'none';\n container.style.width = '300px';\n });\n\n const btnWrapper = this.create('div');\n Object.assign(btnWrapper, {\n padding: '0 1rem',\n position: 'sticky',\n top: '0',\n backgroundColor: 'white'\n });\n btnWrapper.append(crossBtn);\n\n\n const header = this.create('div');\n header.className = 'border-bottom p-3 bg-light';\n Object.assign(header.style, {\n position: 'sticky',\n top: '0'\n });\n\n const headerTitle = this.create('h3');\n headerTitle.className = 'mb-3 d-flex align-items-center';\n headerTitle.textContent = `${headerInfo.title} ${this.details}`;\n headerTitle.style.fontWeight = '600';\n\n const headerIcon = document.querySelector('.page-header-image > div');\n if (headerIcon) {\n headerTitle.prepend(headerIcon.cloneNode(true));\n }\n\n let wordCount = this.wordCounter(status);\n if (timelimitBlock?.textContent) {\n header.append(headerTitle, wordCount, this.timerCountDown(timelimitBlock));\n } else {\n header.append(headerTitle, wordCount);\n }\n\n const content = this.create('div');\n content.className = 'p-3';\n\n content.append(\n this.createBox({\n bg: 'bg-info',\n titleColor: 'text-info',\n icon: Icons.people,\n title: this.studentInfo,\n bodyHTML: this.generateStudentInfo(this.User, courseName)\n })\n );\n\n if (this.module === 'lesson' && progressBar) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.progress,\n bodyHTML: progressBar.innerHTML\n })\n );\n }\n\n if (courseDes && courseDes?.textContent.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.getSidebarTitle().title} ${this.description}`,\n bodyHTML: courseDes.innerHTML\n })\n );\n }\n\n if (this.module === 'forum' && replyId) {\n this.checkForumSubject();\n let replyPost = document.querySelector(`#post-content-${replyId}`);\n if (replyPost?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.replyingto,\n bodyHTML: replyPost.textContent.trim()\n })\n );\n }\n }\n\n if (this.module === 'quiz' && this.editor?.id) {\n\n let questionId = this.getQuestionId(this.editor?.id);\n let question = document.querySelector(`#question-${questionId} .qtext`);\n let intro = atob(this.quizInfo.intro);\n\n if (question?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.answeringto,\n bodyHTML: question.textContent\n })\n );\n }\n\n if (intro && intro.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.quiz} ${this.description}`,\n bodyHTML: intro\n })\n );\n }\n\n if (Number(this.quizInfo.open)) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(Number(this.quizInfo.open), Number(this.quizInfo.close))\n })\n );\n }\n }\n\n if (Object.keys(this.Rubrics).length) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.rubrics,\n bodyHTML: this.generateRubrics(this.Rubrics)\n })\n );\n }\n\n if (Dates) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(openDate, dueDate)\n })\n );\n }\n if (this.module === 'assign') {\n content.append(\n this.createBox({\n bg: 'bg-green',\n titleColor: 'text-success',\n icon: this.moduleIcon,\n title: this.subStatus,\n bodyHTML: this.submissionStatus(this.submission)\n })\n );\n }\n\n container.append(btnWrapper, header, content);\n return container;\n\n }\n // Helper to create info boxes\n createBox({bg, titleColor, icon, title, bodyHTML}) {\n const box = this.create('div');\n box.className = `tiny_cursive-fullpage-card ${bg}`;\n\n const heading = this.create('h4');\n heading.className = `tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`;\n heading.innerHTML = `${icon} ${title}`;\n\n const body = this.create('div');\n body.className = `tiny_cursive-fullpage-card-body`;\n body.innerHTML = bodyHTML;\n\n box.append(heading, body);\n return box;\n }\n\n generateRubrics(Rubrics) {\n const wrapper = this.create('div');\n\n Rubrics.forEach(rubric => {\n const rubricDiv = this.create('div');\n rubricDiv.className = 'tiny_cursive-rubric-card';\n\n const title = this.create('h3');\n title.className = 'tiny_cursive-rubric-title';\n title.textContent = rubric.description;\n rubricDiv.appendChild(title);\n\n Object.values(rubric.levels).forEach(level => {\n const levelDiv = this.create('div');\n const score = Number(level.score);\n\n // Assign background color class based on score\n if (score === 0) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-low';\n } else if (score <= 2) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-mid';\n } else {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-high';\n }\n\n levelDiv.textContent = `${level.definition} / ${level.score}`;\n rubricDiv.appendChild(levelDiv);\n });\n\n wrapper.appendChild(rubricDiv);\n });\n\n return wrapper.innerHTML;\n }\n\n submissionStatus(submission) {\n const wrapper = this.create('div');\n\n const statusWrapper = this.create('div');\n statusWrapper.className = 'tiny_cursive-status-row';\n\n const statusName = this.create('span');\n statusName.textContent = `${this.status}:`;\n\n const statusValue = this.create('span');\n const isNew = submission?.current?.status === 'new';\n statusValue.textContent = isNew ? this.draftnot : this.draft;\n statusValue.className = `tiny_cursive-status-value ${isNew ? 'tiny_cursive-status-red' : 'tiny_cursive-status-green'}`;\n\n statusWrapper.append(statusName, statusValue);\n\n const modifiedWrapper = this.create('div');\n modifiedWrapper.className = 'tiny_cursive-status-row';\n\n const modifiedName = this.create('span');\n modifiedName.textContent = `${this.lastModified}: `;\n\n const modifiedValue = this.create('span');\n if (submission?.current?.timemodified) {\n const date = new Date(submission.current.timemodified * 1000);\n modifiedValue.textContent = this.formatDate(date);\n } else {\n modifiedValue.textContent = 'N/A';\n }\n modifiedWrapper.append(modifiedName, modifiedValue);\n\n const gradeWrapper = this.create('div');\n gradeWrapper.className = 'tiny_cursive-status-row';\n\n const gradeName = this.create('span');\n gradeName.textContent = `${this.gradings}: `;\n\n const gradeValue = this.create('span');\n\n if (submission?.grade) {\n gradeValue.textContent = Number(submission.grade.grade) > 0\n ? submission.grade.grade\n : this.gradenot;\n } else {\n gradeValue.textContent = this.gradenot;\n }\n\n gradeWrapper.append(gradeName, gradeValue);\n wrapper.append(statusWrapper, gradeWrapper, modifiedWrapper);\n return wrapper.innerHTML;\n }\n\n wordCounter(status) {\n const wordCount = this.create('div');\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n\n icon.className = 'me-2';\n icon.innerHTML = Icons.assignment;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.wordCount}:`;\n value.textContent = '0';\n value.className = 'text-primary';\n value.style.fontWeight = '600';\n value.style.fontSize = '14px';\n\n wordCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n wordCount.append(labelDiv, value);\n wordCount.style.fontSize = '12px';\n\n const observer = new MutationObserver(() => {\n const newText = status.textContent.trim();\n value.textContent = `${newText.replace('words', '')}`;\n });\n\n observer.observe(status, {\n characterData: true,\n subtree: true,\n childList: true\n });\n\n return wordCount;\n }\n\n\n timerCountDown(timer) {\n\n let warningDiv = document.querySelector('#user-notifications > div');\n if (warningDiv) {\n let clone = warningDiv.cloneNode(true);\n clone.querySelector('button')?.remove();\n this.editor.notificationManager.open({\n text: clone.textContent,\n type: 'error'\n });\n }\n\n\n const timerCount = this.create('div');\n timerCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n icon.innerHTML = Icons.time;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.timeleft}: }`;\n value.textContent = '00:00:00';\n value.className = warningDiv ? 'text-danger' : 'text-primary';\n Object.assign(value.style, {\n fontWeight: '600',\n fontSize: '14px'\n });\n\n\n timerCount.append(labelDiv, value);\n timerCount.style.fontSize = '12px';\n if (timer) {\n const observer = new MutationObserver(() => {\n const newText = timer.textContent.trim();\n value.textContent = `${newText}`;\n });\n observer.observe(timer, {\n characterData: true,\n subtree: true,\n childList: true\n });\n } else {\n value.textContent = this.nolimit;\n }\n\n\n return timerCount;\n }\n\n\n generateStudentInfo(user, course) {\n\n const wrapper = this.create('div');\n\n const nameWrapper = this.create('div');\n const usernameWrapper = this.create('div');\n const courseWrapper = this.create('div');\n\n const nameLabel = this.create('span');\n const nameValue = this.create('span');\n const usernameLabel = this.create('span');\n const usernameValue = this.create('span');\n const courseLabel = this.create('span');\n const courseValue = this.create('span');\n\n nameLabel.textContent = `${this.name}`;\n nameValue.textContent = user.fullname;\n\n usernameLabel.textContent = `${this.userename}: `;\n usernameValue.textContent = user.username;\n\n courseLabel.textContent = `${this.course}: `;\n courseValue.textContent = course.title;\n\n nameWrapper.className = 'd-flex justify-content-between';\n usernameWrapper.className = 'd-flex justify-content-between';\n courseWrapper.className = 'd-flex justify-content-between';\n\n nameWrapper.append(nameLabel, nameValue);\n usernameWrapper.append(usernameLabel, usernameValue);\n courseWrapper.append(courseLabel, courseValue);\n\n wrapper.append(nameWrapper, usernameWrapper, courseWrapper);\n\n return wrapper.innerHTML;\n\n }\n\n generateImportantDates(open, due) {\n\n const wrapper = this.create('div');\n let openDate = null;\n let dueDate = null;\n\n const openedWrapper = this.create('div');\n const dueWrapper = this.create('div');\n const remainingWrapper = this.create('div');\n\n const openedLabel = this.create('span');\n const openedValue = this.create('span');\n const dueLabel = this.create('span');\n const dueValue = this.create('span');\n const remainingLabel = this.create('span');\n const remainingValue = this.create('span');\n if (this.module === 'quiz') {\n openDate = open * 1000;\n dueDate = due * 1000;\n } else {\n openDate = this.extractDate(open?.textContent);\n dueDate = this.extractDate(due?.textContent);\n }\n\n openedLabel.textContent = `${this.opened}: `;\n openedValue.textContent = this.formatDate(openDate ? new Date(openDate) : null);\n openedValue.className = 'text-dark';\n\n dueLabel.textContent = `${this.due}: `;\n dueValue.textContent = this.formatDate(dueDate ? new Date(dueDate) : null);\n dueValue.className = 'text-danger';\n\n remainingLabel.textContent = `${this.remaining}: `;\n remainingValue.textContent = this.calculateDate(dueDate);\n remainingValue.className = 'text-danger';\n\n openedWrapper.className = 'd-flex justify-content-between';\n dueWrapper.className = 'd-flex justify-content-between';\n remainingWrapper.className = 'd-flex align-items-center justify-content-between mt-2 pt-2 border-top';\n\n openedWrapper.append(openedLabel, openedValue);\n dueWrapper.append(dueLabel, dueValue);\n remainingWrapper.append(remainingLabel, remainingValue);\n\n wrapper.append(openedWrapper, dueWrapper, remainingWrapper);\n\n return wrapper.innerHTML;\n }\n\n formatDate(date) {\n if (!date) {\n return '-';\n }\n\n let options = {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true};\n return date.toLocaleString('en-US', options);\n }\n\n extractDate(text) {\n if (!text) {\n return '-';\n }\n // Split on first colon and return the right part\n const parts = text?.split(':');\n if (parts.length > 1) {\n return parts.slice(1).join(':').trim();\n }\n\n return text.trim();\n }\n\n\n calculateDate(date) {\n if (!date) {\n return '-';\n }\n const date1 = new Date(date); // Due date (local time)\n const now = new Date(); // Current date/time\n\n // Calculate the difference in milliseconds\n const diffMs = date1 - now;\n\n // Convert to days, hours, minutes\n if (diffMs <= 0) {\n return \"Overdue\";\n } else {\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n const diffHours = Math.floor((diffMs / (1000 * 60 * 60)) % 24);\n\n return `${diffDays} days, ${diffHours} hours`;\n }\n\n }\n\n fullPageModule(module) {\n let current = this.module === 'quiz' ?\n document.getElementById(`${module}_ifr`) : document.querySelector(`#${module}_ifr`);\n\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n let p3 = p2.parentElement;\n let p4 = p3.parentElement;\n\n let statusBar = document.querySelector('.tox-statusbar__right-container > button');\n let assignName = document.querySelector('.page-context-header');\n let header = this.create('div');\n let btn = null;\n\n assignName.classList.remove('mb-2');\n header.id = 'tiny_cursive-fullpage-custom-header';\n Object.assign(header.style, {\n backgroundColor: 'white',\n display: 'flex',\n justifyContent: 'space-between'\n });\n\n if (this.module === 'quiz') {\n btn = document.querySelector('#mod_quiz-next-nav').cloneNode(true);\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.style.margin = '.5rem';\n } else {\n btn = this.create('input');\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.value = this.savechanges;\n btn.type = 'submit';\n btn.style.margin = '.5rem';\n }\n\n\n const leftSide = this.create('div');\n const rightSide = this.create('div');\n let commonStyle = {\n display: 'flex',\n alignItems: 'center',\n margin: '0 1rem'\n };\n\n Object.assign(leftSide.style, commonStyle);\n rightSide.id = 'tiny_cursive-fullpage-right-wrapper';\n Object.assign(rightSide.style, commonStyle);\n\n rightSide.appendChild(btn);\n leftSide.appendChild(assignName.cloneNode(true));\n\n header.appendChild(leftSide);\n header.appendChild(rightSide);\n\n p4.insertBefore(header, p4.firstChild);\n p2.style.backgroundColor = '#efefef';\n Object.assign(current.style, {\n width: '750px',\n minWidth: '750px',\n boxShadow: '0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)'\n });\n\n Object.assign(p1.style, {\n display: 'flex',\n justifyContent: 'center',\n outline: 'none',\n margin: '2rem 0 0'\n });\n const style = this.create('style');\n style.id = 'tiny_cursive-fullpage-mode-style';\n style.textContent = `\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }`;\n document.head.appendChild(style);\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n\n if (iframeBody) {\n iframeBody.style.padding = '0.5in';\n }\n p2.style.position = 'relative';\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let toggle = this.create('div');\n toggle.id = 'cursive-fullpagemode-sidebar-toggle';\n toggle.innerHTML = Icons.hamburger;\n p2.appendChild(toggle);\n p2.appendChild(this.docSideBar(statusBar));\n }\n\n normalizePage(editorId) {\n document.getElementById('tiny_cursive-fullpage-custom-header')?.remove();\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let current = document.getElementById(editorId);\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n\n Object.assign(p2.style, {\n backgroundColor: \"\",\n position: \"\"\n });\n\n Object.assign(current.style, {\n width: '',\n minWidth: '',\n boxShadow: '',\n });\n\n Object.assign(p1.style, {\n display: '',\n justifyContent: '',\n outline: '',\n margin: ''\n });\n\n p1.classList.remove('tiny-cursive-editor-container');\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n if (iframeBody) {\n iframeBody.style.padding = '0';\n }\n document.head.querySelector('#tiny_cursive-fullpage-mode-style')?.remove();\n }\n\n checkForumSubject() {\n const form = document.querySelector('#tiny_cursive-fullpage-right-wrapper > input');\n const msg = this.subjectnot;\n\n if (form) {\n form.addEventListener('click', (e) => {\n const subjectInput = document.getElementById('id_subject');\n let content = this.editor.getContent().trim();\n if (!subjectInput || subjectInput.value.trim() === '' || content === '') {\n e.preventDefault();\n e.stopPropagation();\n this.editor.windowManager.alert(msg);\n }\n });\n }\n }\n\n getSidebarTitle() {\n const [assign, discus, quiz, lesson] = this.getText('sbTitle');\n switch (this.module) {\n case 'assign':\n return {title: assign, icon: Icons.assignment};\n case 'forum':\n return {title: discus, icon: Icons.forum};\n case 'lesson':\n return {title: lesson, icon: Icons.forum};\n case 'quiz':\n return {title: quiz, icon: Icons.quiz};\n default:\n return {title: 'Page', icon: Icons.quiz};\n }\n }\n\n getTimerBlock(module) {\n switch (module) {\n case 'assign':\n return document.querySelector('#mod_assign_timelimit_block > div > div');\n case 'forum':\n return document.querySelector('#mod_forum_timelimit_block');\n case 'lesson':\n return document.querySelector('#lesson-timer');\n case 'quiz':\n return document.querySelector('#quiz-time-left');\n default:\n return null;\n }\n }\n\n getQuestionId(editoId) {\n try {\n if (!editoId || typeof editoId !== 'string') {\n return '';\n }\n return editoId.replace(/^q(\\d+):(\\d+)_.*$/, \"$1-$2\");\n } catch (error) {\n window.console.error('Error getting question ID:', error);\n return '';\n }\n }\n\n initStrings() {\n [\n this.details,\n this.studentInfo,\n this.progress,\n this.description,\n this.replyingto,\n this.answeringto,\n this.importantdates,\n this.rubrics,\n this.subStatus,\n this.status,\n this.draft,\n this.draftnot,\n this.lastModified,\n this.gradings,\n this.gradenot,\n this.wordCount,\n this.timeleft,\n this.nolimit,\n this.name,\n this.userename,\n this.course,\n this.opened,\n this.due,\n this.overdue,\n this.remaining,\n this.savechanges,\n this.subjectnot\n ] = this.getText('docSideBar');\n }\n\n getText(key) {\n return JSON.parse(localStorage.getItem(key)) || [];\n }\n\n create(tag) {\n return document.createElement(tag);\n }\n\n}"],"names":["constructor","User","Rubrics","submission","modulename","editor","quizInfo","module","moduleIcon","Icons","assignment","initStrings","normalMode","id","this","normalizePage","fullPageMode","fullPageModule","_this$editor2","forum","_this$editor3","_this$editor4","quiz","_this$editor5","lesson","_this$editor6","docSideBar","status","replyId","URL","window","location","href","searchParams","get","toggle","document","querySelector","timelimitBlock","getTimerBlock","headerInfo","getSidebarTitle","progressBar","courseName","courseDes","Dates","openDate","dueDate","container","create","Object","assign","className","style","width","overflow","crossBtn","innerHTML","close","addEventListener","transition","display","btnWrapper","padding","position","top","backgroundColor","append","header","headerTitle","textContent","title","details","fontWeight","headerIcon","prepend","cloneNode","wordCount","wordCounter","timerCountDown","content","createBox","bg","titleColor","icon","people","studentInfo","bodyHTML","generateStudentInfo","progress","trim","description","checkForumSubject","replyPost","replyingto","_this$editor7","questionId","getQuestionId","_this$editor8","question","intro","atob","answeringto","Number","open","time","importantdates","generateImportantDates","keys","length","rubrics","generateRubrics","subStatus","submissionStatus","box","heading","body","wrapper","forEach","rubric","rubricDiv","appendChild","values","levels","level","levelDiv","score","definition","statusWrapper","statusName","statusValue","isNew","current","draftnot","draft","modifiedWrapper","modifiedName","lastModified","modifiedValue","_submission$current2","timemodified","date","Date","formatDate","gradeWrapper","gradeName","gradings","gradeValue","grade","gradenot","labelDiv","label","value","fontSize","MutationObserver","newText","replace","observe","characterData","subtree","childList","timer","warningDiv","clone","remove","notificationManager","text","type","timerCount","timeleft","nolimit","user","course","nameWrapper","usernameWrapper","courseWrapper","nameLabel","nameValue","usernameLabel","usernameValue","courseLabel","courseValue","name","fullname","userename","username","due","openedWrapper","dueWrapper","remainingWrapper","openedLabel","openedValue","dueLabel","dueValue","remainingLabel","remainingValue","extractDate","opened","remaining","calculateDate","toLocaleString","year","month","day","hour","minute","hour12","parts","split","slice","join","diffMs","diffDays","Math","floor","diffHours","getElementById","p1","parentElement","p2","p4","statusBar","assignName","btn","classList","justifyContent","margin","savechanges","leftSide","rightSide","commonStyle","alignItems","insertBefore","firstChild","minWidth","boxShadow","outline","head","iframeBody","contentDocument","contentWindow","_current$contentWindo","_current$contentWindo2","hamburger","editorId","_current$contentWindo3","_current$contentWindo4","form","msg","subjectnot","e","subjectInput","getContent","preventDefault","stopPropagation","windowManager","alert","discus","getText","editoId","error","console","overdue","key","JSON","parse","localStorage","getItem","tag","createElement"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/replay.min.js b/amd/build/replay.min.js index 0b222771..f7306974 100644 --- a/amd/build/replay.min.js +++ b/amd/build/replay.min.js @@ -1,3 +1,3 @@ -define("tiny_cursive/replay",["exports","core/ajax","core/templates","jquery","core/str"],(function(_exports,_ajax,_templates,_jquery,Str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_jquery=_interopRequireDefault(_jquery),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str);return _exports.default=class{constructor(elementId,filePath){let speed=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,loop=arguments.length>3&&void 0!==arguments[3]&&arguments[3],controllerId=arguments.length>4?arguments[4]:void 0;this.controllerId=controllerId||"",this.replayInProgress=!1,this.speed=parseFloat(speed),this.loop=loop,this.highlightedChars=[],this.deletedChars=[],this.cursorPosition=0,this.currentEventIndex=0,this.totalEvents=0,this.currentTime=0,this.totalDuration=0,this.usercomments=[],this.pasteTimestamps=[],this.isPasteEvent=!1,this.isControlKeyPressed=!1,this.isShiftKeyPressed=!1,this.isMetaKeyPressed=!1,this.text="",this.pastedEvents=[],this.currentPasteIndex=0,this.pastedChars=[],this.aiEvents=[],this.currentAiIndex=0,this.aiChars=[],this.undoTimestamps=[],this.undoChars=[];const element=document.getElementById(elementId);if(!element)throw new Error(`Element with id '${elementId}' not found`);this.outputElement=element,this.loadJSON(filePath).then((data=>(data.status?(this.processData(data),this.totalEvents=this.logData.length,this.identifyPasteEvents(),this.identifyUndoEvents(),this.controllerId&&this.logData&&this.constructController(this.controllerId),this.startReplay()):this.handleNoSubmission(),data))).catch((error=>{this.handleNoSubmission(),window.console.error("Error loading JSON file:",error.message)})),localStorage.getItem("nopasteevent")&&localStorage.getItem("pasteEvent")||(Str.get_string("nopasteevent","tiny_cursive").then((str=>(localStorage.setItem("nopasteevent",str),str))).catch((error=>window.console.log(error))),Str.get_string("pasteEvent","tiny_cursive").then((str=>(localStorage.setItem("pasteEvent",str),str))).catch((error=>window.console.log(error))))}processData(data){this.logData=JSON.parse(data.data),data.comments&&(this.usercomments=Array.isArray(JSON.parse(data.comments))?JSON.parse(data.comments):[]),"data"in this.logData&&(this.logData=this.logData.data),"payload"in this.logData&&(this.logData=this.logData.payload);for(let i=0;i0&&this.logData[0].unixTimestamp){const startTime=this.logData[0].unixTimestamp;this.logData=this.logData.map((event=>({...event,normalizedTime:event.unixTimestamp-startTime}))),this.totalDuration=this.logData[this.logData.length-1].normalizedTime}}async handleNoSubmission(){try{const[html,str]=await Promise.all([_templates.default.render("tiny_cursive/no_submission"),Str.get_string("warningpayload","tiny_cursive")]),newElement=(0,_jquery.default)(html).text(str);return(0,_jquery.default)(".tiny_cursive").html(newElement)}catch(error){return window.console.error(error),!1}}stopReplay(){if(this.replayInProgress&&(clearTimeout(this.replayTimeout),this.replayInProgress=!1,this.playButton)){const playSvg=document.createElement("img");playSvg.src=M.util.image_url("playicon","tiny_cursive"),this.playButton.querySelector(".play-icon").innerHTML=playSvg.outerHTML}}constructController(controllerId){var _controlContainer$que;this.replayInProgress=!1,this.currentPosition=0,this.speed=1,this.replayIntervalId&&(clearInterval(this.replayIntervalId),this.replayIntervalId=null);const container=document.getElementById(controllerId);if(!container)return void window.console.error("Container not found with ID:",controllerId);const controlContainer=container.querySelector(".tiny_cursive_replay_control");controlContainer?(controlContainer.innerHTML='',this.buildControllerUI(controlContainer,container),null===(_controlContainer$que=controlContainer.querySelector(".tiny_cursive_loading_spinner"))||void 0===_controlContainer$que||_controlContainer$que.remove()):window.console.error("Replay control container not found in:",controllerId)}buildControllerUI(controlContainer,container){const topRow=document.createElement("div");topRow.classList.add("tiny_cursive_top_row"),this.playButton=this.createPlayButton(),topRow.appendChild(this.playButton);const scrubberContainer=this.createScrubberContainer();topRow.appendChild(scrubberContainer),this.timeDisplay=this.createTimeDisplay(),topRow.appendChild(this.timeDisplay);const bottomRow=document.createElement("div");bottomRow.classList.add("tiny_cursive_bottom_row");const speedContainer=this.createSpeedControls();bottomRow.appendChild(speedContainer);const pasteEventsToggle=this.createPasteEventsToggle(container);bottomRow.appendChild(pasteEventsToggle),controlContainer.appendChild(topRow),controlContainer.appendChild(bottomRow),container.appendChild(this.pasteEventsPanel)}createPlayButton(){const playButton=document.createElement("button");playButton.classList.add("tiny_cursive_play_button");const playSvg=document.createElement("i");return playButton.innerHTML=`${playSvg.outerHTML}`,playButton.addEventListener("click",(()=>{this.replayInProgress?this.stopReplay():this.startReplay(!1),(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)('a[id^="rep"]').addClass("active")})),playButton}createScrubberContainer(){const scrubberContainer=document.createElement("div");return scrubberContainer.classList.add("tiny_cursive_scrubber_container"),this.scrubberElement=document.createElement("input"),this.scrubberElement.classList.add("tiny_cursive_timeline_scrubber","timeline-scrubber"),this.scrubberElement.type="range",this.scrubberElement.max="100",this.scrubberElement.min="0",this.scrubberElement.value="0",this.scrubberElement.addEventListener("input",(()=>{this.skipToTime(parseInt(this.scrubberElement.value,10))})),scrubberContainer.appendChild(this.scrubberElement),scrubberContainer}createTimeDisplay(){const timeDisplay=document.createElement("div");return timeDisplay.classList.add("tiny_cursive_time_display"),timeDisplay.textContent="00:00 / 00:00",timeDisplay}createSpeedControls(){const speedContainer=document.createElement("div");speedContainer.classList.add("tiny_cursive_speed_controls","speed-controls");const speedLabel=document.createElement("span");speedLabel.classList.add("tiny_cursive_speed_label"),speedLabel.textContent="Speed: ",speedContainer.appendChild(speedLabel);const speedGroup=document.createElement("div");return speedGroup.classList.add("tiny_cursive_speed_group"),[1,1.5,2,5,10].forEach((speed=>{const speedBtn=document.createElement("button");speedBtn.textContent=`${speed}x`,speedBtn.classList.add("tiny_cursive_speed_btn","speed-btn"),parseFloat(speed)===this.speed&&speedBtn.classList.add("active"),speedBtn.dataset.speed=speed,speedBtn.addEventListener("click",(()=>{document.querySelectorAll(".tiny_cursive_speed_btn").forEach((btn=>btn.classList.remove("active"))),speedBtn.classList.add("active"),this.speed=parseFloat(speedBtn.dataset.speed),this.replayInProgress&&(this.stopReplay(),this.startReplay(!1))})),speedGroup.appendChild(speedBtn)})),speedContainer.appendChild(speedGroup),speedContainer}createPasteEventsToggle(container){const pasteEventsToggle=document.createElement("div");pasteEventsToggle.classList.add("tiny_cursive_paste_events_toggle","paste-events-toggle");const pasteEventsIcon=document.createElement("span"),pasteIcon=document.createElement("img");pasteIcon.src=M.util.image_url("pasteicon","tiny_cursive"),pasteEventsIcon.innerHTML=pasteIcon.outerHTML,pasteEventsIcon.classList.add("tiny_cursive_paste_events_icon");const pasteEventsText=document.createElement("span");pasteEventsText.textContent=localStorage.getItem("pasteEvent"),this.pasteEventCount=document.createElement("span"),this.pasteEventCount.textContent=`(${this.pasteTimestamps.length})`,this.pasteEventCount.className="paste-event-count",this.pasteEventCount.style.marginLeft="2px";const chevronIcon=document.createElement("span"),chevron=document.createElement("i");return chevron.className="fa fa-chevron-down",chevronIcon.innerHTML=chevron.outerHTML,chevronIcon.style.marginLeft="5px",chevronIcon.style.transition="transform 0.3s ease",pasteEventsToggle.appendChild(pasteEventsIcon),pasteEventsToggle.appendChild(pasteEventsText),pasteEventsToggle.appendChild(this.pasteEventCount),pasteEventsToggle.appendChild(chevronIcon),this.pasteEventsPanel=this.createPasteEventsPanel(container),pasteEventsToggle.addEventListener("click",(()=>{const isHidden="none"===this.pasteEventsPanel.style.display;this.pasteEventsPanel.style.display=isHidden?"block":"none",chevronIcon.style.transform=isHidden?"rotate(180deg)":"rotate(0deg)"})),pasteEventsToggle}createPasteEventsPanel(container){const existingPanel=container.querySelector(".paste-events-panel");existingPanel&&existingPanel.remove();const pasteEventsPanel=document.createElement("div");return pasteEventsPanel.classList.add("tiny_cursive_paste_events_panel","paste-events-panel"),pasteEventsPanel.style.display="none",this.populatePasteEventsPanel(pasteEventsPanel),pasteEventsPanel}identifyPasteEvents(){this.pasteTimestamps=[];let controlPressed=!1,metaPressed=!1,shiftPressed=!1,pasteCount=0;for(let i=0;i';const nextButton=document.createElement("button");nextButton.classList.add("paste-event-next-btn","tiny_cursive_nav_button"),nextButton.innerHTML='',nextButton.disabled=this.pasteTimestamps.length<=1,navButtons.appendChild(prevButton),navButtons.appendChild(nextButton),navigationRow.appendChild(counterDisplay),navigationRow.appendChild(navButtons);const contentContainer=document.createElement("div");contentContainer.className="paste-events-content tiny_cursive_content_container",contentContainer.appendChild(this.createPasteEventDisplay(this.pasteTimestamps[0])),carouselContainer.appendChild(navigationRow),carouselContainer.appendChild(contentContainer),panel.appendChild(carouselContainer);let currentIndex=0;const updateDisplay=()=>{contentContainer.innerHTML="",contentContainer.appendChild(this.createPasteEventDisplay(this.pasteTimestamps[currentIndex])),counterDisplay.textContent="Paste Events",prevButton.disabled=0===currentIndex,prevButton.style.opacity=0===currentIndex?"0.5":"1",nextButton.disabled=currentIndex===this.pasteTimestamps.length-1,nextButton.style.opacity=currentIndex===this.pasteTimestamps.length-1?"0.5":"1"};prevButton.addEventListener("click",(()=>{currentIndex>0&&(currentIndex--,updateDisplay())})),nextButton.addEventListener("click",(()=>{currentIndexthis.jumpToTimestamp(pasteEvent.timestamp))),headerRow.appendChild(textContainer),headerRow.appendChild(playButton),eventRow.appendChild(headerRow),eventRow}jumpToTimestamp(timestamp){const percentage=this.totalDuration>0?timestamp/this.totalDuration*100:0;this.skipToTime(percentage),this.replayInProgress||this.startReplay(!1)}setScrubberVal(value){if(this.scrubberElement&&(this.scrubberElement.value=String(value),this.timeDisplay)){const displayTime=Math.min(this.currentTime,this.totalDuration);this.timeDisplay.textContent=`${this.formatTime(displayTime)} / ${this.formatTime(this.totalDuration)}`}}loadJSON(filePath){return(0,_ajax.call)([{methodname:"cursive_get_reply_json",args:{filepath:filePath}}])[0].done((response=>response)).fail((error=>{throw new Error(`Error loading JSON file: ${error.message}`)}))}formatTime(ms){const seconds=Math.floor(ms/1e3),remainingSeconds=seconds%60;return`${Math.floor(seconds/60).toString().padStart(2,"0")}:${remainingSeconds.toString().padStart(2,"0")}`}startReplay(){let reset=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.replayInProgress&&clearTimeout(this.replayTimeout);if((this.totalDuration>0&&this.currentTime>=this.totalDuration||this.currentEventIndex>=this.totalEvents)&&!reset&&(reset=!0),this.replayInProgress=!0,reset&&(this.outputElement.innerHTML="",this.text="",this.cursorPosition=0,this.currentEventIndex=0,this.currentTime=0,this.highlightedChars=[],this.deletedChars=[],this.isControlKeyPressed=!1,this.isMetaKeyPressed=!1,this.currentPasteIndex=0,this.pastedChars=[],this.currentAiIndex=0,this.aiChars=[]),this.playButton){const pauseSvg=document.createElement("i");pauseSvg.className="fa fa-pause",this.playButton.querySelector(".play-icon").innerHTML=pauseSvg.outerHTML}this.replayLog()}replayLog(){if(this.replayInProgress){for(;this.currentEventIndexthis.currentTime)break;let text=this.text||"",cursor=this.cursorPosition,updatedHighlights=[...this.highlightedChars],updatedDeleted=[...this.deletedChars];void 0===event.rePosition||0!==this.currentEventIndex&&"mouseDown"!==event.event&&"mouseUp"!==event.event||(cursor=Math.max(0,Math.min(event.rePosition,text.length))),"keydown"===(null===(_event$event3=event.event)||void 0===_event$event3?void 0:_event$event3.toLowerCase())?({text:text,cursor:cursor,updatedHighlights:updatedHighlights,updatedDeleted:updatedDeleted}=this.processKeydownEvent(event,text,cursor,updatedHighlights,updatedDeleted)):"aiInsert"===event.event&&({text:text,cursor:cursor,updatedHighlights:updatedHighlights,updatedDeleted:updatedDeleted}=this.processAiInsertEvent(event,text,cursor,updatedHighlights,updatedDeleted)),this.text=text,this.cursorPosition=cursor,this.highlightedChars=updatedHighlights.filter((h=>!h.expiresAt||h.expiresAt>this.currentTime)),this.deletedChars=updatedDeleted.filter((d=>!d.expiresAt||d.expiresAt>this.currentTime)),this.currentEventIndex++}if(this.updateDisplayText(this.text,this.cursorPosition,this.highlightedChars,this.deletedChars),this.totalDuration>0){const percentComplete=Math.min(this.currentTime/this.totalDuration*100,100);this.setScrubberVal(percentComplete)}if(this.replayInProgress){const baseIncrement=100,incrementTime=baseIncrement/this.speed;this.currentTime+=baseIncrement,this.currentEventIndex>=this.totalEvents?this.loop?this.startReplay(!0):(this.stopReplay(),this.updateDisplayText(this.text,this.cursorPosition,[],[])):this.replayTimeout=setTimeout((()=>this.replayLog()),incrementTime)}}else this.updateDisplayText(this.text,this.cursorPosition,[],[])}getLineAndColumn(text,pos){const before=text.substring(0,pos);return{lineIndex:before.split("\n").length-1,col:before.length-before.lastIndexOf("\n")-1}}processAiInsertEvent(event,text,cursor,highlights,deletions){if(this.aiEvents&&this.currentAiIndex1,isNewLineInsertion=insertText.startsWith("\n")||insertText.endsWith("\n"),{wordStart:wordStart,wordEnd:wordEnd}=this.findWordToReplace(text,targetPosition,currentCursor,aiWords,isMultiWord,isNewLineInsertion),wordToReplace=text.substring(wordStart,wordEnd);this.markCharsAsDeleted(wordToReplace,wordStart,deletions);const replacedLength=wordToReplace.length;text=text.substring(0,wordStart)+insertText+text.substring(wordEnd);const positionDiff=insertText.length-replacedLength,newCursor=this.calculateNewCursorPosition(currentCursor,targetPosition,wordStart,wordEnd,insertText,isNewLineInsertion);return this.updateCharacterIndices(wordStart,wordEnd,positionDiff,insertText),{text:text,cursor:newCursor}}findWordToReplace(text,targetPosition,currentCursor,aiWords,isMultiWord,isNewLineInsertion){if(isNewLineInsertion)return{wordStart:currentCursor,wordEnd:currentCursor};const{lineStart:lineStart,lineEnd:lineEnd}=this.findLineRange(text,targetPosition),lineText=text.substring(lineStart,lineEnd),words=this.extractWordsFromLine(lineText,lineStart);return 0===words.length?{wordStart:currentCursor,wordEnd:currentCursor}:isMultiWord?this.findMultiWordMatch(words,aiWords,targetPosition):this.findSingleWordMatch(words,aiWords[0],targetPosition)}findLineRange(text,targetPosition){let lineStart=0;for(let i=targetPosition-1;i>=0;i--)if("\n"===text[i]){lineStart=i+1;break}let lineEnd=text.length;for(let i=targetPosition;i=lineText.length)break;const start=pos;for(;posstart&&words.push({text:lineText.substring(start,pos),start:lineStart+start,end:lineStart+pos})}return words}findMultiWordMatch(words,aiWords,targetPosition){let bestMatch={start:-1,end:-1,score:-1,wordCount:0,similarityScore:0};for(let i=0;ibestMatch.score||matchResult.totalScore===bestMatch.score&&matchResult.similarityScore>bestMatch.similarityScore)&&(bestMatch=matchResult)}if(bestMatch.score>10)return{wordStart:bestMatch.start,wordEnd:bestMatch.end};{const closest=this.findClosestWord(words,targetPosition);return{wordStart:closest.start,wordEnd:closest.end}}}evaluateMultiWordSequence(words,aiWords,startIndex,targetPosition){const seqWords=[];for(let j=0;j=seqStart&&targetPosition<=seqEndPos&&(positionScore+=10,targetPosition>=seqWords[0].start&&targetPosition<=seqWords[0].end&&(positionScore+=5)),positionScore}findSingleWordMatch(words,aiWord,targetPosition){const aiWordLower=aiWord.toLowerCase(),bestSimilarityMatch=this.findBestSimilarityMatch(words,aiWordLower);if(bestSimilarityMatch.score>.5)return{wordStart:bestSimilarityMatch.word.start,wordEnd:bestSimilarityMatch.word.end};const bestPositionMatch=this.findBestPositionMatch(words,aiWordLower,targetPosition);return bestPositionMatch.word?{wordStart:bestPositionMatch.word.start,wordEnd:bestPositionMatch.word.end}:this.findWordBoundaryAtPosition(words[0].start,words[words.length-1].end,targetPosition,this.text)}findBestSimilarityMatch(words,aiWordLower){let bestMatch={word:null,score:0};for(const word of words){let similarity=this.calculateSimilarity(aiWordLower,word.text.toLowerCase());const wordLower=word.text.toLowerCase();wordLower.length<.5*aiWordLower.length&&aiWordLower.startsWith(wordLower)&&(similarity*=.3),similarity>bestMatch.score&&(bestMatch={word:word,score:similarity})}return bestMatch}findBestPositionMatch(words,aiWordLower,targetPosition){let bestMatch={word:null,score:-1};for(const word of words){let score=this.calculateWordScore(word,aiWordLower,targetPosition);score>bestMatch.score&&(bestMatch={word:word,score:score})}return bestMatch}calculateWordScore(word,aiWordLower,targetPosition){let score=0;if(targetPosition>=word.start&&targetPosition<=word.end)score+=30;else{const distance=Math.min(Math.abs(targetPosition-word.start),Math.abs(targetPosition-word.end));score+=Math.max(0,20-distance)}let similarity=this.calculateSimilarity(aiWordLower,word.text.toLowerCase());const wordLower=word.text.toLowerCase();return wordLower.length<.5*aiWordLower.length&&aiWordLower.startsWith(wordLower)&&(similarity*=.3),score+=10*similarity,score}findWordBoundaryAtPosition(lineStart,lineEnd,targetPosition,text){let wordStart=targetPosition;for(;wordStart>lineStart&&" "!==text[wordStart-1]&&"\n"!==text[wordStart-1];)wordStart--;let wordEnd=targetPosition;for(;wordEnd0)for(let i=0;i=wordStart&&targetPosition<=wordEnd)return wordStart+insertText.length;const positionDiff=insertText.length-(wordEnd-wordStart);return currentCursor>=wordEnd?currentCursor+positionDiff:currentCursor>wordStart&¤tCursorp.index>=wordEnd?{...p,index:p.index+positionDiff}:p.index>=wordStart&&p.indexnull!==p)))}markCharsAsAiInserted(wordStart,insertText){if(this.aiChars||(this.aiChars=[]),""!==insertText.trim())for(let i=0;i{if(!justAddedIndices.has(p.index)){if(p.index>=wordEnd)return{...p,index:p.index+positionDiff};if(p.index>=wordStart&&p.indexnull!==p))}calculateSimilarity(str1,str2){if(str1===str2)return 1;if(0===str1.length||0===str2.length)return 0;if(str1.startsWith(str2)||str2.startsWith(str1))return.8;const len1=str1.length,len2=str2.length,matrix=Array(len2+1).fill(null).map((()=>Array(len1+1).fill(0)));for(let i=0;i<=len1;i++)matrix[0][i]=i;for(let j=0;j<=len2;j++)matrix[j][0]=j;for(let j=1;j<=len2;j++)for(let i=1;i<=len1;i++){const cost=str1[i-1]===str2[j-1]?0:1;matrix[j][i]=Math.min(matrix[j][i-1]+1,matrix[j-1][i]+1,matrix[j-1][i-1]+cost)}const maxLen=Math.max(len1,len2);return 1-matrix[len2][len1]/maxLen}findClosestWord(words,targetPosition){if(0===words.length)return{start:targetPosition,end:targetPosition};let closest=words[0],minDistance=Math.min(Math.abs(targetPosition-words[0].start),Math.abs(targetPosition-words[0].end));for(const word of words){if(targetPosition>=word.start&&targetPosition<=word.end)return word;const distance=Math.min(Math.abs(targetPosition-word.start),Math.abs(targetPosition-word.end));distance0){const textBeforeUndo=text;text=text.substring(0,newPosition)+text.substring(cursor),cursor=newPosition;for(let i=0;i1}processKeyOperation(key,charToInsert,text,cursor,highlights,deletions,selection){return this.isCtrlBackspace(key,cursor)?({text:text,cursor:cursor}=this.handleCtrlBackspace(text,cursor,deletions)):this.isCtrlDelete(key,cursor,text)?({text:text}=this.handleCtrlDelete(text,cursor,deletions)):this.isCtrlArrowMove(key)?cursor=this.handleCtrlArrowMove(key,text,cursor):this.isRegularBackspace(key,cursor)?({text:text,cursor:cursor}=this.handleBackspace(text,cursor,deletions)):this.isRegularDelete(key,cursor,text)?({text:text}=this.handleDelete(text,cursor,deletions)):this.isArrowUp(key)?cursor=this.handleArrowUp(text,cursor):this.isArrowDown(key)?cursor=this.handleArrowDown(text,cursor):this.isRegularArrowMove(key)?cursor=this.handleArrowMove(key,text,cursor):charToInsert&&charToInsert.length>0&&(selection&&selection.length>0&&({text:text,cursor:cursor}=this.handleSelectionDeletion(selection,text,cursor,deletions)),({text:text,cursor:cursor}=this.handleCharacterInsert(charToInsert,text,cursor,highlights))),{text:text,cursor:cursor,updatedHighlights:highlights,updatedDeleted:deletions}}detectSelection(eventIndex){var _currentEvent$event;const currentEvent=this.logData[eventIndex];if("keydown"===(null===(_currentEvent$event=currentEvent.event)||void 0===_currentEvent$event?void 0:_currentEvent$event.toLowerCase())&&("Backspace"===currentEvent.key||"Delete"===currentEvent.key)){const currentPos=currentEvent.rePosition;return this.processDetection(currentPos,currentEvent,eventIndex)}return null}processDetection(currentPos,currentEvent,eventIndex){for(let i=eventIndex+1;i1)return{start:Math.min(currentPos,nextPos),end:Math.max(currentPos,nextPos),length:positionDiff};if(1===positionDiff)return"Backspace"===currentEvent.key?{start:nextPos,end:currentPos,length:1}:{start:currentPos,end:nextPos,length:1};break}}return null}handleSelectionDeletion(selection,text,cursor,deletions){const{start:start,end:end,length:length}=selection;for(let i=start;ip.index>=startIndex+numDeleted?{...p,index:p.index-numDeleted}:p.index>=startIndex&&p.indexnull!==p)),this.aiChars&&(this.aiChars=this.aiChars.map((p=>p.index>=startIndex+numDeleted?{...p,index:p.index-numDeleted}:p.index>=startIndex&&p.indexnull!==p)))}updateModifierStates(key){"Control"===key?this.isControlKeyPressed=!0:"Shift"===key?this.isShiftKeyPressed=!0:"Meta"===key?this.isMetaKeyPressed=!0:"v"!==key&&"V"!==key||!this.isControlKeyPressed&&!this.isMetaKeyPressed?["Control","Meta","Backspace","Delete","ArrowLeft","ArrowRight"].includes(key)||(this.isControlKeyPressed=!1,this.isShiftKeyPressed=!1,this.isMetaKeyPressed=!1,this.isPasteEvent=!1):this.isPasteEvent=!0}isCtrlBackspace(key,cursor){return"Backspace"===key&&this.isControlKeyPressed&&cursor>0}isCtrlDelete(key,cursor,text){return"Delete"===key&&this.isControlKeyPressed&&cursor0}isRegularDelete(key,cursor,text){return"Delete"===key&&!this.isControlKeyPressed&&cursorp.index>=cursor?{...p,index:p.index+1}:p))),this.aiChars&&(this.aiChars=this.aiChars.map((p=>p.index>=cursor?{...p,index:p.index+1}:p))),""!==charToInsert.trim()&&highlights.push({index:cursor,chars:charToInsert,time:this.currentTime,expiresAt:this.currentTime+1500}),{text:text,cursor:cursor+1}}handleCtrlDelete(text,cursor,deletions){const wordEnd=this.findNextWordBoundary(text,cursor),wordToDelete=text.substring(cursor,wordEnd);for(let i=0;i0){const prevLine=lines[lineIndex-1];cursor=lines.slice(0,lineIndex-1).join("\n").length+1+Math.min(col,prevLine.length)}else cursor=0;return cursor}handleArrowDown(text,cursor){const lines=text.split("\n"),{lineIndex:lineIndex,col:col}=this.getLineAndColumn(text,cursor);if(lineIndex0&&" "===text[wordStart-1];)wordStart--;for(;wordStart>0&&" "!==text[wordStart-1];)wordStart--;const wordToDelete=text.substring(wordStart,cursor);for(let i=0;i=text.length)return cursor;if(" "===text[cursor])for(;cursor=text.length){let lastNonSpace=text.length-1;for(;lastNonSpace>=0&&" "===text[lastNonSpace];)lastNonSpace--;return lastNonSpace+1}let wordEnd=cursor;for(;wordEnd0&&(" "===text[pos]||"\n"===text[pos]);)pos--;for(;pos>0&&" "!==text[pos-1]&&"\n"!==text[pos-1];)pos--;return pos}skipToEnd(){this.replayInProgress&&(this.replayInProgress=!1);let textOutput="";this.logData.forEach((event=>{"keydown"===event.event.toLowerCase()&&(textOutput=this.applyKey(event.key,textOutput))})),this.outputElement.innerHTML=textOutput.slice(0,-1),this.setScrubberVal(100)}skipToTime(percentage){const wasPlaying=this.replayInProgress;this.stopReplay();const targetTime=this.totalDuration*percentage/100;this.currentTime=targetTime,this.currentEventIndex=0,this.text="",this.cursorPosition=0,this.highlightedChars=[],this.deletedChars=[],this.isControlKeyPressed=!1,this.isMetaKeyPressed=!1,this.isPasteEvent=!1,this.pastedChars=[],this.currentPasteIndex=0,this.currentAiIndex=0,this.aiChars=[];let text="",cursor=0,highlights=[],deletions=[],pasteIndex=0,aiIndex=0;for(let i=0;itargetTime){this.currentEventIndex=i;break}void 0===event.rePosition||0!==this.currentEventIndex&&"mouseDown"!==event.event&&"mouseUp"!==event.event||(cursor=Math.max(0,Math.min(event.rePosition,text.length))),"keydown"===(null===(_event$event4=event.event)||void 0===_event$event4?void 0:_event$event4.toLowerCase())?(this.currentPasteIndex=pasteIndex,"v"!==event.key&&"V"!==event.key||!this.isControlKeyPressed&&!this.isMetaKeyPressed||pasteIndex++,({text:text,cursor:cursor,updatedHighlights:highlights,updatedDeleted:deletions}=this.processKeydownEvent(event,text,cursor,highlights,deletions))):"aiInsert"===event.event&&(this.currentAiIndex=aiIndex,({text:text,cursor:cursor,updatedHighlights:highlights,updatedDeleted:deletions}=this.processAiInsertEvent(event,text,cursor,highlights,deletions)),aiIndex++),this.currentEventIndex=i+1}this.currentPasteIndex=pasteIndex,this.currentAiIndex=aiIndex,this.text=text,this.cursorPosition=cursor,this.highlightedChars=highlights.filter((h=>!h.expiresAt||h.expiresAt>targetTime)),this.deletedChars=deletions.filter((d=>!d.expiresAt||d.expiresAt>targetTime)),this.updateDisplayText(this.text,this.cursorPosition,this.highlightedChars,this.deletedChars),this.setScrubberVal(percentage),wasPlaying&&(this.replayInProgress=!0,this.replayLog())}updateDisplayText(text,cursorPosition,highlights,deletions){let html="";const highlightMap={},deletionMap={},pastedMap={},aiMap={},currentTime=this.currentTime;highlights.forEach((h=>{let opacity=1;h.expiresAt&&h.expiresAt-currentTime<500&&(opacity=Math.max(0,(h.expiresAt-currentTime)/500)),highlightMap[h.index]={chars:h.chars,opacity:opacity}})),deletions.forEach((d=>{let opacity=.5;d.expiresAt&&d.expiresAt-currentTime<500&&(opacity=Math.max(0,(d.expiresAt-currentTime)/500*.5)),deletionMap[d.index]={chars:d.chars,opacity:opacity}})),this.pastedChars&&this.pastedChars.forEach((p=>{p.index{p.indexd.index>=text.length)),textLines=text.split("\n");let currentPosition=0;for(let lineIndex=0;lineIndex');const char=line[i];deletionMap[currentPosition]&&(html+=`${deletionMap[currentPosition].chars}`);const isPasted=pastedMap[currentPosition],isAi=aiMap[currentPosition],isHighlighted=highlightMap[currentPosition]&&" "!==char;html+=isPasted&&isHighlighted?`${char}`:isAi&&isHighlighted?`${char}`:isPasted?`${" "===char?" ":this.escapeHtml(char)}`:isAi?`${" "===char?" ":this.escapeHtml(char)}`:isHighlighted?`${char}`:" "===char?" ":this.escapeHtml(char),currentPosition++}currentPosition===cursorPosition&&(html+=''),lineIndex",currentPosition++)}if(cursorPosition!==text.length||html.endsWith('')||(html+=''),outOfRangeDeletions.length>0){outOfRangeDeletions.sort(((a,b)=>a.index-b.index));const cursorHTML='',cursorPos=html.lastIndexOf(cursorHTML);if(-1!==cursorPos){let deletedWordHTML='';outOfRangeDeletions.forEach((d=>{deletedWordHTML+=d.chars})),deletedWordHTML+="",html=html.substring(0,cursorPos)+deletedWordHTML+html.substring(cursorPos)}}const wasScrolledToBottom=this.outputElement.scrollHeight-this.outputElement.clientHeight<=this.outputElement.scrollTop+1;this.outputElement.innerHTML=html,(wasScrolledToBottom||this.isCursorBelowViewport())&&(this.outputElement.scrollTop=this.outputElement.scrollHeight)}isCursorBelowViewport(){const cursorElement=this.outputElement.querySelector(".tiny_cursive-cursor:last-of-type");if(!cursorElement)return!1;const cursorRect=cursorElement.getBoundingClientRect(),outputRect=this.outputElement.getBoundingClientRect();return cursorRect.bottom>outputRect.bottom}escapeHtml(unsafe){return unsafe.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}applyKey(key){switch(key){case"Enter":return"\n";case"Backspace":case"Delete":case"ControlBackspace":return"";case" ":return" ";default:return["Shift","Ctrl","Alt","ArrowDown","ArrowUp","Control","ArrowRight","ArrowLeft","Meta","CapsLock","Tab","Escape","Delete","PageUp","PageDown","Insert","Home","End","NumLock","AudioVolumeUp","AudioVolumeDown","MediaPlayPause","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","PrintScreen","UnIdentified"].includes(key)?"":key}}},_exports.default})); +define("tiny_cursive/replay",["exports","core/ajax","core/templates","jquery","core/str"],(function(_exports,_ajax,_templates,_jquery,Str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_jquery=_interopRequireDefault(_jquery),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str);return _exports.default=class{constructor(elementId,filePath){let speed=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,loop=arguments.length>3&&void 0!==arguments[3]&&arguments[3],controllerId=arguments.length>4?arguments[4]:void 0;this.controllerId=controllerId||"",this.replayInProgress=!1,this.speed=parseFloat(speed),this.loop=loop,this.highlightedChars=[],this.deletedChars=[],this.cursorPosition=0,this.currentEventIndex=0,this.totalEvents=0,this.currentTime=0,this.totalDuration=0,this.usercomments=[],this.pasteTimestamps=[],this.isPasteEvent=!1,this.isControlKeyPressed=!1,this.isShiftKeyPressed=!1,this.isMetaKeyPressed=!1,this.text="",this.pastedEvents=[],this.currentPasteIndex=0,this.pastedChars=[],this.aiEvents=[],this.currentAiIndex=0,this.aiChars=[],this.undoTimestamps=[],this.undoChars=[];const element=document.getElementById(elementId);if(!element)throw new Error("Element with id '".concat(elementId,"' not found"));this.outputElement=element,this.loadJSON(filePath).then((data=>(data.status?(this.processData(data),this.totalEvents=this.logData.length,this.identifyPasteEvents(),this.identifyUndoEvents(),this.controllerId&&this.logData&&this.constructController(this.controllerId),this.startReplay()):this.handleNoSubmission(),data))).catch((error=>{this.handleNoSubmission(),window.console.error("Error loading JSON file:",error.message)})),localStorage.getItem("nopasteevent")&&localStorage.getItem("pasteEvent")||(Str.get_string("nopasteevent","tiny_cursive").then((str=>(localStorage.setItem("nopasteevent",str),str))).catch((error=>window.console.log(error))),Str.get_string("pasteEvent","tiny_cursive").then((str=>(localStorage.setItem("pasteEvent",str),str))).catch((error=>window.console.log(error))))}processData(data){this.logData=JSON.parse(data.data),data.comments&&(this.usercomments=Array.isArray(JSON.parse(data.comments))?JSON.parse(data.comments):[]),"data"in this.logData&&(this.logData=this.logData.data),"payload"in this.logData&&(this.logData=this.logData.payload);for(let i=0;i0&&this.logData[0].unixTimestamp){const startTime=this.logData[0].unixTimestamp;this.logData=this.logData.map((event=>({...event,normalizedTime:event.unixTimestamp-startTime}))),this.totalDuration=this.logData[this.logData.length-1].normalizedTime}}async handleNoSubmission(){try{const[html,str]=await Promise.all([_templates.default.render("tiny_cursive/no_submission"),Str.get_string("warningpayload","tiny_cursive")]),newElement=(0,_jquery.default)(html).text(str);return(0,_jquery.default)(".tiny_cursive").html(newElement)}catch(error){return window.console.error(error),!1}}stopReplay(){if(this.replayInProgress&&(clearTimeout(this.replayTimeout),this.replayInProgress=!1,this.playButton)){const playSvg=document.createElement("img");playSvg.src=M.util.image_url("playicon","tiny_cursive"),this.playButton.querySelector(".play-icon").innerHTML=playSvg.outerHTML}}constructController(controllerId){var _controlContainer$que;this.replayInProgress=!1,this.currentPosition=0,this.speed=1,this.replayIntervalId&&(clearInterval(this.replayIntervalId),this.replayIntervalId=null);const container=document.getElementById(controllerId);if(!container)return void window.console.error("Container not found with ID:",controllerId);const controlContainer=container.querySelector(".tiny_cursive_replay_control");controlContainer?(controlContainer.innerHTML='',this.buildControllerUI(controlContainer,container),null===(_controlContainer$que=controlContainer.querySelector(".tiny_cursive_loading_spinner"))||void 0===_controlContainer$que||_controlContainer$que.remove()):window.console.error("Replay control container not found in:",controllerId)}buildControllerUI(controlContainer,container){const topRow=document.createElement("div");topRow.classList.add("tiny_cursive_top_row"),this.playButton=this.createPlayButton(),topRow.appendChild(this.playButton);const scrubberContainer=this.createScrubberContainer();topRow.appendChild(scrubberContainer),this.timeDisplay=this.createTimeDisplay(),topRow.appendChild(this.timeDisplay);const bottomRow=document.createElement("div");bottomRow.classList.add("tiny_cursive_bottom_row");const speedContainer=this.createSpeedControls();bottomRow.appendChild(speedContainer);const pasteEventsToggle=this.createPasteEventsToggle(container);bottomRow.appendChild(pasteEventsToggle),controlContainer.appendChild(topRow),controlContainer.appendChild(bottomRow),container.appendChild(this.pasteEventsPanel)}createPlayButton(){const playButton=document.createElement("button");playButton.classList.add("tiny_cursive_play_button");const playSvg=document.createElement("i");return playButton.innerHTML=''.concat(playSvg.outerHTML,""),playButton.addEventListener("click",(()=>{this.replayInProgress?this.stopReplay():this.startReplay(!1),(0,_jquery.default)(".tiny_cursive-nav-tab").find(".active").removeClass("active"),(0,_jquery.default)('a[id^="rep"]').addClass("active")})),playButton}createScrubberContainer(){const scrubberContainer=document.createElement("div");return scrubberContainer.classList.add("tiny_cursive_scrubber_container"),this.scrubberElement=document.createElement("input"),this.scrubberElement.classList.add("tiny_cursive_timeline_scrubber","timeline-scrubber"),this.scrubberElement.type="range",this.scrubberElement.max="100",this.scrubberElement.min="0",this.scrubberElement.value="0",this.scrubberElement.addEventListener("input",(()=>{this.skipToTime(parseInt(this.scrubberElement.value,10))})),scrubberContainer.appendChild(this.scrubberElement),scrubberContainer}createTimeDisplay(){const timeDisplay=document.createElement("div");return timeDisplay.classList.add("tiny_cursive_time_display"),timeDisplay.textContent="00:00 / 00:00",timeDisplay}createSpeedControls(){const speedContainer=document.createElement("div");speedContainer.classList.add("tiny_cursive_speed_controls","speed-controls");const speedLabel=document.createElement("span");speedLabel.classList.add("tiny_cursive_speed_label"),speedLabel.textContent="Speed: ",speedContainer.appendChild(speedLabel);const speedGroup=document.createElement("div");return speedGroup.classList.add("tiny_cursive_speed_group"),[1,1.5,2,5,10].forEach((speed=>{const speedBtn=document.createElement("button");speedBtn.textContent="".concat(speed,"x"),speedBtn.classList.add("tiny_cursive_speed_btn","speed-btn"),parseFloat(speed)===this.speed&&speedBtn.classList.add("active"),speedBtn.dataset.speed=speed,speedBtn.addEventListener("click",(()=>{document.querySelectorAll(".tiny_cursive_speed_btn").forEach((btn=>btn.classList.remove("active"))),speedBtn.classList.add("active"),this.speed=parseFloat(speedBtn.dataset.speed),this.replayInProgress&&(this.stopReplay(),this.startReplay(!1))})),speedGroup.appendChild(speedBtn)})),speedContainer.appendChild(speedGroup),speedContainer}createPasteEventsToggle(container){const pasteEventsToggle=document.createElement("div");pasteEventsToggle.classList.add("tiny_cursive_paste_events_toggle","paste-events-toggle");const pasteEventsIcon=document.createElement("span"),pasteIcon=document.createElement("img");pasteIcon.src=M.util.image_url("pasteicon","tiny_cursive"),pasteEventsIcon.innerHTML=pasteIcon.outerHTML,pasteEventsIcon.classList.add("tiny_cursive_paste_events_icon");const pasteEventsText=document.createElement("span");pasteEventsText.textContent=localStorage.getItem("pasteEvent"),this.pasteEventCount=document.createElement("span"),this.pasteEventCount.textContent="(".concat(this.pasteTimestamps.length,")"),this.pasteEventCount.className="paste-event-count",this.pasteEventCount.style.marginLeft="2px";const chevronIcon=document.createElement("span"),chevron=document.createElement("i");return chevron.className="fa fa-chevron-down",chevronIcon.innerHTML=chevron.outerHTML,chevronIcon.style.marginLeft="5px",chevronIcon.style.transition="transform 0.3s ease",pasteEventsToggle.appendChild(pasteEventsIcon),pasteEventsToggle.appendChild(pasteEventsText),pasteEventsToggle.appendChild(this.pasteEventCount),pasteEventsToggle.appendChild(chevronIcon),this.pasteEventsPanel=this.createPasteEventsPanel(container),pasteEventsToggle.addEventListener("click",(()=>{const isHidden="none"===this.pasteEventsPanel.style.display;this.pasteEventsPanel.style.display=isHidden?"block":"none",chevronIcon.style.transform=isHidden?"rotate(180deg)":"rotate(0deg)"})),pasteEventsToggle}createPasteEventsPanel(container){const existingPanel=container.querySelector(".paste-events-panel");existingPanel&&existingPanel.remove();const pasteEventsPanel=document.createElement("div");return pasteEventsPanel.classList.add("tiny_cursive_paste_events_panel","paste-events-panel"),pasteEventsPanel.style.display="none",this.populatePasteEventsPanel(pasteEventsPanel),pasteEventsPanel}identifyPasteEvents(){this.pasteTimestamps=[];let controlPressed=!1,metaPressed=!1,shiftPressed=!1,pasteCount=0;for(let i=0;i';const nextButton=document.createElement("button");nextButton.classList.add("paste-event-next-btn","tiny_cursive_nav_button"),nextButton.innerHTML='',nextButton.disabled=this.pasteTimestamps.length<=1,navButtons.appendChild(prevButton),navButtons.appendChild(nextButton),navigationRow.appendChild(counterDisplay),navigationRow.appendChild(navButtons);const contentContainer=document.createElement("div");contentContainer.className="paste-events-content tiny_cursive_content_container",contentContainer.appendChild(this.createPasteEventDisplay(this.pasteTimestamps[0])),carouselContainer.appendChild(navigationRow),carouselContainer.appendChild(contentContainer),panel.appendChild(carouselContainer);let currentIndex=0;const updateDisplay=()=>{contentContainer.innerHTML="",contentContainer.appendChild(this.createPasteEventDisplay(this.pasteTimestamps[currentIndex])),counterDisplay.textContent="Paste Events",prevButton.disabled=0===currentIndex,prevButton.style.opacity=0===currentIndex?"0.5":"1",nextButton.disabled=currentIndex===this.pasteTimestamps.length-1,nextButton.style.opacity=currentIndex===this.pasteTimestamps.length-1?"0.5":"1"};prevButton.addEventListener("click",(()=>{currentIndex>0&&(currentIndex--,updateDisplay())})),nextButton.addEventListener("click",(()=>{currentIndexthis.jumpToTimestamp(pasteEvent.timestamp))),headerRow.appendChild(textContainer),headerRow.appendChild(playButton),eventRow.appendChild(headerRow),eventRow}jumpToTimestamp(timestamp){const percentage=this.totalDuration>0?timestamp/this.totalDuration*100:0;this.skipToTime(percentage),this.replayInProgress||this.startReplay(!1)}setScrubberVal(value){if(this.scrubberElement&&(this.scrubberElement.value=String(value),this.timeDisplay)){const displayTime=Math.min(this.currentTime,this.totalDuration);this.timeDisplay.textContent="".concat(this.formatTime(displayTime)," / ").concat(this.formatTime(this.totalDuration))}}loadJSON(filePath){return(0,_ajax.call)([{methodname:"cursive_get_reply_json",args:{filepath:filePath}}])[0].done((response=>response)).fail((error=>{throw new Error("Error loading JSON file: ".concat(error.message))}))}formatTime(ms){const seconds=Math.floor(ms/1e3),minutes=Math.floor(seconds/60),remainingSeconds=seconds%60;return"".concat(minutes.toString().padStart(2,"0"),":").concat(remainingSeconds.toString().padStart(2,"0"))}startReplay(){let reset=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.replayInProgress&&clearTimeout(this.replayTimeout);if((this.totalDuration>0&&this.currentTime>=this.totalDuration||this.currentEventIndex>=this.totalEvents)&&!reset&&(reset=!0),this.replayInProgress=!0,reset&&(this.outputElement.innerHTML="",this.text="",this.cursorPosition=0,this.currentEventIndex=0,this.currentTime=0,this.highlightedChars=[],this.deletedChars=[],this.isControlKeyPressed=!1,this.isMetaKeyPressed=!1,this.currentPasteIndex=0,this.pastedChars=[],this.currentAiIndex=0,this.aiChars=[]),this.playButton){const pauseSvg=document.createElement("i");pauseSvg.className="fa fa-pause",this.playButton.querySelector(".play-icon").innerHTML=pauseSvg.outerHTML}this.replayLog()}replayLog(){if(this.replayInProgress){for(;this.currentEventIndexthis.currentTime)break;let text=this.text||"",cursor=this.cursorPosition,updatedHighlights=[...this.highlightedChars],updatedDeleted=[...this.deletedChars];void 0===event.rePosition||0!==this.currentEventIndex&&"mouseDown"!==event.event&&"mouseUp"!==event.event||(cursor=Math.max(0,Math.min(event.rePosition,text.length))),"keydown"===(null===(_event$event3=event.event)||void 0===_event$event3?void 0:_event$event3.toLowerCase())?({text:text,cursor:cursor,updatedHighlights:updatedHighlights,updatedDeleted:updatedDeleted}=this.processKeydownEvent(event,text,cursor,updatedHighlights,updatedDeleted)):"aiInsert"===event.event&&({text:text,cursor:cursor,updatedHighlights:updatedHighlights,updatedDeleted:updatedDeleted}=this.processAiInsertEvent(event,text,cursor,updatedHighlights,updatedDeleted)),this.text=text,this.cursorPosition=cursor,this.highlightedChars=updatedHighlights.filter((h=>!h.expiresAt||h.expiresAt>this.currentTime)),this.deletedChars=updatedDeleted.filter((d=>!d.expiresAt||d.expiresAt>this.currentTime)),this.currentEventIndex++}if(this.updateDisplayText(this.text,this.cursorPosition,this.highlightedChars,this.deletedChars),this.totalDuration>0){const percentComplete=Math.min(this.currentTime/this.totalDuration*100,100);this.setScrubberVal(percentComplete)}if(this.replayInProgress){const baseIncrement=100,incrementTime=baseIncrement/this.speed;this.currentTime+=baseIncrement,this.currentEventIndex>=this.totalEvents?this.loop?this.startReplay(!0):(this.stopReplay(),this.updateDisplayText(this.text,this.cursorPosition,[],[])):this.replayTimeout=setTimeout((()=>this.replayLog()),incrementTime)}}else this.updateDisplayText(this.text,this.cursorPosition,[],[])}getLineAndColumn(text,pos){const before=text.substring(0,pos);return{lineIndex:before.split("\n").length-1,col:before.length-before.lastIndexOf("\n")-1}}processAiInsertEvent(event,text,cursor,highlights,deletions){if(this.aiEvents&&this.currentAiIndex1,isNewLineInsertion=insertText.startsWith("\n")||insertText.endsWith("\n"),{wordStart:wordStart,wordEnd:wordEnd}=this.findWordToReplace(text,targetPosition,currentCursor,aiWords,isMultiWord,isNewLineInsertion),wordToReplace=text.substring(wordStart,wordEnd);this.markCharsAsDeleted(wordToReplace,wordStart,deletions);const replacedLength=wordToReplace.length;text=text.substring(0,wordStart)+insertText+text.substring(wordEnd);const positionDiff=insertText.length-replacedLength,newCursor=this.calculateNewCursorPosition(currentCursor,targetPosition,wordStart,wordEnd,insertText,isNewLineInsertion);return this.updateCharacterIndices(wordStart,wordEnd,positionDiff,insertText),{text:text,cursor:newCursor}}findWordToReplace(text,targetPosition,currentCursor,aiWords,isMultiWord,isNewLineInsertion){if(isNewLineInsertion)return{wordStart:currentCursor,wordEnd:currentCursor};const{lineStart:lineStart,lineEnd:lineEnd}=this.findLineRange(text,targetPosition),lineText=text.substring(lineStart,lineEnd),words=this.extractWordsFromLine(lineText,lineStart);return 0===words.length?{wordStart:currentCursor,wordEnd:currentCursor}:isMultiWord?this.findMultiWordMatch(words,aiWords,targetPosition):this.findSingleWordMatch(words,aiWords[0],targetPosition)}findLineRange(text,targetPosition){let lineStart=0;for(let i=targetPosition-1;i>=0;i--)if("\n"===text[i]){lineStart=i+1;break}let lineEnd=text.length;for(let i=targetPosition;i=lineText.length)break;const start=pos;for(;posstart&&words.push({text:lineText.substring(start,pos),start:lineStart+start,end:lineStart+pos})}return words}findMultiWordMatch(words,aiWords,targetPosition){let bestMatch={start:-1,end:-1,score:-1,wordCount:0,similarityScore:0};for(let i=0;ibestMatch.score||matchResult.totalScore===bestMatch.score&&matchResult.similarityScore>bestMatch.similarityScore)&&(bestMatch=matchResult)}if(bestMatch.score>10)return{wordStart:bestMatch.start,wordEnd:bestMatch.end};{const closest=this.findClosestWord(words,targetPosition);return{wordStart:closest.start,wordEnd:closest.end}}}evaluateMultiWordSequence(words,aiWords,startIndex,targetPosition){const seqWords=[];for(let j=0;j=seqStart&&targetPosition<=seqEndPos&&(positionScore+=10,targetPosition>=seqWords[0].start&&targetPosition<=seqWords[0].end&&(positionScore+=5)),positionScore}findSingleWordMatch(words,aiWord,targetPosition){const aiWordLower=aiWord.toLowerCase(),bestSimilarityMatch=this.findBestSimilarityMatch(words,aiWordLower);if(bestSimilarityMatch.score>.5)return{wordStart:bestSimilarityMatch.word.start,wordEnd:bestSimilarityMatch.word.end};const bestPositionMatch=this.findBestPositionMatch(words,aiWordLower,targetPosition);return bestPositionMatch.word?{wordStart:bestPositionMatch.word.start,wordEnd:bestPositionMatch.word.end}:this.findWordBoundaryAtPosition(words[0].start,words[words.length-1].end,targetPosition,this.text)}findBestSimilarityMatch(words,aiWordLower){let bestMatch={word:null,score:0};for(const word of words){let similarity=this.calculateSimilarity(aiWordLower,word.text.toLowerCase());const wordLower=word.text.toLowerCase();wordLower.length<.5*aiWordLower.length&&aiWordLower.startsWith(wordLower)&&(similarity*=.3),similarity>bestMatch.score&&(bestMatch={word:word,score:similarity})}return bestMatch}findBestPositionMatch(words,aiWordLower,targetPosition){let bestMatch={word:null,score:-1};for(const word of words){let score=this.calculateWordScore(word,aiWordLower,targetPosition);score>bestMatch.score&&(bestMatch={word:word,score:score})}return bestMatch}calculateWordScore(word,aiWordLower,targetPosition){let score=0;if(targetPosition>=word.start&&targetPosition<=word.end)score+=30;else{const distance=Math.min(Math.abs(targetPosition-word.start),Math.abs(targetPosition-word.end));score+=Math.max(0,20-distance)}let similarity=this.calculateSimilarity(aiWordLower,word.text.toLowerCase());const wordLower=word.text.toLowerCase();return wordLower.length<.5*aiWordLower.length&&aiWordLower.startsWith(wordLower)&&(similarity*=.3),score+=10*similarity,score}findWordBoundaryAtPosition(lineStart,lineEnd,targetPosition,text){let wordStart=targetPosition;for(;wordStart>lineStart&&" "!==text[wordStart-1]&&"\n"!==text[wordStart-1];)wordStart--;let wordEnd=targetPosition;for(;wordEnd0)for(let i=0;i=wordStart&&targetPosition<=wordEnd)return wordStart+insertText.length;const positionDiff=insertText.length-(wordEnd-wordStart);return currentCursor>=wordEnd?currentCursor+positionDiff:currentCursor>wordStart&¤tCursorp.index>=wordEnd?{...p,index:p.index+positionDiff}:p.index>=wordStart&&p.indexnull!==p)))}markCharsAsAiInserted(wordStart,insertText){if(this.aiChars||(this.aiChars=[]),""!==insertText.trim())for(let i=0;i{if(!justAddedIndices.has(p.index)){if(p.index>=wordEnd)return{...p,index:p.index+positionDiff};if(p.index>=wordStart&&p.indexnull!==p))}calculateSimilarity(str1,str2){if(str1===str2)return 1;if(0===str1.length||0===str2.length)return 0;if(str1.startsWith(str2)||str2.startsWith(str1))return.8;const len1=str1.length,len2=str2.length,matrix=Array(len2+1).fill(null).map((()=>Array(len1+1).fill(0)));for(let i=0;i<=len1;i++)matrix[0][i]=i;for(let j=0;j<=len2;j++)matrix[j][0]=j;for(let j=1;j<=len2;j++)for(let i=1;i<=len1;i++){const cost=str1[i-1]===str2[j-1]?0:1;matrix[j][i]=Math.min(matrix[j][i-1]+1,matrix[j-1][i]+1,matrix[j-1][i-1]+cost)}const maxLen=Math.max(len1,len2);return 1-matrix[len2][len1]/maxLen}findClosestWord(words,targetPosition){if(0===words.length)return{start:targetPosition,end:targetPosition};let closest=words[0],minDistance=Math.min(Math.abs(targetPosition-words[0].start),Math.abs(targetPosition-words[0].end));for(const word of words){if(targetPosition>=word.start&&targetPosition<=word.end)return word;const distance=Math.min(Math.abs(targetPosition-word.start),Math.abs(targetPosition-word.end));distance0){const textBeforeUndo=text;text=text.substring(0,newPosition)+text.substring(cursor),cursor=newPosition;for(let i=0;i1}processKeyOperation(key,charToInsert,text,cursor,highlights,deletions,selection){return this.isCtrlBackspace(key,cursor)?({text:text,cursor:cursor}=this.handleCtrlBackspace(text,cursor,deletions)):this.isCtrlDelete(key,cursor,text)?({text:text}=this.handleCtrlDelete(text,cursor,deletions)):this.isCtrlArrowMove(key)?cursor=this.handleCtrlArrowMove(key,text,cursor):this.isRegularBackspace(key,cursor)?({text:text,cursor:cursor}=this.handleBackspace(text,cursor,deletions)):this.isRegularDelete(key,cursor,text)?({text:text}=this.handleDelete(text,cursor,deletions)):this.isArrowUp(key)?cursor=this.handleArrowUp(text,cursor):this.isArrowDown(key)?cursor=this.handleArrowDown(text,cursor):this.isRegularArrowMove(key)?cursor=this.handleArrowMove(key,text,cursor):charToInsert&&charToInsert.length>0&&(selection&&selection.length>0&&({text:text,cursor:cursor}=this.handleSelectionDeletion(selection,text,cursor,deletions)),({text:text,cursor:cursor}=this.handleCharacterInsert(charToInsert,text,cursor,highlights))),{text:text,cursor:cursor,updatedHighlights:highlights,updatedDeleted:deletions}}detectSelection(eventIndex){var _currentEvent$event;const currentEvent=this.logData[eventIndex];if("keydown"===(null===(_currentEvent$event=currentEvent.event)||void 0===_currentEvent$event?void 0:_currentEvent$event.toLowerCase())&&("Backspace"===currentEvent.key||"Delete"===currentEvent.key)){const currentPos=currentEvent.rePosition;return this.processDetection(currentPos,currentEvent,eventIndex)}return null}processDetection(currentPos,currentEvent,eventIndex){for(let i=eventIndex+1;i1)return{start:Math.min(currentPos,nextPos),end:Math.max(currentPos,nextPos),length:positionDiff};if(1===positionDiff)return"Backspace"===currentEvent.key?{start:nextPos,end:currentPos,length:1}:{start:currentPos,end:nextPos,length:1};break}}return null}handleSelectionDeletion(selection,text,cursor,deletions){const{start:start,end:end,length:length}=selection;for(let i=start;ip.index>=startIndex+numDeleted?{...p,index:p.index-numDeleted}:p.index>=startIndex&&p.indexnull!==p)),this.aiChars&&(this.aiChars=this.aiChars.map((p=>p.index>=startIndex+numDeleted?{...p,index:p.index-numDeleted}:p.index>=startIndex&&p.indexnull!==p)))}updateModifierStates(key){"Control"===key?this.isControlKeyPressed=!0:"Shift"===key?this.isShiftKeyPressed=!0:"Meta"===key?this.isMetaKeyPressed=!0:"v"!==key&&"V"!==key||!this.isControlKeyPressed&&!this.isMetaKeyPressed?["Control","Meta","Backspace","Delete","ArrowLeft","ArrowRight"].includes(key)||(this.isControlKeyPressed=!1,this.isShiftKeyPressed=!1,this.isMetaKeyPressed=!1,this.isPasteEvent=!1):this.isPasteEvent=!0}isCtrlBackspace(key,cursor){return"Backspace"===key&&this.isControlKeyPressed&&cursor>0}isCtrlDelete(key,cursor,text){return"Delete"===key&&this.isControlKeyPressed&&cursor0}isRegularDelete(key,cursor,text){return"Delete"===key&&!this.isControlKeyPressed&&cursorp.index>=cursor?{...p,index:p.index+1}:p))),this.aiChars&&(this.aiChars=this.aiChars.map((p=>p.index>=cursor?{...p,index:p.index+1}:p))),""!==charToInsert.trim()&&highlights.push({index:cursor,chars:charToInsert,time:this.currentTime,expiresAt:this.currentTime+1500}),{text:text,cursor:cursor+1}}handleCtrlDelete(text,cursor,deletions){const wordEnd=this.findNextWordBoundary(text,cursor),wordToDelete=text.substring(cursor,wordEnd);for(let i=0;i0){const prevLine=lines[lineIndex-1];cursor=lines.slice(0,lineIndex-1).join("\n").length+1+Math.min(col,prevLine.length)}else cursor=0;return cursor}handleArrowDown(text,cursor){const lines=text.split("\n"),{lineIndex:lineIndex,col:col}=this.getLineAndColumn(text,cursor);if(lineIndex0&&" "===text[wordStart-1];)wordStart--;for(;wordStart>0&&" "!==text[wordStart-1];)wordStart--;const wordToDelete=text.substring(wordStart,cursor);for(let i=0;i=text.length)return cursor;if(" "===text[cursor])for(;cursor=text.length){let lastNonSpace=text.length-1;for(;lastNonSpace>=0&&" "===text[lastNonSpace];)lastNonSpace--;return lastNonSpace+1}let wordEnd=cursor;for(;wordEnd0&&(" "===text[pos]||"\n"===text[pos]);)pos--;for(;pos>0&&" "!==text[pos-1]&&"\n"!==text[pos-1];)pos--;return pos}skipToEnd(){this.replayInProgress&&(this.replayInProgress=!1);let textOutput="";this.logData.forEach((event=>{"keydown"===event.event.toLowerCase()&&(textOutput=this.applyKey(event.key,textOutput))})),this.outputElement.innerHTML=textOutput.slice(0,-1),this.setScrubberVal(100)}skipToTime(percentage){const wasPlaying=this.replayInProgress;this.stopReplay();const targetTime=this.totalDuration*percentage/100;this.currentTime=targetTime,this.currentEventIndex=0,this.text="",this.cursorPosition=0,this.highlightedChars=[],this.deletedChars=[],this.isControlKeyPressed=!1,this.isMetaKeyPressed=!1,this.isPasteEvent=!1,this.pastedChars=[],this.currentPasteIndex=0,this.currentAiIndex=0,this.aiChars=[];let text="",cursor=0,highlights=[],deletions=[],pasteIndex=0,aiIndex=0;for(let i=0;itargetTime){this.currentEventIndex=i;break}void 0===event.rePosition||0!==this.currentEventIndex&&"mouseDown"!==event.event&&"mouseUp"!==event.event||(cursor=Math.max(0,Math.min(event.rePosition,text.length))),"keydown"===(null===(_event$event4=event.event)||void 0===_event$event4?void 0:_event$event4.toLowerCase())?(this.currentPasteIndex=pasteIndex,"v"!==event.key&&"V"!==event.key||!this.isControlKeyPressed&&!this.isMetaKeyPressed||pasteIndex++,({text:text,cursor:cursor,updatedHighlights:highlights,updatedDeleted:deletions}=this.processKeydownEvent(event,text,cursor,highlights,deletions))):"aiInsert"===event.event&&(this.currentAiIndex=aiIndex,({text:text,cursor:cursor,updatedHighlights:highlights,updatedDeleted:deletions}=this.processAiInsertEvent(event,text,cursor,highlights,deletions)),aiIndex++),this.currentEventIndex=i+1}this.currentPasteIndex=pasteIndex,this.currentAiIndex=aiIndex,this.text=text,this.cursorPosition=cursor,this.highlightedChars=highlights.filter((h=>!h.expiresAt||h.expiresAt>targetTime)),this.deletedChars=deletions.filter((d=>!d.expiresAt||d.expiresAt>targetTime)),this.updateDisplayText(this.text,this.cursorPosition,this.highlightedChars,this.deletedChars),this.setScrubberVal(percentage),wasPlaying&&(this.replayInProgress=!0,this.replayLog())}updateDisplayText(text,cursorPosition,highlights,deletions){let html="";const highlightMap={},deletionMap={},pastedMap={},aiMap={},currentTime=this.currentTime;highlights.forEach((h=>{let opacity=1;h.expiresAt&&h.expiresAt-currentTime<500&&(opacity=Math.max(0,(h.expiresAt-currentTime)/500)),highlightMap[h.index]={chars:h.chars,opacity:opacity}})),deletions.forEach((d=>{let opacity=.5;d.expiresAt&&d.expiresAt-currentTime<500&&(opacity=Math.max(0,(d.expiresAt-currentTime)/500*.5)),deletionMap[d.index]={chars:d.chars,opacity:opacity}})),this.pastedChars&&this.pastedChars.forEach((p=>{p.index{p.indexd.index>=text.length)),textLines=text.split("\n");let currentPosition=0;for(let lineIndex=0;lineIndex');const char=line[i];deletionMap[currentPosition]&&(html+='').concat(deletionMap[currentPosition].chars,""));const isPasted=pastedMap[currentPosition],isAi=aiMap[currentPosition],isHighlighted=highlightMap[currentPosition]&&" "!==char;html+=isPasted&&isHighlighted?'').concat(char,""):isAi&&isHighlighted?'').concat(char,""):isPasted?''.concat(" "===char?" ":this.escapeHtml(char),""):isAi?''.concat(" "===char?" ":this.escapeHtml(char),""):isHighlighted?'').concat(char,""):" "===char?" ":this.escapeHtml(char),currentPosition++}currentPosition===cursorPosition&&(html+=''),lineIndex",currentPosition++)}if(cursorPosition!==text.length||html.endsWith('')||(html+=''),outOfRangeDeletions.length>0){outOfRangeDeletions.sort(((a,b)=>a.index-b.index));const cursorHTML='',cursorPos=html.lastIndexOf(cursorHTML);if(-1!==cursorPos){let deletedWordHTML='';outOfRangeDeletions.forEach((d=>{deletedWordHTML+=d.chars})),deletedWordHTML+="",html=html.substring(0,cursorPos)+deletedWordHTML+html.substring(cursorPos)}}const wasScrolledToBottom=this.outputElement.scrollHeight-this.outputElement.clientHeight<=this.outputElement.scrollTop+1;this.outputElement.innerHTML=html,(wasScrolledToBottom||this.isCursorBelowViewport())&&(this.outputElement.scrollTop=this.outputElement.scrollHeight)}isCursorBelowViewport(){const cursorElement=this.outputElement.querySelector(".tiny_cursive-cursor:last-of-type");if(!cursorElement)return!1;const cursorRect=cursorElement.getBoundingClientRect(),outputRect=this.outputElement.getBoundingClientRect();return cursorRect.bottom>outputRect.bottom}escapeHtml(unsafe){return unsafe.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}applyKey(key){switch(key){case"Enter":return"\n";case"Backspace":case"Delete":case"ControlBackspace":return"";case" ":return" ";default:return["Shift","Ctrl","Alt","ArrowDown","ArrowUp","Control","ArrowRight","ArrowLeft","Meta","CapsLock","Tab","Escape","Delete","PageUp","PageDown","Insert","Home","End","NumLock","AudioVolumeUp","AudioVolumeDown","MediaPlayPause","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","PrintScreen","UnIdentified"].includes(key)?"":key}}},_exports.default})); //# sourceMappingURL=replay.min.js.map \ No newline at end of file diff --git a/amd/build/replay.min.js.map b/amd/build/replay.min.js.map index 126f99bc..681f26ef 100644 --- a/amd/build/replay.min.js.map +++ b/amd/build/replay.min.js.map @@ -1 +1 @@ -{"version":3,"file":"replay.min.js","sources":["../src/replay.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/replay\n * @category TinyMCE Editor\n * @copyright CTI \n * @author kuldeep singh \n */\n\nimport {call as fetchJson} from 'core/ajax';\nimport templates from 'core/templates';\nimport $ from 'jquery';\nimport * as Str from 'core/str';\n\nexport default class Replay {\n constructor(elementId, filePath, speed = 1, loop = false, controllerId) {\n // Initialize core properties\n this.controllerId = controllerId || '';\n this.replayInProgress = false;\n this.speed = parseFloat(speed);\n this.loop = loop;\n this.highlightedChars = [];\n this.deletedChars = [];\n this.cursorPosition = 0;\n this.currentEventIndex = 0;\n this.totalEvents = 0;\n this.currentTime = 0;\n this.totalDuration = 0;\n this.usercomments = [];\n this.pasteTimestamps = [];\n this.isPasteEvent = false;\n this.isControlKeyPressed = false;\n this.isShiftKeyPressed = false;\n this.isMetaKeyPressed = false;\n this.text = '';\n this.pastedEvents = [];\n this.currentPasteIndex = 0;\n this.pastedChars = [];\n this.aiEvents = [];\n this.currentAiIndex = 0;\n this.aiChars = [];\n this.undoTimestamps = [];\n this.undoChars = [];\n\n const element = document.getElementById(elementId);\n if (!element) {\n throw new Error(`Element with id '${elementId}' not found`);\n }\n this.outputElement = element;\n\n // Load JSON data and initialize replay\n this.loadJSON(filePath).then(data => {\n if (data.status) {\n this.processData(data);\n this.totalEvents = this.logData.length;\n this.identifyPasteEvents();\n this.identifyUndoEvents();\n if (this.controllerId && this.logData) {\n this.constructController(this.controllerId);\n }\n this.startReplay();\n } else {\n this.handleNoSubmission();\n }\n return data;\n }).catch(error => {\n this.handleNoSubmission();\n window.console.error('Error loading JSON file:', error.message);\n });\n if (!localStorage.getItem('nopasteevent') || !localStorage.getItem('pasteEvent')) {\n Str.get_string('nopasteevent', 'tiny_cursive').then(str => {\n localStorage.setItem('nopasteevent', str);\n return str;\n }).catch(error => window.console.log(error));\n Str.get_string('pasteEvent', 'tiny_cursive').then(str => {\n localStorage.setItem('pasteEvent', str);\n return str;\n }).catch(error => window.console.log(error));\n }\n }\n\n // Process JSON data and normalize timestamps\n processData(data) {\n this.logData = JSON.parse(data.data);\n if (data.comments) {\n this.usercomments = Array.isArray(JSON.parse(data.comments)) ? JSON.parse(data.comments) : [];\n }\n if ('data' in this.logData) {\n this.logData = this.logData.data;\n }\n if ('payload' in this.logData) {\n this.logData = this.logData.payload;\n }\n for (let i = 0; i < this.logData.length; i++) {\n const event = this.logData[i];\n if (event.event === 'Paste') {\n if (typeof event.pastedContent === 'string' && event.pastedContent.trim() !== '') {\n this.pastedEvents.push(event.pastedContent);\n }\n }\n if (event.event === 'aiInsert' && event.aiContent) {\n this.aiEvents.push(event.aiContent);\n }\n }\n if (this.logData.length > 0 && this.logData[0].unixTimestamp) {\n const startTime = this.logData[0].unixTimestamp;\n this.logData = this.logData.map(event => ({\n ...event,\n normalizedTime: event.unixTimestamp - startTime\n }));\n this.totalDuration = this.logData[this.logData.length - 1].normalizedTime;\n }\n }\n\n async handleNoSubmission() {\n try {\n const [html, str] = await Promise.all([\n templates.render('tiny_cursive/no_submission'),\n Str.get_string('warningpayload', 'tiny_cursive')\n ]);\n const newElement = $(html).text(str);\n return $('.tiny_cursive').html(newElement);\n } catch (error) {\n window.console.error(error);\n return false;\n }\n }\n\n // Stop the replay and update play button icon\n stopReplay() {\n if (this.replayInProgress) {\n clearTimeout(this.replayTimeout);\n this.replayInProgress = false;\n if (this.playButton) {\n const playSvg = document.createElement('img');\n playSvg.src = M.util.image_url('playicon', 'tiny_cursive');\n this.playButton.querySelector('.play-icon').innerHTML = playSvg.outerHTML;\n }\n }\n }\n\n // Build the replay control UI (play button, scrubber, speed controls)\n constructController(controllerId) {\n this.replayInProgress = false;\n this.currentPosition = 0;\n this.speed = 1;\n if (this.replayIntervalId) {\n clearInterval(this.replayIntervalId);\n this.replayIntervalId = null;\n }\n\n const container = document.getElementById(controllerId);\n if (!container) {\n window.console.error('Container not found with ID:', controllerId);\n return;\n }\n\n const controlContainer = container.querySelector('.tiny_cursive_replay_control');\n if (!controlContainer) {\n window.console.error('Replay control container not found in:', controllerId);\n return;\n }\n controlContainer.innerHTML = '';\n\n this.buildControllerUI(controlContainer, container);\n controlContainer.querySelector('.tiny_cursive_loading_spinner')?.remove();\n }\n\n buildControllerUI(controlContainer, container) {\n const topRow = document.createElement('div');\n topRow.classList.add('tiny_cursive_top_row');\n\n this.playButton = this.createPlayButton();\n topRow.appendChild(this.playButton);\n\n const scrubberContainer = this.createScrubberContainer();\n topRow.appendChild(scrubberContainer);\n\n this.timeDisplay = this.createTimeDisplay();\n topRow.appendChild(this.timeDisplay);\n\n const bottomRow = document.createElement('div');\n bottomRow.classList.add('tiny_cursive_bottom_row');\n\n const speedContainer = this.createSpeedControls();\n bottomRow.appendChild(speedContainer);\n\n const pasteEventsToggle = this.createPasteEventsToggle(container);\n bottomRow.appendChild(pasteEventsToggle);\n\n controlContainer.appendChild(topRow);\n controlContainer.appendChild(bottomRow);\n container.appendChild(this.pasteEventsPanel);\n }\n\n createPlayButton() {\n const playButton = document.createElement('button');\n playButton.classList.add('tiny_cursive_play_button');\n const playSvg = document.createElement('i');\n playButton.innerHTML = `${playSvg.outerHTML}`;\n playButton.addEventListener('click', () => {\n if (this.replayInProgress) {\n this.stopReplay();\n } else {\n this.startReplay(false);\n }\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $('a[id^=\"rep\"]').addClass('active');\n });\n return playButton;\n }\n\n createScrubberContainer() {\n const scrubberContainer = document.createElement('div');\n scrubberContainer.classList.add('tiny_cursive_scrubber_container');\n this.scrubberElement = document.createElement('input');\n this.scrubberElement.classList.add('tiny_cursive_timeline_scrubber', 'timeline-scrubber');\n this.scrubberElement.type = 'range';\n this.scrubberElement.max = '100';\n this.scrubberElement.min = '0';\n this.scrubberElement.value = '0';\n this.scrubberElement.addEventListener('input', () => {\n this.skipToTime(parseInt(this.scrubberElement.value, 10));\n });\n scrubberContainer.appendChild(this.scrubberElement);\n return scrubberContainer;\n }\n\n createTimeDisplay() {\n const timeDisplay = document.createElement('div');\n timeDisplay.classList.add('tiny_cursive_time_display');\n timeDisplay.textContent = '00:00 / 00:00';\n return timeDisplay;\n }\n\n createSpeedControls() {\n const speedContainer = document.createElement('div');\n speedContainer.classList.add('tiny_cursive_speed_controls', 'speed-controls');\n const speedLabel = document.createElement('span');\n speedLabel.classList.add('tiny_cursive_speed_label');\n speedLabel.textContent = 'Speed: ';\n speedContainer.appendChild(speedLabel);\n\n const speedGroup = document.createElement('div');\n speedGroup.classList.add('tiny_cursive_speed_group');\n [1, 1.5, 2, 5, 10].forEach(speed => {\n const speedBtn = document.createElement('button');\n speedBtn.textContent = `${speed}x`;\n speedBtn.classList.add('tiny_cursive_speed_btn', 'speed-btn');\n if (parseFloat(speed) === this.speed) {\n speedBtn.classList.add('active');\n }\n speedBtn.dataset.speed = speed;\n speedBtn.addEventListener('click', () => {\n document.querySelectorAll('.tiny_cursive_speed_btn').forEach(btn => btn.classList.remove('active'));\n speedBtn.classList.add('active');\n this.speed = parseFloat(speedBtn.dataset.speed);\n if (this.replayInProgress) {\n this.stopReplay();\n this.startReplay(false);\n }\n });\n speedGroup.appendChild(speedBtn);\n });\n speedContainer.appendChild(speedGroup);\n return speedContainer;\n }\n\n createPasteEventsToggle(container) {\n const pasteEventsToggle = document.createElement('div');\n pasteEventsToggle.classList.add('tiny_cursive_paste_events_toggle', 'paste-events-toggle');\n\n const pasteEventsIcon = document.createElement('span');\n const pasteIcon = document.createElement('img');\n pasteIcon.src = M.util.image_url('pasteicon', 'tiny_cursive');\n pasteEventsIcon.innerHTML = pasteIcon.outerHTML;\n pasteEventsIcon.classList.add('tiny_cursive_paste_events_icon');\n\n const pasteEventsText = document.createElement('span');\n pasteEventsText.textContent = localStorage.getItem('pasteEvent');\n\n this.pasteEventCount = document.createElement('span');\n this.pasteEventCount.textContent = `(${this.pasteTimestamps.length})`;\n this.pasteEventCount.className = 'paste-event-count';\n this.pasteEventCount.style.marginLeft = '2px';\n\n const chevronIcon = document.createElement('span');\n const chevron = document.createElement('i');\n chevron.className = 'fa fa-chevron-down';\n chevronIcon.innerHTML = chevron.outerHTML;\n chevronIcon.style.marginLeft = '5px';\n chevronIcon.style.transition = 'transform 0.3s ease';\n\n pasteEventsToggle.appendChild(pasteEventsIcon);\n pasteEventsToggle.appendChild(pasteEventsText);\n pasteEventsToggle.appendChild(this.pasteEventCount);\n pasteEventsToggle.appendChild(chevronIcon);\n\n this.pasteEventsPanel = this.createPasteEventsPanel(container);\n pasteEventsToggle.addEventListener('click', () => {\n const isHidden = this.pasteEventsPanel.style.display === 'none';\n this.pasteEventsPanel.style.display = isHidden ? 'block' : 'none';\n chevronIcon.style.transform = isHidden ? 'rotate(180deg)' : 'rotate(0deg)';\n });\n\n return pasteEventsToggle;\n }\n\n createPasteEventsPanel(container) {\n const existingPanel = container.querySelector('.paste-events-panel');\n if (existingPanel) {\n existingPanel.remove();\n }\n const pasteEventsPanel = document.createElement('div');\n pasteEventsPanel.classList.add('tiny_cursive_paste_events_panel', 'paste-events-panel');\n pasteEventsPanel.style.display = 'none';\n this.populatePasteEventsPanel(pasteEventsPanel);\n return pasteEventsPanel;\n }\n\n // Detect Ctrl+V paste events and sync with user comments\n identifyPasteEvents() {\n this.pasteTimestamps = [];\n let controlPressed = false;\n let metaPressed = false;\n /* eslint-disable no-unused-vars */\n let shiftPressed = false;\n let pasteCount = 0;\n\n for (let i = 0; i < this.logData.length; i++) {\n const event = this.logData[i];\n if (event.event?.toLowerCase() === 'keydown') {\n if (event.key === 'Control') {\n controlPressed = true;\n } else if (event.key === 'Meta') {\n metaPressed = true;\n } else if (event.key === 'Shift') {\n shiftPressed = true;\n } else if ((event.key === 'v' || event.key === 'V') && (controlPressed || metaPressed)) {\n if (this.pastedEvents[pasteCount]) {\n const timestamp = event.normalizedTime || 0;\n this.pasteTimestamps.push({\n index: pasteCount,\n time: timestamp,\n formattedTime: this.formatTime(timestamp),\n pastedText: this.pastedEvents[pasteCount],\n timestamp\n });\n }\n pasteCount++;\n controlPressed = false;\n shiftPressed = false;\n metaPressed = false;\n } else {\n controlPressed = false;\n shiftPressed = false;\n metaPressed = false;\n }\n }\n }\n\n if (this.pasteEventsPanel) {\n this.populatePasteEventsPanel(this.pasteEventsPanel);\n }\n }\n\n identifyUndoEvents() {\n this.undoTimestamps = [];\n let controlPressed = false;\n let metaPressed = false;\n let undoCount = 0;\n\n for (let i = 0; i < this.logData.length; i++) {\n const event = this.logData[i];\n if (event.event?.toLowerCase() === 'keydown') {\n if (event.key === 'Control') {\n controlPressed = true;\n } else if (event.key === 'Meta') {\n metaPressed = true;\n } else if ((event.key === 'z' || event.key === 'Z') && (controlPressed || metaPressed)) {\n const timestamp = event.normalizedTime || 0;\n this.undoTimestamps.push({\n index: undoCount,\n time: timestamp,\n formattedTime: this.formatTime(timestamp),\n timestamp\n });\n undoCount++;\n controlPressed = false;\n metaPressed = false;\n } else {\n controlPressed = false;\n metaPressed = false;\n }\n }\n }\n }\n\n // Populate the paste events panel with navigation\n populatePasteEventsPanel(panel) {\n panel.innerHTML = '';\n panel.classList.add('tiny_cursive_event_panel');\n\n if (!this.pasteTimestamps.length) {\n const noEventsMessage = document.createElement('div');\n noEventsMessage.className = 'no-paste-events-message p-3';\n noEventsMessage.textContent = localStorage.getItem('nopasteevent');\n panel.appendChild(noEventsMessage);\n return;\n }\n\n const carouselContainer = document.createElement('div');\n carouselContainer.classList.add('tiny_cursive_paste_events_carousel', 'paste-events-carousel');\n\n const navigationRow = document.createElement('div');\n navigationRow.classList.add('paste-events-navigation', 'tiny_cursive_navigation_row');\n\n const counterDisplay = document.createElement('div');\n counterDisplay.classList.add('paste-events-counter', 'tiny_cursive_counter_display');\n counterDisplay.textContent = 'Paste Events';\n\n const navButtons = document.createElement('div');\n navButtons.classList.add('tiny_cursive_nav_buttons');\n const prevButton = document.createElement('button');\n prevButton.classList.add('paste-event-prev-btn', 'tiny_cursive_nav_button');\n prevButton.innerHTML = '';\n\n const nextButton = document.createElement('button');\n nextButton.classList.add('paste-event-next-btn', 'tiny_cursive_nav_button');\n nextButton.innerHTML = '';\n nextButton.disabled = this.pasteTimestamps.length <= 1;\n\n navButtons.appendChild(prevButton);\n navButtons.appendChild(nextButton);\n navigationRow.appendChild(counterDisplay);\n navigationRow.appendChild(navButtons);\n\n const contentContainer = document.createElement('div');\n contentContainer.className = 'paste-events-content tiny_cursive_content_container';\n contentContainer.appendChild(this.createPasteEventDisplay(this.pasteTimestamps[0]));\n\n carouselContainer.appendChild(navigationRow);\n carouselContainer.appendChild(contentContainer);\n panel.appendChild(carouselContainer);\n\n let currentIndex = 0;\n const updateDisplay = () => {\n contentContainer.innerHTML = '';\n contentContainer.appendChild(this.createPasteEventDisplay(this.pasteTimestamps[currentIndex]));\n counterDisplay.textContent = 'Paste Events';\n prevButton.disabled = currentIndex === 0;\n prevButton.style.opacity = currentIndex === 0 ? '0.5' : '1';\n nextButton.disabled = currentIndex === this.pasteTimestamps.length - 1;\n nextButton.style.opacity = currentIndex === this.pasteTimestamps.length - 1 ? '0.5' : '1';\n };\n\n prevButton.addEventListener('click', () => {\n if (currentIndex > 0) {\n currentIndex--;\n updateDisplay();\n }\n });\n\n nextButton.addEventListener('click', () => {\n if (currentIndex < this.pasteTimestamps.length - 1) {\n currentIndex++;\n updateDisplay();\n }\n });\n }\n\n createPasteEventDisplay(pasteEvent) {\n const eventRow = document.createElement('div');\n eventRow.className = 'tiny_cursive_event_row';\n\n const headerRow = document.createElement('div');\n headerRow.className = 'tiny_cursive_header_row';\n\n const textContainer = document.createElement('div');\n textContainer.className = 'tiny_cursive_text_container';\n\n const timestampContainer = document.createElement('div');\n timestampContainer.className = 'paste-event-timestamp tiny_cursive_paste_event_timestamp';\n timestampContainer.textContent = pasteEvent.formattedTime;\n\n const pastedTextContainer = document.createElement('div');\n pastedTextContainer.className = 'paste-event-text tiny_cursive_pasted_text_container';\n pastedTextContainer.textContent = pasteEvent.pastedText;\n\n textContainer.appendChild(timestampContainer);\n textContainer.appendChild(pastedTextContainer);\n\n const playButton = document.createElement('button');\n playButton.className = 'paste-event-play-btn tiny_cursive_seekplay_button';\n const playIcon = document.createElement('img');\n playIcon.src = M.util.image_url('seekplayicon', 'tiny_cursive');\n playButton.innerHTML = playIcon.outerHTML;\n playButton.addEventListener('click', () => this.jumpToTimestamp(pasteEvent.timestamp));\n\n headerRow.appendChild(textContainer);\n headerRow.appendChild(playButton);\n eventRow.appendChild(headerRow);\n\n return eventRow;\n }\n\n // Jump to a specific timestamp in the replay\n jumpToTimestamp(timestamp) {\n const percentage = this.totalDuration > 0 ? (timestamp / this.totalDuration) * 100 : 0;\n this.skipToTime(percentage);\n if (!this.replayInProgress) {\n this.startReplay(false);\n }\n }\n\n setScrubberVal(value) {\n if (this.scrubberElement) {\n this.scrubberElement.value = String(value);\n if (this.timeDisplay) {\n const displayTime = Math.min(this.currentTime, this.totalDuration);\n this.timeDisplay.textContent = `${this.formatTime(displayTime)} / ${this.formatTime(this.totalDuration)}`;\n }\n }\n }\n\n loadJSON(filePath) {\n return fetchJson([{\n methodname: 'cursive_get_reply_json',\n args: {filepath: filePath}\n }])[0].done(response => response).fail(error => {\n throw new Error(`Error loading JSON file: ${error.message}`);\n });\n }\n\n formatTime(ms) {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;\n }\n\n // Start or restart the replay\n startReplay(reset = true) {\n if (this.replayInProgress) {\n clearTimeout(this.replayTimeout);\n }\n const atEnd = (this.totalDuration > 0 && this.currentTime >= this.totalDuration) ||\n (this.currentEventIndex >= this.totalEvents);\n if (atEnd && !reset) {\n reset = true;\n }\n this.replayInProgress = true;\n if (reset) {\n this.outputElement.innerHTML = '';\n this.text = '';\n this.cursorPosition = 0;\n this.currentEventIndex = 0;\n this.currentTime = 0;\n this.highlightedChars = [];\n this.deletedChars = [];\n this.isControlKeyPressed = false;\n this.isMetaKeyPressed = false;\n this.currentPasteIndex = 0;\n this.pastedChars = [];\n this.currentAiIndex = 0;\n this.aiChars = [];\n }\n if (this.playButton) {\n const pauseSvg = document.createElement('i');\n pauseSvg.className = 'fa fa-pause';\n this.playButton.querySelector('.play-icon').innerHTML = pauseSvg.outerHTML;\n }\n this.replayLog();\n }\n\n // Process events in sequence to simulate typing\n replayLog() {\n if (!this.replayInProgress) {\n this.updateDisplayText(this.text, this.cursorPosition, [], []);\n return;\n }\n\n while (this.currentEventIndex < this.logData.length) {\n const event = this.logData[this.currentEventIndex];\n if (event.normalizedTime && event.normalizedTime > this.currentTime) {\n break;\n }\n\n let text = this.text || '';\n let cursor = this.cursorPosition;\n let updatedHighlights = [...this.highlightedChars];\n let updatedDeleted = [...this.deletedChars];\n\n if (event.rePosition !== undefined && (this.currentEventIndex === 0 ||\n event.event === 'mouseDown' || event.event === 'mouseUp')) {\n cursor = Math.max(0, Math.min(event.rePosition, text.length));\n }\n\n if (event.event?.toLowerCase() === 'keydown') {\n ({text, cursor, updatedHighlights, updatedDeleted} =\n this.processKeydownEvent(event, text, cursor, updatedHighlights, updatedDeleted));\n } else if (event.event === 'aiInsert') {\n ({text, cursor, updatedHighlights, updatedDeleted} =\n this.processAiInsertEvent(event, text, cursor, updatedHighlights, updatedDeleted));\n }\n\n this.text = text;\n this.cursorPosition = cursor;\n this.highlightedChars = updatedHighlights.filter(h => !h.expiresAt || h.expiresAt > this.currentTime);\n this.deletedChars = updatedDeleted.filter(d => !d.expiresAt || d.expiresAt > this.currentTime);\n\n this.currentEventIndex++;\n }\n\n this.updateDisplayText(this.text, this.cursorPosition, this.highlightedChars, this.deletedChars);\n if (this.totalDuration > 0) {\n const percentComplete = Math.min((this.currentTime / this.totalDuration) * 100, 100);\n this.setScrubberVal(percentComplete);\n }\n\n if (this.replayInProgress) {\n const baseIncrement = 100;\n const incrementTime = baseIncrement / this.speed;\n this.currentTime += baseIncrement;\n if (this.currentEventIndex >= this.totalEvents) {\n if (this.loop) {\n this.startReplay(true);\n } else {\n this.stopReplay();\n this.updateDisplayText(this.text, this.cursorPosition, [], []);\n }\n } else {\n this.replayTimeout = setTimeout(() => this.replayLog(), incrementTime);\n }\n }\n }\n\n getLineAndColumn(text, pos) {\n const before = text.substring(0, pos);\n const lineIndex = before.split('\\n').length - 1;\n const col = before.length - before.lastIndexOf('\\n') - 1;\n return {lineIndex, col};\n }\n\n processAiInsertEvent(event, text, cursor, highlights, deletions) {\n if (this.aiEvents && this.currentAiIndex < this.aiEvents.length) {\n const aiContent = this.aiEvents[this.currentAiIndex];\n // Use event.rePosition which points to where the word to replace is\n const targetPosition = event.rePosition;\n\n ({text, cursor} = this.handleAiReplacement(aiContent, text, targetPosition, cursor, deletions));\n this.currentAiIndex++;\n }\n return {\n text,\n cursor,\n updatedHighlights: highlights,\n updatedDeleted: deletions\n };\n }\n\n handleAiReplacement(aiContent, text, targetPosition, currentCursor, deletions) {\n const insertText = aiContent || '';\n const aiWords = insertText.trim().split(/\\s+/);\n const isMultiWord = aiWords.length > 1;\n const isNewLineInsertion = insertText.startsWith('\\n') || insertText.endsWith('\\n');\n\n const {wordStart, wordEnd} = this.findWordToReplace(\n text,\n targetPosition,\n currentCursor,\n aiWords,\n isMultiWord,\n isNewLineInsertion\n );\n\n const wordToReplace = text.substring(wordStart, wordEnd);\n\n // Mark replaced characters as deleted\n this.markCharsAsDeleted(wordToReplace, wordStart, deletions);\n\n // Perform the replacement\n const replacedLength = wordToReplace.length;\n text = text.substring(0, wordStart) + insertText + text.substring(wordEnd);\n const positionDiff = insertText.length - replacedLength;\n\n // Calculate new cursor position\n const newCursor = this.calculateNewCursorPosition(\n currentCursor,\n targetPosition,\n wordStart,\n wordEnd,\n insertText,\n isNewLineInsertion\n );\n\n // Update character tracking arrays\n this.updateCharacterIndices(wordStart, wordEnd, positionDiff, insertText);\n\n return {text, cursor: newCursor};\n }\n\n findWordToReplace(text, targetPosition, currentCursor, aiWords, isMultiWord, isNewLineInsertion) {\n if (isNewLineInsertion) {\n return {wordStart: currentCursor, wordEnd: currentCursor};\n }\n\n const {lineStart, lineEnd} = this.findLineRange(text, targetPosition);\n const lineText = text.substring(lineStart, lineEnd);\n const words = this.extractWordsFromLine(lineText, lineStart);\n\n if (words.length === 0) {\n return {wordStart: currentCursor, wordEnd: currentCursor};\n }\n\n if (isMultiWord) {\n return this.findMultiWordMatch(words, aiWords, targetPosition);\n } else {\n return this.findSingleWordMatch(words, aiWords[0], targetPosition);\n }\n }\n\n findLineRange(text, targetPosition) {\n let lineStart = 0;\n for (let i = targetPosition - 1; i >= 0; i--) {\n if (text[i] === '\\n') {\n lineStart = i + 1;\n break;\n }\n }\n\n let lineEnd = text.length;\n for (let i = targetPosition; i < text.length; i++) {\n if (text[i] === '\\n') {\n lineEnd = i;\n break;\n }\n }\n\n return {lineStart, lineEnd};\n }\n\n extractWordsFromLine(lineText, lineStart) {\n const words = [];\n let pos = 0;\n\n while (pos < lineText.length) {\n // Skip spaces\n while (pos < lineText.length && lineText[pos] === ' ') {\n pos++;\n }\n if (pos >= lineText.length) {\n break;\n }\n\n // Extract word\n const start = pos;\n while (pos < lineText.length && lineText[pos] !== ' ') {\n pos++;\n }\n\n if (pos > start) {\n words.push({\n text: lineText.substring(start, pos),\n start: lineStart + start,\n end: lineStart + pos\n });\n }\n }\n\n return words;\n }\n\n findMultiWordMatch(words, aiWords, targetPosition) {\n let bestMatch = {start: -1, end: -1, score: -1, wordCount: 0, similarityScore: 0};\n\n for (let i = 0; i < words.length; i++) {\n const matchResult = this.evaluateMultiWordSequence(words, aiWords, i, targetPosition);\n\n if (matchResult.totalScore > bestMatch.score ||\n (matchResult.totalScore === bestMatch.score &&\n matchResult.similarityScore > bestMatch.similarityScore)) {\n bestMatch = matchResult;\n }\n }\n\n if (bestMatch.score > 10) {\n return {wordStart: bestMatch.start, wordEnd: bestMatch.end};\n } else {\n const closest = this.findClosestWord(words, targetPosition);\n return {wordStart: closest.start, wordEnd: closest.end};\n }\n }\n\n evaluateMultiWordSequence(words, aiWords, startIndex, targetPosition) {\n const seqWords = [];\n for (let j = 0; j < aiWords.length && startIndex + j < words.length; j++) {\n seqWords.push(words[startIndex + j]);\n }\n\n if (seqWords.length === 0) {\n return {start: -1, end: -1, score: -1, wordCount: 0, similarityScore: 0};\n }\n\n const similarityScore = this.calculateSequenceSimilarity(aiWords, seqWords);\n const positionScore = this.calculatePositionScore(seqWords, targetPosition);\n const totalScore = similarityScore + positionScore + seqWords.length;\n\n return {\n start: seqWords[0].start,\n end: seqWords[seqWords.length - 1].end,\n score: totalScore,\n wordCount: seqWords.length,\n similarityScore: similarityScore\n };\n }\n\n calculateSequenceSimilarity(aiWords, seqWords) {\n let similarityScore = 0;\n const compareLength = Math.min(seqWords.length, aiWords.length);\n\n for (let k = 0; k < compareLength; k++) {\n const ai = aiWords[k].toLowerCase();\n const seq = seqWords[k].text.toLowerCase();\n\n if (ai === seq) {\n similarityScore += 10;\n } else {\n const similarity = this.calculateSimilarity(ai, seq);\n similarityScore += similarity * 10;\n }\n }\n\n return similarityScore;\n }\n\n calculatePositionScore(seqWords, targetPosition) {\n let positionScore = 0;\n const seqStart = seqWords[0].start;\n const seqEndPos = seqWords[seqWords.length - 1].end;\n\n if (targetPosition >= seqStart && targetPosition <= seqEndPos) {\n positionScore += 10;\n if (targetPosition >= seqWords[0].start && targetPosition <= seqWords[0].end) {\n positionScore += 5;\n }\n }\n\n return positionScore;\n }\n\n findSingleWordMatch(words, aiWord, targetPosition) {\n const aiWordLower = aiWord.toLowerCase();\n const bestSimilarityMatch = this.findBestSimilarityMatch(words, aiWordLower);\n\n if (bestSimilarityMatch.score > 0.5) {\n return {wordStart: bestSimilarityMatch.word.start, wordEnd: bestSimilarityMatch.word.end};\n }\n\n const bestPositionMatch = this.findBestPositionMatch(words, aiWordLower, targetPosition);\n\n if (bestPositionMatch.word) {\n return {wordStart: bestPositionMatch.word.start, wordEnd: bestPositionMatch.word.end};\n }\n\n // Fallback to position-based word boundary\n return this.findWordBoundaryAtPosition(words[0].start, words[words.length - 1].end,\n targetPosition, this.text);\n }\n\n findBestSimilarityMatch(words, aiWordLower) {\n let bestMatch = {word: null, score: 0};\n\n for (const word of words) {\n let similarity = this.calculateSimilarity(aiWordLower, word.text.toLowerCase());\n const wordLower = word.text.toLowerCase();\n\n // Penalize short prefix matches\n if (wordLower.length < aiWordLower.length * 0.5 && aiWordLower.startsWith(wordLower)) {\n similarity = similarity * 0.3;\n }\n\n if (similarity > bestMatch.score) {\n bestMatch = {word, score: similarity};\n }\n }\n\n return bestMatch;\n }\n\n findBestPositionMatch(words, aiWordLower, targetPosition) {\n let bestMatch = {word: null, score: -1};\n\n for (const word of words) {\n let score = this.calculateWordScore(word, aiWordLower, targetPosition);\n\n if (score > bestMatch.score) {\n bestMatch = {word, score};\n }\n }\n\n return bestMatch;\n }\n\n calculateWordScore(word, aiWordLower, targetPosition) {\n let score = 0;\n\n // Position score\n if (targetPosition >= word.start && targetPosition <= word.end) {\n score += 30;\n } else {\n const distance = Math.min(\n Math.abs(targetPosition - word.start),\n Math.abs(targetPosition - word.end)\n );\n score += Math.max(0, 20 - distance);\n }\n\n // Similarity score with penalty\n let similarity = this.calculateSimilarity(aiWordLower, word.text.toLowerCase());\n const wordLower = word.text.toLowerCase();\n if (wordLower.length < aiWordLower.length * 0.5 && aiWordLower.startsWith(wordLower)) {\n similarity = similarity * 0.3;\n }\n score += similarity * 10;\n\n return score;\n }\n\n findWordBoundaryAtPosition(lineStart, lineEnd, targetPosition, text) {\n let wordStart = targetPosition;\n while (wordStart > lineStart && text[wordStart - 1] !== ' ' && text[wordStart - 1] !== '\\n') {\n wordStart--;\n }\n let wordEnd = targetPosition;\n while (wordEnd < lineEnd && text[wordEnd] !== ' ' && text[wordEnd] !== '\\n') {\n wordEnd++;\n }\n return {wordStart, wordEnd};\n }\n\n markCharsAsDeleted(wordToReplace, wordStart, deletions) {\n if (wordToReplace.length > 0) {\n for (let i = 0; i < wordToReplace.length; i++) {\n deletions.push({\n index: wordStart + i,\n chars: wordToReplace[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n }\n }\n\n calculateNewCursorPosition(currentCursor, targetPosition, wordStart, wordEnd, insertText, isNewLineInsertion) {\n if (isNewLineInsertion) {\n return wordStart + insertText.length;\n }\n\n if (targetPosition >= wordStart && targetPosition <= wordEnd) {\n return wordStart + insertText.length;\n }\n\n const positionDiff = insertText.length - (wordEnd - wordStart);\n\n if (currentCursor >= wordEnd) {\n return currentCursor + positionDiff;\n } else if (currentCursor > wordStart && currentCursor < wordEnd) {\n return wordStart + insertText.length;\n }\n\n return currentCursor;\n }\n\n updateCharacterIndices(wordStart, wordEnd, positionDiff, insertText) {\n // Update pasted character indices\n this.updatePastedCharIndices(wordStart, wordEnd, positionDiff);\n\n // Mark characters as AI-inserted\n this.markCharsAsAiInserted(wordStart, insertText);\n\n // Update AI character indices\n this.updateAiCharIndices(wordStart, wordEnd, positionDiff, insertText);\n }\n\n updatePastedCharIndices(wordStart, wordEnd, positionDiff) {\n if (this.pastedChars) {\n this.pastedChars = this.pastedChars.map(p => {\n if (p.index >= wordEnd) {\n return {...p, index: p.index + positionDiff};\n } else if (p.index >= wordStart && p.index < wordEnd) {\n return null;\n }\n return p;\n }).filter(p => p !== null);\n }\n }\n\n markCharsAsAiInserted(wordStart, insertText) {\n if (!this.aiChars) {\n this.aiChars = [];\n }\n\n if (insertText.trim() !== '') {\n for (let i = 0; i < insertText.length; i++) {\n this.aiChars.push({\n index: wordStart + i,\n chars: insertText[i]\n });\n }\n }\n }\n\n updateAiCharIndices(wordStart, wordEnd, positionDiff, insertText) {\n const justAddedIndices = new Set();\n for (let i = 0; i < insertText.length; i++) {\n justAddedIndices.add(wordStart + i);\n }\n\n this.aiChars = this.aiChars.map(p => {\n if (!justAddedIndices.has(p.index)) {\n if (p.index >= wordEnd) {\n return {...p, index: p.index + positionDiff};\n } else if (p.index >= wordStart && p.index < wordEnd) {\n return null;\n }\n }\n return p;\n }).filter(p => p !== null);\n }\n\n // Calculate similarity between two strings\n calculateSimilarity(str1, str2) {\n if (str1 === str2) {\n return 1;\n }\n if (str1.length === 0 || str2.length === 0) {\n return 0;\n }\n\n // Check if one string is a prefix of the other\n if (str1.startsWith(str2) || str2.startsWith(str1)) {\n return 0.8;\n }\n\n // Levenshtein distance\n const len1 = str1.length;\n const len2 = str2.length;\n const matrix = Array(len2 + 1).fill(null).map(() => Array(len1 + 1).fill(0));\n\n for (let i = 0; i <= len1; i++) {\n matrix[0][i] = i;\n }\n for (let j = 0; j <= len2; j++) {\n matrix[j][0] = j;\n }\n\n for (let j = 1; j <= len2; j++) {\n for (let i = 1; i <= len1; i++) {\n const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;\n matrix[j][i] = Math.min(\n matrix[j][i - 1] + 1,\n matrix[j - 1][i] + 1,\n matrix[j - 1][i - 1] + cost\n );\n }\n }\n\n const maxLen = Math.max(len1, len2);\n return 1 - (matrix[len2][len1] / maxLen);\n }\n\n // Find the word closest to a target position\n findClosestWord(words, targetPosition) {\n if (words.length === 0) {\n return {start: targetPosition, end: targetPosition};\n }\n\n let closest = words[0];\n let minDistance = Math.min(\n Math.abs(targetPosition - words[0].start),\n Math.abs(targetPosition - words[0].end)\n );\n\n for (const word of words) {\n if (targetPosition >= word.start && targetPosition <= word.end) {\n return word;\n }\n\n const distance = Math.min(\n Math.abs(targetPosition - word.start),\n Math.abs(targetPosition - word.end)\n );\n\n if (distance < minDistance) {\n minDistance = distance;\n closest = word;\n }\n }\n\n return closest;\n }\n\n // Handle keydown events (e.g., typing, backspace, Ctrl+V)\n processKeydownEvent(event, text, cursor, highlights, deletions) {\n const key = event.key;\n const charToInsert = this.applyKey(key);\n\n // Handle copy operation (Ctrl+C)\n if (this.isCopyOperation(key)) {\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n // Handle undo operation (Ctrl+Z)\n if (this.isUndoOperation(key)) {\n return this.handleUndoOperation(event, text, cursor, highlights, deletions);\n }\n\n // Detect selection for current event\n const currentEventIndex = this.currentEventIndex;\n const selection = this.detectSelection(currentEventIndex);\n\n // Handle paste operation (Ctrl+V)\n if (this.isPasteOperation(key, event)) {\n return this.handlePasteOperation(event, selection, text, cursor, highlights, deletions);\n }\n\n // Update modifier key states\n this.updateModifierStates(key);\n\n // Handle selection deletion with Backspace/Delete\n if (this.isSelectionDeletion(key, selection)) {\n ({text, cursor} = this.handleSelectionDeletion(selection, text, cursor, deletions));\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n // Process various key operations\n return this.processKeyOperation(key, charToInsert, text, cursor, highlights, deletions, selection);\n }\n\n isCopyOperation(key) {\n return (key === 'c' || key === 'C') && (this.isControlKeyPressed || this.isMetaKeyPressed);\n }\n\n isUndoOperation(key) {\n return (key === 'z' || key === 'Z') && (this.isControlKeyPressed || this.isMetaKeyPressed);\n }\n\n handleUndoOperation(event, text, cursor, highlights, deletions) {\n const nextEventIndex = this.currentEventIndex + 1;\n if (nextEventIndex < this.logData.length) {\n const nextEvent = this.logData[nextEventIndex];\n\n if (nextEvent.event === 'keyUp' && (nextEvent.key === 'z' || nextEvent.key === 'Z')) {\n const newPosition = nextEvent.rePosition;\n if (newPosition < cursor && text.length > 0) {\n const textBeforeUndo = text;\n text = text.substring(0, newPosition) + text.substring(cursor);\n cursor = newPosition;\n\n // Mark as deleted for visual effect\n for (let i = 0; i < textBeforeUndo.length && i < cursor; i++) {\n deletions.push({\n index: newPosition,\n chars: textBeforeUndo[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n }\n }\n }\n\n this.isControlKeyPressed = false;\n this.isMetaKeyPressed = false;\n\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n isPasteOperation(key, event) {\n if ((key === 'v' || key === 'V') && (this.isControlKeyPressed || this.isMetaKeyPressed)) {\n return (event.pastedContent && event.pastedContent.trim() !== '') ||\n (this.pastedEvents && this.currentPasteIndex < this.pastedEvents.length);\n }\n return false;\n }\n\n handlePasteOperation(event, selection, text, cursor, highlights, deletions) {\n const pastedContent = event.pastedContent || this.pastedEvents[this.currentPasteIndex];\n\n if (selection) {\n ({text, cursor} = this.handleSelectionDeletion(selection, text, cursor, deletions));\n }\n\n ({text, cursor} = this.handlePasteInsert(pastedContent, text, cursor));\n this.currentPasteIndex++;\n this.resetModifierStates();\n this.isPasteEvent = false;\n\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n resetModifierStates() {\n this.isControlKeyPressed = false;\n this.isShiftKeyPressed = false;\n this.isMetaKeyPressed = false;\n }\n\n isSelectionDeletion(key, selection) {\n return (key === 'Backspace' || key === 'Delete') && selection && selection.length > 1;\n }\n\n processKeyOperation(key, charToInsert, text, cursor, highlights, deletions, selection) {\n if (this.isCtrlBackspace(key, cursor)) {\n ({text, cursor} = this.handleCtrlBackspace(text, cursor, deletions));\n } else if (this.isCtrlDelete(key, cursor, text)) {\n ({text} = this.handleCtrlDelete(text, cursor, deletions));\n } else if (this.isCtrlArrowMove(key)) {\n cursor = this.handleCtrlArrowMove(key, text, cursor);\n } else if (this.isRegularBackspace(key, cursor)) {\n ({text, cursor} = this.handleBackspace(text, cursor, deletions));\n } else if (this.isRegularDelete(key, cursor, text)) {\n ({text} = this.handleDelete(text, cursor, deletions));\n } else if (this.isArrowUp(key)) {\n cursor = this.handleArrowUp(text, cursor);\n } else if (this.isArrowDown(key)) {\n cursor = this.handleArrowDown(text, cursor);\n } else if (this.isRegularArrowMove(key)) {\n cursor = this.handleArrowMove(key, text, cursor);\n } else if (charToInsert && charToInsert.length > 0) {\n if (selection && selection.length > 0) {\n ({text, cursor} = this.handleSelectionDeletion(selection, text, cursor, deletions));\n }\n ({text, cursor} = this.handleCharacterInsert(charToInsert, text, cursor, highlights));\n }\n\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n detectSelection(eventIndex) {\n const currentEvent = this.logData[eventIndex];\n\n if (currentEvent.event?.toLowerCase() === 'keydown' &&\n (currentEvent.key === 'Backspace' || currentEvent.key === 'Delete')) {\n\n const currentPos = currentEvent.rePosition;\n return this.processDetection(currentPos, currentEvent, eventIndex);\n }\n return null;\n }\n\n processDetection(currentPos, currentEvent, eventIndex) {\n for (let i = eventIndex + 1; i < this.logData.length; i++) {\n const nextEvent = this.logData[i];\n\n if (nextEvent.event?.toLowerCase() === 'keyup' &&\n nextEvent.key === currentEvent.key) {\n\n const nextPos = nextEvent.rePosition;\n\n // Calculate the difference in positions\n const positionDiff = Math.abs(currentPos - nextPos);\n\n if (positionDiff > 1) {\n return {\n start: Math.min(currentPos, nextPos),\n end: Math.max(currentPos, nextPos),\n length: positionDiff\n };\n } else if (positionDiff === 1) {\n if (currentEvent.key === 'Backspace') {\n return {\n start: nextPos,\n end: currentPos,\n length: 1\n };\n } else {\n return {\n start: currentPos,\n end: nextPos,\n length: 1\n };\n }\n }\n break;\n }\n }\n return null;\n }\n\n handleSelectionDeletion(selection, text, cursor, deletions) {\n const {start, end, length} = selection;\n\n // Add each character in the selection to the deletions array\n for (let i = start; i < end && i < text.length; i++) {\n deletions.push({\n index: start,\n chars: text[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n\n text = text.substring(0, start) + text.substring(end);\n\n this.shiftPastedCharsIndices(start, length);\n\n cursor = start;\n\n return {text, cursor};\n }\n\n // Handle Paste events to highlight pasted text\n handlePasteInsert(pastedContent, text, cursor) {\n const insertText = pastedContent || '';\n text = text.substring(0, cursor) + insertText + text.substring(cursor);\n\n // Mark characters as pasted for bold styling\n if (insertText.trim() !== '') {\n for (let i = 0; i < insertText.length; i++) {\n if (!this.pastedChars) {\n this.pastedChars = [];\n }\n this.pastedChars.push({\n index: cursor + i,\n chars: insertText[i]\n });\n }\n }\n\n return {text, cursor: cursor + insertText.length};\n }\n\n // Adjusts pasted chars indices after deletion to maintain styling for pasted text\n shiftPastedCharsIndices(startIndex, numDeleted) {\n this.pastedChars = this.pastedChars.map(p => {\n if (p.index >= startIndex + numDeleted) {\n return {...p, index: p.index - numDeleted};\n } else if (p.index >= startIndex && p.index < startIndex + numDeleted) {\n // Remove pasted characters that were deleted\n return null;\n }\n return p;\n }).filter(p => p !== null);\n\n if (this.aiChars) {\n this.aiChars = this.aiChars.map(p => {\n if (p.index >= startIndex + numDeleted) {\n return {...p, index: p.index - numDeleted};\n } else if (p.index >= startIndex && p.index < startIndex + numDeleted) {\n return null;\n }\n return p;\n }).filter(p => p !== null);\n }\n }\n\n // Update state for modifier keys (Control, paste events)\n updateModifierStates(key) {\n if (key === 'Control') {\n this.isControlKeyPressed = true;\n } else if (key === 'Shift') {\n this.isShiftKeyPressed = true;\n } else if (key === 'Meta') {\n this.isMetaKeyPressed = true;\n } else if ((key === 'v' || key === 'V') && (this.isControlKeyPressed || this.isMetaKeyPressed)) {\n this.isPasteEvent = true;\n } else if (!['Control', 'Meta', 'Backspace', 'Delete', 'ArrowLeft', 'ArrowRight'].includes(key)) {\n this.isControlKeyPressed = false;\n this.isShiftKeyPressed = false;\n this.isMetaKeyPressed = false;\n this.isPasteEvent = false;\n }\n }\n\n isCtrlBackspace(key, cursor) {\n return key === 'Backspace' && this.isControlKeyPressed && cursor > 0;\n }\n\n isCtrlDelete(key, cursor, text) {\n return key === 'Delete' && this.isControlKeyPressed && cursor < text.length;\n }\n\n isCtrlArrowMove(key) {\n return this.isControlKeyPressed && (key === 'ArrowLeft' || key === 'ArrowRight');\n }\n\n isRegularBackspace(key, cursor) {\n return key === 'Backspace' && !this.isPasteEvent && cursor > 0;\n }\n\n isRegularDelete(key, cursor, text) {\n return key === 'Delete' && !this.isControlKeyPressed && cursor < text.length;\n }\n\n isRegularArrowMove(key) {\n return !this.isControlKeyPressed && (key === 'ArrowLeft' || key === 'ArrowRight');\n }\n\n isArrowUp(key) {\n return key === 'ArrowUp';\n }\n\n isArrowDown(key) {\n return key === 'ArrowDown';\n }\n\n handleCtrlArrowMove(key, text, cursor) {\n return key === 'ArrowLeft'\n ? this.findPreviousWordBoundary(text, cursor)\n : this.findNextWordBoundary(text, cursor);\n }\n\n handleBackspace(text, cursor, deletions) {\n deletions.push({\n index: cursor - 1,\n chars: text[cursor - 1],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n this.shiftPastedCharsIndices(cursor - 1, 1);\n return {\n text: text.substring(0, cursor - 1) + text.substring(cursor),\n cursor: cursor - 1\n };\n }\n\n handleDelete(text, cursor, deletions) {\n deletions.push({\n index: cursor,\n chars: text[cursor],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n this.shiftPastedCharsIndices(cursor, 1);\n return {\n text: text.substring(0, cursor) + text.substring(cursor + 1),\n cursor\n };\n }\n\n handleArrowMove(key, text, cursor) {\n return key === 'ArrowLeft'\n ? Math.max(0, cursor - 1)\n : Math.min(text.length, cursor + 1);\n }\n\n handleCharacterInsert(charToInsert, text, cursor, highlights) {\n text = text.substring(0, cursor) + charToInsert + text.substring(cursor);\n // Shift pasted chars indices after the insertion point\n if (this.pastedChars) {\n this.pastedChars = this.pastedChars.map(p => {\n return p.index >= cursor ? {...p, index: p.index + 1} : p;\n });\n }\n if (this.aiChars) {\n this.aiChars = this.aiChars.map(p => {\n return p.index >= cursor ? {...p, index: p.index + 1} : p;\n });\n }\n if (charToInsert.trim() !== '') {\n highlights.push({\n index: cursor,\n chars: charToInsert,\n time: this.currentTime,\n expiresAt: this.currentTime + 1500\n });\n }\n return {text, cursor: cursor + 1};\n }\n\n handleCtrlDelete(text, cursor, deletions) {\n const wordEnd = this.findNextWordBoundary(text, cursor);\n const wordToDelete = text.substring(cursor, wordEnd);\n for (let i = 0; i < wordToDelete.length; i++) {\n deletions.push({\n index: cursor + i,\n chars: wordToDelete[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n this.shiftPastedCharsIndices(cursor, wordToDelete.length);\n return {\n text: text.substring(0, cursor) + text.substring(wordEnd),\n cursor\n };\n }\n\n handleArrowUp(text, cursor) {\n const lines = text.split('\\n');\n const {lineIndex, col} = this.getLineAndColumn(text, cursor);\n if (lineIndex > 0) {\n const prevLine = lines[lineIndex - 1];\n cursor = lines.slice(0, lineIndex - 1).join('\\n').length + 1 + Math.min(col, prevLine.length);\n } else {\n cursor = 0;\n }\n return cursor;\n }\n\n handleArrowDown(text, cursor) {\n const lines = text.split('\\n');\n const {lineIndex, col} = this.getLineAndColumn(text, cursor);\n if (lineIndex < lines.length - 1) {\n const nextLine = lines[lineIndex + 1];\n cursor = lines.slice(0, lineIndex + 1).join('\\n').length + 1 + Math.min(col, nextLine.length);\n } else {\n cursor = text.length;\n }\n return cursor;\n }\n\n handleCtrlBackspace(text, cursor, deletions) {\n let wordStart = cursor;\n while (wordStart > 0 && text[wordStart - 1] === ' ') {\n wordStart--;\n }\n while (wordStart > 0 && text[wordStart - 1] !== ' ') {\n wordStart--;\n }\n const wordToDelete = text.substring(wordStart, cursor);\n for (let i = 0; i < wordToDelete.length; i++) {\n deletions.push({\n index: wordStart + i,\n chars: wordToDelete[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n this.shiftPastedCharsIndices(wordStart, wordToDelete.length);\n return {text: text.substring(0, wordStart) + text.substring(cursor), cursor: wordStart};\n }\n\n // Finds the index of the next word boundary after the cursor position\n findNextWordBoundary(text, cursor) {\n if (!text || cursor >= text.length) {\n return cursor;\n }\n if (text[cursor] === ' ') {\n while (cursor < text.length && text[cursor] === ' ') {\n cursor++;\n }\n }\n if (cursor >= text.length) {\n let lastNonSpace = text.length - 1;\n while (lastNonSpace >= 0 && text[lastNonSpace] === ' ') {\n lastNonSpace--;\n }\n return lastNonSpace + 1;\n }\n let wordEnd = cursor;\n while (wordEnd < text.length && text[wordEnd] !== ' ') {\n wordEnd++;\n }\n return wordEnd;\n }\n\n // Finds the index of the previous word boundary before the cursor position\n findPreviousWordBoundary(text, cursor) {\n if (cursor <= 0) {\n return 0;\n }\n let pos = cursor - 1;\n while (pos > 0 && (text[pos] === ' ' || text[pos] === '\\n')) {\n pos--;\n }\n while (pos > 0 && text[pos - 1] !== ' ' && text[pos - 1] !== '\\n') {\n pos--;\n }\n\n return pos;\n }\n\n skipToEnd() {\n if (this.replayInProgress) {\n this.replayInProgress = false;\n }\n let textOutput = \"\";\n this.logData.forEach(event => {\n if (event.event.toLowerCase() === 'keydown') {\n textOutput = this.applyKey(event.key, textOutput);\n }\n });\n this.outputElement.innerHTML = textOutput.slice(0, -1);\n this.setScrubberVal(100);\n }\n\n // Used by the scrubber to skip to a certain percentage of data\n skipToTime(percentage) {\n const wasPlaying = this.replayInProgress;\n this.stopReplay();\n\n const targetTime = (this.totalDuration * percentage) / 100;\n this.currentTime = targetTime;\n this.currentEventIndex = 0;\n this.text = '';\n this.cursorPosition = 0;\n this.highlightedChars = [];\n this.deletedChars = [];\n this.isControlKeyPressed = false;\n this.isMetaKeyPressed = false;\n this.isPasteEvent = false;\n this.pastedChars = [];\n this.currentPasteIndex = 0;\n this.currentAiIndex = 0;\n this.aiChars = [];\n let text = '';\n let cursor = 0;\n let highlights = [];\n let deletions = [];\n let pasteIndex = 0;\n let aiIndex = 0;\n\n for (let i = 0; i < this.logData.length; i++) {\n const event = this.logData[i];\n if (event.normalizedTime && event.normalizedTime > targetTime) {\n this.currentEventIndex = i;\n break;\n }\n if (event.rePosition !== undefined && (this.currentEventIndex === 0 ||\n event.event === 'mouseDown' || event.event === 'mouseUp')) {\n cursor = Math.max(0, Math.min(event.rePosition, text.length));\n }\n if (event.event?.toLowerCase() === 'keydown') {\n this.currentPasteIndex = pasteIndex;\n if ((event.key === 'v' || event.key === 'V') && (this.isControlKeyPressed || this.isMetaKeyPressed)) {\n pasteIndex++;\n }\n ({text, cursor, updatedHighlights: highlights, updatedDeleted: deletions} =\n this.processKeydownEvent(event, text, cursor, highlights, deletions));\n } else if (event.event === 'aiInsert') {\n this.currentAiIndex = aiIndex;\n ({text, cursor, updatedHighlights: highlights, updatedDeleted: deletions} =\n this.processAiInsertEvent(event, text, cursor, highlights, deletions));\n aiIndex++;\n }\n this.currentEventIndex = i + 1;\n }\n\n this.currentPasteIndex = pasteIndex;\n this.currentAiIndex = aiIndex;\n this.text = text;\n this.cursorPosition = cursor;\n this.highlightedChars = highlights.filter(h => !h.expiresAt || h.expiresAt > targetTime);\n this.deletedChars = deletions.filter(d => !d.expiresAt || d.expiresAt > targetTime);\n this.updateDisplayText(this.text, this.cursorPosition, this.highlightedChars, this.deletedChars);\n this.setScrubberVal(percentage);\n\n if (wasPlaying) {\n this.replayInProgress = true;\n this.replayLog();\n }\n }\n\n // Update display with text, cursor, highlights and deletions.\n // eslint-disable-next-line complexity\n updateDisplayText(text, cursorPosition, highlights, deletions) {\n let html = '';\n const highlightMap = {};\n const deletionMap = {};\n const pastedMap = {};\n const aiMap = {};\n const currentTime = this.currentTime;\n\n highlights.forEach(h => {\n let opacity = 1;\n if (h.expiresAt && h.expiresAt - currentTime < 500) {\n opacity = Math.max(0, (h.expiresAt - currentTime) / 500);\n }\n highlightMap[h.index] = {chars: h.chars, opacity};\n });\n\n deletions.forEach(d => {\n let opacity = 0.5;\n if (d.expiresAt && d.expiresAt - currentTime < 500) {\n opacity = Math.max(0, ((d.expiresAt - currentTime) / 500) * 0.5);\n }\n deletionMap[d.index] = {chars: d.chars, opacity};\n });\n\n // Process pasted characters for bold styling\n if (this.pastedChars) {\n this.pastedChars.forEach(p => {\n if (p.index < text.length) {\n pastedMap[p.index] = true;\n }\n });\n }\n\n // Process AI characters for styling\n if (this.aiChars) {\n this.aiChars.forEach(p => {\n if (p.index < text.length) {\n aiMap[p.index] = true;\n }\n });\n }\n\n // Find if we have out-of-bounds deletions (from Control+Backspace)\n const outOfRangeDeletions = deletions.filter(d => d.index >= text.length);\n const textLines = text.split('\\n');\n let currentPosition = 0;\n\n for (let lineIndex = 0; lineIndex < textLines.length; lineIndex++) {\n const line = textLines[lineIndex];\n for (let i = 0; i < line.length; i++) {\n if (currentPosition === cursorPosition) {\n html += '';\n }\n const char = line[i];\n if (deletionMap[currentPosition]) {\n html += `${deletionMap[currentPosition].chars}`;\n }\n const isPasted = pastedMap[currentPosition];\n const isAi = aiMap[currentPosition];\n const isHighlighted = highlightMap[currentPosition] && char !== ' ';\n\n if (isPasted && isHighlighted) {\n html += `${char}`;\n } else if (isAi && isHighlighted) {\n html += `${char}`;\n } else if (isPasted) {\n html += `${char === ' ' ? ' ' : this.escapeHtml(char)}`;\n } else if (isAi) {\n html += `${char === ' ' ? ' ' : this.escapeHtml(char)}`;\n } else if (isHighlighted) {\n html += `${char}`;\n } else {\n html += char === ' ' ? ' ' : this.escapeHtml(char);\n }\n currentPosition++;\n }\n if (currentPosition === cursorPosition) {\n html += '';\n }\n if (lineIndex < textLines.length - 1) {\n html += '
';\n currentPosition++;\n }\n }\n\n if (cursorPosition === text.length && !html.endsWith('')) {\n html += '';\n }\n\n if (outOfRangeDeletions.length > 0) {\n outOfRangeDeletions.sort((a, b) => a.index - b.index);\n const cursorHTML = '';\n const cursorPos = html.lastIndexOf(cursorHTML);\n if (cursorPos !== -1) {\n let deletedWordHTML = '';\n outOfRangeDeletions.forEach(d => {\n deletedWordHTML += d.chars;\n });\n deletedWordHTML += '';\n html = html.substring(0, cursorPos) + deletedWordHTML + html.substring(cursorPos);\n }\n }\n\n const wasScrolledToBottom = this.outputElement.scrollHeight -\n this.outputElement.clientHeight <= this.outputElement.scrollTop + 1;\n this.outputElement.innerHTML = html;\n\n if (wasScrolledToBottom || this.isCursorBelowViewport()) {\n this.outputElement.scrollTop = this.outputElement.scrollHeight;\n }\n }\n\n // Check if cursor is below visible viewport\n isCursorBelowViewport() {\n const cursorElement = this.outputElement.querySelector('.tiny_cursive-cursor:last-of-type');\n if (!cursorElement) {\n return false;\n }\n\n const cursorRect = cursorElement.getBoundingClientRect();\n const outputRect = this.outputElement.getBoundingClientRect();\n\n return cursorRect.bottom > outputRect.bottom;\n }\n\n escapeHtml(unsafe) {\n return unsafe\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n }\n\n // Used in various places to add a keydown, backspace, etc. to the output\n applyKey(key) {\n switch (key) {\n case 'Enter':\n return '\\n';\n case 'Backspace':\n case 'Delete':\n case 'ControlBackspace':\n return '';\n case ' ':\n return ' ';\n default:\n return !['Shift', 'Ctrl', 'Alt', 'ArrowDown', 'ArrowUp', 'Control', 'ArrowRight',\n 'ArrowLeft', 'Meta', 'CapsLock', 'Tab', 'Escape', 'Delete', 'PageUp', 'PageDown',\n 'Insert', 'Home', 'End', 'NumLock', 'AudioVolumeUp', 'AudioVolumeDown',\n 'MediaPlayPause', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10',\n 'F11', 'F12', 'PrintScreen', 'UnIdentified'].includes(key) ? key : '';\n }\n }\n}\n"],"names":["constructor","elementId","filePath","speed","loop","controllerId","replayInProgress","parseFloat","highlightedChars","deletedChars","cursorPosition","currentEventIndex","totalEvents","currentTime","totalDuration","usercomments","pasteTimestamps","isPasteEvent","isControlKeyPressed","isShiftKeyPressed","isMetaKeyPressed","text","pastedEvents","currentPasteIndex","pastedChars","aiEvents","currentAiIndex","aiChars","undoTimestamps","undoChars","element","document","getElementById","Error","outputElement","loadJSON","then","data","status","processData","this","logData","length","identifyPasteEvents","identifyUndoEvents","constructController","startReplay","handleNoSubmission","catch","error","window","console","message","localStorage","getItem","Str","get_string","str","setItem","log","JSON","parse","comments","Array","isArray","payload","i","event","pastedContent","trim","push","aiContent","unixTimestamp","startTime","map","normalizedTime","html","Promise","all","templates","render","newElement","stopReplay","clearTimeout","replayTimeout","playButton","playSvg","createElement","src","M","util","image_url","querySelector","innerHTML","outerHTML","currentPosition","replayIntervalId","clearInterval","container","controlContainer","buildControllerUI","remove","topRow","classList","add","createPlayButton","appendChild","scrubberContainer","createScrubberContainer","timeDisplay","createTimeDisplay","bottomRow","speedContainer","createSpeedControls","pasteEventsToggle","createPasteEventsToggle","pasteEventsPanel","addEventListener","find","removeClass","addClass","scrubberElement","type","max","min","value","skipToTime","parseInt","textContent","speedLabel","speedGroup","forEach","speedBtn","dataset","querySelectorAll","btn","pasteEventsIcon","pasteIcon","pasteEventsText","pasteEventCount","className","style","marginLeft","chevronIcon","chevron","transition","createPasteEventsPanel","isHidden","display","transform","existingPanel","populatePasteEventsPanel","controlPressed","metaPressed","shiftPressed","pasteCount","toLowerCase","key","timestamp","index","time","formattedTime","formatTime","pastedText","undoCount","panel","noEventsMessage","carouselContainer","navigationRow","counterDisplay","navButtons","prevButton","nextButton","disabled","contentContainer","createPasteEventDisplay","currentIndex","updateDisplay","opacity","pasteEvent","eventRow","headerRow","textContainer","timestampContainer","pastedTextContainer","playIcon","jumpToTimestamp","percentage","setScrubberVal","String","displayTime","Math","methodname","args","filepath","done","response","fail","ms","seconds","floor","remainingSeconds","toString","padStart","reset","pauseSvg","replayLog","cursor","updatedHighlights","updatedDeleted","undefined","rePosition","processKeydownEvent","processAiInsertEvent","filter","h","expiresAt","d","updateDisplayText","percentComplete","baseIncrement","incrementTime","setTimeout","getLineAndColumn","pos","before","substring","lineIndex","split","col","lastIndexOf","highlights","deletions","targetPosition","handleAiReplacement","currentCursor","insertText","aiWords","isMultiWord","isNewLineInsertion","startsWith","endsWith","wordStart","wordEnd","findWordToReplace","wordToReplace","markCharsAsDeleted","replacedLength","positionDiff","newCursor","calculateNewCursorPosition","updateCharacterIndices","lineStart","lineEnd","findLineRange","lineText","words","extractWordsFromLine","findMultiWordMatch","findSingleWordMatch","start","end","bestMatch","score","wordCount","similarityScore","matchResult","evaluateMultiWordSequence","totalScore","closest","findClosestWord","startIndex","seqWords","j","calculateSequenceSimilarity","calculatePositionScore","compareLength","k","ai","seq","calculateSimilarity","positionScore","seqStart","seqEndPos","aiWord","aiWordLower","bestSimilarityMatch","findBestSimilarityMatch","word","bestPositionMatch","findBestPositionMatch","findWordBoundaryAtPosition","similarity","wordLower","calculateWordScore","distance","abs","chars","updatePastedCharIndices","markCharsAsAiInserted","updateAiCharIndices","p","justAddedIndices","Set","has","str1","str2","len1","len2","matrix","fill","cost","maxLen","minDistance","charToInsert","applyKey","isCopyOperation","isUndoOperation","handleUndoOperation","selection","detectSelection","isPasteOperation","handlePasteOperation","updateModifierStates","isSelectionDeletion","handleSelectionDeletion","processKeyOperation","nextEventIndex","nextEvent","newPosition","textBeforeUndo","handlePasteInsert","resetModifierStates","isCtrlBackspace","handleCtrlBackspace","isCtrlDelete","handleCtrlDelete","isCtrlArrowMove","handleCtrlArrowMove","isRegularBackspace","handleBackspace","isRegularDelete","handleDelete","isArrowUp","handleArrowUp","isArrowDown","handleArrowDown","isRegularArrowMove","handleArrowMove","handleCharacterInsert","eventIndex","currentEvent","currentPos","processDetection","nextPos","shiftPastedCharsIndices","numDeleted","includes","findPreviousWordBoundary","findNextWordBoundary","wordToDelete","lines","prevLine","slice","join","nextLine","lastNonSpace","skipToEnd","textOutput","wasPlaying","targetTime","pasteIndex","aiIndex","highlightMap","deletionMap","pastedMap","aiMap","outOfRangeDeletions","textLines","line","char","isPasted","isAi","isHighlighted","escapeHtml","sort","a","b","cursorHTML","cursorPos","deletedWordHTML","wasScrolledToBottom","scrollHeight","clientHeight","scrollTop","isCursorBelowViewport","cursorElement","cursorRect","getBoundingClientRect","outputRect","bottom","unsafe","replace"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"replay.min.js","sources":["../src/replay.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/replay\n * @category TinyMCE Editor\n * @copyright CTI \n * @author kuldeep singh \n */\n\nimport {call as fetchJson} from 'core/ajax';\nimport templates from 'core/templates';\nimport $ from 'jquery';\nimport * as Str from 'core/str';\n\nexport default class Replay {\n constructor(elementId, filePath, speed = 1, loop = false, controllerId) {\n // Initialize core properties\n this.controllerId = controllerId || '';\n this.replayInProgress = false;\n this.speed = parseFloat(speed);\n this.loop = loop;\n this.highlightedChars = [];\n this.deletedChars = [];\n this.cursorPosition = 0;\n this.currentEventIndex = 0;\n this.totalEvents = 0;\n this.currentTime = 0;\n this.totalDuration = 0;\n this.usercomments = [];\n this.pasteTimestamps = [];\n this.isPasteEvent = false;\n this.isControlKeyPressed = false;\n this.isShiftKeyPressed = false;\n this.isMetaKeyPressed = false;\n this.text = '';\n this.pastedEvents = [];\n this.currentPasteIndex = 0;\n this.pastedChars = [];\n this.aiEvents = [];\n this.currentAiIndex = 0;\n this.aiChars = [];\n this.undoTimestamps = [];\n this.undoChars = [];\n\n const element = document.getElementById(elementId);\n if (!element) {\n throw new Error(`Element with id '${elementId}' not found`);\n }\n this.outputElement = element;\n\n // Load JSON data and initialize replay\n this.loadJSON(filePath).then(data => {\n if (data.status) {\n this.processData(data);\n this.totalEvents = this.logData.length;\n this.identifyPasteEvents();\n this.identifyUndoEvents();\n if (this.controllerId && this.logData) {\n this.constructController(this.controllerId);\n }\n this.startReplay();\n } else {\n this.handleNoSubmission();\n }\n return data;\n }).catch(error => {\n this.handleNoSubmission();\n window.console.error('Error loading JSON file:', error.message);\n });\n if (!localStorage.getItem('nopasteevent') || !localStorage.getItem('pasteEvent')) {\n Str.get_string('nopasteevent', 'tiny_cursive').then(str => {\n localStorage.setItem('nopasteevent', str);\n return str;\n }).catch(error => window.console.log(error));\n Str.get_string('pasteEvent', 'tiny_cursive').then(str => {\n localStorage.setItem('pasteEvent', str);\n return str;\n }).catch(error => window.console.log(error));\n }\n }\n\n // Process JSON data and normalize timestamps\n processData(data) {\n this.logData = JSON.parse(data.data);\n if (data.comments) {\n this.usercomments = Array.isArray(JSON.parse(data.comments)) ? JSON.parse(data.comments) : [];\n }\n if ('data' in this.logData) {\n this.logData = this.logData.data;\n }\n if ('payload' in this.logData) {\n this.logData = this.logData.payload;\n }\n for (let i = 0; i < this.logData.length; i++) {\n const event = this.logData[i];\n if (event.event === 'Paste') {\n if (typeof event.pastedContent === 'string' && event.pastedContent.trim() !== '') {\n this.pastedEvents.push(event.pastedContent);\n }\n }\n if (event.event === 'aiInsert' && event.aiContent) {\n this.aiEvents.push(event.aiContent);\n }\n }\n if (this.logData.length > 0 && this.logData[0].unixTimestamp) {\n const startTime = this.logData[0].unixTimestamp;\n this.logData = this.logData.map(event => ({\n ...event,\n normalizedTime: event.unixTimestamp - startTime\n }));\n this.totalDuration = this.logData[this.logData.length - 1].normalizedTime;\n }\n }\n\n async handleNoSubmission() {\n try {\n const [html, str] = await Promise.all([\n templates.render('tiny_cursive/no_submission'),\n Str.get_string('warningpayload', 'tiny_cursive')\n ]);\n const newElement = $(html).text(str);\n return $('.tiny_cursive').html(newElement);\n } catch (error) {\n window.console.error(error);\n return false;\n }\n }\n\n // Stop the replay and update play button icon\n stopReplay() {\n if (this.replayInProgress) {\n clearTimeout(this.replayTimeout);\n this.replayInProgress = false;\n if (this.playButton) {\n const playSvg = document.createElement('img');\n playSvg.src = M.util.image_url('playicon', 'tiny_cursive');\n this.playButton.querySelector('.play-icon').innerHTML = playSvg.outerHTML;\n }\n }\n }\n\n // Build the replay control UI (play button, scrubber, speed controls)\n constructController(controllerId) {\n this.replayInProgress = false;\n this.currentPosition = 0;\n this.speed = 1;\n if (this.replayIntervalId) {\n clearInterval(this.replayIntervalId);\n this.replayIntervalId = null;\n }\n\n const container = document.getElementById(controllerId);\n if (!container) {\n window.console.error('Container not found with ID:', controllerId);\n return;\n }\n\n const controlContainer = container.querySelector('.tiny_cursive_replay_control');\n if (!controlContainer) {\n window.console.error('Replay control container not found in:', controllerId);\n return;\n }\n controlContainer.innerHTML = '';\n\n this.buildControllerUI(controlContainer, container);\n controlContainer.querySelector('.tiny_cursive_loading_spinner')?.remove();\n }\n\n buildControllerUI(controlContainer, container) {\n const topRow = document.createElement('div');\n topRow.classList.add('tiny_cursive_top_row');\n\n this.playButton = this.createPlayButton();\n topRow.appendChild(this.playButton);\n\n const scrubberContainer = this.createScrubberContainer();\n topRow.appendChild(scrubberContainer);\n\n this.timeDisplay = this.createTimeDisplay();\n topRow.appendChild(this.timeDisplay);\n\n const bottomRow = document.createElement('div');\n bottomRow.classList.add('tiny_cursive_bottom_row');\n\n const speedContainer = this.createSpeedControls();\n bottomRow.appendChild(speedContainer);\n\n const pasteEventsToggle = this.createPasteEventsToggle(container);\n bottomRow.appendChild(pasteEventsToggle);\n\n controlContainer.appendChild(topRow);\n controlContainer.appendChild(bottomRow);\n container.appendChild(this.pasteEventsPanel);\n }\n\n createPlayButton() {\n const playButton = document.createElement('button');\n playButton.classList.add('tiny_cursive_play_button');\n const playSvg = document.createElement('i');\n playButton.innerHTML = `${playSvg.outerHTML}`;\n playButton.addEventListener('click', () => {\n if (this.replayInProgress) {\n this.stopReplay();\n } else {\n this.startReplay(false);\n }\n $('.tiny_cursive-nav-tab').find('.active').removeClass('active');\n $('a[id^=\"rep\"]').addClass('active');\n });\n return playButton;\n }\n\n createScrubberContainer() {\n const scrubberContainer = document.createElement('div');\n scrubberContainer.classList.add('tiny_cursive_scrubber_container');\n this.scrubberElement = document.createElement('input');\n this.scrubberElement.classList.add('tiny_cursive_timeline_scrubber', 'timeline-scrubber');\n this.scrubberElement.type = 'range';\n this.scrubberElement.max = '100';\n this.scrubberElement.min = '0';\n this.scrubberElement.value = '0';\n this.scrubberElement.addEventListener('input', () => {\n this.skipToTime(parseInt(this.scrubberElement.value, 10));\n });\n scrubberContainer.appendChild(this.scrubberElement);\n return scrubberContainer;\n }\n\n createTimeDisplay() {\n const timeDisplay = document.createElement('div');\n timeDisplay.classList.add('tiny_cursive_time_display');\n timeDisplay.textContent = '00:00 / 00:00';\n return timeDisplay;\n }\n\n createSpeedControls() {\n const speedContainer = document.createElement('div');\n speedContainer.classList.add('tiny_cursive_speed_controls', 'speed-controls');\n const speedLabel = document.createElement('span');\n speedLabel.classList.add('tiny_cursive_speed_label');\n speedLabel.textContent = 'Speed: ';\n speedContainer.appendChild(speedLabel);\n\n const speedGroup = document.createElement('div');\n speedGroup.classList.add('tiny_cursive_speed_group');\n [1, 1.5, 2, 5, 10].forEach(speed => {\n const speedBtn = document.createElement('button');\n speedBtn.textContent = `${speed}x`;\n speedBtn.classList.add('tiny_cursive_speed_btn', 'speed-btn');\n if (parseFloat(speed) === this.speed) {\n speedBtn.classList.add('active');\n }\n speedBtn.dataset.speed = speed;\n speedBtn.addEventListener('click', () => {\n document.querySelectorAll('.tiny_cursive_speed_btn').forEach(btn => btn.classList.remove('active'));\n speedBtn.classList.add('active');\n this.speed = parseFloat(speedBtn.dataset.speed);\n if (this.replayInProgress) {\n this.stopReplay();\n this.startReplay(false);\n }\n });\n speedGroup.appendChild(speedBtn);\n });\n speedContainer.appendChild(speedGroup);\n return speedContainer;\n }\n\n createPasteEventsToggle(container) {\n const pasteEventsToggle = document.createElement('div');\n pasteEventsToggle.classList.add('tiny_cursive_paste_events_toggle', 'paste-events-toggle');\n\n const pasteEventsIcon = document.createElement('span');\n const pasteIcon = document.createElement('img');\n pasteIcon.src = M.util.image_url('pasteicon', 'tiny_cursive');\n pasteEventsIcon.innerHTML = pasteIcon.outerHTML;\n pasteEventsIcon.classList.add('tiny_cursive_paste_events_icon');\n\n const pasteEventsText = document.createElement('span');\n pasteEventsText.textContent = localStorage.getItem('pasteEvent');\n\n this.pasteEventCount = document.createElement('span');\n this.pasteEventCount.textContent = `(${this.pasteTimestamps.length})`;\n this.pasteEventCount.className = 'paste-event-count';\n this.pasteEventCount.style.marginLeft = '2px';\n\n const chevronIcon = document.createElement('span');\n const chevron = document.createElement('i');\n chevron.className = 'fa fa-chevron-down';\n chevronIcon.innerHTML = chevron.outerHTML;\n chevronIcon.style.marginLeft = '5px';\n chevronIcon.style.transition = 'transform 0.3s ease';\n\n pasteEventsToggle.appendChild(pasteEventsIcon);\n pasteEventsToggle.appendChild(pasteEventsText);\n pasteEventsToggle.appendChild(this.pasteEventCount);\n pasteEventsToggle.appendChild(chevronIcon);\n\n this.pasteEventsPanel = this.createPasteEventsPanel(container);\n pasteEventsToggle.addEventListener('click', () => {\n const isHidden = this.pasteEventsPanel.style.display === 'none';\n this.pasteEventsPanel.style.display = isHidden ? 'block' : 'none';\n chevronIcon.style.transform = isHidden ? 'rotate(180deg)' : 'rotate(0deg)';\n });\n\n return pasteEventsToggle;\n }\n\n createPasteEventsPanel(container) {\n const existingPanel = container.querySelector('.paste-events-panel');\n if (existingPanel) {\n existingPanel.remove();\n }\n const pasteEventsPanel = document.createElement('div');\n pasteEventsPanel.classList.add('tiny_cursive_paste_events_panel', 'paste-events-panel');\n pasteEventsPanel.style.display = 'none';\n this.populatePasteEventsPanel(pasteEventsPanel);\n return pasteEventsPanel;\n }\n\n // Detect Ctrl+V paste events and sync with user comments\n identifyPasteEvents() {\n this.pasteTimestamps = [];\n let controlPressed = false;\n let metaPressed = false;\n /* eslint-disable no-unused-vars */\n let shiftPressed = false;\n let pasteCount = 0;\n\n for (let i = 0; i < this.logData.length; i++) {\n const event = this.logData[i];\n if (event.event?.toLowerCase() === 'keydown') {\n if (event.key === 'Control') {\n controlPressed = true;\n } else if (event.key === 'Meta') {\n metaPressed = true;\n } else if (event.key === 'Shift') {\n shiftPressed = true;\n } else if ((event.key === 'v' || event.key === 'V') && (controlPressed || metaPressed)) {\n if (this.pastedEvents[pasteCount]) {\n const timestamp = event.normalizedTime || 0;\n this.pasteTimestamps.push({\n index: pasteCount,\n time: timestamp,\n formattedTime: this.formatTime(timestamp),\n pastedText: this.pastedEvents[pasteCount],\n timestamp\n });\n }\n pasteCount++;\n controlPressed = false;\n shiftPressed = false;\n metaPressed = false;\n } else {\n controlPressed = false;\n shiftPressed = false;\n metaPressed = false;\n }\n }\n }\n\n if (this.pasteEventsPanel) {\n this.populatePasteEventsPanel(this.pasteEventsPanel);\n }\n }\n\n identifyUndoEvents() {\n this.undoTimestamps = [];\n let controlPressed = false;\n let metaPressed = false;\n let undoCount = 0;\n\n for (let i = 0; i < this.logData.length; i++) {\n const event = this.logData[i];\n if (event.event?.toLowerCase() === 'keydown') {\n if (event.key === 'Control') {\n controlPressed = true;\n } else if (event.key === 'Meta') {\n metaPressed = true;\n } else if ((event.key === 'z' || event.key === 'Z') && (controlPressed || metaPressed)) {\n const timestamp = event.normalizedTime || 0;\n this.undoTimestamps.push({\n index: undoCount,\n time: timestamp,\n formattedTime: this.formatTime(timestamp),\n timestamp\n });\n undoCount++;\n controlPressed = false;\n metaPressed = false;\n } else {\n controlPressed = false;\n metaPressed = false;\n }\n }\n }\n }\n\n // Populate the paste events panel with navigation\n populatePasteEventsPanel(panel) {\n panel.innerHTML = '';\n panel.classList.add('tiny_cursive_event_panel');\n\n if (!this.pasteTimestamps.length) {\n const noEventsMessage = document.createElement('div');\n noEventsMessage.className = 'no-paste-events-message p-3';\n noEventsMessage.textContent = localStorage.getItem('nopasteevent');\n panel.appendChild(noEventsMessage);\n return;\n }\n\n const carouselContainer = document.createElement('div');\n carouselContainer.classList.add('tiny_cursive_paste_events_carousel', 'paste-events-carousel');\n\n const navigationRow = document.createElement('div');\n navigationRow.classList.add('paste-events-navigation', 'tiny_cursive_navigation_row');\n\n const counterDisplay = document.createElement('div');\n counterDisplay.classList.add('paste-events-counter', 'tiny_cursive_counter_display');\n counterDisplay.textContent = 'Paste Events';\n\n const navButtons = document.createElement('div');\n navButtons.classList.add('tiny_cursive_nav_buttons');\n const prevButton = document.createElement('button');\n prevButton.classList.add('paste-event-prev-btn', 'tiny_cursive_nav_button');\n prevButton.innerHTML = '';\n\n const nextButton = document.createElement('button');\n nextButton.classList.add('paste-event-next-btn', 'tiny_cursive_nav_button');\n nextButton.innerHTML = '';\n nextButton.disabled = this.pasteTimestamps.length <= 1;\n\n navButtons.appendChild(prevButton);\n navButtons.appendChild(nextButton);\n navigationRow.appendChild(counterDisplay);\n navigationRow.appendChild(navButtons);\n\n const contentContainer = document.createElement('div');\n contentContainer.className = 'paste-events-content tiny_cursive_content_container';\n contentContainer.appendChild(this.createPasteEventDisplay(this.pasteTimestamps[0]));\n\n carouselContainer.appendChild(navigationRow);\n carouselContainer.appendChild(contentContainer);\n panel.appendChild(carouselContainer);\n\n let currentIndex = 0;\n const updateDisplay = () => {\n contentContainer.innerHTML = '';\n contentContainer.appendChild(this.createPasteEventDisplay(this.pasteTimestamps[currentIndex]));\n counterDisplay.textContent = 'Paste Events';\n prevButton.disabled = currentIndex === 0;\n prevButton.style.opacity = currentIndex === 0 ? '0.5' : '1';\n nextButton.disabled = currentIndex === this.pasteTimestamps.length - 1;\n nextButton.style.opacity = currentIndex === this.pasteTimestamps.length - 1 ? '0.5' : '1';\n };\n\n prevButton.addEventListener('click', () => {\n if (currentIndex > 0) {\n currentIndex--;\n updateDisplay();\n }\n });\n\n nextButton.addEventListener('click', () => {\n if (currentIndex < this.pasteTimestamps.length - 1) {\n currentIndex++;\n updateDisplay();\n }\n });\n }\n\n createPasteEventDisplay(pasteEvent) {\n const eventRow = document.createElement('div');\n eventRow.className = 'tiny_cursive_event_row';\n\n const headerRow = document.createElement('div');\n headerRow.className = 'tiny_cursive_header_row';\n\n const textContainer = document.createElement('div');\n textContainer.className = 'tiny_cursive_text_container';\n\n const timestampContainer = document.createElement('div');\n timestampContainer.className = 'paste-event-timestamp tiny_cursive_paste_event_timestamp';\n timestampContainer.textContent = pasteEvent.formattedTime;\n\n const pastedTextContainer = document.createElement('div');\n pastedTextContainer.className = 'paste-event-text tiny_cursive_pasted_text_container';\n pastedTextContainer.textContent = pasteEvent.pastedText;\n\n textContainer.appendChild(timestampContainer);\n textContainer.appendChild(pastedTextContainer);\n\n const playButton = document.createElement('button');\n playButton.className = 'paste-event-play-btn tiny_cursive_seekplay_button';\n const playIcon = document.createElement('img');\n playIcon.src = M.util.image_url('seekplayicon', 'tiny_cursive');\n playButton.innerHTML = playIcon.outerHTML;\n playButton.addEventListener('click', () => this.jumpToTimestamp(pasteEvent.timestamp));\n\n headerRow.appendChild(textContainer);\n headerRow.appendChild(playButton);\n eventRow.appendChild(headerRow);\n\n return eventRow;\n }\n\n // Jump to a specific timestamp in the replay\n jumpToTimestamp(timestamp) {\n const percentage = this.totalDuration > 0 ? (timestamp / this.totalDuration) * 100 : 0;\n this.skipToTime(percentage);\n if (!this.replayInProgress) {\n this.startReplay(false);\n }\n }\n\n setScrubberVal(value) {\n if (this.scrubberElement) {\n this.scrubberElement.value = String(value);\n if (this.timeDisplay) {\n const displayTime = Math.min(this.currentTime, this.totalDuration);\n this.timeDisplay.textContent = `${this.formatTime(displayTime)} / ${this.formatTime(this.totalDuration)}`;\n }\n }\n }\n\n loadJSON(filePath) {\n return fetchJson([{\n methodname: 'cursive_get_reply_json',\n args: {filepath: filePath}\n }])[0].done(response => response).fail(error => {\n throw new Error(`Error loading JSON file: ${error.message}`);\n });\n }\n\n formatTime(ms) {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;\n }\n\n // Start or restart the replay\n startReplay(reset = true) {\n if (this.replayInProgress) {\n clearTimeout(this.replayTimeout);\n }\n const atEnd = (this.totalDuration > 0 && this.currentTime >= this.totalDuration) ||\n (this.currentEventIndex >= this.totalEvents);\n if (atEnd && !reset) {\n reset = true;\n }\n this.replayInProgress = true;\n if (reset) {\n this.outputElement.innerHTML = '';\n this.text = '';\n this.cursorPosition = 0;\n this.currentEventIndex = 0;\n this.currentTime = 0;\n this.highlightedChars = [];\n this.deletedChars = [];\n this.isControlKeyPressed = false;\n this.isMetaKeyPressed = false;\n this.currentPasteIndex = 0;\n this.pastedChars = [];\n this.currentAiIndex = 0;\n this.aiChars = [];\n }\n if (this.playButton) {\n const pauseSvg = document.createElement('i');\n pauseSvg.className = 'fa fa-pause';\n this.playButton.querySelector('.play-icon').innerHTML = pauseSvg.outerHTML;\n }\n this.replayLog();\n }\n\n // Process events in sequence to simulate typing\n replayLog() {\n if (!this.replayInProgress) {\n this.updateDisplayText(this.text, this.cursorPosition, [], []);\n return;\n }\n\n while (this.currentEventIndex < this.logData.length) {\n const event = this.logData[this.currentEventIndex];\n if (event.normalizedTime && event.normalizedTime > this.currentTime) {\n break;\n }\n\n let text = this.text || '';\n let cursor = this.cursorPosition;\n let updatedHighlights = [...this.highlightedChars];\n let updatedDeleted = [...this.deletedChars];\n\n if (event.rePosition !== undefined && (this.currentEventIndex === 0 ||\n event.event === 'mouseDown' || event.event === 'mouseUp')) {\n cursor = Math.max(0, Math.min(event.rePosition, text.length));\n }\n\n if (event.event?.toLowerCase() === 'keydown') {\n ({text, cursor, updatedHighlights, updatedDeleted} =\n this.processKeydownEvent(event, text, cursor, updatedHighlights, updatedDeleted));\n } else if (event.event === 'aiInsert') {\n ({text, cursor, updatedHighlights, updatedDeleted} =\n this.processAiInsertEvent(event, text, cursor, updatedHighlights, updatedDeleted));\n }\n\n this.text = text;\n this.cursorPosition = cursor;\n this.highlightedChars = updatedHighlights.filter(h => !h.expiresAt || h.expiresAt > this.currentTime);\n this.deletedChars = updatedDeleted.filter(d => !d.expiresAt || d.expiresAt > this.currentTime);\n\n this.currentEventIndex++;\n }\n\n this.updateDisplayText(this.text, this.cursorPosition, this.highlightedChars, this.deletedChars);\n if (this.totalDuration > 0) {\n const percentComplete = Math.min((this.currentTime / this.totalDuration) * 100, 100);\n this.setScrubberVal(percentComplete);\n }\n\n if (this.replayInProgress) {\n const baseIncrement = 100;\n const incrementTime = baseIncrement / this.speed;\n this.currentTime += baseIncrement;\n if (this.currentEventIndex >= this.totalEvents) {\n if (this.loop) {\n this.startReplay(true);\n } else {\n this.stopReplay();\n this.updateDisplayText(this.text, this.cursorPosition, [], []);\n }\n } else {\n this.replayTimeout = setTimeout(() => this.replayLog(), incrementTime);\n }\n }\n }\n\n getLineAndColumn(text, pos) {\n const before = text.substring(0, pos);\n const lineIndex = before.split('\\n').length - 1;\n const col = before.length - before.lastIndexOf('\\n') - 1;\n return {lineIndex, col};\n }\n\n processAiInsertEvent(event, text, cursor, highlights, deletions) {\n if (this.aiEvents && this.currentAiIndex < this.aiEvents.length) {\n const aiContent = this.aiEvents[this.currentAiIndex];\n // Use event.rePosition which points to where the word to replace is\n const targetPosition = event.rePosition;\n\n ({text, cursor} = this.handleAiReplacement(aiContent, text, targetPosition, cursor, deletions));\n this.currentAiIndex++;\n }\n return {\n text,\n cursor,\n updatedHighlights: highlights,\n updatedDeleted: deletions\n };\n }\n\n handleAiReplacement(aiContent, text, targetPosition, currentCursor, deletions) {\n const insertText = aiContent || '';\n const aiWords = insertText.trim().split(/\\s+/);\n const isMultiWord = aiWords.length > 1;\n const isNewLineInsertion = insertText.startsWith('\\n') || insertText.endsWith('\\n');\n\n const {wordStart, wordEnd} = this.findWordToReplace(\n text,\n targetPosition,\n currentCursor,\n aiWords,\n isMultiWord,\n isNewLineInsertion\n );\n\n const wordToReplace = text.substring(wordStart, wordEnd);\n\n // Mark replaced characters as deleted\n this.markCharsAsDeleted(wordToReplace, wordStart, deletions);\n\n // Perform the replacement\n const replacedLength = wordToReplace.length;\n text = text.substring(0, wordStart) + insertText + text.substring(wordEnd);\n const positionDiff = insertText.length - replacedLength;\n\n // Calculate new cursor position\n const newCursor = this.calculateNewCursorPosition(\n currentCursor,\n targetPosition,\n wordStart,\n wordEnd,\n insertText,\n isNewLineInsertion\n );\n\n // Update character tracking arrays\n this.updateCharacterIndices(wordStart, wordEnd, positionDiff, insertText);\n\n return {text, cursor: newCursor};\n }\n\n findWordToReplace(text, targetPosition, currentCursor, aiWords, isMultiWord, isNewLineInsertion) {\n if (isNewLineInsertion) {\n return {wordStart: currentCursor, wordEnd: currentCursor};\n }\n\n const {lineStart, lineEnd} = this.findLineRange(text, targetPosition);\n const lineText = text.substring(lineStart, lineEnd);\n const words = this.extractWordsFromLine(lineText, lineStart);\n\n if (words.length === 0) {\n return {wordStart: currentCursor, wordEnd: currentCursor};\n }\n\n if (isMultiWord) {\n return this.findMultiWordMatch(words, aiWords, targetPosition);\n } else {\n return this.findSingleWordMatch(words, aiWords[0], targetPosition);\n }\n }\n\n findLineRange(text, targetPosition) {\n let lineStart = 0;\n for (let i = targetPosition - 1; i >= 0; i--) {\n if (text[i] === '\\n') {\n lineStart = i + 1;\n break;\n }\n }\n\n let lineEnd = text.length;\n for (let i = targetPosition; i < text.length; i++) {\n if (text[i] === '\\n') {\n lineEnd = i;\n break;\n }\n }\n\n return {lineStart, lineEnd};\n }\n\n extractWordsFromLine(lineText, lineStart) {\n const words = [];\n let pos = 0;\n\n while (pos < lineText.length) {\n // Skip spaces\n while (pos < lineText.length && lineText[pos] === ' ') {\n pos++;\n }\n if (pos >= lineText.length) {\n break;\n }\n\n // Extract word\n const start = pos;\n while (pos < lineText.length && lineText[pos] !== ' ') {\n pos++;\n }\n\n if (pos > start) {\n words.push({\n text: lineText.substring(start, pos),\n start: lineStart + start,\n end: lineStart + pos\n });\n }\n }\n\n return words;\n }\n\n findMultiWordMatch(words, aiWords, targetPosition) {\n let bestMatch = {start: -1, end: -1, score: -1, wordCount: 0, similarityScore: 0};\n\n for (let i = 0; i < words.length; i++) {\n const matchResult = this.evaluateMultiWordSequence(words, aiWords, i, targetPosition);\n\n if (matchResult.totalScore > bestMatch.score ||\n (matchResult.totalScore === bestMatch.score &&\n matchResult.similarityScore > bestMatch.similarityScore)) {\n bestMatch = matchResult;\n }\n }\n\n if (bestMatch.score > 10) {\n return {wordStart: bestMatch.start, wordEnd: bestMatch.end};\n } else {\n const closest = this.findClosestWord(words, targetPosition);\n return {wordStart: closest.start, wordEnd: closest.end};\n }\n }\n\n evaluateMultiWordSequence(words, aiWords, startIndex, targetPosition) {\n const seqWords = [];\n for (let j = 0; j < aiWords.length && startIndex + j < words.length; j++) {\n seqWords.push(words[startIndex + j]);\n }\n\n if (seqWords.length === 0) {\n return {start: -1, end: -1, score: -1, wordCount: 0, similarityScore: 0};\n }\n\n const similarityScore = this.calculateSequenceSimilarity(aiWords, seqWords);\n const positionScore = this.calculatePositionScore(seqWords, targetPosition);\n const totalScore = similarityScore + positionScore + seqWords.length;\n\n return {\n start: seqWords[0].start,\n end: seqWords[seqWords.length - 1].end,\n score: totalScore,\n wordCount: seqWords.length,\n similarityScore: similarityScore\n };\n }\n\n calculateSequenceSimilarity(aiWords, seqWords) {\n let similarityScore = 0;\n const compareLength = Math.min(seqWords.length, aiWords.length);\n\n for (let k = 0; k < compareLength; k++) {\n const ai = aiWords[k].toLowerCase();\n const seq = seqWords[k].text.toLowerCase();\n\n if (ai === seq) {\n similarityScore += 10;\n } else {\n const similarity = this.calculateSimilarity(ai, seq);\n similarityScore += similarity * 10;\n }\n }\n\n return similarityScore;\n }\n\n calculatePositionScore(seqWords, targetPosition) {\n let positionScore = 0;\n const seqStart = seqWords[0].start;\n const seqEndPos = seqWords[seqWords.length - 1].end;\n\n if (targetPosition >= seqStart && targetPosition <= seqEndPos) {\n positionScore += 10;\n if (targetPosition >= seqWords[0].start && targetPosition <= seqWords[0].end) {\n positionScore += 5;\n }\n }\n\n return positionScore;\n }\n\n findSingleWordMatch(words, aiWord, targetPosition) {\n const aiWordLower = aiWord.toLowerCase();\n const bestSimilarityMatch = this.findBestSimilarityMatch(words, aiWordLower);\n\n if (bestSimilarityMatch.score > 0.5) {\n return {wordStart: bestSimilarityMatch.word.start, wordEnd: bestSimilarityMatch.word.end};\n }\n\n const bestPositionMatch = this.findBestPositionMatch(words, aiWordLower, targetPosition);\n\n if (bestPositionMatch.word) {\n return {wordStart: bestPositionMatch.word.start, wordEnd: bestPositionMatch.word.end};\n }\n\n // Fallback to position-based word boundary\n return this.findWordBoundaryAtPosition(words[0].start, words[words.length - 1].end,\n targetPosition, this.text);\n }\n\n findBestSimilarityMatch(words, aiWordLower) {\n let bestMatch = {word: null, score: 0};\n\n for (const word of words) {\n let similarity = this.calculateSimilarity(aiWordLower, word.text.toLowerCase());\n const wordLower = word.text.toLowerCase();\n\n // Penalize short prefix matches\n if (wordLower.length < aiWordLower.length * 0.5 && aiWordLower.startsWith(wordLower)) {\n similarity = similarity * 0.3;\n }\n\n if (similarity > bestMatch.score) {\n bestMatch = {word, score: similarity};\n }\n }\n\n return bestMatch;\n }\n\n findBestPositionMatch(words, aiWordLower, targetPosition) {\n let bestMatch = {word: null, score: -1};\n\n for (const word of words) {\n let score = this.calculateWordScore(word, aiWordLower, targetPosition);\n\n if (score > bestMatch.score) {\n bestMatch = {word, score};\n }\n }\n\n return bestMatch;\n }\n\n calculateWordScore(word, aiWordLower, targetPosition) {\n let score = 0;\n\n // Position score\n if (targetPosition >= word.start && targetPosition <= word.end) {\n score += 30;\n } else {\n const distance = Math.min(\n Math.abs(targetPosition - word.start),\n Math.abs(targetPosition - word.end)\n );\n score += Math.max(0, 20 - distance);\n }\n\n // Similarity score with penalty\n let similarity = this.calculateSimilarity(aiWordLower, word.text.toLowerCase());\n const wordLower = word.text.toLowerCase();\n if (wordLower.length < aiWordLower.length * 0.5 && aiWordLower.startsWith(wordLower)) {\n similarity = similarity * 0.3;\n }\n score += similarity * 10;\n\n return score;\n }\n\n findWordBoundaryAtPosition(lineStart, lineEnd, targetPosition, text) {\n let wordStart = targetPosition;\n while (wordStart > lineStart && text[wordStart - 1] !== ' ' && text[wordStart - 1] !== '\\n') {\n wordStart--;\n }\n let wordEnd = targetPosition;\n while (wordEnd < lineEnd && text[wordEnd] !== ' ' && text[wordEnd] !== '\\n') {\n wordEnd++;\n }\n return {wordStart, wordEnd};\n }\n\n markCharsAsDeleted(wordToReplace, wordStart, deletions) {\n if (wordToReplace.length > 0) {\n for (let i = 0; i < wordToReplace.length; i++) {\n deletions.push({\n index: wordStart + i,\n chars: wordToReplace[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n }\n }\n\n calculateNewCursorPosition(currentCursor, targetPosition, wordStart, wordEnd, insertText, isNewLineInsertion) {\n if (isNewLineInsertion) {\n return wordStart + insertText.length;\n }\n\n if (targetPosition >= wordStart && targetPosition <= wordEnd) {\n return wordStart + insertText.length;\n }\n\n const positionDiff = insertText.length - (wordEnd - wordStart);\n\n if (currentCursor >= wordEnd) {\n return currentCursor + positionDiff;\n } else if (currentCursor > wordStart && currentCursor < wordEnd) {\n return wordStart + insertText.length;\n }\n\n return currentCursor;\n }\n\n updateCharacterIndices(wordStart, wordEnd, positionDiff, insertText) {\n // Update pasted character indices\n this.updatePastedCharIndices(wordStart, wordEnd, positionDiff);\n\n // Mark characters as AI-inserted\n this.markCharsAsAiInserted(wordStart, insertText);\n\n // Update AI character indices\n this.updateAiCharIndices(wordStart, wordEnd, positionDiff, insertText);\n }\n\n updatePastedCharIndices(wordStart, wordEnd, positionDiff) {\n if (this.pastedChars) {\n this.pastedChars = this.pastedChars.map(p => {\n if (p.index >= wordEnd) {\n return {...p, index: p.index + positionDiff};\n } else if (p.index >= wordStart && p.index < wordEnd) {\n return null;\n }\n return p;\n }).filter(p => p !== null);\n }\n }\n\n markCharsAsAiInserted(wordStart, insertText) {\n if (!this.aiChars) {\n this.aiChars = [];\n }\n\n if (insertText.trim() !== '') {\n for (let i = 0; i < insertText.length; i++) {\n this.aiChars.push({\n index: wordStart + i,\n chars: insertText[i]\n });\n }\n }\n }\n\n updateAiCharIndices(wordStart, wordEnd, positionDiff, insertText) {\n const justAddedIndices = new Set();\n for (let i = 0; i < insertText.length; i++) {\n justAddedIndices.add(wordStart + i);\n }\n\n this.aiChars = this.aiChars.map(p => {\n if (!justAddedIndices.has(p.index)) {\n if (p.index >= wordEnd) {\n return {...p, index: p.index + positionDiff};\n } else if (p.index >= wordStart && p.index < wordEnd) {\n return null;\n }\n }\n return p;\n }).filter(p => p !== null);\n }\n\n // Calculate similarity between two strings\n calculateSimilarity(str1, str2) {\n if (str1 === str2) {\n return 1;\n }\n if (str1.length === 0 || str2.length === 0) {\n return 0;\n }\n\n // Check if one string is a prefix of the other\n if (str1.startsWith(str2) || str2.startsWith(str1)) {\n return 0.8;\n }\n\n // Levenshtein distance\n const len1 = str1.length;\n const len2 = str2.length;\n const matrix = Array(len2 + 1).fill(null).map(() => Array(len1 + 1).fill(0));\n\n for (let i = 0; i <= len1; i++) {\n matrix[0][i] = i;\n }\n for (let j = 0; j <= len2; j++) {\n matrix[j][0] = j;\n }\n\n for (let j = 1; j <= len2; j++) {\n for (let i = 1; i <= len1; i++) {\n const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;\n matrix[j][i] = Math.min(\n matrix[j][i - 1] + 1,\n matrix[j - 1][i] + 1,\n matrix[j - 1][i - 1] + cost\n );\n }\n }\n\n const maxLen = Math.max(len1, len2);\n return 1 - (matrix[len2][len1] / maxLen);\n }\n\n // Find the word closest to a target position\n findClosestWord(words, targetPosition) {\n if (words.length === 0) {\n return {start: targetPosition, end: targetPosition};\n }\n\n let closest = words[0];\n let minDistance = Math.min(\n Math.abs(targetPosition - words[0].start),\n Math.abs(targetPosition - words[0].end)\n );\n\n for (const word of words) {\n if (targetPosition >= word.start && targetPosition <= word.end) {\n return word;\n }\n\n const distance = Math.min(\n Math.abs(targetPosition - word.start),\n Math.abs(targetPosition - word.end)\n );\n\n if (distance < minDistance) {\n minDistance = distance;\n closest = word;\n }\n }\n\n return closest;\n }\n\n // Handle keydown events (e.g., typing, backspace, Ctrl+V)\n processKeydownEvent(event, text, cursor, highlights, deletions) {\n const key = event.key;\n const charToInsert = this.applyKey(key);\n\n // Handle copy operation (Ctrl+C)\n if (this.isCopyOperation(key)) {\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n // Handle undo operation (Ctrl+Z)\n if (this.isUndoOperation(key)) {\n return this.handleUndoOperation(event, text, cursor, highlights, deletions);\n }\n\n // Detect selection for current event\n const currentEventIndex = this.currentEventIndex;\n const selection = this.detectSelection(currentEventIndex);\n\n // Handle paste operation (Ctrl+V)\n if (this.isPasteOperation(key, event)) {\n return this.handlePasteOperation(event, selection, text, cursor, highlights, deletions);\n }\n\n // Update modifier key states\n this.updateModifierStates(key);\n\n // Handle selection deletion with Backspace/Delete\n if (this.isSelectionDeletion(key, selection)) {\n ({text, cursor} = this.handleSelectionDeletion(selection, text, cursor, deletions));\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n // Process various key operations\n return this.processKeyOperation(key, charToInsert, text, cursor, highlights, deletions, selection);\n }\n\n isCopyOperation(key) {\n return (key === 'c' || key === 'C') && (this.isControlKeyPressed || this.isMetaKeyPressed);\n }\n\n isUndoOperation(key) {\n return (key === 'z' || key === 'Z') && (this.isControlKeyPressed || this.isMetaKeyPressed);\n }\n\n handleUndoOperation(event, text, cursor, highlights, deletions) {\n const nextEventIndex = this.currentEventIndex + 1;\n if (nextEventIndex < this.logData.length) {\n const nextEvent = this.logData[nextEventIndex];\n\n if (nextEvent.event === 'keyUp' && (nextEvent.key === 'z' || nextEvent.key === 'Z')) {\n const newPosition = nextEvent.rePosition;\n if (newPosition < cursor && text.length > 0) {\n const textBeforeUndo = text;\n text = text.substring(0, newPosition) + text.substring(cursor);\n cursor = newPosition;\n\n // Mark as deleted for visual effect\n for (let i = 0; i < textBeforeUndo.length && i < cursor; i++) {\n deletions.push({\n index: newPosition,\n chars: textBeforeUndo[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n }\n }\n }\n\n this.isControlKeyPressed = false;\n this.isMetaKeyPressed = false;\n\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n isPasteOperation(key, event) {\n if ((key === 'v' || key === 'V') && (this.isControlKeyPressed || this.isMetaKeyPressed)) {\n return (event.pastedContent && event.pastedContent.trim() !== '') ||\n (this.pastedEvents && this.currentPasteIndex < this.pastedEvents.length);\n }\n return false;\n }\n\n handlePasteOperation(event, selection, text, cursor, highlights, deletions) {\n const pastedContent = event.pastedContent || this.pastedEvents[this.currentPasteIndex];\n\n if (selection) {\n ({text, cursor} = this.handleSelectionDeletion(selection, text, cursor, deletions));\n }\n\n ({text, cursor} = this.handlePasteInsert(pastedContent, text, cursor));\n this.currentPasteIndex++;\n this.resetModifierStates();\n this.isPasteEvent = false;\n\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n resetModifierStates() {\n this.isControlKeyPressed = false;\n this.isShiftKeyPressed = false;\n this.isMetaKeyPressed = false;\n }\n\n isSelectionDeletion(key, selection) {\n return (key === 'Backspace' || key === 'Delete') && selection && selection.length > 1;\n }\n\n processKeyOperation(key, charToInsert, text, cursor, highlights, deletions, selection) {\n if (this.isCtrlBackspace(key, cursor)) {\n ({text, cursor} = this.handleCtrlBackspace(text, cursor, deletions));\n } else if (this.isCtrlDelete(key, cursor, text)) {\n ({text} = this.handleCtrlDelete(text, cursor, deletions));\n } else if (this.isCtrlArrowMove(key)) {\n cursor = this.handleCtrlArrowMove(key, text, cursor);\n } else if (this.isRegularBackspace(key, cursor)) {\n ({text, cursor} = this.handleBackspace(text, cursor, deletions));\n } else if (this.isRegularDelete(key, cursor, text)) {\n ({text} = this.handleDelete(text, cursor, deletions));\n } else if (this.isArrowUp(key)) {\n cursor = this.handleArrowUp(text, cursor);\n } else if (this.isArrowDown(key)) {\n cursor = this.handleArrowDown(text, cursor);\n } else if (this.isRegularArrowMove(key)) {\n cursor = this.handleArrowMove(key, text, cursor);\n } else if (charToInsert && charToInsert.length > 0) {\n if (selection && selection.length > 0) {\n ({text, cursor} = this.handleSelectionDeletion(selection, text, cursor, deletions));\n }\n ({text, cursor} = this.handleCharacterInsert(charToInsert, text, cursor, highlights));\n }\n\n return {text, cursor, updatedHighlights: highlights, updatedDeleted: deletions};\n }\n\n detectSelection(eventIndex) {\n const currentEvent = this.logData[eventIndex];\n\n if (currentEvent.event?.toLowerCase() === 'keydown' &&\n (currentEvent.key === 'Backspace' || currentEvent.key === 'Delete')) {\n\n const currentPos = currentEvent.rePosition;\n return this.processDetection(currentPos, currentEvent, eventIndex);\n }\n return null;\n }\n\n processDetection(currentPos, currentEvent, eventIndex) {\n for (let i = eventIndex + 1; i < this.logData.length; i++) {\n const nextEvent = this.logData[i];\n\n if (nextEvent.event?.toLowerCase() === 'keyup' &&\n nextEvent.key === currentEvent.key) {\n\n const nextPos = nextEvent.rePosition;\n\n // Calculate the difference in positions\n const positionDiff = Math.abs(currentPos - nextPos);\n\n if (positionDiff > 1) {\n return {\n start: Math.min(currentPos, nextPos),\n end: Math.max(currentPos, nextPos),\n length: positionDiff\n };\n } else if (positionDiff === 1) {\n if (currentEvent.key === 'Backspace') {\n return {\n start: nextPos,\n end: currentPos,\n length: 1\n };\n } else {\n return {\n start: currentPos,\n end: nextPos,\n length: 1\n };\n }\n }\n break;\n }\n }\n return null;\n }\n\n handleSelectionDeletion(selection, text, cursor, deletions) {\n const {start, end, length} = selection;\n\n // Add each character in the selection to the deletions array\n for (let i = start; i < end && i < text.length; i++) {\n deletions.push({\n index: start,\n chars: text[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n\n text = text.substring(0, start) + text.substring(end);\n\n this.shiftPastedCharsIndices(start, length);\n\n cursor = start;\n\n return {text, cursor};\n }\n\n // Handle Paste events to highlight pasted text\n handlePasteInsert(pastedContent, text, cursor) {\n const insertText = pastedContent || '';\n text = text.substring(0, cursor) + insertText + text.substring(cursor);\n\n // Mark characters as pasted for bold styling\n if (insertText.trim() !== '') {\n for (let i = 0; i < insertText.length; i++) {\n if (!this.pastedChars) {\n this.pastedChars = [];\n }\n this.pastedChars.push({\n index: cursor + i,\n chars: insertText[i]\n });\n }\n }\n\n return {text, cursor: cursor + insertText.length};\n }\n\n // Adjusts pasted chars indices after deletion to maintain styling for pasted text\n shiftPastedCharsIndices(startIndex, numDeleted) {\n this.pastedChars = this.pastedChars.map(p => {\n if (p.index >= startIndex + numDeleted) {\n return {...p, index: p.index - numDeleted};\n } else if (p.index >= startIndex && p.index < startIndex + numDeleted) {\n // Remove pasted characters that were deleted\n return null;\n }\n return p;\n }).filter(p => p !== null);\n\n if (this.aiChars) {\n this.aiChars = this.aiChars.map(p => {\n if (p.index >= startIndex + numDeleted) {\n return {...p, index: p.index - numDeleted};\n } else if (p.index >= startIndex && p.index < startIndex + numDeleted) {\n return null;\n }\n return p;\n }).filter(p => p !== null);\n }\n }\n\n // Update state for modifier keys (Control, paste events)\n updateModifierStates(key) {\n if (key === 'Control') {\n this.isControlKeyPressed = true;\n } else if (key === 'Shift') {\n this.isShiftKeyPressed = true;\n } else if (key === 'Meta') {\n this.isMetaKeyPressed = true;\n } else if ((key === 'v' || key === 'V') && (this.isControlKeyPressed || this.isMetaKeyPressed)) {\n this.isPasteEvent = true;\n } else if (!['Control', 'Meta', 'Backspace', 'Delete', 'ArrowLeft', 'ArrowRight'].includes(key)) {\n this.isControlKeyPressed = false;\n this.isShiftKeyPressed = false;\n this.isMetaKeyPressed = false;\n this.isPasteEvent = false;\n }\n }\n\n isCtrlBackspace(key, cursor) {\n return key === 'Backspace' && this.isControlKeyPressed && cursor > 0;\n }\n\n isCtrlDelete(key, cursor, text) {\n return key === 'Delete' && this.isControlKeyPressed && cursor < text.length;\n }\n\n isCtrlArrowMove(key) {\n return this.isControlKeyPressed && (key === 'ArrowLeft' || key === 'ArrowRight');\n }\n\n isRegularBackspace(key, cursor) {\n return key === 'Backspace' && !this.isPasteEvent && cursor > 0;\n }\n\n isRegularDelete(key, cursor, text) {\n return key === 'Delete' && !this.isControlKeyPressed && cursor < text.length;\n }\n\n isRegularArrowMove(key) {\n return !this.isControlKeyPressed && (key === 'ArrowLeft' || key === 'ArrowRight');\n }\n\n isArrowUp(key) {\n return key === 'ArrowUp';\n }\n\n isArrowDown(key) {\n return key === 'ArrowDown';\n }\n\n handleCtrlArrowMove(key, text, cursor) {\n return key === 'ArrowLeft'\n ? this.findPreviousWordBoundary(text, cursor)\n : this.findNextWordBoundary(text, cursor);\n }\n\n handleBackspace(text, cursor, deletions) {\n deletions.push({\n index: cursor - 1,\n chars: text[cursor - 1],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n this.shiftPastedCharsIndices(cursor - 1, 1);\n return {\n text: text.substring(0, cursor - 1) + text.substring(cursor),\n cursor: cursor - 1\n };\n }\n\n handleDelete(text, cursor, deletions) {\n deletions.push({\n index: cursor,\n chars: text[cursor],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n this.shiftPastedCharsIndices(cursor, 1);\n return {\n text: text.substring(0, cursor) + text.substring(cursor + 1),\n cursor\n };\n }\n\n handleArrowMove(key, text, cursor) {\n return key === 'ArrowLeft'\n ? Math.max(0, cursor - 1)\n : Math.min(text.length, cursor + 1);\n }\n\n handleCharacterInsert(charToInsert, text, cursor, highlights) {\n text = text.substring(0, cursor) + charToInsert + text.substring(cursor);\n // Shift pasted chars indices after the insertion point\n if (this.pastedChars) {\n this.pastedChars = this.pastedChars.map(p => {\n return p.index >= cursor ? {...p, index: p.index + 1} : p;\n });\n }\n if (this.aiChars) {\n this.aiChars = this.aiChars.map(p => {\n return p.index >= cursor ? {...p, index: p.index + 1} : p;\n });\n }\n if (charToInsert.trim() !== '') {\n highlights.push({\n index: cursor,\n chars: charToInsert,\n time: this.currentTime,\n expiresAt: this.currentTime + 1500\n });\n }\n return {text, cursor: cursor + 1};\n }\n\n handleCtrlDelete(text, cursor, deletions) {\n const wordEnd = this.findNextWordBoundary(text, cursor);\n const wordToDelete = text.substring(cursor, wordEnd);\n for (let i = 0; i < wordToDelete.length; i++) {\n deletions.push({\n index: cursor + i,\n chars: wordToDelete[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n this.shiftPastedCharsIndices(cursor, wordToDelete.length);\n return {\n text: text.substring(0, cursor) + text.substring(wordEnd),\n cursor\n };\n }\n\n handleArrowUp(text, cursor) {\n const lines = text.split('\\n');\n const {lineIndex, col} = this.getLineAndColumn(text, cursor);\n if (lineIndex > 0) {\n const prevLine = lines[lineIndex - 1];\n cursor = lines.slice(0, lineIndex - 1).join('\\n').length + 1 + Math.min(col, prevLine.length);\n } else {\n cursor = 0;\n }\n return cursor;\n }\n\n handleArrowDown(text, cursor) {\n const lines = text.split('\\n');\n const {lineIndex, col} = this.getLineAndColumn(text, cursor);\n if (lineIndex < lines.length - 1) {\n const nextLine = lines[lineIndex + 1];\n cursor = lines.slice(0, lineIndex + 1).join('\\n').length + 1 + Math.min(col, nextLine.length);\n } else {\n cursor = text.length;\n }\n return cursor;\n }\n\n handleCtrlBackspace(text, cursor, deletions) {\n let wordStart = cursor;\n while (wordStart > 0 && text[wordStart - 1] === ' ') {\n wordStart--;\n }\n while (wordStart > 0 && text[wordStart - 1] !== ' ') {\n wordStart--;\n }\n const wordToDelete = text.substring(wordStart, cursor);\n for (let i = 0; i < wordToDelete.length; i++) {\n deletions.push({\n index: wordStart + i,\n chars: wordToDelete[i],\n time: this.currentTime,\n expiresAt: this.currentTime + 2000\n });\n }\n this.shiftPastedCharsIndices(wordStart, wordToDelete.length);\n return {text: text.substring(0, wordStart) + text.substring(cursor), cursor: wordStart};\n }\n\n // Finds the index of the next word boundary after the cursor position\n findNextWordBoundary(text, cursor) {\n if (!text || cursor >= text.length) {\n return cursor;\n }\n if (text[cursor] === ' ') {\n while (cursor < text.length && text[cursor] === ' ') {\n cursor++;\n }\n }\n if (cursor >= text.length) {\n let lastNonSpace = text.length - 1;\n while (lastNonSpace >= 0 && text[lastNonSpace] === ' ') {\n lastNonSpace--;\n }\n return lastNonSpace + 1;\n }\n let wordEnd = cursor;\n while (wordEnd < text.length && text[wordEnd] !== ' ') {\n wordEnd++;\n }\n return wordEnd;\n }\n\n // Finds the index of the previous word boundary before the cursor position\n findPreviousWordBoundary(text, cursor) {\n if (cursor <= 0) {\n return 0;\n }\n let pos = cursor - 1;\n while (pos > 0 && (text[pos] === ' ' || text[pos] === '\\n')) {\n pos--;\n }\n while (pos > 0 && text[pos - 1] !== ' ' && text[pos - 1] !== '\\n') {\n pos--;\n }\n\n return pos;\n }\n\n skipToEnd() {\n if (this.replayInProgress) {\n this.replayInProgress = false;\n }\n let textOutput = \"\";\n this.logData.forEach(event => {\n if (event.event.toLowerCase() === 'keydown') {\n textOutput = this.applyKey(event.key, textOutput);\n }\n });\n this.outputElement.innerHTML = textOutput.slice(0, -1);\n this.setScrubberVal(100);\n }\n\n // Used by the scrubber to skip to a certain percentage of data\n skipToTime(percentage) {\n const wasPlaying = this.replayInProgress;\n this.stopReplay();\n\n const targetTime = (this.totalDuration * percentage) / 100;\n this.currentTime = targetTime;\n this.currentEventIndex = 0;\n this.text = '';\n this.cursorPosition = 0;\n this.highlightedChars = [];\n this.deletedChars = [];\n this.isControlKeyPressed = false;\n this.isMetaKeyPressed = false;\n this.isPasteEvent = false;\n this.pastedChars = [];\n this.currentPasteIndex = 0;\n this.currentAiIndex = 0;\n this.aiChars = [];\n let text = '';\n let cursor = 0;\n let highlights = [];\n let deletions = [];\n let pasteIndex = 0;\n let aiIndex = 0;\n\n for (let i = 0; i < this.logData.length; i++) {\n const event = this.logData[i];\n if (event.normalizedTime && event.normalizedTime > targetTime) {\n this.currentEventIndex = i;\n break;\n }\n if (event.rePosition !== undefined && (this.currentEventIndex === 0 ||\n event.event === 'mouseDown' || event.event === 'mouseUp')) {\n cursor = Math.max(0, Math.min(event.rePosition, text.length));\n }\n if (event.event?.toLowerCase() === 'keydown') {\n this.currentPasteIndex = pasteIndex;\n if ((event.key === 'v' || event.key === 'V') && (this.isControlKeyPressed || this.isMetaKeyPressed)) {\n pasteIndex++;\n }\n ({text, cursor, updatedHighlights: highlights, updatedDeleted: deletions} =\n this.processKeydownEvent(event, text, cursor, highlights, deletions));\n } else if (event.event === 'aiInsert') {\n this.currentAiIndex = aiIndex;\n ({text, cursor, updatedHighlights: highlights, updatedDeleted: deletions} =\n this.processAiInsertEvent(event, text, cursor, highlights, deletions));\n aiIndex++;\n }\n this.currentEventIndex = i + 1;\n }\n\n this.currentPasteIndex = pasteIndex;\n this.currentAiIndex = aiIndex;\n this.text = text;\n this.cursorPosition = cursor;\n this.highlightedChars = highlights.filter(h => !h.expiresAt || h.expiresAt > targetTime);\n this.deletedChars = deletions.filter(d => !d.expiresAt || d.expiresAt > targetTime);\n this.updateDisplayText(this.text, this.cursorPosition, this.highlightedChars, this.deletedChars);\n this.setScrubberVal(percentage);\n\n if (wasPlaying) {\n this.replayInProgress = true;\n this.replayLog();\n }\n }\n\n // Update display with text, cursor, highlights and deletions.\n // eslint-disable-next-line complexity\n updateDisplayText(text, cursorPosition, highlights, deletions) {\n let html = '';\n const highlightMap = {};\n const deletionMap = {};\n const pastedMap = {};\n const aiMap = {};\n const currentTime = this.currentTime;\n\n highlights.forEach(h => {\n let opacity = 1;\n if (h.expiresAt && h.expiresAt - currentTime < 500) {\n opacity = Math.max(0, (h.expiresAt - currentTime) / 500);\n }\n highlightMap[h.index] = {chars: h.chars, opacity};\n });\n\n deletions.forEach(d => {\n let opacity = 0.5;\n if (d.expiresAt && d.expiresAt - currentTime < 500) {\n opacity = Math.max(0, ((d.expiresAt - currentTime) / 500) * 0.5);\n }\n deletionMap[d.index] = {chars: d.chars, opacity};\n });\n\n // Process pasted characters for bold styling\n if (this.pastedChars) {\n this.pastedChars.forEach(p => {\n if (p.index < text.length) {\n pastedMap[p.index] = true;\n }\n });\n }\n\n // Process AI characters for styling\n if (this.aiChars) {\n this.aiChars.forEach(p => {\n if (p.index < text.length) {\n aiMap[p.index] = true;\n }\n });\n }\n\n // Find if we have out-of-bounds deletions (from Control+Backspace)\n const outOfRangeDeletions = deletions.filter(d => d.index >= text.length);\n const textLines = text.split('\\n');\n let currentPosition = 0;\n\n for (let lineIndex = 0; lineIndex < textLines.length; lineIndex++) {\n const line = textLines[lineIndex];\n for (let i = 0; i < line.length; i++) {\n if (currentPosition === cursorPosition) {\n html += '';\n }\n const char = line[i];\n if (deletionMap[currentPosition]) {\n html += `${deletionMap[currentPosition].chars}`;\n }\n const isPasted = pastedMap[currentPosition];\n const isAi = aiMap[currentPosition];\n const isHighlighted = highlightMap[currentPosition] && char !== ' ';\n\n if (isPasted && isHighlighted) {\n html += `${char}`;\n } else if (isAi && isHighlighted) {\n html += `${char}`;\n } else if (isPasted) {\n html += `${char === ' ' ? ' ' : this.escapeHtml(char)}`;\n } else if (isAi) {\n html += `${char === ' ' ? ' ' : this.escapeHtml(char)}`;\n } else if (isHighlighted) {\n html += `${char}`;\n } else {\n html += char === ' ' ? ' ' : this.escapeHtml(char);\n }\n currentPosition++;\n }\n if (currentPosition === cursorPosition) {\n html += '';\n }\n if (lineIndex < textLines.length - 1) {\n html += '
';\n currentPosition++;\n }\n }\n\n if (cursorPosition === text.length && !html.endsWith('')) {\n html += '';\n }\n\n if (outOfRangeDeletions.length > 0) {\n outOfRangeDeletions.sort((a, b) => a.index - b.index);\n const cursorHTML = '';\n const cursorPos = html.lastIndexOf(cursorHTML);\n if (cursorPos !== -1) {\n let deletedWordHTML = '';\n outOfRangeDeletions.forEach(d => {\n deletedWordHTML += d.chars;\n });\n deletedWordHTML += '';\n html = html.substring(0, cursorPos) + deletedWordHTML + html.substring(cursorPos);\n }\n }\n\n const wasScrolledToBottom = this.outputElement.scrollHeight -\n this.outputElement.clientHeight <= this.outputElement.scrollTop + 1;\n this.outputElement.innerHTML = html;\n\n if (wasScrolledToBottom || this.isCursorBelowViewport()) {\n this.outputElement.scrollTop = this.outputElement.scrollHeight;\n }\n }\n\n // Check if cursor is below visible viewport\n isCursorBelowViewport() {\n const cursorElement = this.outputElement.querySelector('.tiny_cursive-cursor:last-of-type');\n if (!cursorElement) {\n return false;\n }\n\n const cursorRect = cursorElement.getBoundingClientRect();\n const outputRect = this.outputElement.getBoundingClientRect();\n\n return cursorRect.bottom > outputRect.bottom;\n }\n\n escapeHtml(unsafe) {\n return unsafe\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n }\n\n // Used in various places to add a keydown, backspace, etc. to the output\n applyKey(key) {\n switch (key) {\n case 'Enter':\n return '\\n';\n case 'Backspace':\n case 'Delete':\n case 'ControlBackspace':\n return '';\n case ' ':\n return ' ';\n default:\n return !['Shift', 'Ctrl', 'Alt', 'ArrowDown', 'ArrowUp', 'Control', 'ArrowRight',\n 'ArrowLeft', 'Meta', 'CapsLock', 'Tab', 'Escape', 'Delete', 'PageUp', 'PageDown',\n 'Insert', 'Home', 'End', 'NumLock', 'AudioVolumeUp', 'AudioVolumeDown',\n 'MediaPlayPause', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10',\n 'F11', 'F12', 'PrintScreen', 'UnIdentified'].includes(key) ? key : '';\n }\n }\n}\n"],"names":["constructor","elementId","filePath","speed","loop","controllerId","replayInProgress","parseFloat","highlightedChars","deletedChars","cursorPosition","currentEventIndex","totalEvents","currentTime","totalDuration","usercomments","pasteTimestamps","isPasteEvent","isControlKeyPressed","isShiftKeyPressed","isMetaKeyPressed","text","pastedEvents","currentPasteIndex","pastedChars","aiEvents","currentAiIndex","aiChars","undoTimestamps","undoChars","element","document","getElementById","Error","outputElement","loadJSON","then","data","status","processData","this","logData","length","identifyPasteEvents","identifyUndoEvents","constructController","startReplay","handleNoSubmission","catch","error","window","console","message","localStorage","getItem","Str","get_string","str","setItem","log","JSON","parse","comments","Array","isArray","payload","i","event","pastedContent","trim","push","aiContent","unixTimestamp","startTime","map","normalizedTime","html","Promise","all","templates","render","newElement","stopReplay","clearTimeout","replayTimeout","playButton","playSvg","createElement","src","M","util","image_url","querySelector","innerHTML","outerHTML","currentPosition","replayIntervalId","clearInterval","container","controlContainer","buildControllerUI","remove","topRow","classList","add","createPlayButton","appendChild","scrubberContainer","createScrubberContainer","timeDisplay","createTimeDisplay","bottomRow","speedContainer","createSpeedControls","pasteEventsToggle","createPasteEventsToggle","pasteEventsPanel","addEventListener","find","removeClass","addClass","scrubberElement","type","max","min","value","skipToTime","parseInt","textContent","speedLabel","speedGroup","forEach","speedBtn","dataset","querySelectorAll","btn","pasteEventsIcon","pasteIcon","pasteEventsText","pasteEventCount","className","style","marginLeft","chevronIcon","chevron","transition","createPasteEventsPanel","isHidden","display","transform","existingPanel","populatePasteEventsPanel","controlPressed","metaPressed","shiftPressed","pasteCount","toLowerCase","key","timestamp","index","time","formattedTime","formatTime","pastedText","undoCount","panel","noEventsMessage","carouselContainer","navigationRow","counterDisplay","navButtons","prevButton","nextButton","disabled","contentContainer","createPasteEventDisplay","currentIndex","updateDisplay","opacity","pasteEvent","eventRow","headerRow","textContainer","timestampContainer","pastedTextContainer","playIcon","jumpToTimestamp","percentage","setScrubberVal","String","displayTime","Math","methodname","args","filepath","done","response","fail","ms","seconds","floor","minutes","remainingSeconds","toString","padStart","reset","pauseSvg","replayLog","cursor","updatedHighlights","updatedDeleted","undefined","rePosition","processKeydownEvent","processAiInsertEvent","filter","h","expiresAt","d","updateDisplayText","percentComplete","baseIncrement","incrementTime","setTimeout","getLineAndColumn","pos","before","substring","lineIndex","split","col","lastIndexOf","highlights","deletions","targetPosition","handleAiReplacement","currentCursor","insertText","aiWords","isMultiWord","isNewLineInsertion","startsWith","endsWith","wordStart","wordEnd","findWordToReplace","wordToReplace","markCharsAsDeleted","replacedLength","positionDiff","newCursor","calculateNewCursorPosition","updateCharacterIndices","lineStart","lineEnd","findLineRange","lineText","words","extractWordsFromLine","findMultiWordMatch","findSingleWordMatch","start","end","bestMatch","score","wordCount","similarityScore","matchResult","evaluateMultiWordSequence","totalScore","closest","findClosestWord","startIndex","seqWords","j","calculateSequenceSimilarity","calculatePositionScore","compareLength","k","ai","seq","calculateSimilarity","positionScore","seqStart","seqEndPos","aiWord","aiWordLower","bestSimilarityMatch","findBestSimilarityMatch","word","bestPositionMatch","findBestPositionMatch","findWordBoundaryAtPosition","similarity","wordLower","calculateWordScore","distance","abs","chars","updatePastedCharIndices","markCharsAsAiInserted","updateAiCharIndices","p","justAddedIndices","Set","has","str1","str2","len1","len2","matrix","fill","cost","maxLen","minDistance","charToInsert","applyKey","isCopyOperation","isUndoOperation","handleUndoOperation","selection","detectSelection","isPasteOperation","handlePasteOperation","updateModifierStates","isSelectionDeletion","handleSelectionDeletion","processKeyOperation","nextEventIndex","nextEvent","newPosition","textBeforeUndo","handlePasteInsert","resetModifierStates","isCtrlBackspace","handleCtrlBackspace","isCtrlDelete","handleCtrlDelete","isCtrlArrowMove","handleCtrlArrowMove","isRegularBackspace","handleBackspace","isRegularDelete","handleDelete","isArrowUp","handleArrowUp","isArrowDown","handleArrowDown","isRegularArrowMove","handleArrowMove","handleCharacterInsert","eventIndex","currentEvent","currentPos","processDetection","nextPos","shiftPastedCharsIndices","numDeleted","includes","findPreviousWordBoundary","findNextWordBoundary","wordToDelete","lines","prevLine","slice","join","nextLine","lastNonSpace","skipToEnd","textOutput","wasPlaying","targetTime","pasteIndex","aiIndex","highlightMap","deletionMap","pastedMap","aiMap","outOfRangeDeletions","textLines","line","char","isPasted","isAi","isHighlighted","escapeHtml","sort","a","b","cursorHTML","cursorPos","deletedWordHTML","wasScrolledToBottom","scrollHeight","clientHeight","scrollTop","isCursorBelowViewport","cursorElement","cursorRect","getBoundingClientRect","outputRect","bottom","unsafe","replace"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/scatter_chart.min.js b/amd/build/scatter_chart.min.js index 4dcd1cc7..4fc870b1 100644 --- a/amd/build/scatter_chart.min.js +++ b/amd/build/scatter_chart.min.js @@ -6,6 +6,6 @@ define("tiny_cursive/scatter_chart",["exports","core/chartjs","core/str"],(funct * @module tiny_cursive/scatter_chart * @copyright 2025 Cursive Technology, Inc. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_chartjs=(obj=_chartjs)&&obj.__esModule?obj:{default:obj};_exports.init=async(data,apiKey,caption)=>{const ctx=document.getElementById("effortScatterChart").getContext("2d");data&&(data=JSON.parse(document.getElementById("scatter-chart-data").dataset.data));let display=!0,isEmpty="";var dataset=[];const[applyFilter,noSubmission,noPayload,freemium]=await(0,_str.get_strings)([{key:"apply_filter",component:"tiny_cursive"},{key:"no_submission",component:"tiny_cursive"},{key:"nopaylod",component:"tiny_cursive"},{key:"freemium",component:"tiny_cursive"},{key:"chart_result",component:"tiny_cursive"}]);Array.isArray(data)&&!data.state&&apiKey&&(dataset=data,isEmpty=data.some((ds=>Array.isArray(ds.data)&&ds.data.some((point=>point&&"object"==typeof point&&Object.keys(point).length>0))))),apiKey&&0!==data.length&&isEmpty&&!1!==data||(display=!1);const fallbackMessagePlugin={id:"fallbackMessagePlugin",afterDraw(chart){apiKey?"apply_filter"!=data.state?"no_submission"!==data.state?isEmpty||data.state||drawMessage("âš  "+noPayload,chart):drawMessage("âš  "+noSubmission,chart):drawMessage("âš  "+applyFilter,chart):drawMessage("âš  "+freemium,chart)}};function formatTime(value){const minutes=Math.floor(value/60),seconds=value%60;return`${String(minutes).padStart(2,"0")}:${String(seconds).padStart(2,"0")}`}function drawMessage(text,chart){const{ctx:ctx,chartArea:{left:left,right:right,top:top,bottom:bottom}}=chart;ctx.save(),ctx.textAlign="center",ctx.textBaseline="middle",ctx.font='bold 16px "Segoe UI", Arial',ctx.fillStyle="#666";const centerX=(left+right)/2,centerY=(top+bottom)/2;ctx.fillText(text,centerX,centerY),ctx.restore()}new _chartjs.default(ctx,{type:"scatter",data:{datasets:dataset},options:{plugins:{title:{display:display,text:caption,font:{size:16,weight:"bold"},color:"#333",padding:{top:10,bottom:20},align:"center"},legend:{display:!0,position:"bottom",labels:{usePointStyle:!0,pointStyle:"circle",padding:20}},tooltip:{backgroundColor:"rgba(252, 252, 252, 0.8)",titleColor:"#000",bodyColor:"#000",borderColor:"#cccccc",borderWidth:1,displayColors:!1,callbacks:{title:function(context){return context[0].raw.label},label:function(context){const d=context.raw;return[`Time: ${formatTime(d.x)}`,`Effort: ${Math.round(100*d.effort*100)/100}%`,`Words: ${d.words}`,`WPM: ${d.wpm}`]}}}},scales:{x:{title:{display:!0,text:"Time Spent (mm:ss)"},min:0,ticks:{callback:function(value){return formatTime(value)}}},y:{title:{display:!0,text:"Effort Score"},min:0,ticks:{stepSize:.5}}}},plugins:[fallbackMessagePlugin]})}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_chartjs=(obj=_chartjs)&&obj.__esModule?obj:{default:obj};_exports.init=async(data,apiKey,caption)=>{const ctx=document.getElementById("effortScatterChart").getContext("2d");data&&(data=JSON.parse(document.getElementById("scatter-chart-data").dataset.data));let display=!0,isEmpty="";var dataset=[];const[applyFilter,noSubmission,noPayload,freemium]=await(0,_str.get_strings)([{key:"apply_filter",component:"tiny_cursive"},{key:"no_submission",component:"tiny_cursive"},{key:"nopaylod",component:"tiny_cursive"},{key:"freemium",component:"tiny_cursive"},{key:"chart_result",component:"tiny_cursive"}]);Array.isArray(data)&&!data.state&&apiKey&&(dataset=data,isEmpty=data.some((ds=>Array.isArray(ds.data)&&ds.data.some((point=>point&&"object"==typeof point&&Object.keys(point).length>0))))),apiKey&&0!==data.length&&isEmpty&&!1!==data||(display=!1);const fallbackMessagePlugin={id:"fallbackMessagePlugin",afterDraw(chart){apiKey?"apply_filter"!=data.state?"no_submission"!==data.state?isEmpty||data.state||drawMessage("âš  "+noPayload,chart):drawMessage("âš  "+noSubmission,chart):drawMessage("âš  "+applyFilter,chart):drawMessage("âš  "+freemium,chart)}};function formatTime(value){const minutes=Math.floor(value/60),seconds=value%60;return"".concat(String(minutes).padStart(2,"0"),":").concat(String(seconds).padStart(2,"0"))}function drawMessage(text,chart){const{ctx:ctx,chartArea:{left:left,right:right,top:top,bottom:bottom}}=chart;ctx.save(),ctx.textAlign="center",ctx.textBaseline="middle",ctx.font='bold 16px "Segoe UI", Arial',ctx.fillStyle="#666";const centerX=(left+right)/2,centerY=(top+bottom)/2;ctx.fillText(text,centerX,centerY),ctx.restore()}new _chartjs.default(ctx,{type:"scatter",data:{datasets:dataset},options:{plugins:{title:{display:display,text:caption,font:{size:16,weight:"bold"},color:"#333",padding:{top:10,bottom:20},align:"center"},legend:{display:!0,position:"bottom",labels:{usePointStyle:!0,pointStyle:"circle",padding:20}},tooltip:{backgroundColor:"rgba(252, 252, 252, 0.8)",titleColor:"#000",bodyColor:"#000",borderColor:"#cccccc",borderWidth:1,displayColors:!1,callbacks:{title:function(context){return context[0].raw.label},label:function(context){const d=context.raw;return["Time: ".concat(formatTime(d.x)),"Effort: ".concat(Math.round(100*d.effort*100)/100,"%"),"Words: ".concat(d.words),"WPM: ".concat(d.wpm)]}}}},scales:{x:{title:{display:!0,text:"Time Spent (mm:ss)"},min:0,ticks:{callback:function(value){return formatTime(value)}}},y:{title:{display:!0,text:"Effort Score"},min:0,ticks:{stepSize:.5}}}},plugins:[fallbackMessagePlugin]})}})); //# sourceMappingURL=scatter_chart.min.js.map \ No newline at end of file diff --git a/amd/build/scatter_chart.min.js.map b/amd/build/scatter_chart.min.js.map index 8b434228..b072c128 100644 --- a/amd/build/scatter_chart.min.js.map +++ b/amd/build/scatter_chart.min.js.map @@ -1 +1 @@ -{"version":3,"file":"scatter_chart.min.js","sources":["../src/scatter_chart.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A module that creates a scatter chart to visualize student effort data using Chart.js.\n * The chart displays effort scores against time spent, with tooltips showing additional metrics.\n *\n * @module tiny_cursive/scatter_chart\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Chart from 'core/chartjs';\nimport {get_strings as getStrings} from 'core/str';\nexport const init = async(data, apiKey, caption) => {\n\n const ctx = document.getElementById('effortScatterChart').getContext('2d');\n if (data) {\n data = JSON.parse(document.getElementById('scatter-chart-data').dataset.data);\n }\n\n let display = true;\n let isEmpty = \"\";\n var dataset = [];\n\n const [\n applyFilter,\n noSubmission,\n noPayload,\n freemium,\n ] = await getStrings([\n {key: 'apply_filter', component: 'tiny_cursive'},\n {key: 'no_submission', component: 'tiny_cursive'},\n {key: 'nopaylod', component: 'tiny_cursive'},\n {key: 'freemium', component: 'tiny_cursive'},\n {key: 'chart_result', component: 'tiny_cursive'}\n ]);\n\n if (Array.isArray(data) && !data.state && apiKey) {\n dataset = data;\n isEmpty = data.some(ds =>\n Array.isArray(ds.data) &&\n ds.data.some(point =>\n point && typeof point === 'object' && Object.keys(point).length > 0\n )\n );\n }\n\n if (!apiKey || data.length === 0 || !isEmpty || data === false) {\n display = false;\n }\n\n const fallbackMessagePlugin = {\n id: 'fallbackMessagePlugin',\n afterDraw(chart) {\n // âš  Case 1: Freemium user\n if (!apiKey) {\n drawMessage('âš  ' + freemium, chart);\n return;\n }\n // âš  Case 2: Apply filter (data is empty array)\n if (data.state == \"apply_filter\") {\n drawMessage('âš  ' + applyFilter, chart);\n return;\n }\n if (data.state === \"no_submission\") {\n drawMessage('âš  ' + noSubmission, chart);\n return;\n }\n // âš  Case 3: No payload data (all `data` arrays are empty or full of empty objects)\n if (!isEmpty && !data.state) {\n drawMessage('âš  ' + noPayload, chart);\n }\n\n }\n };\n\n new Chart(ctx, {\n type: 'scatter',\n data: {\n datasets: dataset,\n },\n options: {\n plugins: {\n title: {\n display: display,\n text: caption,\n font: {\n size: 16,\n weight: 'bold',\n },\n color: '#333',\n padding: {\n top: 10,\n bottom: 20\n },\n align: 'center'\n },\n legend: {\n display: true,\n position: 'bottom',\n labels: {\n usePointStyle: true,\n pointStyle: 'circle',\n padding: 20\n }\n },\n tooltip: {\n backgroundColor: 'rgba(252, 252, 252, 0.8)',\n titleColor: '#000',\n bodyColor: '#000',\n borderColor: '#cccccc',\n borderWidth: 1,\n displayColors: false,\n callbacks: {\n title: function(context) {\n const d = context[0].raw;\n return d.label; // This appears as bold title.\n },\n label: function(context) {\n const d = context.raw;\n return [\n `Time: ${formatTime(d.x)}`,\n `Effort: ${Math.round(d.effort * 100 * 100) / 100}%`,\n `Words: ${d.words}`,\n `WPM: ${d.wpm}`\n ];\n }\n }\n }\n },\n scales: {\n x: {\n title: {\n display: true,\n text: 'Time Spent (mm:ss)'\n },\n min: 0,\n ticks: {\n callback: function(value) {\n return formatTime(value);\n }\n }\n },\n y: {\n title: {\n display: true,\n text: 'Effort Score'\n },\n min: 0,\n ticks: {\n stepSize: 0.5\n }\n }\n }\n },\n plugins: [fallbackMessagePlugin]\n });\n\n /**\n * Formats a time value in seconds to a mm:ss string format\n * @param {number} value - The time value in seconds\n * @returns {string} The formatted time string in mm:ss format\n */\n function formatTime(value) {\n const minutes = Math.floor(value / 60);\n const seconds = value % 60;\n return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;\n }\n\n /**\n * Draws a message on the chart canvas\n * @param {string} text - The message to be displayed\n * @param {Chart} chart - The Chart.js chart object\n */\n function drawMessage(text, chart) {\n\n const {ctx, chartArea: {left, right, top, bottom}} = chart;\n ctx.save();\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.font = 'bold 16px \"Segoe UI\", Arial';\n ctx.fillStyle = '#666';\n\n const centerX = (left + right) / 2;\n const centerY = (top + bottom) / 2;\n\n ctx.fillText(text, centerX, centerY);\n ctx.restore();\n }\n};"],"names":["async","data","apiKey","caption","ctx","document","getElementById","getContext","JSON","parse","dataset","display","isEmpty","applyFilter","noSubmission","noPayload","freemium","key","component","Array","isArray","state","some","ds","point","Object","keys","length","fallbackMessagePlugin","id","afterDraw","chart","drawMessage","formatTime","value","minutes","Math","floor","seconds","String","padStart","text","chartArea","left","right","top","bottom","save","textAlign","textBaseline","font","fillStyle","centerX","centerY","fillText","restore","Chart","type","datasets","options","plugins","title","size","weight","color","padding","align","legend","position","labels","usePointStyle","pointStyle","tooltip","backgroundColor","titleColor","bodyColor","borderColor","borderWidth","displayColors","callbacks","context","raw","label","d","x","round","effort","words","wpm","scales","min","ticks","callback","y","stepSize"],"mappings":";;;;;;;;0JA0BoBA,MAAMC,KAAMC,OAAQC,iBAE9BC,IAAMC,SAASC,eAAe,sBAAsBC,WAAW,MACjEN,OACAA,KAAOO,KAAKC,MAAMJ,SAASC,eAAe,sBAAsBI,QAAQT,WAGxEU,SAAU,EACVC,QAAU,OACVF,QAAU,SAGVG,YACAC,aACAC,UACAC,gBACM,oBAAW,CACjB,CAACC,IAAK,eAAgBC,UAAW,gBACjC,CAACD,IAAK,gBAAiBC,UAAW,gBAClC,CAACD,IAAK,WAAYC,UAAW,gBAC7B,CAACD,IAAK,WAAYC,UAAW,gBAC7B,CAACD,IAAK,eAAgBC,UAAW,kBAGjCC,MAAMC,QAAQnB,QAAUA,KAAKoB,OAASnB,SACtCQ,QAAUT,KACVW,QAAUX,KAAKqB,MAAKC,IAChBJ,MAAMC,QAAQG,GAAGtB,OACjBsB,GAAGtB,KAAKqB,MAAKE,OACTA,OAA0B,iBAAVA,OAAsBC,OAAOC,KAAKF,OAAOG,OAAS,OAKzEzB,QAA0B,IAAhBD,KAAK0B,QAAiBf,UAAoB,IAATX,OAC5CU,SAAU,SAGRiB,sBAAwB,CAC1BC,GAAI,wBACJC,UAAUC,OAED7B,OAKa,gBAAdD,KAAKoB,MAIU,kBAAfpB,KAAKoB,MAKJT,SAAYX,KAAKoB,OAClBW,YAAY,KAAOjB,UAAWgB,OAL9BC,YAAY,KAAOlB,aAAciB,OAJjCC,YAAY,KAAOnB,YAAakB,OALhCC,YAAY,KAAOhB,SAAUe,kBA2GhCE,WAAWC,aACVC,QAAUC,KAAKC,MAAMH,MAAQ,IAC7BI,QAAUJ,MAAQ,SAChB,GAAEK,OAAOJ,SAASK,SAAS,EAAG,QAAQD,OAAOD,SAASE,SAAS,EAAG,gBAQrER,YAAYS,KAAMV,aAEjB3B,IAACA,IAAKsC,WAAWC,KAACA,KAADC,MAAOA,MAAPC,IAAcA,IAAdC,OAAmBA,SAAWf,MACrD3B,IAAI2C,OACJ3C,IAAI4C,UAAY,SAChB5C,IAAI6C,aAAe,SACnB7C,IAAI8C,KAAO,8BACX9C,IAAI+C,UAAY,aAEVC,SAAWT,KAAOC,OAAS,EAC3BS,SAAWR,IAAMC,QAAU,EAEjC1C,IAAIkD,SAASb,KAAMW,QAASC,SAC5BjD,IAAImD,cA/GJC,iBAAMpD,IAAK,CACXqD,KAAM,UACNxD,KAAM,CACFyD,SAAUhD,SAEdiD,QAAS,CACLC,QAAS,CACLC,MAAO,CACHlD,QAASA,QACT8B,KAAMtC,QACN+C,KAAM,CACFY,KAAM,GACNC,OAAQ,QAEZC,MAAO,OACPC,QAAS,CACLpB,IAAK,GACLC,OAAQ,IAEZoB,MAAO,UAEXC,OAAQ,CACJxD,SAAS,EACTyD,SAAU,SACVC,OAAQ,CACJC,eAAe,EACfC,WAAY,SACZN,QAAS,KAGjBO,QAAS,CACLC,gBAAiB,2BACjBC,WAAY,OACZC,UAAW,OACXC,YAAa,UACbC,YAAa,EACbC,eAAe,EACfC,UAAW,CACPlB,MAAO,SAASmB,gBACFA,QAAQ,GAAGC,IACZC,OAEbA,MAAO,SAASF,eACNG,EAAIH,QAAQC,UACX,CACF,SAAQhD,WAAWkD,EAAEC,KACrB,WAAUhD,KAAKiD,MAAiB,IAAXF,EAAEG,OAAe,KAAO,OAC7C,UAASH,EAAEI,QACX,QAAOJ,EAAEK,WAM9BC,OAAQ,CACJL,EAAG,CACCvB,MAAO,CACHlD,SAAS,EACT8B,KAAM,sBAEViD,IAAK,EACLC,MAAO,CACHC,SAAU,SAAS1D,cACRD,WAAWC,UAI9B2D,EAAG,CACChC,MAAO,CACHlD,SAAS,EACT8B,KAAM,gBAEViD,IAAK,EACLC,MAAO,CACHG,SAAU,OAK1BlC,QAAS,CAAChC"} \ No newline at end of file +{"version":3,"file":"scatter_chart.min.js","sources":["../src/scatter_chart.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A module that creates a scatter chart to visualize student effort data using Chart.js.\n * The chart displays effort scores against time spent, with tooltips showing additional metrics.\n *\n * @module tiny_cursive/scatter_chart\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Chart from 'core/chartjs';\nimport {get_strings as getStrings} from 'core/str';\nexport const init = async(data, apiKey, caption) => {\n\n const ctx = document.getElementById('effortScatterChart').getContext('2d');\n if (data) {\n data = JSON.parse(document.getElementById('scatter-chart-data').dataset.data);\n }\n\n let display = true;\n let isEmpty = \"\";\n var dataset = [];\n\n const [\n applyFilter,\n noSubmission,\n noPayload,\n freemium,\n ] = await getStrings([\n {key: 'apply_filter', component: 'tiny_cursive'},\n {key: 'no_submission', component: 'tiny_cursive'},\n {key: 'nopaylod', component: 'tiny_cursive'},\n {key: 'freemium', component: 'tiny_cursive'},\n {key: 'chart_result', component: 'tiny_cursive'}\n ]);\n\n if (Array.isArray(data) && !data.state && apiKey) {\n dataset = data;\n isEmpty = data.some(ds =>\n Array.isArray(ds.data) &&\n ds.data.some(point =>\n point && typeof point === 'object' && Object.keys(point).length > 0\n )\n );\n }\n\n if (!apiKey || data.length === 0 || !isEmpty || data === false) {\n display = false;\n }\n\n const fallbackMessagePlugin = {\n id: 'fallbackMessagePlugin',\n afterDraw(chart) {\n // âš  Case 1: Freemium user\n if (!apiKey) {\n drawMessage('âš  ' + freemium, chart);\n return;\n }\n // âš  Case 2: Apply filter (data is empty array)\n if (data.state == \"apply_filter\") {\n drawMessage('âš  ' + applyFilter, chart);\n return;\n }\n if (data.state === \"no_submission\") {\n drawMessage('âš  ' + noSubmission, chart);\n return;\n }\n // âš  Case 3: No payload data (all `data` arrays are empty or full of empty objects)\n if (!isEmpty && !data.state) {\n drawMessage('âš  ' + noPayload, chart);\n }\n\n }\n };\n\n new Chart(ctx, {\n type: 'scatter',\n data: {\n datasets: dataset,\n },\n options: {\n plugins: {\n title: {\n display: display,\n text: caption,\n font: {\n size: 16,\n weight: 'bold',\n },\n color: '#333',\n padding: {\n top: 10,\n bottom: 20\n },\n align: 'center'\n },\n legend: {\n display: true,\n position: 'bottom',\n labels: {\n usePointStyle: true,\n pointStyle: 'circle',\n padding: 20\n }\n },\n tooltip: {\n backgroundColor: 'rgba(252, 252, 252, 0.8)',\n titleColor: '#000',\n bodyColor: '#000',\n borderColor: '#cccccc',\n borderWidth: 1,\n displayColors: false,\n callbacks: {\n title: function(context) {\n const d = context[0].raw;\n return d.label; // This appears as bold title.\n },\n label: function(context) {\n const d = context.raw;\n return [\n `Time: ${formatTime(d.x)}`,\n `Effort: ${Math.round(d.effort * 100 * 100) / 100}%`,\n `Words: ${d.words}`,\n `WPM: ${d.wpm}`\n ];\n }\n }\n }\n },\n scales: {\n x: {\n title: {\n display: true,\n text: 'Time Spent (mm:ss)'\n },\n min: 0,\n ticks: {\n callback: function(value) {\n return formatTime(value);\n }\n }\n },\n y: {\n title: {\n display: true,\n text: 'Effort Score'\n },\n min: 0,\n ticks: {\n stepSize: 0.5\n }\n }\n }\n },\n plugins: [fallbackMessagePlugin]\n });\n\n /**\n * Formats a time value in seconds to a mm:ss string format\n * @param {number} value - The time value in seconds\n * @returns {string} The formatted time string in mm:ss format\n */\n function formatTime(value) {\n const minutes = Math.floor(value / 60);\n const seconds = value % 60;\n return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;\n }\n\n /**\n * Draws a message on the chart canvas\n * @param {string} text - The message to be displayed\n * @param {Chart} chart - The Chart.js chart object\n */\n function drawMessage(text, chart) {\n\n const {ctx, chartArea: {left, right, top, bottom}} = chart;\n ctx.save();\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.font = 'bold 16px \"Segoe UI\", Arial';\n ctx.fillStyle = '#666';\n\n const centerX = (left + right) / 2;\n const centerY = (top + bottom) / 2;\n\n ctx.fillText(text, centerX, centerY);\n ctx.restore();\n }\n};"],"names":["async","data","apiKey","caption","ctx","document","getElementById","getContext","JSON","parse","dataset","display","isEmpty","applyFilter","noSubmission","noPayload","freemium","key","component","Array","isArray","state","some","ds","point","Object","keys","length","fallbackMessagePlugin","id","afterDraw","chart","drawMessage","formatTime","value","minutes","Math","floor","seconds","String","padStart","text","chartArea","left","right","top","bottom","save","textAlign","textBaseline","font","fillStyle","centerX","centerY","fillText","restore","Chart","type","datasets","options","plugins","title","size","weight","color","padding","align","legend","position","labels","usePointStyle","pointStyle","tooltip","backgroundColor","titleColor","bodyColor","borderColor","borderWidth","displayColors","callbacks","context","raw","label","d","x","round","effort","words","wpm","scales","min","ticks","callback","y","stepSize"],"mappings":";;;;;;;;0JA0BoBA,MAAMC,KAAMC,OAAQC,iBAE9BC,IAAMC,SAASC,eAAe,sBAAsBC,WAAW,MACjEN,OACAA,KAAOO,KAAKC,MAAMJ,SAASC,eAAe,sBAAsBI,QAAQT,WAGxEU,SAAU,EACVC,QAAU,OACVF,QAAU,SAGVG,YACAC,aACAC,UACAC,gBACM,oBAAW,CACjB,CAACC,IAAK,eAAgBC,UAAW,gBACjC,CAACD,IAAK,gBAAiBC,UAAW,gBAClC,CAACD,IAAK,WAAYC,UAAW,gBAC7B,CAACD,IAAK,WAAYC,UAAW,gBAC7B,CAACD,IAAK,eAAgBC,UAAW,kBAGjCC,MAAMC,QAAQnB,QAAUA,KAAKoB,OAASnB,SACtCQ,QAAUT,KACVW,QAAUX,KAAKqB,MAAKC,IAChBJ,MAAMC,QAAQG,GAAGtB,OACjBsB,GAAGtB,KAAKqB,MAAKE,OACTA,OAA0B,iBAAVA,OAAsBC,OAAOC,KAAKF,OAAOG,OAAS,OAKzEzB,QAA0B,IAAhBD,KAAK0B,QAAiBf,UAAoB,IAATX,OAC5CU,SAAU,SAGRiB,sBAAwB,CAC1BC,GAAI,wBACJC,UAAUC,OAED7B,OAKa,gBAAdD,KAAKoB,MAIU,kBAAfpB,KAAKoB,MAKJT,SAAYX,KAAKoB,OAClBW,YAAY,KAAOjB,UAAWgB,OAL9BC,YAAY,KAAOlB,aAAciB,OAJjCC,YAAY,KAAOnB,YAAakB,OALhCC,YAAY,KAAOhB,SAAUe,kBA2GhCE,WAAWC,aACVC,QAAUC,KAAKC,MAAMH,MAAQ,IAC7BI,QAAUJ,MAAQ,mBACdK,OAAOJ,SAASK,SAAS,EAAG,iBAAQD,OAAOD,SAASE,SAAS,EAAG,eAQrER,YAAYS,KAAMV,aAEjB3B,IAACA,IAAKsC,WAAWC,KAACA,KAADC,MAAOA,MAAPC,IAAcA,IAAdC,OAAmBA,SAAWf,MACrD3B,IAAI2C,OACJ3C,IAAI4C,UAAY,SAChB5C,IAAI6C,aAAe,SACnB7C,IAAI8C,KAAO,8BACX9C,IAAI+C,UAAY,aAEVC,SAAWT,KAAOC,OAAS,EAC3BS,SAAWR,IAAMC,QAAU,EAEjC1C,IAAIkD,SAASb,KAAMW,QAASC,SAC5BjD,IAAImD,cA/GJC,iBAAMpD,IAAK,CACXqD,KAAM,UACNxD,KAAM,CACFyD,SAAUhD,SAEdiD,QAAS,CACLC,QAAS,CACLC,MAAO,CACHlD,QAASA,QACT8B,KAAMtC,QACN+C,KAAM,CACFY,KAAM,GACNC,OAAQ,QAEZC,MAAO,OACPC,QAAS,CACLpB,IAAK,GACLC,OAAQ,IAEZoB,MAAO,UAEXC,OAAQ,CACJxD,SAAS,EACTyD,SAAU,SACVC,OAAQ,CACJC,eAAe,EACfC,WAAY,SACZN,QAAS,KAGjBO,QAAS,CACLC,gBAAiB,2BACjBC,WAAY,OACZC,UAAW,OACXC,YAAa,UACbC,YAAa,EACbC,eAAe,EACfC,UAAW,CACPlB,MAAO,SAASmB,gBACFA,QAAQ,GAAGC,IACZC,OAEbA,MAAO,SAASF,eACNG,EAAIH,QAAQC,UACX,iBACMhD,WAAWkD,EAAEC,sBACXhD,KAAKiD,MAAiB,IAAXF,EAAEG,OAAe,KAAO,0BACpCH,EAAEI,sBACJJ,EAAEK,UAM9BC,OAAQ,CACJL,EAAG,CACCvB,MAAO,CACHlD,SAAS,EACT8B,KAAM,sBAEViD,IAAK,EACLC,MAAO,CACHC,SAAU,SAAS1D,cACRD,WAAWC,UAI9B2D,EAAG,CACChC,MAAO,CACHlD,SAAS,EACT8B,KAAM,gBAEViD,IAAK,EACLC,MAAO,CACHG,SAAU,OAK1BlC,QAAS,CAAChC"} \ No newline at end of file diff --git a/amd/build/svg_repo.min.js b/amd/build/svg_repo.min.js index ef22ae26..963b7faa 100644 --- a/amd/build/svg_repo.min.js +++ b/amd/build/svg_repo.min.js @@ -1,11 +1,3 @@ -define("tiny_cursive/svg_repo",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0; -/** - * SVG repository for icons used in the Curs - * - * @module tiny_cursive/svg_repo - * @copyright 2025 Cursive Technology, Inc. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -var _default={people:'\n \n \n \n \n ',assignment:'\n \n \n \n ',time:'\n \n \n ',offline:'',forum:'',close:'\n \n ',hamburger:'\n \n ',quiz:'',cloudSave:'',lesson:''};return _exports.default=_default,_exports.default})); +define("tiny_cursive/svg_repo",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={people:'\n \n \n \n \n ',assignment:'\n \n \n \n ',time:'\n \n \n ',offline:'',forum:'',close:'\n \n ',hamburger:'\n \n ',quiz:'',cloudSave:'',lesson:''},_exports.default})); //# sourceMappingURL=svg_repo.min.js.map \ No newline at end of file diff --git a/amd/build/svg_repo.min.js.map b/amd/build/svg_repo.min.js.map index 5b3ccebe..4fbf06a2 100644 --- a/amd/build/svg_repo.min.js.map +++ b/amd/build/svg_repo.min.js.map @@ -1 +1 @@ -{"version":3,"file":"svg_repo.min.js","sources":["../src/svg_repo.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * SVG repository for icons used in the Curs\n *\n * @module tiny_cursive/svg_repo\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n people: `\n \n \n \n \n `,\n assignment: `\n \n \n \n `,\n time: `\n \n \n `,\n offline: ``,\n forum: ``,\n close: `\n \n `,\n hamburger: `\n \n `,\n quiz: ``,\n cloudSave: ``,\n lesson: ``\n};\n"],"names":["people","assignment","time","offline","forum","close","hamburger","quiz","cloudSave","lesson"],"mappings":";;;;;;;;aAuBe,CACbA,OAAS,ijBASTC,WAAa,siBAQbC,KAAO,qaAOPC,QAAU,qgBAMVC,MAAQ,mtDAeNC,MAAQ,85BAQRC,UAAY,6jBAKZC,KAAO,ksFAuBPC,UAAY,qzBAOZC,OAAS"} \ No newline at end of file +{"version":3,"file":"svg_repo.min.js","sources":["../src/svg_repo.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * SVG repository for icons used in the Curs\n *\n * @module tiny_cursive/svg_repo\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n people: `\n \n \n \n \n `,\n assignment: `\n \n \n \n `,\n time: `\n \n \n `,\n offline: ``,\n forum: ``,\n close: `\n \n `,\n hamburger: `\n \n `,\n quiz: ``,\n cloudSave: ``,\n lesson: ``\n};\n"],"names":["people","assignment","time","offline","forum","close","hamburger","quiz","cloudSave","lesson"],"mappings":"uKAuBe,CACbA,wjBASAC,ijBAQAC,0aAOAC,6gBAMAC,ytDAeEC,o6BAQAC,ukBAKAC,usFAuBAC,+zBAOAC"} \ No newline at end of file diff --git a/amd/build/texteditor.min.js b/amd/build/texteditor.min.js index d78e256c..3e4318a3 100644 --- a/amd/build/texteditor.min.js +++ b/amd/build/texteditor.min.js @@ -6,6 +6,6 @@ define("tiny_cursive/texteditor",["exports","core/config"],(function(_exports,Co * @copyright 2022 CTI * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -let tinyMCEPromise;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getTinyMCE=_exports.baseUrl=void 0;const baseUrl=`${(Config=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Config)).wwwroot}/lib/editor/tiny/loader.php/${M.cfg.jsrev}`;_exports.baseUrl=baseUrl;_exports.getTinyMCE=()=>tinyMCEPromise||(tinyMCEPromise=new Promise(((resolve,reject)=>{const head=document.querySelector("head");let script=head.querySelector('script[data-tinymce="tinymce"]');script&&resolve(window.tinyMCE),script=document.createElement("script"),script.dataset.tinymce="tinymce",script.src=`${baseUrl}/tinymce.js`,script.async=!0,script.addEventListener("load",(()=>{resolve(window.tinyMCE)}),!1),script.addEventListener("error",(err=>{reject(err)}),!1),head.append(script)})),tinyMCEPromise)})); +let tinyMCEPromise;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getTinyMCE=_exports.baseUrl=void 0,Config=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Config);const baseUrl="".concat(Config.wwwroot,"/lib/editor/tiny/loader.php/").concat(M.cfg.jsrev);_exports.baseUrl=baseUrl;_exports.getTinyMCE=()=>tinyMCEPromise||(tinyMCEPromise=new Promise(((resolve,reject)=>{const head=document.querySelector("head");let script=head.querySelector('script[data-tinymce="tinymce"]');script&&resolve(window.tinyMCE),script=document.createElement("script"),script.dataset.tinymce="tinymce",script.src="".concat(baseUrl,"/tinymce.js"),script.async=!0,script.addEventListener("load",(()=>{resolve(window.tinyMCE)}),!1),script.addEventListener("error",(err=>{reject(err)}),!1),head.append(script)})),tinyMCEPromise)})); //# sourceMappingURL=texteditor.min.js.map \ No newline at end of file diff --git a/amd/build/texteditor.min.js.map b/amd/build/texteditor.min.js.map index 053cad8b..cd6b3f19 100644 --- a/amd/build/texteditor.min.js.map +++ b/amd/build/texteditor.min.js.map @@ -1 +1 @@ -{"version":3,"file":"texteditor.min.js","sources":["../src/texteditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Tiny Loader for Moodle\n *\n * @module tiny_cursive/texteditor\n * @copyright 2022 CTI \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nlet tinyMCEPromise;\n\nimport * as Config from 'core/config';\n\nexport const baseUrl = `${Config.wwwroot}/lib/editor/tiny/loader.php/${M.cfg.jsrev}`;\n\n/**\n * Get the TinyMCE API Object.\n *\n * @returns {Promise} The TinyMCE API Object\n */\nexport const getTinyMCE = () => {\n if (tinyMCEPromise) {\n return tinyMCEPromise;\n }\n\n tinyMCEPromise = new Promise((resolve, reject) => {\n const head = document.querySelector('head');\n let script = head.querySelector('script[data-tinymce=\"tinymce\"]');\n if (script) {\n resolve(window.tinyMCE);\n }\n\n script = document.createElement('script');\n script.dataset.tinymce = 'tinymce';\n script.src = `${baseUrl}/tinymce.js`;\n script.async = true;\n\n script.addEventListener('load', () => {\n resolve(window.tinyMCE);\n }, false);\n\n script.addEventListener('error', (err) => {\n reject(err);\n }, false);\n\n head.append(script);\n });\n\n return tinyMCEPromise;\n\n};\n\n\n"],"names":["tinyMCEPromise","baseUrl","wwwroot","M","cfg","jsrev","Promise","resolve","reject","head","document","querySelector","script","window","tinyMCE","createElement","dataset","tinymce","src","async","addEventListener","err","append"],"mappings":";;;;;;;;IAuBIA,yHAISC,QAAW,iqBAASC,sCAAsCC,EAAEC,IAAIC,qDAOnD,IAClBL,iBAIJA,eAAiB,IAAIM,SAAQ,CAACC,QAASC,gBAC7BC,KAAOC,SAASC,cAAc,YAChCC,OAASH,KAAKE,cAAc,kCAC5BC,QACAL,QAAQM,OAAOC,SAGnBF,OAASF,SAASK,cAAc,UAChCH,OAAOI,QAAQC,QAAU,UACzBL,OAAOM,IAAO,GAAEjB,qBAChBW,OAAOO,OAAQ,EAEfP,OAAOQ,iBAAiB,QAAQ,KAC5Bb,QAAQM,OAAOC,YAChB,GAEHF,OAAOQ,iBAAiB,SAAUC,MAC9Bb,OAAOa,QACR,GAEHZ,KAAKa,OAAOV,WAGTZ"} \ No newline at end of file +{"version":3,"file":"texteditor.min.js","sources":["../src/texteditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Tiny Loader for Moodle\n *\n * @module tiny_cursive/texteditor\n * @copyright 2022 CTI \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nlet tinyMCEPromise;\n\nimport * as Config from 'core/config';\n\nexport const baseUrl = `${Config.wwwroot}/lib/editor/tiny/loader.php/${M.cfg.jsrev}`;\n\n/**\n * Get the TinyMCE API Object.\n *\n * @returns {Promise} The TinyMCE API Object\n */\nexport const getTinyMCE = () => {\n if (tinyMCEPromise) {\n return tinyMCEPromise;\n }\n\n tinyMCEPromise = new Promise((resolve, reject) => {\n const head = document.querySelector('head');\n let script = head.querySelector('script[data-tinymce=\"tinymce\"]');\n if (script) {\n resolve(window.tinyMCE);\n }\n\n script = document.createElement('script');\n script.dataset.tinymce = 'tinymce';\n script.src = `${baseUrl}/tinymce.js`;\n script.async = true;\n\n script.addEventListener('load', () => {\n resolve(window.tinyMCE);\n }, false);\n\n script.addEventListener('error', (err) => {\n reject(err);\n }, false);\n\n head.append(script);\n });\n\n return tinyMCEPromise;\n\n};\n\n\n"],"names":["tinyMCEPromise","baseUrl","Config","wwwroot","M","cfg","jsrev","Promise","resolve","reject","head","document","querySelector","script","window","tinyMCE","createElement","dataset","tinymce","src","async","addEventListener","err","append"],"mappings":";;;;;;;;IAuBIA,qxBAISC,kBAAaC,OAAOC,+CAAsCC,EAAEC,IAAIC,oDAOnD,IAClBN,iBAIJA,eAAiB,IAAIO,SAAQ,CAACC,QAASC,gBAC7BC,KAAOC,SAASC,cAAc,YAChCC,OAASH,KAAKE,cAAc,kCAC5BC,QACAL,QAAQM,OAAOC,SAGnBF,OAASF,SAASK,cAAc,UAChCH,OAAOI,QAAQC,QAAU,UACzBL,OAAOM,cAASlB,uBAChBY,OAAOO,OAAQ,EAEfP,OAAOQ,iBAAiB,QAAQ,KAC5Bb,QAAQM,OAAOC,YAChB,GAEHF,OAAOQ,iBAAiB,SAAUC,MAC9Bb,OAAOa,QACR,GAEHZ,KAAKa,OAAOV,WAGTb"} \ No newline at end of file diff --git a/amd/build/token_approve.min.js b/amd/build/token_approve.min.js index 96a5ed03..20e600ba 100644 --- a/amd/build/token_approve.min.js +++ b/amd/build/token_approve.min.js @@ -1,3 +1,3 @@ -define("tiny_cursive/token_approve",["jquery","core/ajax","core/str"],(function($,AJAX,str){var usersTable={init:function(page){str.get_strings([{key:"field_require",component:"tiny_cursive"}]).done((function(){usersTable.getToken(page),usersTable.generateToken()}))},getToken:function(){$("#approve_token").click((function(){var token=$("#id_s_tiny_cursive_secretkey").val();AJAX.call([{methodname:"cursive_approve_token",args:{token:token}}])[0].done((function(json){var data=JSON.parse(json),messageAlert="";messageAlert=1==data.status?""+data.message+"":""+data.message+"",$("#token_message").html(messageAlert)}))}))},generateToken(){const generateToken=$("#generate_cursivetoken"),cursiveDisable=$("#cursivedisable"),cursiveEnable=$("#cursiveenable");generateToken.on("click",(function(e){e.preventDefault();var promise1=AJAX.call([{methodname:"cursive_generate_webtoken",args:[]}]);promise1[0].done((function(data){var messageAlert="";str.get_strings([{key:"webservtokengensucc",component:"tiny_cursive"},{key:"webservtokengenfail",component:"tiny_cursive"}]).then((function(_ref){let[success,fail]=_ref;return data.token?($("#id_s_tiny_cursive_cursivetoken").val(data.token),messageAlert=`${success}`):messageAlert=`${fail}`,$("#cursivetoken_").html(messageAlert),setTimeout((()=>{$("#cursivetoken_").empty()}),3e3),!0})).catch((error=>window.console.error(error)))})),promise1[0].fail((function(textStatus){var errorMessage="";str.get_string("webservtokenerror","tiny_cursive").then((str=>(errorMessage+=str+" "+textStatus.error+"",$("#cursivetoken_").html(errorMessage),setTimeout((function(){$("#cursivetoken_").empty()}),3e3),!0))).catch((error=>window.console.error(error)))}))})),cursiveDisable.on("click",(function(e){e.preventDefault();var promise1=AJAX.call([{methodname:"cursive_disable_all_course",args:{disable:!0}}]);promise1[0].done((function(data){var messageAlert="";str.get_strings([{key:"cursive:dis:succ",component:"tiny_cursive"},{key:"cursive:dis:fail",component:"tiny_cursive"}]).then((function(_ref2){let[success,fail]=_ref2;return messageAlert=data?`${success}`:`${fail}`,$("#cursivedisable_").html(messageAlert),setTimeout((()=>{$("#cursivedisable_").empty()}),3e3),!0})).catch((error=>window.console.error(error)))})),promise1[0].fail((function(textStatus){var errorMessage="";str.get_string("cursive:status","tiny_cursive").then((str=>(errorMessage+=str+" "+textStatus.error+"",$("#cursivedisable_").html(errorMessage),setTimeout((function(){$("#cursivedisable_").empty()}),3e3),!0))).catch((error=>window.console.error(error)))}))})),cursiveEnable.on("click",(function(e){e.preventDefault();var promise1=AJAX.call([{methodname:"cursive_disable_all_course",args:{disable:!1}}]);promise1[0].done((function(data){var messageAlert="";str.get_strings([{key:"cursive:ena:succ",component:"tiny_cursive"},{key:"cursive:ena:fail",component:"tiny_cursive"}]).then((function(_ref3){let[success,fail]=_ref3;return messageAlert=data?`${success}`:`${fail}`,$("#cursivedisable_").html(messageAlert),setTimeout((()=>{$("#cursivedisable_").empty()}),3e3),!0})).catch((error=>window.console.error(error)))})),promise1[0].fail((function(textStatus){var errorMessage="";str.get_string("cursive:status","tiny_cursive").then((str=>(errorMessage+=str+" "+textStatus.error+"",$("#cursivedisable_").html(errorMessage),setTimeout((function(){$("#cursivedisable_").empty()}),3e3),!0))).catch((error=>window.console.error(error)))}))}))}};return usersTable})); +define("tiny_cursive/token_approve",["jquery","core/ajax","core/str"],(function($,AJAX,str){var usersTable={init:function(page){str.get_strings([{key:"field_require",component:"tiny_cursive"}]).done((function(){usersTable.getToken(page),usersTable.generateToken()}))},getToken:function(){$("#approve_token").click((function(){var token=$("#id_s_tiny_cursive_secretkey").val();AJAX.call([{methodname:"cursive_approve_token",args:{token:token}}])[0].done((function(json){var data=JSON.parse(json),messageAlert="";messageAlert=1==data.status?""+data.message+"":""+data.message+"",$("#token_message").html(messageAlert)}))}))},generateToken(){const generateToken=$("#generate_cursivetoken"),cursiveDisable=$("#cursivedisable"),cursiveEnable=$("#cursiveenable");generateToken.on("click",(function(e){e.preventDefault();var promise1=AJAX.call([{methodname:"cursive_generate_webtoken",args:[]}]);promise1[0].done((function(data){var messageAlert="";str.get_strings([{key:"webservtokengensucc",component:"tiny_cursive"},{key:"webservtokengenfail",component:"tiny_cursive"}]).then((function(_ref){let[success,fail]=_ref;return data.token?($("#id_s_tiny_cursive_cursivetoken").val(data.token),messageAlert="".concat(success,"")):messageAlert="".concat(fail,""),$("#cursivetoken_").html(messageAlert),setTimeout((()=>{$("#cursivetoken_").empty()}),3e3),!0})).catch((error=>window.console.error(error)))})),promise1[0].fail((function(textStatus){var errorMessage="";str.get_string("webservtokenerror","tiny_cursive").then((str=>(errorMessage+=str+" "+textStatus.error+"",$("#cursivetoken_").html(errorMessage),setTimeout((function(){$("#cursivetoken_").empty()}),3e3),!0))).catch((error=>window.console.error(error)))}))})),cursiveDisable.on("click",(function(e){e.preventDefault();var promise1=AJAX.call([{methodname:"cursive_disable_all_course",args:{disable:!0}}]);promise1[0].done((function(data){var messageAlert="";str.get_strings([{key:"cursive:dis:succ",component:"tiny_cursive"},{key:"cursive:dis:fail",component:"tiny_cursive"}]).then((function(_ref2){let[success,fail]=_ref2;return messageAlert=data?"".concat(success,""):"".concat(fail,""),$("#cursivedisable_").html(messageAlert),setTimeout((()=>{$("#cursivedisable_").empty()}),3e3),!0})).catch((error=>window.console.error(error)))})),promise1[0].fail((function(textStatus){var errorMessage="";str.get_string("cursive:status","tiny_cursive").then((str=>(errorMessage+=str+" "+textStatus.error+"",$("#cursivedisable_").html(errorMessage),setTimeout((function(){$("#cursivedisable_").empty()}),3e3),!0))).catch((error=>window.console.error(error)))}))})),cursiveEnable.on("click",(function(e){e.preventDefault();var promise1=AJAX.call([{methodname:"cursive_disable_all_course",args:{disable:!1}}]);promise1[0].done((function(data){var messageAlert="";str.get_strings([{key:"cursive:ena:succ",component:"tiny_cursive"},{key:"cursive:ena:fail",component:"tiny_cursive"}]).then((function(_ref3){let[success,fail]=_ref3;return messageAlert=data?"".concat(success,""):"".concat(fail,""),$("#cursivedisable_").html(messageAlert),setTimeout((()=>{$("#cursivedisable_").empty()}),3e3),!0})).catch((error=>window.console.error(error)))})),promise1[0].fail((function(textStatus){var errorMessage="";str.get_string("cursive:status","tiny_cursive").then((str=>(errorMessage+=str+" "+textStatus.error+"",$("#cursivedisable_").html(errorMessage),setTimeout((function(){$("#cursivedisable_").empty()}),3e3),!0))).catch((error=>window.console.error(error)))}))}))}};return usersTable})); //# sourceMappingURL=token_approve.min.js.map \ No newline at end of file diff --git a/amd/build/token_approve.min.js.map b/amd/build/token_approve.min.js.map index 44d98c9a..2236c250 100644 --- a/amd/build/token_approve.min.js.map +++ b/amd/build/token_approve.min.js.map @@ -1 +1 @@ -{"version":3,"file":"token_approve.min.js","sources":["../src/token_approve.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/token_approve\n * @category TinyMCE Editor\n * @copyright CTI \n * @author kuldeep singh \n */\n\ndefine([\"jquery\", \"core/ajax\", \"core/str\"], function($, AJAX, str) {\n var usersTable = {\n init: function(page) {\n str\n .get_strings([{key: \"field_require\", component: \"tiny_cursive\"}])\n .done(function() {\n usersTable.getToken(page);\n usersTable.generateToken();\n });\n },\n getToken: function() {\n $(\"#approve_token\").click(function() {\n var token = $(\"#id_s_tiny_cursive_secretkey\").val();\n var promise1 = AJAX.call([\n {\n methodname: \"cursive_approve_token\",\n args: {\n token: token,\n },\n },\n ]);\n promise1[0].done(function(json) {\n var data = JSON.parse(json);\n var messageAlert = \"\";\n if (data.status == true) {\n messageAlert =\n \"\" +\n data.message +\n \"\";\n } else {\n messageAlert =\n \"\" +\n data.message +\n \"\";\n }\n $(\"#token_message\").html(messageAlert);\n });\n });\n },\n\n generateToken() {\n const generateToken = $(\"#generate_cursivetoken\");\n const cursiveDisable = $(\"#cursivedisable\");\n const cursiveEnable = $(\"#cursiveenable\");\n\n generateToken.on(\"click\", function(e) {\n e.preventDefault();\n var promise1 = AJAX.call([\n {\n methodname: \"cursive_generate_webtoken\",\n args: [],\n },\n ]);\n promise1[0].done(function(data) {\n var messageAlert = \"\";\n str.get_strings([\n {key: \"webservtokengensucc\", component: \"tiny_cursive\"},\n {key: \"webservtokengenfail\", component: \"tiny_cursive\"}\n ]).then(function([success, fail]) {\n\n if (data.token) {\n $(\"#id_s_tiny_cursive_cursivetoken\").val(data.token);\n messageAlert = `${success}`;\n } else {\n messageAlert = `${fail}`;\n }\n $(\"#cursivetoken_\").html(messageAlert);\n setTimeout(() => {\n $(\"#cursivetoken_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n promise1[0].fail(function(textStatus) {\n var errorMessage = \"\";\n str\n .get_string(\"webservtokenerror\", \"tiny_cursive\")\n .then((str) => {\n errorMessage += str + \" \" + textStatus.error + \"\";\n\n $(\"#cursivetoken_\").html(errorMessage);\n // Clear the error message after 3 seconds.\n setTimeout(function() {\n $(\"#cursivetoken_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n });\n\n cursiveDisable.on(\"click\", function(e) {\n e.preventDefault();\n\n var promise1 = AJAX.call([\n {\n methodname: \"cursive_disable_all_course\",\n args: {\n disable: true,\n },\n },\n ]);\n promise1[0].done(function(data) {\n var messageAlert = \"\";\n str.get_strings([\n {key: \"cursive:dis:succ\", component: \"tiny_cursive\"},\n {key: \"cursive:dis:fail\", component: \"tiny_cursive\"}\n ]).then(function([success, fail]) {\n if (data) {\n messageAlert = `${success}`;\n } else {\n messageAlert = `${fail}`;\n }\n\n $(\"#cursivedisable_\").html(messageAlert);\n setTimeout(() => {\n $(\"#cursivedisable_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n promise1[0].fail(function(textStatus) {\n var errorMessage = \"\";\n str\n .get_string(\"cursive:status\", \"tiny_cursive\")\n .then((str) => {\n errorMessage += str + \" \" + textStatus.error + \"\";\n\n $(\"#cursivedisable_\").html(errorMessage);\n // Clear the error message after 3 seconds.\n setTimeout(function() {\n $(\"#cursivedisable_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n });\n cursiveEnable.on(\"click\", function(e) {\n e.preventDefault();\n\n var promise1 = AJAX.call([\n {\n methodname: \"cursive_disable_all_course\",\n args: {\n disable: false,\n },\n },\n ]);\n promise1[0].done(function(data) {\n var messageAlert = \"\";\n str.get_strings([\n {key: \"cursive:ena:succ\", component: \"tiny_cursive\"},\n {key: \"cursive:ena:fail\", component: \"tiny_cursive\"}\n ]).then(function([success, fail]) {\n if (data) {\n messageAlert = `${success}`;\n } else {\n messageAlert = `${fail}`;\n }\n\n $(\"#cursivedisable_\").html(messageAlert);\n setTimeout(() => {\n $(\"#cursivedisable_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n promise1[0].fail(function(textStatus) {\n var errorMessage = \"\";\n str.get_string(\"cursive:status\", \"tiny_cursive\")\n .then((str) => {\n errorMessage += str + \" \" + textStatus.error + \"\";\n\n $(\"#cursivedisable_\").html(errorMessage);\n // Clear the error message after 3 seconds.\n setTimeout(function() {\n $(\"#cursivedisable_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n });\n },\n };\n return usersTable;\n});\n"],"names":["define","$","AJAX","str","usersTable","init","page","get_strings","key","component","done","getToken","generateToken","click","token","val","call","methodname","args","json","data","JSON","parse","messageAlert","status","message","html","cursiveDisable","cursiveEnable","on","e","preventDefault","promise1","then","success","fail","setTimeout","empty","catch","error","window","console","textStatus","errorMessage","get_string","disable"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"token_approve.min.js","sources":["../src/token_approve.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/token_approve\n * @category TinyMCE Editor\n * @copyright CTI \n * @author kuldeep singh \n */\n\ndefine([\"jquery\", \"core/ajax\", \"core/str\"], function($, AJAX, str) {\n var usersTable = {\n init: function(page) {\n str\n .get_strings([{key: \"field_require\", component: \"tiny_cursive\"}])\n .done(function() {\n usersTable.getToken(page);\n usersTable.generateToken();\n });\n },\n getToken: function() {\n $(\"#approve_token\").click(function() {\n var token = $(\"#id_s_tiny_cursive_secretkey\").val();\n var promise1 = AJAX.call([\n {\n methodname: \"cursive_approve_token\",\n args: {\n token: token,\n },\n },\n ]);\n promise1[0].done(function(json) {\n var data = JSON.parse(json);\n var messageAlert = \"\";\n if (data.status == true) {\n messageAlert =\n \"\" +\n data.message +\n \"\";\n } else {\n messageAlert =\n \"\" +\n data.message +\n \"\";\n }\n $(\"#token_message\").html(messageAlert);\n });\n });\n },\n\n generateToken() {\n const generateToken = $(\"#generate_cursivetoken\");\n const cursiveDisable = $(\"#cursivedisable\");\n const cursiveEnable = $(\"#cursiveenable\");\n\n generateToken.on(\"click\", function(e) {\n e.preventDefault();\n var promise1 = AJAX.call([\n {\n methodname: \"cursive_generate_webtoken\",\n args: [],\n },\n ]);\n promise1[0].done(function(data) {\n var messageAlert = \"\";\n str.get_strings([\n {key: \"webservtokengensucc\", component: \"tiny_cursive\"},\n {key: \"webservtokengenfail\", component: \"tiny_cursive\"}\n ]).then(function([success, fail]) {\n\n if (data.token) {\n $(\"#id_s_tiny_cursive_cursivetoken\").val(data.token);\n messageAlert = `${success}`;\n } else {\n messageAlert = `${fail}`;\n }\n $(\"#cursivetoken_\").html(messageAlert);\n setTimeout(() => {\n $(\"#cursivetoken_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n promise1[0].fail(function(textStatus) {\n var errorMessage = \"\";\n str\n .get_string(\"webservtokenerror\", \"tiny_cursive\")\n .then((str) => {\n errorMessage += str + \" \" + textStatus.error + \"\";\n\n $(\"#cursivetoken_\").html(errorMessage);\n // Clear the error message after 3 seconds.\n setTimeout(function() {\n $(\"#cursivetoken_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n });\n\n cursiveDisable.on(\"click\", function(e) {\n e.preventDefault();\n\n var promise1 = AJAX.call([\n {\n methodname: \"cursive_disable_all_course\",\n args: {\n disable: true,\n },\n },\n ]);\n promise1[0].done(function(data) {\n var messageAlert = \"\";\n str.get_strings([\n {key: \"cursive:dis:succ\", component: \"tiny_cursive\"},\n {key: \"cursive:dis:fail\", component: \"tiny_cursive\"}\n ]).then(function([success, fail]) {\n if (data) {\n messageAlert = `${success}`;\n } else {\n messageAlert = `${fail}`;\n }\n\n $(\"#cursivedisable_\").html(messageAlert);\n setTimeout(() => {\n $(\"#cursivedisable_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n promise1[0].fail(function(textStatus) {\n var errorMessage = \"\";\n str\n .get_string(\"cursive:status\", \"tiny_cursive\")\n .then((str) => {\n errorMessage += str + \" \" + textStatus.error + \"\";\n\n $(\"#cursivedisable_\").html(errorMessage);\n // Clear the error message after 3 seconds.\n setTimeout(function() {\n $(\"#cursivedisable_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n });\n cursiveEnable.on(\"click\", function(e) {\n e.preventDefault();\n\n var promise1 = AJAX.call([\n {\n methodname: \"cursive_disable_all_course\",\n args: {\n disable: false,\n },\n },\n ]);\n promise1[0].done(function(data) {\n var messageAlert = \"\";\n str.get_strings([\n {key: \"cursive:ena:succ\", component: \"tiny_cursive\"},\n {key: \"cursive:ena:fail\", component: \"tiny_cursive\"}\n ]).then(function([success, fail]) {\n if (data) {\n messageAlert = `${success}`;\n } else {\n messageAlert = `${fail}`;\n }\n\n $(\"#cursivedisable_\").html(messageAlert);\n setTimeout(() => {\n $(\"#cursivedisable_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n promise1[0].fail(function(textStatus) {\n var errorMessage = \"\";\n str.get_string(\"cursive:status\", \"tiny_cursive\")\n .then((str) => {\n errorMessage += str + \" \" + textStatus.error + \"\";\n\n $(\"#cursivedisable_\").html(errorMessage);\n // Clear the error message after 3 seconds.\n setTimeout(function() {\n $(\"#cursivedisable_\").empty();\n }, 3000);\n return true;\n }).catch(error => window.console.error(error));\n });\n });\n },\n };\n return usersTable;\n});\n"],"names":["define","$","AJAX","str","usersTable","init","page","get_strings","key","component","done","getToken","generateToken","click","token","val","call","methodname","args","json","data","JSON","parse","messageAlert","status","message","html","cursiveDisable","cursiveEnable","on","e","preventDefault","promise1","then","success","fail","setTimeout","empty","catch","error","window","console","textStatus","errorMessage","get_string","disable"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/autosaver.js b/amd/src/autosaver.js index a3948532..2adc09db 100644 --- a/amd/src/autosaver.js +++ b/amd/src/autosaver.js @@ -60,9 +60,6 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, let shouldBlockPaste = false; let isPasteAllowed = false; - if (modulename !== 'assign') { - PASTE_SETTING = 'cite_source'; - } const postOne = async(methodname, args) => { try { const response = await call([{ diff --git a/lib.php b/lib.php index d5761124..691bf123 100644 --- a/lib.php +++ b/lib.php @@ -175,7 +175,7 @@ function tiny_cursive_coursemodule_standard_elements($formwrapper, $mform) { $mform->setdefault('cursive', $state); } - if ($state && $module == 'assign') { + if ($state) { $pasteoptions = [ 'allow' => get_string('paste_allow', 'tiny_cursive'), 'block' => get_string('paste_block', 'tiny_cursive'), From 75418ddb941d8faa720d548d11735b212acf2971 Mon Sep 17 00:00:00 2001 From: Tareq-Adnan Date: Wed, 21 Jan 2026 14:20:34 +0600 Subject: [PATCH 09/14] empty orignal text fix --- amd/build/autosaver.min.js | 4 ++++ amd/build/autosaver.min.js.map | 6 +++++- amd/src/autosaver.js | 25 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/amd/build/autosaver.min.js b/amd/build/autosaver.min.js index d582ed30..015e6df0 100644 --- a/amd/build/autosaver.min.js +++ b/amd/build/autosaver.min.js @@ -1,3 +1,7 @@ +<<<<<<< Updated upstream define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,parm=new URL(ur),modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return checkIsPdfAnnotator(),{resourceId:resourceId,name:modulename}}(ur,parm,MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;"assign"!==modulename&&(PASTE_SETTING="cite_source"),ur.includes("pdfannotator")&&document.addEventListener("click",(e=>{if("dropdown-item comment-edit-a"===e.target.className){let id=e.target.id;resourceId=id.replace("editButton",""),localStorage.setItem("isEditing","1")}"commentSubmit"===e.target.id&&syncData()}));const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:`
${title}
\n ${titledes}
`,body:``,removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename=`${userid}_${resourceId}_${cmid}_${modulename}_attempt`,"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename=`${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey=`${userid}_${resourceId}_${cmid}_position`;let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){checkIsPdfAnnotator();let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename),editor.fire("change");let originalText=editor.getContent({format:"text"});try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector(`#${elementId}`)&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector(`#${elementId}`)||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId=`tiny_cursive_tooltip${index}`;tooltipText.then((text=>setTooltip(text,document.querySelector(`#${elementId}`),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)(`#${elementId}`).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)(`#${tooltipId}`).css(_common.tooltipCss)})),(0,_jquery.default)(`#${elementId}`).on("mouseleave",(function(){(0,_jquery.default)(`#${tooltipId}`).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector(`#${tooltipId}`)&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}function checkIsPdfAnnotator(){ur.includes("pdfannotator")&&(resourceId="id_pdfannotator_content"!==editor.id&&parseInt(localStorage.getItem("isEditing"))?parseInt(null==editor?void 0:editor.id.replace("editarea","")):0)}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); +======= +define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,parm=new URL(ur),modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return checkIsPdfAnnotator(),{resourceId:resourceId,name:modulename}}(ur,parm,MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;ur.includes("pdfannotator")&&document.addEventListener("click",(e=>{if("dropdown-item comment-edit-a"===e.target.className){let id=e.target.id;resourceId=id.replace("editButton",""),localStorage.setItem("isEditing","1")}"commentSubmit"===e.target.id&&syncData()}));const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:`
${title}
\n ${titledes}
`,body:``,removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename=`${userid}_${resourceId}_${cmid}_${modulename}_attempt`,"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename=`${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey=`${userid}_${resourceId}_${cmid}_position`;let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){checkIsPdfAnnotator();let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename),editor.fire("change");let originalText=editor.getContent({format:"text"});originalText||(originalText=function(editor){let editorId=null==editor?void 0:editor.id;if(editorId){var _iframe$contentDocume,_iframe$contentWindow,_iframe$contentWindow2;let iframe=document.querySelector(`#${editorId}_ifr`),iframeBody=(null===(_iframe$contentDocume=iframe.contentDocument)||void 0===_iframe$contentDocume?void 0:_iframe$contentDocume.body)||(null===(_iframe$contentWindow=iframe.contentWindow)||void 0===_iframe$contentWindow||null===(_iframe$contentWindow2=_iframe$contentWindow.document)||void 0===_iframe$contentWindow2?void 0:_iframe$contentWindow2.body);return null==iframeBody?void 0:iframeBody.textContent}return""}(editor));try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector(`#${elementId}`)&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector(`#${elementId}`)||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId=`tiny_cursive_tooltip${index}`;tooltipText.then((text=>setTooltip(text,document.querySelector(`#${elementId}`),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)(`#${elementId}`).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)(`#${tooltipId}`).css(_common.tooltipCss)})),(0,_jquery.default)(`#${elementId}`).on("mouseleave",(function(){(0,_jquery.default)(`#${tooltipId}`).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector(`#${tooltipId}`)&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}function checkIsPdfAnnotator(){ur.includes("pdfannotator")&&(resourceId="id_pdfannotator_content"!==editor.id&&parseInt(localStorage.getItem("isEditing"))?parseInt(null==editor?void 0:editor.id.replace("editarea","")):0)}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); +>>>>>>> Stashed changes //# sourceMappingURL=autosaver.min.js.map \ No newline at end of file diff --git a/amd/build/autosaver.min.js.map b/amd/build/autosaver.min.js.map index 2f7ef148..49c0c3a3 100644 --- a/amd/build/autosaver.min.js.map +++ b/amd/build/autosaver.min.js.map @@ -1 +1,5 @@ -{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (modulename !== 'assign') {\n PASTE_SETTING = 'cite_source';\n }\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","querySelector","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"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"} \ No newline at end of file +<<<<<<< Updated upstream +{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (modulename !== 'assign') {\n PASTE_SETTING = 'cite_source';\n }\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","querySelector","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"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"} +======= +{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n if (!originalText) {\n originalText = getRawText(editor);\n }\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n\n /**\n * Gets the raw text content from a TinyMCE editor iframe\n * @param {Object} editor - The TinyMCE editor instance\n * @returns {string} The raw text content of the editor body, or empty string if not found\n * @description Attempts to get the raw text content from the editor's iframe body by:\n * 1. Getting the editor ID\n * 2. Finding the associated iframe element\n * 3. Accessing the iframe's document body\n * 4. Returning the text content\n * Returns empty string if any step fails\n */\n function getRawText(editor) {\n let editorId = editor?.id;\n if (editorId) {\n let iframe = document.querySelector(`#${editorId}_ifr`);\n let iframeBody = iframe.contentDocument?.body || iframe.contentWindow?.document?.body;\n return iframeBody?.textContent;\n }\n return \"\";\n }\n\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","editorId","iframe","querySelector","iframeBody","contentDocument","contentWindow","_iframe$contentWindow","_iframe$contentWindow2","getRawText","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"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"} +>>>>>>> Stashed changes diff --git a/amd/src/autosaver.js b/amd/src/autosaver.js index 614c6372..a782249c 100644 --- a/amd/src/autosaver.js +++ b/amd/src/autosaver.js @@ -568,6 +568,9 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, localStorage.removeItem(filename); editor.fire('change'); let originalText = editor.getContent({format: 'text'}); + if (!originalText) { + originalText = getRawText(editor); + } try { Autosave.updateSavingState('saving'); // eslint-disable-next-line @@ -587,6 +590,28 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, } } } + + /** + * Gets the raw text content from a TinyMCE editor iframe + * @param {Object} editor - The TinyMCE editor instance + * @returns {string} The raw text content of the editor body, or empty string if not found + * @description Attempts to get the raw text content from the editor's iframe body by: + * 1. Getting the editor ID + * 2. Finding the associated iframe element + * 3. Accessing the iframe's document body + * 4. Returning the text content + * Returns empty string if any step fails + */ + function getRawText(editor) { + let editorId = editor?.id; + if (editorId) { + let iframe = document.querySelector(`#${editorId}_ifr`); + let iframeBody = iframe.contentDocument?.body || iframe.contentWindow?.document?.body; + return iframeBody?.textContent; + } + return ""; + } + /** * Sets up custom tooltip functionality for the Cursive icon * Initializes tooltip text, positions the icon in the menubar, From 85064049ea5058e6dea9a2beb69e4bd3ea686e1c Mon Sep 17 00:00:00 2001 From: Tareq-Adnan Date: Wed, 21 Jan 2026 14:54:20 +0600 Subject: [PATCH 10/14] assignment description misformat fix in full page mode --- amd/build/autosaver.min.js | 6 +----- amd/build/autosaver.min.js.map | 6 +----- amd/build/document_view.min.js | 2 +- amd/build/document_view.min.js.map | 2 +- amd/src/document_view.js | 6 ++++++ styles.css | 6 +++++- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/amd/build/autosaver.min.js b/amd/build/autosaver.min.js index 015e6df0..e2021137 100644 --- a/amd/build/autosaver.min.js +++ b/amd/build/autosaver.min.js @@ -1,7 +1,3 @@ -<<<<<<< Updated upstream -define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,parm=new URL(ur),modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return checkIsPdfAnnotator(),{resourceId:resourceId,name:modulename}}(ur,parm,MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;"assign"!==modulename&&(PASTE_SETTING="cite_source"),ur.includes("pdfannotator")&&document.addEventListener("click",(e=>{if("dropdown-item comment-edit-a"===e.target.className){let id=e.target.id;resourceId=id.replace("editButton",""),localStorage.setItem("isEditing","1")}"commentSubmit"===e.target.id&&syncData()}));const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:`
${title}
\n ${titledes}
`,body:``,removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename=`${userid}_${resourceId}_${cmid}_${modulename}_attempt`,"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename=`${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey=`${userid}_${resourceId}_${cmid}_position`;let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){checkIsPdfAnnotator();let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename),editor.fire("change");let originalText=editor.getContent({format:"text"});try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector(`#${elementId}`)&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector(`#${elementId}`)||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId=`tiny_cursive_tooltip${index}`;tooltipText.then((text=>setTooltip(text,document.querySelector(`#${elementId}`),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)(`#${elementId}`).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)(`#${tooltipId}`).css(_common.tooltipCss)})),(0,_jquery.default)(`#${elementId}`).on("mouseleave",(function(){(0,_jquery.default)(`#${tooltipId}`).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector(`#${tooltipId}`)&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}function checkIsPdfAnnotator(){ur.includes("pdfannotator")&&(resourceId="id_pdfannotator_content"!==editor.id&&parseInt(localStorage.getItem("isEditing"))?parseInt(null==editor?void 0:editor.id.replace("editarea","")):0)}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); -======= -define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,parm=new URL(ur),modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return checkIsPdfAnnotator(),{resourceId:resourceId,name:modulename}}(ur,parm,MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;ur.includes("pdfannotator")&&document.addEventListener("click",(e=>{if("dropdown-item comment-edit-a"===e.target.className){let id=e.target.id;resourceId=id.replace("editButton",""),localStorage.setItem("isEditing","1")}"commentSubmit"===e.target.id&&syncData()}));const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:`
${title}
\n ${titledes}
`,body:``,removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename=`${userid}_${resourceId}_${cmid}_${modulename}_attempt`,"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename=`${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey=`${userid}_${resourceId}_${cmid}_position`;let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){checkIsPdfAnnotator();let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename),editor.fire("change");let originalText=editor.getContent({format:"text"});originalText||(originalText=function(editor){let editorId=null==editor?void 0:editor.id;if(editorId){var _iframe$contentDocume,_iframe$contentWindow,_iframe$contentWindow2;let iframe=document.querySelector(`#${editorId}_ifr`),iframeBody=(null===(_iframe$contentDocume=iframe.contentDocument)||void 0===_iframe$contentDocume?void 0:_iframe$contentDocume.body)||(null===(_iframe$contentWindow=iframe.contentWindow)||void 0===_iframe$contentWindow||null===(_iframe$contentWindow2=_iframe$contentWindow.document)||void 0===_iframe$contentWindow2?void 0:_iframe$contentWindow2.body);return null==iframeBody?void 0:iframeBody.textContent}return""}(editor));try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector(`#${elementId}`)&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector(`#${elementId}`)||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId=`tiny_cursive_tooltip${index}`;tooltipText.then((text=>setTooltip(text,document.querySelector(`#${elementId}`),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)(`#${elementId}`).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)(`#${tooltipId}`).css(_common.tooltipCss)})),(0,_jquery.default)(`#${elementId}`).on("mouseleave",(function(){(0,_jquery.default)(`#${tooltipId}`).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector(`#${tooltipId}`)&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}function checkIsPdfAnnotator(){ur.includes("pdfannotator")&&(resourceId="id_pdfannotator_content"!==editor.id&&parseInt(localStorage.getItem("isEditing"))?parseInt(null==editor?void 0:editor.id.replace("editarea","")):0)}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); ->>>>>>> Stashed changes +define("tiny_cursive/autosaver",["exports","core/ajax","core/modal_factory","core/str","core/modal_events","jquery","tiny_cursive/common","tiny_cursive/cursive_autosave","tiny_cursive/document_view"],(function(_exports,_ajax,_modal_factory,_str,_modal_events,_jquery,_common,_cursive_autosave,_document_view){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=void 0,_jquery=_interopRequireDefault(_jquery),_cursive_autosave=_interopRequireDefault(_cursive_autosave),_document_view=_interopRequireDefault(_document_view);_exports.register=(editor,interval,userId,hasApiKey,MODULES,Rubrics,submission,quizInfo,pasteSetting)=>{var isStudent=!(0,_jquery.default)("#body").hasClass("teacher_admin"),intervention=(0,_jquery.default)("#body").hasClass("intervention"),host=M.cfg.wwwroot,userid=userId,courseid=M.cfg.courseId,editorid=null==editor?void 0:editor.id,cmid=M.cfg.contextInstanceId,ed="",event="",filename="",questionid=0,quizSubmit=(0,_jquery.default)("#mod_quiz-next-nav");let assignSubmit=(0,_jquery.default)("#id_submitbutton");var syncInterval=interval?1e3*interval:1e4,lastCaretPos=1;let aiContents=[];var isFullScreen=!1,user=null;let ur=window.location.href,parm=new URL(ur),modulesInfo=function(ur,parm,MODULES){if(function(){localStorage.getItem("sbTitle")||Promise.all([(0,_str.get_string)("assignment","tiny_cursive"),(0,_str.get_string)("discussion","tiny_cursive"),(0,_str.get_string)("pluginname","mod_quiz"),(0,_str.get_string)("pluginname","mod_lesson"),(0,_str.get_string)("description","tiny_cursive")]).then((function(strings){return localStorage.setItem("sbTitle",JSON.stringify(strings))})).catch((error=>window.console.error(error)));localStorage.getItem("docSideBar")||Promise.all([(0,_str.get_string)("details","tiny_cursive"),(0,_str.get_string)("student_info","tiny_cursive"),(0,_str.get_string)("progress","tiny_cursive"),(0,_str.get_string)("description","tiny_cursive"),(0,_str.get_string)("replyingto","tiny_cursive"),(0,_str.get_string)("answeringto","tiny_cursive"),(0,_str.get_string)("importantdates","tiny_cursive"),(0,_str.get_string)("rubrics","tiny_cursive"),(0,_str.get_string)("submission_status","tiny_cursive"),(0,_str.get_string)("status","tiny_cursive"),(0,_str.get_string)("draft","tiny_cursive"),(0,_str.get_string)("draftnot","tiny_cursive"),(0,_str.get_string)("last_modified","tiny_cursive"),(0,_str.get_string)("gradings","tiny_cursive"),(0,_str.get_string)("gradenot","tiny_cursive"),(0,_str.get_string)("word_count","tiny_cursive"),(0,_str.get_string)("timeleft","tiny_cursive"),(0,_str.get_string)("nolimit","tiny_cursive"),(0,_str.get_string)("name","tiny_cursive"),(0,_str.get_string)("userename","tiny_cursive"),(0,_str.get_string)("course","tiny_cursive"),(0,_str.get_string)("opened","tiny_cursive"),(0,_str.get_string)("due","tiny_cursive"),(0,_str.get_string)("overdue","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive"),(0,_str.get_string)("savechanges","tiny_cursive"),(0,_str.get_string)("subjectnot","tiny_cursive"),(0,_str.get_string)("remaining","tiny_cursive")]).then((function(strings){return localStorage.setItem("docSideBar",JSON.stringify(strings))})).catch((error=>window.console.error(error)))}(),!MODULES.some((module=>ur.includes(module))))return!1;resourceId=ur.includes("forum")&&!ur.includes("assign")?parm.searchParams.get("edit"):parm.searchParams.get("attempt");null===resourceId&&(resourceId=0);for(const module of MODULES)if(ur.includes(module)){modulename=module,"lesson"===module||"assign"===module?resourceId=cmid:"oublog"===module&&(resourceId=0);break}return checkIsPdfAnnotator(),{resourceId:resourceId,name:modulename}}(ur,parm,MODULES);var resourceId=modulesInfo.resourceId,modulename=modulesInfo.name,errorAlert=!0;let PASTE_SETTING=pasteSetting||"allow",shouldBlockPaste=!1,isPasteAllowed=!1;"assign"!==modulename&&(PASTE_SETTING="cite_source"),ur.includes("pdfannotator")&&document.addEventListener("click",(e=>{if("dropdown-item comment-edit-a"===e.target.className){let id=e.target.id;resourceId=id.replace("editButton",""),localStorage.setItem("isEditing","1")}"commentSubmit"===e.target.id&&syncData()}));const postOne=async(methodname,args)=>{try{const response=await(0,_ajax.call)([{methodname:methodname,args:args}])[0];return response&&setTimeout((()=>{_cursive_autosave.default.updateSavingState("saved")}),1e3),response}catch(error){throw _cursive_autosave.default.updateSavingState("offline"),window.console.error("Error in postOne:",error),error}};(0,_ajax.call)([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[userid]}}])[0].done((response=>{user=response[0]})).fail((ex=>{window.console.error("Error fetching user data:",ex)})),assignSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{assignSubmit.off("click").click()})):assignSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")})),quizSubmit.on("click",(async function(e){e.preventDefault(),filename?syncData().then((()=>{quizSubmit.off("click").click()})):quizSubmit.off("click").click(),localStorage.removeItem("lastCopyCutContent")}));const getModal=()=>{Promise.all([(0,_str.get_string)("tiny_cursive_srcurl","tiny_cursive"),(0,_str.get_string)("tiny_cursive_srcurl_des","tiny_cursive"),(0,_str.get_string)("tiny_cursive_placeholder","tiny_cursive")]).then((function(_ref){let[title,titledes,placeholder]=_ref;return(0,_modal_factory.create)({type:"SAVE_CANCEL",title:`
${title}
\n ${titledes}
`,body:``,removeOnClose:!0}).done((modal=>{modal.getRoot().addClass("tiny-cursive-modal"),modal.show();var lastEvent="";return modal.getRoot().on(_modal_events.save,(function(){var number=document.getElementById("inputUrl").value.trim();""===number||null==number?(editor.execCommand("Undo"),(0,_str.get_string)("pastewarning","tiny_cursive").then((str=>alert(str)))):editor.execCommand("Paste"),postOne("cursive_user_comments",{modulename:modulename,cmid:cmid,resourceid:resourceId,courseid:courseid,usercomment:number,timemodified:Date.now(),editorid:editorid||""}),lastEvent="save",modal.destroy()})),modal.getRoot().on(_modal_events.cancel,(function(){editor.execCommand("Undo"),lastEvent="cancel"})),modal.getRoot().on(_modal_events.hidden,(function(){"cancel"!=lastEvent&&"save"!=lastEvent&&editor.execCommand("Undo")})),modal}))})).catch((error=>window.console.error(error)))},sendKeyEvent=(events,editor)=>{if(ed=editor,event=events,filename=`${userid}_${resourceId}_${cmid}_${modulename}_attempt`,"quiz"===modulename&&(questionid=editorid.split(":")[1].split("_")[0],filename=`${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`),localStorage.getItem(filename)){let data=JSON.parse(localStorage.getItem(filename));data.push({resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}),localStorage.setItem(filename,JSON.stringify(data))}else{let data=[{resourceId:resourceId,key:editor.key,keyCode:editor.keyCode,event:event,courseId:courseid,unixTimestamp:Date.now(),clientId:host,personId:userid,position:ed.caretPosition,rePosition:ed.rePosition,pastedContent:editor.pastedContent,aiContent:editor.aiContent}];localStorage.setItem(filename,JSON.stringify(data))}};function constructMouseEvent(editor){let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,editor.key=function(editor){switch(editor.button){case 0:return"left";case 1:return"middle";case 2:return"right"}return null}(editor),editor.keyCode=editor.button}function getCaretPosition(){let skip=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{if(!editor||!editor.selection)return{caretPosition:0,rePosition:0};const range=editor.selection.getRng(),body=editor.getBody(),preCaretRange=range.cloneRange();preCaretRange.selectNodeContents(body),preCaretRange.setEnd(range.endContainer,range.endOffset);const fragment=preCaretRange.cloneContents(),tempDiv=document.createElement("div");tempDiv.appendChild(fragment);let textBeforeCursor=tempDiv.innerText||"";const endContainer=range.endContainer;0===range.endOffset&&endContainer.nodeType===Node.ELEMENT_NODE&&editor.dom.isBlock(endContainer)&&endContainer.previousSibling&&(textBeforeCursor+="\n");const blockElements=tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li");let emptyBlockCount=0;blockElements.forEach((block=>{""===(block.innerText||block.textContent||"").trim()&&1===block.childNodes.length&&"BR"===block.childNodes[0].nodeName&&emptyBlockCount++})),emptyBlockCount>0&&(textBeforeCursor+="\n".repeat(emptyBlockCount));const absolutePosition=textBeforeCursor.length;if(skip)return{caretPosition:lastCaretPos,rePosition:absolutePosition};const storageKey=`${userid}_${resourceId}_${cmid}_position`;let storedPos=parseInt(sessionStorage.getItem(storageKey),10);return isNaN(storedPos)&&(storedPos=0),storedPos++,lastCaretPos=storedPos,sessionStorage.setItem(storageKey,storedPos),{caretPosition:storedPos,rePosition:absolutePosition}}catch(e){return window.console.warn("Error getting caret position:",e),{caretPosition:lastCaretPos||1,rePosition:0}}}async function syncData(){checkIsPdfAnnotator();let data=localStorage.getItem(filename);if(data&&0!==data.length){localStorage.removeItem(filename),editor.fire("change");let originalText=editor.getContent({format:"text"});originalText||(originalText=function(editor){let editorId=null==editor?void 0:editor.id;if(editorId){var _iframe$contentDocume,_iframe$contentWindow,_iframe$contentWindow2;let iframe=document.querySelector(`#${editorId}_ifr`),iframeBody=(null===(_iframe$contentDocume=iframe.contentDocument)||void 0===_iframe$contentDocume?void 0:_iframe$contentDocume.body)||(null===(_iframe$contentWindow=iframe.contentWindow)||void 0===_iframe$contentWindow||null===(_iframe$contentWindow2=_iframe$contentWindow.document)||void 0===_iframe$contentWindow2?void 0:_iframe$contentWindow2.body);return null==iframeBody?void 0:iframeBody.textContent}return""}(editor));try{return _cursive_autosave.default.updateSavingState("saving"),await postOne("cursive_write_local_to_json",{key:ed.key,event:event,keyCode:ed.keyCode,resourceId:resourceId,cmid:cmid,modulename:modulename,editorid:editorid,json_data:data,originalText:originalText})}catch(error){window.console.error("Error submitting data:",error)}}}function customTooltip(){try{const tooltipText=async function(){const[buttonTitle,buttonDes]=await Promise.all([(0,_str.get_string)("cursive:state:active","tiny_cursive"),(0,_str.get_string)("cursive:state:active:des","tiny_cursive")]);return{buttonTitle:buttonTitle,buttonDes:buttonDes}}(),menubarDiv=document.querySelectorAll('div[role="menubar"].tox-menubar');let classArray=[];menubarDiv.length&&menubarDiv.forEach((function(element,index){let className="cursive-menu-"+(index+=1);element.classList.add(className),classArray.push(className)}));const cursiveIcon=document.createElement("img");cursiveIcon.src=hasApiKey?_common.iconUrl:_common.iconGrayUrl,cursiveIcon.setAttribute("class","tiny_cursive_StateButton"),cursiveIcon.style.display="inline-block",function(cursiveIcon,menubarDiv,classArray){if(!menubarDiv)return;for(let index in classArray){const rightWrapper=document.createElement("div"),imgWrapper=document.createElement("span"),iconClone=cursiveIcon.cloneNode(!0),targetMenu=document.querySelector("."+classArray[index]);let elementId="tiny_cursive_StateIcon"+index;rightWrapper.style.cssText="\n margin-left: auto;\n display: flex;\n align-items: center;\n ",imgWrapper.id=elementId,imgWrapper.style.marginLeft=".2rem",imgWrapper.appendChild(iconClone),rightWrapper.appendChild(imgWrapper);let moduleIds={resourceId:resourceId,cmid:cmid,modulename:modulename,questionid:questionid,userid:userid,courseid:courseid};if(!isFullScreen||"assign"!==modulename&&"forum"!==modulename&&"lesson"!==modulename)if(isFullScreen&&"quiz"===modulename){var _editor$container,_editor$container$chi,_editor$container$chi2,_editor$container$chi3,_editor$container2;let existingElement=null===(_editor$container=editor.container)||void 0===_editor$container||null===(_editor$container$chi=_editor$container.childNodes[1])||void 0===_editor$container$chi||null===(_editor$container$chi2=_editor$container$chi.childNodes[0])||void 0===_editor$container$chi2||null===(_editor$container$chi3=_editor$container$chi2.childNodes[0])||void 0===_editor$container$chi3?void 0:_editor$container$chi3.childNodes[7],newHeader=null===(_editor$container2=editor.container)||void 0===_editor$container2?void 0:_editor$container2.childNodes[0];existingElement&&existingElement.remove(),newHeader&&!newHeader.querySelector("span[id*=tiny_cursive_StateIcon]")&&(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{var _editor$container3,_editor$container3$ch,_editor$container3$ch2;let menubar=null==editor||null===(_editor$container3=editor.container)||void 0===_editor$container3||null===(_editor$container3$ch=_editor$container3.children[0])||void 0===_editor$container3$ch||null===(_editor$container3$ch2=_editor$container3$ch.childNodes[0])||void 0===_editor$container3$ch2?void 0:_editor$container3$ch2.childNodes[0];if(targetMenu&&!targetMenu.querySelector(`#${elementId}`)&&targetMenu.appendChild(rightWrapper),"quiz"===modulename&&menubar){let wrapper=menubar.querySelector('span[id*="tiny_cursive_StateIcon"]');wrapper&&(_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,null==wrapper?void 0:wrapper.parentElement,moduleIds,isFullScreen))}else _cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}else{let existsElement=document.querySelector('.tox-menubar[class*="cursive-menu-"] > div');existsElement&&existsElement.remove(),document.querySelector(`#${elementId}`)||(rightWrapper.style.marginTop="3px",document.querySelector("#tiny_cursive-fullpage-right-wrapper").prepend(rightWrapper)),_cursive_autosave.default.destroyInstance(),_cursive_autosave.default.getInstance(editor,rightWrapper,moduleIds,isFullScreen)}}}(cursiveIcon,menubarDiv,classArray);for(let index in classArray){const elementId="tiny_cursive_StateIcon"+index,tooltipId=`tiny_cursive_tooltip${index}`;tooltipText.then((text=>setTooltip(text,document.querySelector(`#${elementId}`),tooltipId))).catch((error=>window.console.error(error))),(0,_jquery.default)(`#${elementId}`).on("mouseenter",(function(){(0,_jquery.default)(this).css("position","relative"),(0,_jquery.default)(`#${tooltipId}`).css(_common.tooltipCss)})),(0,_jquery.default)(`#${elementId}`).on("mouseleave",(function(){(0,_jquery.default)(`#${tooltipId}`).css("display","none")}))}}catch(error){window.console.error("Error setting up custom tooltip:",error)}}function setTooltip(text,cursiveIcon,tooltipId){if(!document.querySelector(`#${tooltipId}`)&&cursiveIcon){const tooltipSpan=document.createElement("span"),description=document.createElement("span"),linebreak=document.createElement("br"),tooltipTitle=document.createElement("strong");tooltipSpan.style.display="none",tooltipTitle.textContent=text.buttonTitle,tooltipTitle.style.fontSize="16px",tooltipTitle.style.fontWeight="bold",description.textContent=text.buttonDes,description.style.fontSize="14px",tooltipSpan.id=tooltipId,tooltipSpan.classList.add("shadow"),tooltipSpan.appendChild(tooltipTitle),tooltipSpan.appendChild(linebreak),tooltipSpan.appendChild(description),cursiveIcon.appendChild(tooltipSpan)}}function checkIsPdfAnnotator(){ur.includes("pdfannotator")&&(resourceId="id_pdfannotator_content"!==editor.id&&parseInt(localStorage.getItem("isEditing"))?parseInt(null==editor?void 0:editor.id.replace("editarea","")):0)}editor.on("keyUp",(editor=>{customTooltip();let position=getCaretPosition(!1);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyUp",editor)})),editor.on("Paste",(async e=>{customTooltip();const pastedContent=(e.clipboardData||e.originalEvent.clipboardData).getData("text");if(!pastedContent)return;const trimmedPastedContent=pastedContent.trim(),lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&trimmedPastedContent===lastCopyCutContent;if(isStudent&&intervention){if("block"===PASTE_SETTING)return isFromOwnEditor?(shouldBlockPaste=!1,void(isPasteAllowed=!0)):(e.preventDefault(),shouldBlockPaste=!0,isPasteAllowed=!1,e.stopPropagation(),e.stopImmediatePropagation(),(0,_str.get_string)("paste_blocked","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error))),void setTimeout((()=>{isPasteAllowed=!0,shouldBlockPaste=!1}),100));if("cite_source"===PASTE_SETTING)return isFromOwnEditor||(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),getModal()),void(isPasteAllowed=!0)}isPasteAllowed=!0})),editor.on("Redo",(async e=>{customTooltip(),isStudent&&intervention&&getModal()})),editor.on("keyDown",(editor=>{customTooltip();if(("v"===editor.key||"V"===editor.key)&&(editor.ctrlKey||editor.metaKey)&&isStudent&&intervention&&"block"===PASTE_SETTING&&!isPasteAllowed)return void setTimeout((()=>{isPasteAllowed=!0}),100);let position=getCaretPosition();editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,sendKeyEvent("keyDown",editor)})),editor.on("Cut",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("Copy",(()=>{const selectedContent=editor.selection.getContent({format:"text"});localStorage.setItem("lastCopyCutContent",selectedContent.trim())})),editor.on("mouseDown",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseDown",editor)}),0)})),editor.on("mouseUp",(async editor=>{setTimeout((()=>{constructMouseEvent(editor),sendKeyEvent("mouseUp",editor)}),10)})),editor.on("init",(()=>{customTooltip(),localStorage.removeItem("lastCopyCutContent")})),editor.on("SetContent",(()=>{customTooltip()})),editor.on("FullscreenStateChanged",(e=>{let view=new _document_view.default(user,Rubrics,submission,modulename,editor,quizInfo);isFullScreen=e.state;try{e.state?view.fullPageMode():view.normalMode()}catch(error){errorAlert&&(errorAlert=!1,(0,_str.get_string)("fullmodeerror","tiny_cursive").then((str=>editor.windowManager.alert(str))).catch((error=>window.console.error(error)))),view.normalMode(),window.console.error("Error ResizeEditor event:",error)}})),editor.on("execcommand",(function(e){if("mceInsertContent"===e.command){const contentObj=e.value,isPaste=contentObj&&"object"==typeof contentObj&&!0===contentObj.paste;let insertedContent=contentObj.content||contentObj,tempDiv=document.createElement("div");tempDiv.innerHTML=insertedContent;let text=tempDiv.textContent||tempDiv.innerText||"",pastedText=tempDiv.textContent||tempDiv.innerText||"",position=getCaretPosition(!0);if(editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition,isPaste){if(shouldBlockPaste)return shouldBlockPaste=!1,e.preventDefault(),void editor.undoManager.undo();const lastCopyCutContent=localStorage.getItem("lastCopyCutContent"),isFromOwnEditor=lastCopyCutContent&&pastedText.trim()===lastCopyCutContent;if(isStudent&&intervention&&"block"===PASTE_SETTING&&!isFromOwnEditor)return isPasteAllowed=!1,void editor.undoManager.undo();sendKeyEvent("Paste",{key:"v",keyCode:86,caretPosition:editor.caretPosition,rePosition:editor.rePosition,pastedContent:pastedText,srcElement:{baseURI:window.location.href}})}else aiContents.push(text),sendKeyEvent("aiInsert",{key:"ai",keyCode:0,caretPosition:editor.caretPosition,rePosition:editor.rePosition,aiContent:text,srcElement:{baseURI:window.location.href}})}})),editor.on("input",(function(e){let position=getCaretPosition(!0);editor.caretPosition=position.caretPosition,editor.rePosition=position.rePosition;let aiContent=e.data;("insertReplacementText"===e.inputType||"insertText"===e.inputType&&aiContent&&aiContent.length>1)&&(aiContents.push(aiContent),e.key="ai",e.keyCode=0,e.caretPosition=position.caretPosition,e.rePosition=position.rePosition,e.aiContent=aiContent,sendKeyEvent("aiInsert",e))})),window.addEventListener("unload",(()=>{syncData()})),setInterval(syncData,syncInterval)}})); //# sourceMappingURL=autosaver.min.js.map \ No newline at end of file diff --git a/amd/build/autosaver.min.js.map b/amd/build/autosaver.min.js.map index 49c0c3a3..5f51e35f 100644 --- a/amd/build/autosaver.min.js.map +++ b/amd/build/autosaver.min.js.map @@ -1,5 +1 @@ -<<<<<<< Updated upstream -{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (modulename !== 'assign') {\n PASTE_SETTING = 'cite_source';\n }\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","querySelector","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"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"} -======= -{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n if (!originalText) {\n originalText = getRawText(editor);\n }\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n\n /**\n * Gets the raw text content from a TinyMCE editor iframe\n * @param {Object} editor - The TinyMCE editor instance\n * @returns {string} The raw text content of the editor body, or empty string if not found\n * @description Attempts to get the raw text content from the editor's iframe body by:\n * 1. Getting the editor ID\n * 2. Finding the associated iframe element\n * 3. Accessing the iframe's document body\n * 4. Returning the text content\n * Returns empty string if any step fails\n */\n function getRawText(editor) {\n let editorId = editor?.id;\n if (editorId) {\n let iframe = document.querySelector(`#${editorId}_ifr`);\n let iframeBody = iframe.contentDocument?.body || iframe.contentWindow?.document?.body;\n return iframeBody?.textContent;\n }\n return \"\";\n }\n\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","editorId","iframe","querySelector","iframeBody","contentDocument","contentWindow","_iframe$contentWindow","_iframe$contentWindow2","getRawText","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"ooBAgCwB,CAACA,OAAQC,SAAUC,OAAQC,UAAWC,QAASC,QAASC,WAAYC,SAAUC,oBAE9FC,YAAc,mBAAE,SAASC,SAAS,iBAClCC,cAAe,mBAAE,SAASD,SAAS,gBACnCE,KAAOC,EAAEC,IAAIC,QACbC,OAASd,OACTe,SAAWJ,EAAEC,IAAII,SACjBC,SAAWnB,MAAAA,cAAAA,OAAQoB,GACnBC,KAAOR,EAAEC,IAAIQ,kBACbC,GAAK,GACLC,MAAQ,GACRC,SAAW,GACXC,WAAa,EACbC,YAAa,mBAAE,0BACfC,cAAe,mBAAE,wBACjBC,aAAe5B,SAAsB,IAAXA,SAAkB,IAC5C6B,aAAe,MACfC,WAAa,OACbC,cAAe,EACfC,KAAO,SACPC,GAAKC,OAAOC,SAASC,KACrBC,KAAO,IAAIC,IAAIL,IACfM,qBAivBoBN,GAAII,KAAMlC,uBA0CzBqC,aAAaC,QAAQ,YACtBC,QAAQC,IAAI,EACR,mBAAU,aAAc,iBACxB,mBAAU,aAAc,iBACxB,mBAAU,aAAc,aACxB,mBAAU,aAAc,eACxB,mBAAU,cAAe,kBAC1BC,MAAK,SAASC,gBACNL,aAAaM,QAAQ,UAAWC,KAAKC,UAAUH,aACvDI,OAAMC,OAAShB,OAAOiB,QAAQD,MAAMA,SAEtCV,aAAaC,QAAQ,eACtBC,QAAQC,IAAI,EACR,mBAAU,UAAW,iBACrB,mBAAU,eAAgB,iBAC1B,mBAAU,WAAY,iBACtB,mBAAU,cAAe,iBACzB,mBAAU,aAAc,iBACxB,mBAAU,cAAe,iBACzB,mBAAU,iBAAkB,iBAC5B,mBAAU,UAAW,iBACrB,mBAAU,oBAAqB,iBAC/B,mBAAU,SAAU,iBACpB,mBAAU,QAAS,iBACnB,mBAAU,WAAY,iBACtB,mBAAU,gBAAiB,iBAC3B,mBAAU,WAAY,iBACtB,mBAAU,WAAY,iBACtB,mBAAU,aAAc,iBACxB,mBAAU,WAAY,iBACtB,mBAAU,UAAW,iBACrB,mBAAU,OAAQ,iBAClB,mBAAU,YAAa,iBACvB,mBAAU,SAAU,iBACpB,mBAAU,SAAU,iBACpB,mBAAU,MAAO,iBACjB,mBAAU,UAAW,iBACrB,mBAAU,YAAa,iBACvB,mBAAU,cAAe,iBACzB,mBAAU,aAAc,iBACxB,mBAAU,YAAa,kBACxBC,MAAK,SAASC,gBACNL,aAAaM,QAAQ,aAAcC,KAAKC,UAAUH,aAC1DI,OAAMC,OAAShB,OAAOiB,QAAQD,MAAMA,SApF3CE,IAEKjD,QAAQkD,MAAKC,QAAUrB,GAAGsB,SAASD,iBAC7B,EAIPE,WADAvB,GAAGsB,SAAS,WAAatB,GAAGsB,SAAS,UACxBlB,KAAKoB,aAAaC,IAAI,QAEtBrB,KAAKoB,aAAaC,IAAI,WAGpB,OAAfF,aACAA,WAAa,OAGZ,MAAMF,UAAUnD,WACb8B,GAAGsB,SAASD,QAAS,CACrBK,WAAaL,OACE,WAAXA,QAAkC,WAAXA,OACvBE,WAAapC,KACK,WAAXkC,SACPE,WAAa,gBAMzBI,sBAEO,CAACJ,WAAYA,WAAYK,KAAMF,YAhxBxBG,CAAe7B,GAAII,KAAMlC,aACvCqD,WAAajB,YAAYiB,WACzBG,WAAapB,YAAYsB,KACzBE,YAAa,MACbC,cAAgBzD,cAAgB,QAChC0D,kBAAmB,EACnBC,gBAAiB,EAEjBjC,GAAGsB,SAAS,iBACZY,SAASC,iBAAiB,SAASC,OACJ,iCAAvBA,EAAEC,OAAOC,UAA8C,KACnDpD,GAAKkD,EAAEC,OAAOnD,GAClBqC,WAAarC,GAAGqD,QAAQ,aAAc,IACtChC,aAAaM,QAAQ,YAAa,KAElB,kBAAhBuB,EAAEC,OAAOnD,IACTsD,oBAKNC,QAAUC,MAAMC,WAAYC,kBAEpBC,eAAiB,cAAK,CAAC,CACzBF,WAAAA,WACAC,KAAAA,QACA,UACAC,UACAC,YAAW,+BACEC,kBAAkB,WAC5B,KAEAF,SACT,MAAO5B,uCACI8B,kBAAkB,WAC3B9C,OAAOiB,QAAQD,MAAM,oBAAqBA,OACpCA,uBAIN,CAAC,CACD0B,WAAY,+BACZC,KAAM,CAACI,MAAO,KAAMC,OAAQ,CAACnE,YAC7B,GAAGoE,MAAKL,WACR9C,KAAO8C,SAAS,MACjBM,MAAMC,KACLnD,OAAOiB,QAAQD,MAAM,4BAA6BmC,OAG1D1D,aAAa2D,GAAG,SAASX,eAAeN,GACpCA,EAAEkB,iBACE/D,SAEAiD,WAAW7B,MAAK,KACZjB,aAAa6D,IAAI,SAASC,WAG9B9D,aAAa6D,IAAI,SAASC,QAE9BjD,aAAakD,WAAW,yBAG5BhE,WAAW4D,GAAG,SAASX,eAAeN,GAClCA,EAAEkB,iBACE/D,SAEAiD,WAAW7B,MAAK,KACZlB,WAAW8D,IAAI,SAASC,WAG5B/D,WAAW8D,IAAI,SAASC,QAE5BjD,aAAakD,WAAW,+BAGtBC,SAAW,KAEbjD,QAAQC,IAAI,EACR,mBAAU,sBAAuB,iBACjC,mBAAU,0BAA2B,iBACrC,mBAAU,2BAA4B,kBACvCC,MAAK,mBAAUgD,MAAOC,SAAUC,yBAExB,yBAAO,CACVC,KAAM,cACNH,MAAQ,6CAA4CA,8EACJC,wBAChDG,KAAO,gFAA+EF,2BACtFG,eAAe,IAEdd,MAAKe,QACFA,MAAMC,UAAUC,SAAS,sBACzBF,MAAMG,WACFC,UAAY,UAEhBJ,MAAMC,UAAUb,GAAGiB,oBAAM,eAEjBC,OAASrC,SAASsC,eAAe,YAAYC,MAAMC,OAExC,KAAXH,QAAAA,MAAiBA,QACjBzG,OAAO6G,YAAY,4BAET,eAAgB,gBAAgBhE,MAAKiE,KAAOC,MAAMD,QAE5D9G,OAAO6G,YAAY,SAGvBlC,QAAQ,wBAAyB,CAC7Bf,WAAYA,WACZvC,KAAMA,KACN2F,WAAYvD,WACZxC,SAAUA,SACVgG,YAAaR,OACbS,aAAcC,KAAKC,MACnBjG,SAAUA,UAAsB,KAGpCoF,UAAY,OACZJ,MAAMkB,aAEVlB,MAAMC,UAAUb,GAAG+B,sBAAQ,WACvBtH,OAAO6G,YAAY,QACnBN,UAAY,YAGhBJ,MAAMC,UAAUb,GAAGgC,sBAAQ,WACN,UAAbhB,WAAsC,QAAbA,WACzBvG,OAAO6G,YAAY,WAGpBV,YAEhBjD,OAAMC,OAAShB,OAAOiB,QAAQD,MAAMA,UAIrCqE,aAAe,CAACC,OAAQzH,aAC1BuB,GAAKvB,OACLwB,MAAQiG,OAERhG,SAAY,GAAET,UAAUyC,cAAcpC,QAAQuC,qBAE3B,SAAfA,aACAlC,WAAaP,SAASuG,MAAM,KAAK,GAAGA,MAAM,KAAK,GAC/CjG,SAAY,GAAET,UAAUyC,cAAcpC,QAAQK,cAAckC,sBAG5DnB,aAAaC,QAAQjB,UAAW,KAC5BkG,KAAO3E,KAAK4E,MAAMnF,aAAaC,QAAQjB,WAC3CkG,KAAKE,KAAK,CACNpE,WAAYA,WACZqE,IAAK9H,OAAO8H,IACZC,QAAS/H,OAAO+H,QAChBvG,MAAOA,MACPN,SAAUD,SACV+G,cAAeb,KAAKC,MACpBa,SAAUrH,KACVsH,SAAUlH,OACVmH,SAAU5G,GAAG6G,cACbC,WAAY9G,GAAG8G,WACfC,cAAetI,OAAOsI,cACtBC,UAAWvI,OAAOuI,YAEtB9F,aAAaM,QAAQtB,SAAUuB,KAAKC,UAAU0E,WAC3C,KACCA,KAAO,CAAC,CACRlE,WAAYA,WACZqE,IAAK9H,OAAO8H,IACZC,QAAS/H,OAAO+H,QAChBvG,MAAOA,MACPN,SAAUD,SACV+G,cAAeb,KAAKC,MACpBa,SAAUrH,KACVsH,SAAUlH,OACVmH,SAAU5G,GAAG6G,cACbC,WAAY9G,GAAG8G,WACfC,cAAetI,OAAOsI,cACtBC,UAAWvI,OAAOuI,YAEtB9F,aAAaM,QAAQtB,SAAUuB,KAAKC,UAAU0E,kBAgN7Ca,oBAAoBxI,YACrBmI,SAAWM,kBAAiB,GAChCzI,OAAOoI,cAAgBD,SAASC,cAChCpI,OAAOqI,WAAaF,SAASE,WAC7BrI,OAAO8H,aASa9H,eAEZA,OAAO0I,aACN,QACM,YACN,QACM,cACN,QACM,eAER,KAnBMC,CAAe3I,QAC5BA,OAAO+H,QAAU/H,OAAO0I,gBA6BnBD,uBAAiBG,qEAEb5I,SAAWA,OAAO6I,gBAChB,CAACT,cAAe,EAAGC,WAAY,SAGhCS,MAAQ9I,OAAO6I,UAAUE,SACzB9C,KAAOjG,OAAOgJ,UAGdC,cAAgBH,MAAMI,aAC5BD,cAAcE,mBAAmBlD,MACjCgD,cAAcG,OAAON,MAAMO,aAAcP,MAAMQ,iBAEzCC,SAAWN,cAAcO,gBACzBC,QAAUrF,SAASsF,cAAc,OACvCD,QAAQE,YAAYJ,cAChBK,iBAAmBH,QAAQI,WAAa,SAEtCR,aAAeP,MAAMO,aAGT,IAFAP,MAAMQ,WAGpBD,aAAaS,WAAaC,KAAKC,cAC/BhK,OAAOiK,IAAIC,QAAQb,eACnBA,aAAac,kBACbP,kBAAoB,YAElBQ,cAAgBX,QAAQY,iBAAiB,0CAC3CC,gBAAkB,EACtBF,cAAcG,SAAQC,QAEF,MADPA,MAAMX,WAAaW,MAAMC,aAAe,IAC5C7D,QAA6C,IAA5B4D,MAAME,WAAWC,QACN,OAAjCH,MAAME,WAAW,GAAGE,UACpBN,qBAKAA,gBAAkB,IACtBV,kBAAoB,KAAKiB,OAAOP,wBAG1BQ,iBAAmBlB,iBAAiBe,UAEtC/B,WACG,CACHR,cAAetG,aACfuG,WAAYyC,wBAIVC,WAAc,GAAE/J,UAAUyC,cAAcpC,oBAC1C2J,UAAYC,SAASC,eAAexI,QAAQqI,YAAa,WACzDI,MAAMH,aACVA,UAAY,GAEZA,YACAlJ,aAAekJ,UACfE,eAAenI,QAAQgI,WAAYC,WAE5B,CACP5C,cAAe4C,UACf3C,WAAYyC,kBAGd,MAAOxG,UACLnC,OAAOiB,QAAQgI,KAAK,gCAAiC9G,GAC9C,CAAC8D,cAAetG,cAAgB,EAAGuG,WAAY,mBAa/C3D,WACXb,0BACI8D,KAAOlF,aAAaC,QAAQjB,aAE3BkG,MAAwB,IAAhBA,KAAKgD,OAEX,CACHlI,aAAakD,WAAWlE,UACxBzB,OAAOqL,KAAK,cACRC,aAAetL,OAAOuL,WAAW,CAACC,OAAQ,SACzCF,eACDA,sBAiCQtL,YACZyL,SAAWzL,MAAAA,cAAAA,OAAQoB,MACnBqK,SAAU,4EACNC,OAAStH,SAASuH,cAAe,IAAGF,gBACpCG,0CAAaF,OAAOG,8EAAiB5F,sCAAQyF,OAAOI,+EAAPC,sBAAsB3H,kDAAtB4H,uBAAgC/F,aAC1E2F,MAAAA,kBAAAA,WAAYnB,kBAEhB,GAxCgBwB,CAAWjM,8CAGjBiF,kBAAkB,gBAEdN,QAAQ,8BAA+B,CAChDmD,IAAKvG,GAAGuG,IACRtG,MAAOA,MACPuG,QAASxG,GAAGwG,QACZtE,WAAYA,WACZpC,KAAMA,KACNuC,WAAYA,WACZzC,SAAUA,mBACGwG,KACb2D,aAAcA,eAEpB,MAAOnI,OACLhB,OAAOiB,QAAQD,MAAM,yBAA0BA,kBAgClD+I,0BAEKC,mCAmDNC,YACAC,iBACM1J,QAAQC,IAAI,EAClB,mBAAU,uBAAwB,iBAClC,mBAAU,2BAA4B,wBAEnC,CAACwJ,YAAAA,YAAaC,UAAAA,WAzDGC,GACdC,WAAanI,SAASiG,iBAAiB,uCACzCmC,WAAa,GAEbD,WAAW5B,QACX4B,WAAWhC,SAAQ,SAASkC,QAASC,WAE7BlI,UAAY,iBADhBkI,OAAS,GAETD,QAAQE,UAAUC,IAAIpI,WACtBgI,WAAW3E,KAAKrD,oBAIlBqI,YAAczI,SAASsF,cAAc,OAC3CmD,YAAYC,IAAM3M,UAAY4M,gBAAUC,oBAExCH,YAAYI,aAAa,QAAS,4BAClCJ,YAAYK,MAAMC,QAAU,wBAiDdN,YAAaN,WAAYC,gBACtCD,sBAIA,IAAIG,SAASF,WAAY,OACpBY,aAAehJ,SAASsF,cAAc,OACtC2D,WAAajJ,SAASsF,cAAc,QACpC4D,UAAYT,YAAYU,WAAU,GAClCC,WAAapJ,SAASuH,cAAc,IAAMa,WAAWE,YACvDe,UAAY,yBAA2Bf,MAE3CU,aAAaF,MAAMQ,QAAW,2JAM9BL,WAAWjM,GAAKqM,UAChBJ,WAAWH,MAAMS,WAAa,QAC9BN,WAAW1D,YAAY2D,WACvBF,aAAazD,YAAY0D,gBAErBO,UAAY,CACZnK,WAAYA,WACZpC,KAAMA,KACNuC,WAAYA,WACZlC,WAAYA,WACZV,OAAQA,OACRC,SAAUA,cAEVe,cAAgC,WAAf4B,YAA0C,UAAfA,YAC1B,WAAfA,WAaA,GAAI5B,cAA+B,SAAf4B,WAAuB,kHAC1CiK,0CAAkB7N,OAAO8N,sEAAPC,kBAAkBrD,WAAW,oEAA7BsD,sBAAiCtD,WAAW,qEAA5CuD,uBAAgDvD,WAAW,4CAA3DwD,uBAA+DxD,WAAW,GAC5FyD,qCAAYnO,OAAO8N,+CAAPM,mBAAkB1D,WAAW,GACzCmD,iBACAA,gBAAgBQ,SAGhBF,YAAcA,UAAUxC,cAAe,sCACvCyB,aAAaF,MAAMoB,UAAY,MAC/BlK,SAASuH,cAAc,wCAAwC4C,QAAQnB,yCAElEoB,4CACAC,YAAYzO,OAAQoN,aAAcQ,UAAW5L,kBACnD,yEACC0M,QAAU1O,MAAAA,mCAAAA,OAAQ8N,uEAARa,mBAAmBC,SAAS,oEAA5BC,sBAAgCnE,WAAW,4CAA3CoE,uBAA+CpE,WAAW,MAEpE8C,aAAeA,WAAW7B,cAAe,IAAG8B,cAC5CD,WAAW7D,YAAYyD,cAGR,SAAfxJ,YAAyB8K,QAAS,KAC9BK,QAAUL,QAAQ/C,cAAc,sCAEhCoD,oCACSP,4CACAC,YAAYzO,OAAQ+O,MAAAA,eAAAA,QAASC,cAAepB,UAAW5L,8CAG3DwM,4CACAC,YAAYzO,OAAQoN,aAAcQ,UAAW5L,kBA1C7B,KACzBiN,cAAgB7K,SAASuH,cAAc,8CACvCsD,eACAA,cAAcZ,SAGbjK,SAASuH,cAAe,IAAG8B,eAC5BL,aAAaF,MAAMoB,UAAY,MAC/BlK,SAASuH,cAAc,wCAAwC4C,QAAQnB,yCAGlEoB,4CACAC,YAAYzO,OAAQoN,aAAcQ,UAAW5L,gBA3F1DkN,CAAarC,YAAaN,WAAYC,gBAEjC,IAAIE,SAASF,WAAY,OACpBiB,UAAY,yBAA2Bf,MACvCyC,UAAa,uBAAsBzC,QAEzCP,YAAYtJ,MAAMuM,MACPC,WAAWD,KAAMhL,SAASuH,cAAe,IAAG8B,aAAc0B,aAClEjM,OAAMC,OAAShB,OAAOiB,QAAQD,MAAMA,6BAEpC,IAAGsK,aAAalI,GAAG,cAAc,+BAC9B+J,MAAMC,IAAI,WAAY,gCACrB,IAAGJ,aAAaI,IAAIC,2CAGxB,IAAG/B,aAAalI,GAAG,cAAc,+BAC7B,IAAG4J,aAAaI,IAAI,UAAW,YAG5C,MAAOpM,OACLhB,OAAOiB,QAAQD,MAAM,mCAAoCA,iBAmHxDkM,WAAWD,KAAMvC,YAAasC,eAE/B/K,SAASuH,cAAe,IAAGwD,cAG3BtC,YAAa,OAEP4C,YAAcrL,SAASsF,cAAc,QACrCgG,YAActL,SAASsF,cAAc,QACrCiG,UAAYvL,SAASsF,cAAc,MACnCkG,aAAexL,SAASsF,cAAc,UAE5C+F,YAAYvC,MAAMC,QAAU,OAC5ByC,aAAanF,YAAc2E,KAAKhD,YAChCwD,aAAa1C,MAAM2C,SAAW,OAC9BD,aAAa1C,MAAM4C,WAAa,OAChCJ,YAAYjF,YAAc2E,KAAK/C,UAC/BqD,YAAYxC,MAAM2C,SAAW,OAE7BJ,YAAYrO,GAAK+N,UACjBM,YAAY9C,UAAUC,IAAK,UAC3B6C,YAAY9F,YAAYiG,cACxBH,YAAY9F,YAAYgG,WACxBF,YAAY9F,YAAY+F,aACxB7C,YAAYlD,YAAY8F,uBA6GtB5L,sBACF3B,GAAGsB,SAAS,kBAERC,WADc,4BAAdzD,OAAOoB,IAAoC6J,SAASxI,aAAaC,QAAQ,cAC5DuI,SAASjL,MAAAA,cAAAA,OAAQoB,GAAGqD,QAAQ,WAAY,KAExC,GAjqBzBzE,OAAOuF,GAAG,SAAUvF,SAChBkM,oBACI/D,SAAWM,kBAAiB,GAChCzI,OAAOoI,cAAgBD,SAASC,cAChCpI,OAAOqI,WAAaF,SAASE,WAC7Bb,aAAa,QAASxH,WAE1BA,OAAOuF,GAAG,SAASX,MAAAA,IACfsH,sBACM5D,eAAiBhE,EAAEyL,eAAiBzL,EAAE0L,cAAcD,eAAeE,QAAQ,YAC5E3H,2BAIC4H,qBAAuB5H,cAAc1B,OACrCuJ,mBAAqB1N,aAAaC,QAAQ,sBAC1C0N,gBAAkBD,oBAAsBD,uBAAyBC,sBAEnE1P,WAAaE,aAAc,IAEL,UAAlBsD,qBACKmM,iBAeLlM,kBAAmB,OACnBC,gBAAiB,KAfbG,EAAEkB,iBACFtB,kBAAmB,EACnBC,gBAAiB,EACjBG,EAAE+L,kBACF/L,EAAEgM,+CACQ,gBAAiB,gBAAgBzN,MAAKiE,KACtC9G,OAAOuQ,cAAcxJ,MAAMD,OAClC5D,OAAMC,OAAShB,OAAOiB,QAAQD,MAAMA,cACvC6B,YAAW,KACPb,gBAAiB,EACjBD,kBAAmB,IACpB,SAOW,gBAAlBD,qBACKmM,kBACD9L,EAAEkB,iBACFlB,EAAE+L,kBACF/L,EAAEgM,2BACF1K,iBAEJzB,gBAAiB,GAIzBA,gBAAiB,KAErBnE,OAAOuF,GAAG,QAAQX,MAAAA,IACdsH,gBACIzL,WAAaE,cACbiF,cAGR5F,OAAOuF,GAAG,WAAYvF,SAClBkM,oBACuC,MAAflM,OAAO8H,KAA8B,MAAf9H,OAAO8H,OACpD9H,OAAOwQ,SAAWxQ,OAAOyQ,UACJhQ,WAAaE,cAAkC,UAAlBsD,gBAA8BE,2BAC7Ea,YAAW,KACPb,gBAAiB,IAClB,SAGHgE,SAAWM,mBACfzI,OAAOoI,cAAgBD,SAASC,cAChCpI,OAAOqI,WAAaF,SAASE,WAC7Bb,aAAa,UAAWxH,WAE5BA,OAAOuF,GAAG,OAAO,WACPmL,gBAAkB1Q,OAAO6I,UAAU0C,WAAW,CAACC,OAAQ,SAC7D/I,aAAaM,QAAQ,qBAAsB2N,gBAAgB9J,WAE/D5G,OAAOuF,GAAG,QAAQ,WACRmL,gBAAkB1Q,OAAO6I,UAAU0C,WAAW,CAACC,OAAQ,SAC7D/I,aAAaM,QAAQ,qBAAsB2N,gBAAgB9J,WAE/D5G,OAAOuF,GAAG,aAAaX,MAAAA,SACnBI,YAAW,KACPwD,oBAAoBxI,QACpBwH,aAAa,YAAaxH,UAC3B,MAEPA,OAAOuF,GAAG,WAAWX,MAAAA,SACjBI,YAAW,KACPwD,oBAAoBxI,QACpBwH,aAAa,UAAWxH,UACzB,OAEPA,OAAOuF,GAAG,QAAQ,KACd2G,gBACAzJ,aAAakD,WAAW,yBAE5B3F,OAAOuF,GAAG,cAAc,KACpB2G,mBAEJlM,OAAOuF,GAAG,0BAA2BjB,QAC7BqM,KAAO,IAAIC,uBAAa3O,KAAM5B,QAASC,WAAYsD,WAAY5D,OAAQO,UAC3EyB,aAAesC,EAAEuM,UAERvM,EAAEuM,MAGHF,KAAKG,eAFLH,KAAKI,aAIX,MAAO5N,OACDa,aACAA,YAAa,sBACH,gBAAiB,gBAAgBnB,MAAKiE,KACrC9G,OAAOuQ,cAAcxJ,MAAMD,OACnC5D,OAAMC,OAAShB,OAAOiB,QAAQD,MAAMA,UAE3CwN,KAAKI,aACL5O,OAAOiB,QAAQD,MAAM,4BAA6BA,WAI1DnD,OAAOuF,GAAG,eAAe,SAASjB,MACZ,qBAAdA,EAAE0M,QAAgC,OAC5BC,WAAa3M,EAAEqC,MAEfuK,QAAUD,YAAoC,iBAAfA,aAAgD,IAArBA,WAAWE,UAEvEC,gBAAkBH,WAAWI,SAAWJ,WACxCxH,QAAUrF,SAASsF,cAAc,OACrCD,QAAQ6H,UAAYF,oBAChBhC,KAAO3F,QAAQgB,aAAehB,QAAQI,WAAa,GACnD0H,WAAa9H,QAAQgB,aAAehB,QAAQI,WAAa,GAEzD1B,SAAWM,kBAAiB,MAChCzI,OAAOoI,cAAgBD,SAASC,cAChCpI,OAAOqI,WAAaF,SAASE,WAEzB6I,QAAS,IACLhN,wBACAA,kBAAmB,EACnBI,EAAEkB,sBACFxF,OAAOwR,YAAYC,aAGjBtB,mBAAqB1N,aAAaC,QAAQ,sBAC1C0N,gBAAkBD,oBAAsBoB,WAAW3K,SAAWuJ,sBAEhE1P,WAAaE,cAAkC,UAAlBsD,gBAA8BmM,uBAC3DjM,gBAAiB,OACjBnE,OAAOwR,YAAYC,OAIvBjK,aAAa,QAAS,CAClBM,IAAK,IACLC,QAAS,GACTK,cAAepI,OAAOoI,cACtBC,WAAYrI,OAAOqI,WACnBC,cAAeiJ,WACfG,WAAY,CAACC,QAASxP,OAAOC,SAASC,aAG1CN,WAAW8F,KAAKuH,MAEhB5H,aAAa,WAAY,CACrBM,IAAK,KACLC,QAAS,EACTK,cAAepI,OAAOoI,cACtBC,WAAYrI,OAAOqI,WACnBE,UAAW6G,KACXsC,WAAY,CAACC,QAASxP,OAAOC,SAASC,YAMtDrC,OAAOuF,GAAG,SAAS,SAASjB,OACpB6D,SAAWM,kBAAiB,GAChCzI,OAAOoI,cAAgBD,SAASC,cAChCpI,OAAOqI,WAAaF,SAASE,eACzBE,UAAYjE,EAAEqD,MAEE,0BAAhBrD,EAAEsN,WAA0D,eAAhBtN,EAAEsN,WAA8BrJ,WAAaA,UAAUoC,OAAS,KAE5G5I,WAAW8F,KAAKU,WAEhBjE,EAAEwD,IAAM,KACRxD,EAAEyD,QAAU,EACZzD,EAAE8D,cAAgBD,SAASC,cAC3B9D,EAAE+D,WAAaF,SAASE,WACxB/D,EAAEiE,UAAYA,UAEdf,aAAa,WAAYlD,OAqejCnC,OAAOkC,iBAAiB,UAAU,KAC9BK,cAGJmN,YAAYnN,SAAU7C"} ->>>>>>> Stashed changes +{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (modulename !== 'assign') {\n PASTE_SETTING = 'cite_source';\n }\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n if (!originalText) {\n originalText = getRawText(editor);\n }\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n\n /**\n * Gets the raw text content from a TinyMCE editor iframe\n * @param {Object} editor - The TinyMCE editor instance\n * @returns {string} The raw text content of the editor body, or empty string if not found\n * @description Attempts to get the raw text content from the editor's iframe body by:\n * 1. Getting the editor ID\n * 2. Finding the associated iframe element\n * 3. Accessing the iframe's document body\n * 4. Returning the text content\n * Returns empty string if any step fails\n */\n function getRawText(editor) {\n let editorId = editor?.id;\n if (editorId) {\n let iframe = document.querySelector(`#${editorId}_ifr`);\n let iframeBody = iframe.contentDocument?.body || iframe.contentWindow?.document?.body;\n return iframeBody?.textContent;\n }\n return \"\";\n }\n\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","editorId","iframe","querySelector","iframeBody","contentDocument","contentWindow","_iframe$contentWindow","_iframe$contentWindow2","getRawText","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/document_view.min.js b/amd/build/document_view.min.js index ce7b48b7..dfc9c4cb 100644 --- a/amd/build/document_view.min.js +++ b/amd/build/document_view.min.js @@ -5,6 +5,6 @@ define("tiny_cursive/document_view",["exports","tiny_cursive/svg_repo"],(functio * @module tiny_cursive/document_view * @copyright 2025 Cursive Technology, Inc. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_svg_repo=(obj=_svg_repo)&&obj.__esModule?obj:{default:obj};return _exports.default=class{constructor(User,Rubrics,submission,modulename,editor,quizInfo){this.User=User,this.Rubrics=Rubrics,this.submission=submission,this.module=modulename,this.editor=editor,this.moduleIcon=_svg_repo.default.assignment,this.quizInfo=quizInfo,this.initStrings()}normalMode(){var _this$editor;let id=(null===(_this$editor=this.editor)||void 0===_this$editor?void 0:_this$editor.id)+"_ifr";("assign"===this.module||"quiz"===this.module||"forum"===this.module||"lesson"===this.module||"pdfannotator"===this.module)&&this.normalizePage(id)}fullPageMode(){var _this$editor4,_this$editor2;if("assign"===this.module)this.moduleIcon=_svg_repo.default.assignment,this.fullPageModule(null===(_this$editor2=this.editor)||void 0===_this$editor2?void 0:_this$editor2.id);else if("forum"===this.module){var _this$editor3;this.moduleIcon=_svg_repo.default.forum,this.fullPageModule(null===(_this$editor3=this.editor)||void 0===_this$editor3?void 0:_this$editor3.id)}else if("quiz"===this.module&&null!==(_this$editor4=this.editor)&&void 0!==_this$editor4&&_this$editor4.id){var _this$editor5;this.moduleIcon=_svg_repo.default.quiz,this.fullPageModule(null===(_this$editor5=this.editor)||void 0===_this$editor5?void 0:_this$editor5.id)}else if("lesson"===this.module){var _this$editor6;this.moduleIcon=_svg_repo.default.lesson,this.fullPageModule(null===(_this$editor6=this.editor)||void 0===_this$editor6?void 0:_this$editor6.id)}else if("pdfannotator"===this.module){var _this$editor7;this.moduleIcon=_svg_repo.default.pdfannotator,this.fullPageModule(null===(_this$editor7=this.editor)||void 0===_this$editor7?void 0:_this$editor7.id)}}docSideBar(status){var _this$editor8;const replyId=new URL(window.location.href).searchParams.get("reply"),toggle=document.querySelector("#cursive-fullpagemode-sidebar-toggle"),timelimitBlock=this.getTimerBlock(this.module),headerInfo=this.getSidebarTitle(),progressBar=document.querySelector(".box.progress_bar"),courseName=document.querySelector("#page-navbar > nav > ol > li:nth-child(1) > a"),courseDes=document.querySelector("#intro"),Dates=document.querySelector(".activity-dates");let openDate=null==Dates?void 0:Dates.querySelector("div:nth-child(1)"),dueDate=null==Dates?void 0:Dates.querySelector("div:nth-child(2)");const container=this.create("div");Object.assign(container,{id:"cursive-fullpagemode-sidebar",className:"bg-white h-100 shadow"}),Object.assign(container.style,{width:"300px",overflow:"auto"});const crossBtn=this.create("span");Object.assign(crossBtn,{id:"cursive-collapse-sidebar",className:"btn p-2",innerHTML:_svg_repo.default.close}),crossBtn.addEventListener("click",(()=>{container.style.transition="width 0.3s ease",container.style.width="0",toggle.style.display="flex"})),null==toggle||toggle.addEventListener("click",(function(){toggle.style.display="none",container.style.width="300px"}));const btnWrapper=this.create("div");Object.assign(btnWrapper,{padding:"0 1rem",position:"sticky",top:"0",backgroundColor:"white"}),btnWrapper.append(crossBtn);const header=this.create("div");header.className="border-bottom p-3 bg-light",Object.assign(header.style,{position:"sticky",top:"0"});const headerTitle=this.create("h3");headerTitle.className="mb-3 d-flex align-items-center",headerTitle.textContent=`${headerInfo.title} ${this.details}`,headerTitle.style.fontWeight="600";const headerIcon=document.querySelector(".page-header-image > div");headerIcon&&headerTitle.prepend(headerIcon.cloneNode(!0));let wordCount=this.wordCounter(status);null!=timelimitBlock&&timelimitBlock.textContent?header.append(headerTitle,wordCount,this.timerCountDown(timelimitBlock)):header.append(headerTitle,wordCount);const content=this.create("div");if(content.className="p-3",content.append(this.createBox({bg:"bg-info",titleColor:"text-info",icon:_svg_repo.default.people,title:this.studentInfo,bodyHTML:this.generateStudentInfo(this.User,courseName)})),"lesson"===this.module&&progressBar&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.progress,bodyHTML:progressBar.innerHTML})),courseDes&&""!==(null==courseDes?void 0:courseDes.textContent.trim())&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.getSidebarTitle().title} ${this.description}`,bodyHTML:courseDes.innerHTML})),"forum"===this.module&&replyId){this.checkForumSubject();let replyPost=document.querySelector(`#post-content-${replyId}`);null!=replyPost&&replyPost.textContent.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.replyingto,bodyHTML:replyPost.textContent.trim()}))}if("quiz"===this.module&&null!==(_this$editor8=this.editor)&&void 0!==_this$editor8&&_this$editor8.id){var _this$editor9;let questionId=this.getQuestionId(null===(_this$editor9=this.editor)||void 0===_this$editor9?void 0:_this$editor9.id),question=document.querySelector(`#question-${questionId} .qtext`),intro=atob(this.quizInfo.intro);null!=question&&question.textContent.trim()&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:this.moduleIcon,title:this.answeringto,bodyHTML:question.textContent})),intro&&""!==intro.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.quiz} ${this.description}`,bodyHTML:intro})),Number(this.quizInfo.open)&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(Number(this.quizInfo.open),Number(this.quizInfo.close))}))}return Object.keys(this.Rubrics).length&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.rubrics,bodyHTML:this.generateRubrics(this.Rubrics)})),Dates&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(openDate,dueDate)})),"assign"===this.module&&content.append(this.createBox({bg:"bg-green",titleColor:"text-success",icon:this.moduleIcon,title:this.subStatus,bodyHTML:this.submissionStatus(this.submission)})),container.append(btnWrapper,header,content),container}createBox(_ref){let{bg:bg,titleColor:titleColor,icon:icon,title:title,bodyHTML:bodyHTML}=_ref;const box=this.create("div");box.className=`tiny_cursive-fullpage-card ${bg}`;const heading=this.create("h4");heading.className=`tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`,heading.innerHTML=`${icon} ${title}`;const body=this.create("div");return body.className="tiny_cursive-fullpage-card-body",body.innerHTML=bodyHTML,box.append(heading,body),box}generateRubrics(Rubrics){const wrapper=this.create("div");return Rubrics.forEach((rubric=>{const rubricDiv=this.create("div");rubricDiv.className="tiny_cursive-rubric-card";const title=this.create("h3");title.className="tiny_cursive-rubric-title",title.textContent=rubric.description,rubricDiv.appendChild(title),Object.values(rubric.levels).forEach((level=>{const levelDiv=this.create("div"),score=Number(level.score);levelDiv.className=0===score?"tiny_cursive-rubric-level tiny_cursive-rubric-low":score<=2?"tiny_cursive-rubric-level tiny_cursive-rubric-mid":"tiny_cursive-rubric-level tiny_cursive-rubric-high",levelDiv.textContent=`${level.definition} / ${level.score}`,rubricDiv.appendChild(levelDiv)})),wrapper.appendChild(rubricDiv)})),wrapper.innerHTML}submissionStatus(submission){var _submission$current,_submission$current2;const wrapper=this.create("div"),statusWrapper=this.create("div");statusWrapper.className="tiny_cursive-status-row";const statusName=this.create("span");statusName.textContent=`${this.status}:`;const statusValue=this.create("span"),isNew="new"===(null==submission||null===(_submission$current=submission.current)||void 0===_submission$current?void 0:_submission$current.status);statusValue.textContent=isNew?this.draftnot:this.draft,statusValue.className="tiny_cursive-status-value "+(isNew?"tiny_cursive-status-red":"tiny_cursive-status-green"),statusWrapper.append(statusName,statusValue);const modifiedWrapper=this.create("div");modifiedWrapper.className="tiny_cursive-status-row";const modifiedName=this.create("span");modifiedName.textContent=`${this.lastModified}: `;const modifiedValue=this.create("span");if(null!=submission&&null!==(_submission$current2=submission.current)&&void 0!==_submission$current2&&_submission$current2.timemodified){const date=new Date(1e3*submission.current.timemodified);modifiedValue.textContent=this.formatDate(date)}else modifiedValue.textContent="N/A";modifiedWrapper.append(modifiedName,modifiedValue);const gradeWrapper=this.create("div");gradeWrapper.className="tiny_cursive-status-row";const gradeName=this.create("span");gradeName.textContent=`${this.gradings}: `;const gradeValue=this.create("span");return null!=submission&&submission.grade?gradeValue.textContent=Number(submission.grade.grade)>0?submission.grade.grade:this.gradenot:gradeValue.textContent=this.gradenot,gradeWrapper.append(gradeName,gradeValue),wrapper.append(statusWrapper,gradeWrapper,modifiedWrapper),wrapper.innerHTML}wordCounter(status){const wordCount=this.create("div"),labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");icon.className="me-2",icon.innerHTML=_svg_repo.default.assignment,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.wordCount}:`,value.textContent="0",value.className="text-primary",value.style.fontWeight="600",value.style.fontSize="14px",wordCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2",wordCount.append(labelDiv,value),wordCount.style.fontSize="12px";return new MutationObserver((()=>{const newText=status.textContent.trim();value.textContent=`${newText.replace("words","")}`})).observe(status,{characterData:!0,subtree:!0,childList:!0}),wordCount}timerCountDown(timer){let warningDiv=document.querySelector("#user-notifications > div");if(warningDiv){var _clone$querySelector;let clone=warningDiv.cloneNode(!0);null===(_clone$querySelector=clone.querySelector("button"))||void 0===_clone$querySelector||_clone$querySelector.remove(),this.editor.notificationManager.open({text:clone.textContent,type:"error"})}const timerCount=this.create("div");timerCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2";const labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");if(icon.innerHTML=_svg_repo.default.time,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.timeleft}:`,value.textContent="00:00:00",value.className=warningDiv?"text-danger":"text-primary",Object.assign(value.style,{fontWeight:"600",fontSize:"14px"}),timerCount.append(labelDiv,value),timerCount.style.fontSize="12px",timer){new MutationObserver((()=>{const newText=timer.textContent.trim();value.textContent=`${newText}`})).observe(timer,{characterData:!0,subtree:!0,childList:!0})}else value.textContent=this.nolimit;return timerCount}generateStudentInfo(user,course){const wrapper=this.create("div"),nameWrapper=this.create("div"),usernameWrapper=this.create("div"),courseWrapper=this.create("div"),nameLabel=this.create("strong"),nameValue=this.create("span"),usernameLabel=this.create("strong"),usernameValue=this.create("span"),courseLabel=this.create("strong"),courseValue=this.create("span");return nameLabel.textContent=`${this.name}`,nameValue.textContent=user.fullname,usernameLabel.textContent=`${this.userename}: `,usernameValue.textContent=user.username,courseLabel.textContent=`${this.course}: `,courseValue.textContent=course.title,usernameLabel.className="cfw-bold me-2",usernameValue.className="cursiveFw-wrap",courseLabel.className="cfw-bold me-2",courseValue.className="cursiveFw-wrap",nameLabel.className="cfw-bold me-2",nameValue.className="cursiveFw-wrap",nameWrapper.append(nameLabel,nameValue),usernameWrapper.append(usernameLabel,usernameValue),courseWrapper.append(courseLabel,courseValue),wrapper.append(nameWrapper,usernameWrapper,courseWrapper),wrapper.innerHTML}generateImportantDates(open,due){const wrapper=this.create("div");let openDate=null,dueDate=null;const openedWrapper=this.create("div"),dueWrapper=this.create("div"),remainingWrapper=this.create("div"),openedLabel=this.create("span"),openedValue=this.create("span"),dueLabel=this.create("span"),dueValue=this.create("span"),remainingLabel=this.create("span"),remainingValue=this.create("span");return"quiz"===this.module?(openDate=1e3*open,dueDate=1e3*due):(openDate=this.extractDate(null==open?void 0:open.textContent),dueDate=this.extractDate(null==due?void 0:due.textContent)),openedLabel.textContent=`${this.opened}: `,openedValue.textContent=this.formatDate(openDate?new Date(openDate):null),openedValue.className="text-dark",dueLabel.textContent=`${this.due}: `,dueValue.textContent=this.formatDate(dueDate?new Date(dueDate):null),dueValue.className="text-danger",remainingLabel.textContent=`${this.remaining}: `,remainingValue.textContent=this.calculateDate(dueDate),remainingValue.className="text-danger",openedWrapper.className="d-flex justify-content-between",dueWrapper.className="d-flex justify-content-between",remainingWrapper.className="d-flex align-items-center justify-content-between mt-2 pt-2 border-top",openedWrapper.append(openedLabel,openedValue),dueWrapper.append(dueLabel,dueValue),remainingWrapper.append(remainingLabel,remainingValue),wrapper.append(openedWrapper,dueWrapper,remainingWrapper),wrapper.innerHTML}formatDate(date){if(!date)return"-";return date.toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"numeric",minute:"numeric",hour12:!0})}extractDate(text){if(!text)return"-";const parts=null==text?void 0:text.split(":");return parts.length>1?parts.slice(1).join(":").trim():text.trim()}calculateDate(date){if(!date)return"-";const diffMs=new Date(date)-new Date;if(diffMs<=0)return"Overdue";return`${Math.floor(diffMs/864e5)} days, ${Math.floor(diffMs/36e5%24)} hours`}fullPageModule(module){var _current$contentDocum,_current$contentWindo,_current$contentWindo2,_document$getElementB;let current="quiz"===this.module?document.getElementById(`${module}_ifr`):document.querySelector(`#${module}_ifr`),p1=current.parentElement,p2=p1.parentElement,p4=p2.parentElement.parentElement,statusBar=document.querySelector(".tox-statusbar__right-container > button"),assignName=document.querySelector(".page-context-header"),header=this.create("div"),btn=null;if(assignName.classList.remove("mb-2"),header.id="tiny_cursive-fullpage-custom-header",Object.assign(header.style,{backgroundColor:"white",display:"flex",justifyContent:"space-between"}),"quiz"===this.module?(btn=document.querySelector("#mod_quiz-next-nav").cloneNode(!0),btn.className="tiny_cursive-fullpage-submit-btn",btn.style.margin=".5rem"):(btn=this.create("input"),btn.className="tiny_cursive-fullpage-submit-btn",btn.value=this.savechanges,btn.type="submit",btn.style.margin=".5rem"),"pdfannotator"===this.module){const style=document.createElement("style");style.id="cursiveForceStyle",style.textContent="\n .path-mod-pdfannotator #comment-wrapper h4,\n .path-mod-pdfannotator #comment-nav {\n margin: 0 !important;\n }\n ",document.head.appendChild(style)}const leftSide=this.create("div"),rightSide=this.create("div");let commonStyle={display:"flex",alignItems:"center",margin:"0 1rem"};Object.assign(leftSide.style,commonStyle),rightSide.id="tiny_cursive-fullpage-right-wrapper",Object.assign(rightSide.style,commonStyle),rightSide.appendChild(btn),leftSide.appendChild(assignName.cloneNode(!0)),header.appendChild(leftSide),header.appendChild(rightSide),p4.insertBefore(header,p4.firstChild),p2.style.backgroundColor="#efefef",Object.assign(current.style,{width:"750px",minWidth:"750px",boxShadow:"0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)"}),Object.assign(p1.style,{display:"flex",justifyContent:"center",outline:"none",margin:"2rem 0 0"});const style=this.create("style");style.id="tiny_cursive-fullpage-mode-style",style.textContent="\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }",document.head.appendChild(style);let iframeBody=(null===(_current$contentDocum=current.contentDocument)||void 0===_current$contentDocum?void 0:_current$contentDocum.body)||(null===(_current$contentWindo=current.contentWindow)||void 0===_current$contentWindo||null===(_current$contentWindo2=_current$contentWindo.document)||void 0===_current$contentWindo2?void 0:_current$contentWindo2.body);iframeBody&&(iframeBody.style.padding="0.5in"),p2.style.position="relative",null===(_document$getElementB=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB||_document$getElementB.remove();let toggle=this.create("div");toggle.id="cursive-fullpagemode-sidebar-toggle",toggle.innerHTML=_svg_repo.default.hamburger,p2.appendChild(toggle),p2.appendChild(this.docSideBar(statusBar))}normalizePage(editorId){var _document$getElementB2,_document$getElementB3,_current$contentDocum2,_current$contentWindo3,_current$contentWindo4,_document$head$queryS,_document$head$queryS2;null===(_document$getElementB2=document.getElementById("tiny_cursive-fullpage-custom-header"))||void 0===_document$getElementB2||_document$getElementB2.remove(),null===(_document$getElementB3=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB3||_document$getElementB3.remove();let current=document.getElementById(editorId),p1=current.parentElement,p2=p1.parentElement;Object.assign(p2.style,{backgroundColor:"",position:""}),Object.assign(current.style,{width:"",minWidth:"",boxShadow:""}),Object.assign(p1.style,{display:"",justifyContent:"",outline:"",margin:""}),p1.classList.remove("tiny-cursive-editor-container");let iframeBody=(null===(_current$contentDocum2=current.contentDocument)||void 0===_current$contentDocum2?void 0:_current$contentDocum2.body)||(null===(_current$contentWindo3=current.contentWindow)||void 0===_current$contentWindo3||null===(_current$contentWindo4=_current$contentWindo3.document)||void 0===_current$contentWindo4?void 0:_current$contentWindo4.body);iframeBody&&(iframeBody.style.padding="0"),null===(_document$head$queryS=document.head.querySelector("#tiny_cursive-fullpage-mode-style"))||void 0===_document$head$queryS||_document$head$queryS.remove(),null===(_document$head$queryS2=document.head.querySelector("#cursiveForceStyle"))||void 0===_document$head$queryS2||_document$head$queryS2.remove()}checkForumSubject(){const form=document.querySelector("#tiny_cursive-fullpage-right-wrapper > input"),msg=this.subjectnot;form&&form.addEventListener("click",(e=>{const subjectInput=document.getElementById("id_subject");let content=this.editor.getContent().trim();subjectInput&&""!==subjectInput.value.trim()&&""!==content||(e.preventDefault(),e.stopPropagation(),this.editor.windowManager.alert(msg))}))}getSidebarTitle(){const[assign,discus,quiz,lesson]=this.getText("sbTitle");switch(this.module){case"assign":return{title:assign,icon:_svg_repo.default.assignment};case"forum":return{title:discus,icon:_svg_repo.default.forum};case"lesson":return{title:lesson,icon:_svg_repo.default.forum};case"quiz":return{title:quiz,icon:_svg_repo.default.quiz};case"pdfannotator":return{title:"PDF Annotation",icon:_svg_repo.default.pdfannotator};default:return{title:"Page",icon:_svg_repo.default.quiz}}}getTimerBlock(module){switch(module){case"assign":return document.querySelector("#mod_assign_timelimit_block > div > div");case"forum":return document.querySelector("#mod_forum_timelimit_block");case"lesson":return document.querySelector("#lesson-timer");case"quiz":return document.querySelector("#quiz-time-left");default:return null}}getQuestionId(editoId){try{return editoId&&"string"==typeof editoId?editoId.replace(/^q(\d+):(\d+)_.*$/,"$1-$2"):""}catch(error){return window.console.error("Error getting question ID:",error),""}}initStrings(){[this.details,this.studentInfo,this.progress,this.description,this.replyingto,this.answeringto,this.importantdates,this.rubrics,this.subStatus,this.status,this.draft,this.draftnot,this.lastModified,this.gradings,this.gradenot,this.wordCount,this.timeleft,this.nolimit,this.name,this.userename,this.course,this.opened,this.due,this.overdue,this.remaining,this.savechanges,this.subjectnot]=this.getText("docSideBar")}getText(key){return JSON.parse(localStorage.getItem(key))||[]}create(tag){return document.createElement(tag)}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_svg_repo=(obj=_svg_repo)&&obj.__esModule?obj:{default:obj};return _exports.default=class{constructor(User,Rubrics,submission,modulename,editor,quizInfo){this.User=User,this.Rubrics=Rubrics,this.submission=submission,this.module=modulename,this.editor=editor,this.moduleIcon=_svg_repo.default.assignment,this.quizInfo=quizInfo,this.initStrings()}normalMode(){var _this$editor;let id=(null===(_this$editor=this.editor)||void 0===_this$editor?void 0:_this$editor.id)+"_ifr";("assign"===this.module||"quiz"===this.module||"forum"===this.module||"lesson"===this.module||"pdfannotator"===this.module)&&this.normalizePage(id)}fullPageMode(){var _this$editor4,_this$editor2;if("assign"===this.module)this.moduleIcon=_svg_repo.default.assignment,this.fullPageModule(null===(_this$editor2=this.editor)||void 0===_this$editor2?void 0:_this$editor2.id);else if("forum"===this.module){var _this$editor3;this.moduleIcon=_svg_repo.default.forum,this.fullPageModule(null===(_this$editor3=this.editor)||void 0===_this$editor3?void 0:_this$editor3.id)}else if("quiz"===this.module&&null!==(_this$editor4=this.editor)&&void 0!==_this$editor4&&_this$editor4.id){var _this$editor5;this.moduleIcon=_svg_repo.default.quiz,this.fullPageModule(null===(_this$editor5=this.editor)||void 0===_this$editor5?void 0:_this$editor5.id)}else if("lesson"===this.module){var _this$editor6;this.moduleIcon=_svg_repo.default.lesson,this.fullPageModule(null===(_this$editor6=this.editor)||void 0===_this$editor6?void 0:_this$editor6.id)}else if("pdfannotator"===this.module){var _this$editor7;this.moduleIcon=_svg_repo.default.pdfannotator,this.fullPageModule(null===(_this$editor7=this.editor)||void 0===_this$editor7?void 0:_this$editor7.id)}}docSideBar(status){var _this$editor8;const replyId=new URL(window.location.href).searchParams.get("reply"),toggle=document.querySelector("#cursive-fullpagemode-sidebar-toggle"),timelimitBlock=this.getTimerBlock(this.module),headerInfo=this.getSidebarTitle(),progressBar=document.querySelector(".box.progress_bar"),courseName=document.querySelector("#page-navbar > nav > ol > li:nth-child(1) > a"),courseDes=document.querySelector("#intro"),Dates=document.querySelector(".activity-dates");let openDate=null==Dates?void 0:Dates.querySelector("div:nth-child(1)"),dueDate=null==Dates?void 0:Dates.querySelector("div:nth-child(2)");const container=this.create("div");Object.assign(container,{id:"cursive-fullpagemode-sidebar",className:"bg-white h-100 shadow"}),Object.assign(container.style,{width:"300px",overflow:"auto"});const crossBtn=this.create("span");Object.assign(crossBtn,{id:"cursive-collapse-sidebar",className:"btn p-2",innerHTML:_svg_repo.default.close}),crossBtn.addEventListener("click",(()=>{container.style.transition="width 0.3s ease",container.style.width="0",toggle.style.display="flex"})),null==toggle||toggle.addEventListener("click",(function(){toggle.style.display="none",container.style.width="300px"}));const btnWrapper=this.create("div");Object.assign(btnWrapper,{padding:"0 1rem",position:"sticky",top:"0",backgroundColor:"white"}),btnWrapper.append(crossBtn);const header=this.create("div");header.className="border-bottom p-3 bg-light",Object.assign(header.style,{position:"sticky",top:"0"});const headerTitle=this.create("h3");headerTitle.className="mb-3 d-flex align-items-center",headerTitle.textContent=`${headerInfo.title} ${this.details}`,headerTitle.style.fontWeight="600";const headerIcon=document.querySelector(".page-header-image > div");headerIcon&&headerTitle.prepend(headerIcon.cloneNode(!0));let wordCount=this.wordCounter(status);null!=timelimitBlock&&timelimitBlock.textContent?header.append(headerTitle,wordCount,this.timerCountDown(timelimitBlock)):header.append(headerTitle,wordCount);const content=this.create("div");if(content.className="p-3",content.append(this.createBox({bg:"bg-info",titleColor:"text-info",icon:_svg_repo.default.people,title:this.studentInfo,bodyHTML:this.generateStudentInfo(this.User,courseName)})),"lesson"===this.module&&progressBar&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.progress,bodyHTML:progressBar.innerHTML})),courseDes&&""!==(null==courseDes?void 0:courseDes.textContent.trim())){let fileSubDiv=document.querySelectorAll(".fileuploadsubmission");fileSubDiv&&fileSubDiv.forEach((Element=>{Element.style.verticalAlign="middle"})),content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.getSidebarTitle().title} ${this.description}`,bodyHTML:courseDes.innerHTML}))}if("forum"===this.module&&replyId){this.checkForumSubject();let replyPost=document.querySelector(`#post-content-${replyId}`);null!=replyPost&&replyPost.textContent.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.replyingto,bodyHTML:replyPost.textContent.trim()}))}if("quiz"===this.module&&null!==(_this$editor8=this.editor)&&void 0!==_this$editor8&&_this$editor8.id){var _this$editor9;let questionId=this.getQuestionId(null===(_this$editor9=this.editor)||void 0===_this$editor9?void 0:_this$editor9.id),question=document.querySelector(`#question-${questionId} .qtext`),intro=atob(this.quizInfo.intro);null!=question&&question.textContent.trim()&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:this.moduleIcon,title:this.answeringto,bodyHTML:question.textContent})),intro&&""!==intro.trim()&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:`${this.quiz} ${this.description}`,bodyHTML:intro})),Number(this.quizInfo.open)&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(Number(this.quizInfo.open),Number(this.quizInfo.close))}))}return Object.keys(this.Rubrics).length&&content.append(this.createBox({bg:"bg-gray",titleColor:"text-dark",icon:this.moduleIcon,title:this.rubrics,bodyHTML:this.generateRubrics(this.Rubrics)})),Dates&&content.append(this.createBox({bg:"bg-amber",titleColor:"text-dark",icon:_svg_repo.default.time,title:this.importantdates,bodyHTML:this.generateImportantDates(openDate,dueDate)})),"assign"===this.module&&content.append(this.createBox({bg:"bg-green",titleColor:"text-success",icon:this.moduleIcon,title:this.subStatus,bodyHTML:this.submissionStatus(this.submission)})),container.append(btnWrapper,header,content),container}createBox(_ref){let{bg:bg,titleColor:titleColor,icon:icon,title:title,bodyHTML:bodyHTML}=_ref;const box=this.create("div");box.className=`tiny_cursive-fullpage-card ${bg}`;const heading=this.create("h4");heading.className=`tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`,heading.innerHTML=`${icon} ${title}`;const body=this.create("div");return body.className="tiny_cursive-fullpage-card-body",body.innerHTML=bodyHTML,box.append(heading,body),box}generateRubrics(Rubrics){const wrapper=this.create("div");return Rubrics.forEach((rubric=>{const rubricDiv=this.create("div");rubricDiv.className="tiny_cursive-rubric-card";const title=this.create("h3");title.className="tiny_cursive-rubric-title",title.textContent=rubric.description,rubricDiv.appendChild(title),Object.values(rubric.levels).forEach((level=>{const levelDiv=this.create("div"),score=Number(level.score);levelDiv.className=0===score?"tiny_cursive-rubric-level tiny_cursive-rubric-low":score<=2?"tiny_cursive-rubric-level tiny_cursive-rubric-mid":"tiny_cursive-rubric-level tiny_cursive-rubric-high",levelDiv.textContent=`${level.definition} / ${level.score}`,rubricDiv.appendChild(levelDiv)})),wrapper.appendChild(rubricDiv)})),wrapper.innerHTML}submissionStatus(submission){var _submission$current,_submission$current2;const wrapper=this.create("div"),statusWrapper=this.create("div");statusWrapper.className="tiny_cursive-status-row";const statusName=this.create("span");statusName.textContent=`${this.status}:`;const statusValue=this.create("span"),isNew="new"===(null==submission||null===(_submission$current=submission.current)||void 0===_submission$current?void 0:_submission$current.status);statusValue.textContent=isNew?this.draftnot:this.draft,statusValue.className="tiny_cursive-status-value "+(isNew?"tiny_cursive-status-red":"tiny_cursive-status-green"),statusWrapper.append(statusName,statusValue);const modifiedWrapper=this.create("div");modifiedWrapper.className="tiny_cursive-status-row";const modifiedName=this.create("span");modifiedName.textContent=`${this.lastModified}: `;const modifiedValue=this.create("span");if(null!=submission&&null!==(_submission$current2=submission.current)&&void 0!==_submission$current2&&_submission$current2.timemodified){const date=new Date(1e3*submission.current.timemodified);modifiedValue.textContent=this.formatDate(date)}else modifiedValue.textContent="N/A";modifiedWrapper.append(modifiedName,modifiedValue);const gradeWrapper=this.create("div");gradeWrapper.className="tiny_cursive-status-row";const gradeName=this.create("span");gradeName.textContent=`${this.gradings}: `;const gradeValue=this.create("span");return null!=submission&&submission.grade?gradeValue.textContent=Number(submission.grade.grade)>0?submission.grade.grade:this.gradenot:gradeValue.textContent=this.gradenot,gradeWrapper.append(gradeName,gradeValue),wrapper.append(statusWrapper,gradeWrapper,modifiedWrapper),wrapper.innerHTML}wordCounter(status){const wordCount=this.create("div"),labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");icon.className="me-2",icon.innerHTML=_svg_repo.default.assignment,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.wordCount}:`,value.textContent="0",value.className="text-primary",value.style.fontWeight="600",value.style.fontSize="14px",wordCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2",wordCount.append(labelDiv,value),wordCount.style.fontSize="12px";return new MutationObserver((()=>{const newText=status.textContent.trim();value.textContent=`${newText.replace("words","")}`})).observe(status,{characterData:!0,subtree:!0,childList:!0}),wordCount}timerCountDown(timer){let warningDiv=document.querySelector("#user-notifications > div");if(warningDiv){var _clone$querySelector;let clone=warningDiv.cloneNode(!0);null===(_clone$querySelector=clone.querySelector("button"))||void 0===_clone$querySelector||_clone$querySelector.remove(),this.editor.notificationManager.open({text:clone.textContent,type:"error"})}const timerCount=this.create("div");timerCount.className="bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2";const labelDiv=this.create("div"),label=this.create("span"),value=this.create("span"),icon=this.create("span");if(icon.innerHTML=_svg_repo.default.time,labelDiv.appendChild(icon),labelDiv.append(label),label.textContent=`${this.timeleft}:`,value.textContent="00:00:00",value.className=warningDiv?"text-danger":"text-primary",Object.assign(value.style,{fontWeight:"600",fontSize:"14px"}),timerCount.append(labelDiv,value),timerCount.style.fontSize="12px",timer){new MutationObserver((()=>{const newText=timer.textContent.trim();value.textContent=`${newText}`})).observe(timer,{characterData:!0,subtree:!0,childList:!0})}else value.textContent=this.nolimit;return timerCount}generateStudentInfo(user,course){const wrapper=this.create("div"),nameWrapper=this.create("div"),usernameWrapper=this.create("div"),courseWrapper=this.create("div"),nameLabel=this.create("strong"),nameValue=this.create("span"),usernameLabel=this.create("strong"),usernameValue=this.create("span"),courseLabel=this.create("strong"),courseValue=this.create("span");return nameLabel.textContent=`${this.name}`,nameValue.textContent=user.fullname,usernameLabel.textContent=`${this.userename}: `,usernameValue.textContent=user.username,courseLabel.textContent=`${this.course}: `,courseValue.textContent=course.title,usernameLabel.className="cfw-bold me-2",usernameValue.className="cursiveFw-wrap",courseLabel.className="cfw-bold me-2",courseValue.className="cursiveFw-wrap",nameLabel.className="cfw-bold me-2",nameValue.className="cursiveFw-wrap",nameWrapper.append(nameLabel,nameValue),usernameWrapper.append(usernameLabel,usernameValue),courseWrapper.append(courseLabel,courseValue),wrapper.append(nameWrapper,usernameWrapper,courseWrapper),wrapper.innerHTML}generateImportantDates(open,due){const wrapper=this.create("div");let openDate=null,dueDate=null;const openedWrapper=this.create("div"),dueWrapper=this.create("div"),remainingWrapper=this.create("div"),openedLabel=this.create("span"),openedValue=this.create("span"),dueLabel=this.create("span"),dueValue=this.create("span"),remainingLabel=this.create("span"),remainingValue=this.create("span");return"quiz"===this.module?(openDate=1e3*open,dueDate=1e3*due):(openDate=this.extractDate(null==open?void 0:open.textContent),dueDate=this.extractDate(null==due?void 0:due.textContent)),openedLabel.textContent=`${this.opened}: `,openedValue.textContent=this.formatDate(openDate?new Date(openDate):null),openedValue.className="text-dark",dueLabel.textContent=`${this.due}: `,dueValue.textContent=this.formatDate(dueDate?new Date(dueDate):null),dueValue.className="text-danger",remainingLabel.textContent=`${this.remaining}: `,remainingValue.textContent=this.calculateDate(dueDate),remainingValue.className="text-danger",openedWrapper.className="d-flex justify-content-between",dueWrapper.className="d-flex justify-content-between",remainingWrapper.className="d-flex align-items-center justify-content-between mt-2 pt-2 border-top",openedWrapper.append(openedLabel,openedValue),dueWrapper.append(dueLabel,dueValue),remainingWrapper.append(remainingLabel,remainingValue),wrapper.append(openedWrapper,dueWrapper,remainingWrapper),wrapper.innerHTML}formatDate(date){if(!date)return"-";return date.toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"numeric",minute:"numeric",hour12:!0})}extractDate(text){if(!text)return"-";const parts=null==text?void 0:text.split(":");return parts.length>1?parts.slice(1).join(":").trim():text.trim()}calculateDate(date){if(!date)return"-";const diffMs=new Date(date)-new Date;if(diffMs<=0)return"Overdue";return`${Math.floor(diffMs/864e5)} days, ${Math.floor(diffMs/36e5%24)} hours`}fullPageModule(module){var _current$contentDocum,_current$contentWindo,_current$contentWindo2,_document$getElementB;let current="quiz"===this.module?document.getElementById(`${module}_ifr`):document.querySelector(`#${module}_ifr`),p1=current.parentElement,p2=p1.parentElement,p4=p2.parentElement.parentElement,statusBar=document.querySelector(".tox-statusbar__right-container > button"),assignName=document.querySelector(".page-context-header"),header=this.create("div"),btn=null;if(assignName.classList.remove("mb-2"),header.id="tiny_cursive-fullpage-custom-header",Object.assign(header.style,{backgroundColor:"white",display:"flex",justifyContent:"space-between"}),"quiz"===this.module?(btn=document.querySelector("#mod_quiz-next-nav").cloneNode(!0),btn.className="tiny_cursive-fullpage-submit-btn",btn.style.margin=".5rem"):(btn=this.create("input"),btn.className="tiny_cursive-fullpage-submit-btn",btn.value=this.savechanges,btn.type="submit",btn.style.margin=".5rem"),"pdfannotator"===this.module){const style=document.createElement("style");style.id="cursiveForceStyle",style.textContent="\n .path-mod-pdfannotator #comment-wrapper h4,\n .path-mod-pdfannotator #comment-nav {\n margin: 0 !important;\n }\n ",document.head.appendChild(style)}const leftSide=this.create("div"),rightSide=this.create("div");let commonStyle={display:"flex",alignItems:"center",margin:"0 1rem"};Object.assign(leftSide.style,commonStyle),rightSide.id="tiny_cursive-fullpage-right-wrapper",Object.assign(rightSide.style,commonStyle),rightSide.appendChild(btn),leftSide.appendChild(assignName.cloneNode(!0)),header.appendChild(leftSide),header.appendChild(rightSide),p4.insertBefore(header,p4.firstChild),p2.style.backgroundColor="#efefef",Object.assign(current.style,{width:"750px",minWidth:"750px",boxShadow:"0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)"}),Object.assign(p1.style,{display:"flex",justifyContent:"center",outline:"none",margin:"2rem 0 0"});const style=this.create("style");style.id="tiny_cursive-fullpage-mode-style",style.textContent="\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }",document.head.appendChild(style);let iframeBody=(null===(_current$contentDocum=current.contentDocument)||void 0===_current$contentDocum?void 0:_current$contentDocum.body)||(null===(_current$contentWindo=current.contentWindow)||void 0===_current$contentWindo||null===(_current$contentWindo2=_current$contentWindo.document)||void 0===_current$contentWindo2?void 0:_current$contentWindo2.body);iframeBody&&(iframeBody.style.padding="0.5in"),p2.style.position="relative",null===(_document$getElementB=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB||_document$getElementB.remove();let toggle=this.create("div");toggle.id="cursive-fullpagemode-sidebar-toggle",toggle.innerHTML=_svg_repo.default.hamburger,p2.appendChild(toggle),p2.appendChild(this.docSideBar(statusBar))}normalizePage(editorId){var _document$getElementB2,_document$getElementB3,_current$contentDocum2,_current$contentWindo3,_current$contentWindo4,_document$head$queryS,_document$head$queryS2;null===(_document$getElementB2=document.getElementById("tiny_cursive-fullpage-custom-header"))||void 0===_document$getElementB2||_document$getElementB2.remove(),null===(_document$getElementB3=document.getElementById("cursive-fullpagemode-sidebar"))||void 0===_document$getElementB3||_document$getElementB3.remove();let current=document.getElementById(editorId),p1=current.parentElement,p2=p1.parentElement;Object.assign(p2.style,{backgroundColor:"",position:""}),Object.assign(current.style,{width:"",minWidth:"",boxShadow:""}),Object.assign(p1.style,{display:"",justifyContent:"",outline:"",margin:""}),p1.classList.remove("tiny-cursive-editor-container");let iframeBody=(null===(_current$contentDocum2=current.contentDocument)||void 0===_current$contentDocum2?void 0:_current$contentDocum2.body)||(null===(_current$contentWindo3=current.contentWindow)||void 0===_current$contentWindo3||null===(_current$contentWindo4=_current$contentWindo3.document)||void 0===_current$contentWindo4?void 0:_current$contentWindo4.body);iframeBody&&(iframeBody.style.padding="0"),null===(_document$head$queryS=document.head.querySelector("#tiny_cursive-fullpage-mode-style"))||void 0===_document$head$queryS||_document$head$queryS.remove(),null===(_document$head$queryS2=document.head.querySelector("#cursiveForceStyle"))||void 0===_document$head$queryS2||_document$head$queryS2.remove()}checkForumSubject(){const form=document.querySelector("#tiny_cursive-fullpage-right-wrapper > input"),msg=this.subjectnot;form&&form.addEventListener("click",(e=>{const subjectInput=document.getElementById("id_subject");let content=this.editor.getContent().trim();subjectInput&&""!==subjectInput.value.trim()&&""!==content||(e.preventDefault(),e.stopPropagation(),this.editor.windowManager.alert(msg))}))}getSidebarTitle(){const[assign,discus,quiz,lesson]=this.getText("sbTitle");switch(this.module){case"assign":return{title:assign,icon:_svg_repo.default.assignment};case"forum":return{title:discus,icon:_svg_repo.default.forum};case"lesson":return{title:lesson,icon:_svg_repo.default.forum};case"quiz":return{title:quiz,icon:_svg_repo.default.quiz};case"pdfannotator":return{title:"PDF Annotation",icon:_svg_repo.default.pdfannotator};default:return{title:"Page",icon:_svg_repo.default.quiz}}}getTimerBlock(module){switch(module){case"assign":return document.querySelector("#mod_assign_timelimit_block > div > div");case"forum":return document.querySelector("#mod_forum_timelimit_block");case"lesson":return document.querySelector("#lesson-timer");case"quiz":return document.querySelector("#quiz-time-left");default:return null}}getQuestionId(editoId){try{return editoId&&"string"==typeof editoId?editoId.replace(/^q(\d+):(\d+)_.*$/,"$1-$2"):""}catch(error){return window.console.error("Error getting question ID:",error),""}}initStrings(){[this.details,this.studentInfo,this.progress,this.description,this.replyingto,this.answeringto,this.importantdates,this.rubrics,this.subStatus,this.status,this.draft,this.draftnot,this.lastModified,this.gradings,this.gradenot,this.wordCount,this.timeleft,this.nolimit,this.name,this.userename,this.course,this.opened,this.due,this.overdue,this.remaining,this.savechanges,this.subjectnot]=this.getText("docSideBar")}getText(key){return JSON.parse(localStorage.getItem(key))||[]}create(tag){return document.createElement(tag)}},_exports.default})); //# sourceMappingURL=document_view.min.js.map \ No newline at end of file diff --git a/amd/build/document_view.min.js.map b/amd/build/document_view.min.js.map index b19d13d7..e291a61f 100644 --- a/amd/build/document_view.min.js.map +++ b/amd/build/document_view.min.js.map @@ -1 +1 @@ -{"version":3,"file":"document_view.min.js","sources":["../src/document_view.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module provides functionality for document view management in the Tiny editor,\n * including full page mode display and sidebar information\n * @module tiny_cursive/document_view\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Icons from 'tiny_cursive/svg_repo';\nexport default class DocumentView {\n\n constructor(User, Rubrics, submission, modulename, editor, quizInfo) {\n this.User = User;\n this.Rubrics = Rubrics;\n this.submission = submission;\n this.module = modulename;\n this.editor = editor;\n this.moduleIcon = Icons.assignment;\n this.quizInfo = quizInfo;\n this.initStrings();\n }\n\n normalMode() {\n let id = this.editor?.id + \"_ifr\";\n if (this.module === 'assign') {\n this.normalizePage(id);\n } else if (this.module === 'quiz') {\n this.normalizePage(id);\n } else if (this.module === 'forum') {\n this.normalizePage(id);\n } else if (this.module === 'lesson') {\n this.normalizePage(id);\n } else if(this.module === 'pdfannotator') {\n this.normalizePage(id);\n }\n }\n\n fullPageMode() {\n\n if (this.module === 'assign') {\n this.moduleIcon = Icons.assignment;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'forum') {\n this.moduleIcon = Icons.forum;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'quiz' && this.editor?.id) {\n this.moduleIcon = Icons.quiz;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'lesson') {\n this.moduleIcon = Icons.lesson;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'pdfannotator') {\n this.moduleIcon = Icons.pdfannotator;\n this.fullPageModule(this.editor?.id);\n }\n }\n\n docSideBar(status) {\n\n const url = new URL(window.location.href);\n const replyId = url.searchParams.get(\"reply\");\n const toggle = document.querySelector('#cursive-fullpagemode-sidebar-toggle');\n const timelimitBlock = this.getTimerBlock(this.module);\n const headerInfo = this.getSidebarTitle();\n const progressBar = document.querySelector('.box.progress_bar');\n\n const courseName = document.querySelector('#page-navbar > nav > ol > li:nth-child(1) > a');\n const courseDes = document.querySelector('#intro');\n const Dates = document.querySelector('.activity-dates');\n\n let openDate = Dates?.querySelector('div:nth-child(1)');\n let dueDate = Dates?.querySelector('div:nth-child(2)');\n\n const container = this.create('div');\n Object.assign(container, {\n id: 'cursive-fullpagemode-sidebar',\n className: 'bg-white h-100 shadow'\n });\n Object.assign(container.style, {\n width: '300px',\n overflow: 'auto'\n });\n\n const crossBtn = this.create('span');\n Object.assign(crossBtn, {\n id: 'cursive-collapse-sidebar',\n className: 'btn p-2',\n innerHTML: Icons.close\n });\n\n crossBtn.addEventListener('click', () => {\n container.style.transition = 'width 0.3s ease';\n container.style.width = '0';\n toggle.style.display = 'flex';\n });\n toggle?.addEventListener('click', function() {\n toggle.style.display = 'none';\n container.style.width = '300px';\n });\n\n const btnWrapper = this.create('div');\n Object.assign(btnWrapper, {\n padding: '0 1rem',\n position: 'sticky',\n top: '0',\n backgroundColor: 'white'\n });\n btnWrapper.append(crossBtn);\n\n\n const header = this.create('div');\n header.className = 'border-bottom p-3 bg-light';\n Object.assign(header.style, {\n position: 'sticky',\n top: '0'\n });\n\n const headerTitle = this.create('h3');\n headerTitle.className = 'mb-3 d-flex align-items-center';\n headerTitle.textContent = `${headerInfo.title} ${this.details}`;\n headerTitle.style.fontWeight = '600';\n\n const headerIcon = document.querySelector('.page-header-image > div');\n if (headerIcon) {\n headerTitle.prepend(headerIcon.cloneNode(true));\n }\n\n let wordCount = this.wordCounter(status);\n if (timelimitBlock?.textContent) {\n header.append(headerTitle, wordCount, this.timerCountDown(timelimitBlock));\n } else {\n header.append(headerTitle, wordCount);\n }\n\n const content = this.create('div');\n content.className = 'p-3';\n\n content.append(\n this.createBox({\n bg: 'bg-info',\n titleColor: 'text-info',\n icon: Icons.people,\n title: this.studentInfo,\n bodyHTML: this.generateStudentInfo(this.User, courseName)\n })\n );\n\n if (this.module === 'lesson' && progressBar) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.progress,\n bodyHTML: progressBar.innerHTML\n })\n );\n }\n\n if (courseDes && courseDes?.textContent.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.getSidebarTitle().title} ${this.description}`,\n bodyHTML: courseDes.innerHTML\n })\n );\n }\n\n if (this.module === 'forum' && replyId) {\n this.checkForumSubject();\n let replyPost = document.querySelector(`#post-content-${replyId}`);\n if (replyPost?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.replyingto,\n bodyHTML: replyPost.textContent.trim()\n })\n );\n }\n }\n\n if (this.module === 'quiz' && this.editor?.id) {\n\n let questionId = this.getQuestionId(this.editor?.id);\n let question = document.querySelector(`#question-${questionId} .qtext`);\n let intro = atob(this.quizInfo.intro);\n\n if (question?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.answeringto,\n bodyHTML: question.textContent\n })\n );\n }\n\n if (intro && intro.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.quiz} ${this.description}`,\n bodyHTML: intro\n })\n );\n }\n\n if (Number(this.quizInfo.open)) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(Number(this.quizInfo.open), Number(this.quizInfo.close))\n })\n );\n }\n }\n\n if (Object.keys(this.Rubrics).length) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.rubrics,\n bodyHTML: this.generateRubrics(this.Rubrics)\n })\n );\n }\n\n if (Dates) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(openDate, dueDate)\n })\n );\n }\n if (this.module === 'assign') {\n content.append(\n this.createBox({\n bg: 'bg-green',\n titleColor: 'text-success',\n icon: this.moduleIcon,\n title: this.subStatus,\n bodyHTML: this.submissionStatus(this.submission)\n })\n );\n }\n\n container.append(btnWrapper, header, content);\n return container;\n\n }\n // Helper to create info boxes\n createBox({bg, titleColor, icon, title, bodyHTML}) {\n const box = this.create('div');\n box.className = `tiny_cursive-fullpage-card ${bg}`;\n\n const heading = this.create('h4');\n heading.className = `tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`;\n heading.innerHTML = `${icon} ${title}`;\n\n const body = this.create('div');\n body.className = `tiny_cursive-fullpage-card-body`;\n body.innerHTML = bodyHTML;\n\n box.append(heading, body);\n return box;\n }\n\n generateRubrics(Rubrics) {\n const wrapper = this.create('div');\n\n Rubrics.forEach(rubric => {\n const rubricDiv = this.create('div');\n rubricDiv.className = 'tiny_cursive-rubric-card';\n\n const title = this.create('h3');\n title.className = 'tiny_cursive-rubric-title';\n title.textContent = rubric.description;\n rubricDiv.appendChild(title);\n\n Object.values(rubric.levels).forEach(level => {\n const levelDiv = this.create('div');\n const score = Number(level.score);\n\n // Assign background color class based on score\n if (score === 0) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-low';\n } else if (score <= 2) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-mid';\n } else {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-high';\n }\n\n levelDiv.textContent = `${level.definition} / ${level.score}`;\n rubricDiv.appendChild(levelDiv);\n });\n\n wrapper.appendChild(rubricDiv);\n });\n\n return wrapper.innerHTML;\n }\n\n submissionStatus(submission) {\n const wrapper = this.create('div');\n\n const statusWrapper = this.create('div');\n statusWrapper.className = 'tiny_cursive-status-row';\n\n const statusName = this.create('span');\n statusName.textContent = `${this.status}:`;\n\n const statusValue = this.create('span');\n const isNew = submission?.current?.status === 'new';\n statusValue.textContent = isNew ? this.draftnot : this.draft;\n statusValue.className = `tiny_cursive-status-value ${isNew ? 'tiny_cursive-status-red' : 'tiny_cursive-status-green'}`;\n\n statusWrapper.append(statusName, statusValue);\n\n const modifiedWrapper = this.create('div');\n modifiedWrapper.className = 'tiny_cursive-status-row';\n\n const modifiedName = this.create('span');\n modifiedName.textContent = `${this.lastModified}: `;\n\n const modifiedValue = this.create('span');\n if (submission?.current?.timemodified) {\n const date = new Date(submission.current.timemodified * 1000);\n modifiedValue.textContent = this.formatDate(date);\n } else {\n modifiedValue.textContent = 'N/A';\n }\n modifiedWrapper.append(modifiedName, modifiedValue);\n\n const gradeWrapper = this.create('div');\n gradeWrapper.className = 'tiny_cursive-status-row';\n\n const gradeName = this.create('span');\n gradeName.textContent = `${this.gradings}: `;\n\n const gradeValue = this.create('span');\n\n if (submission?.grade) {\n gradeValue.textContent = Number(submission.grade.grade) > 0\n ? submission.grade.grade\n : this.gradenot;\n } else {\n gradeValue.textContent = this.gradenot;\n }\n\n gradeWrapper.append(gradeName, gradeValue);\n wrapper.append(statusWrapper, gradeWrapper, modifiedWrapper);\n return wrapper.innerHTML;\n }\n\n wordCounter(status) {\n const wordCount = this.create('div');\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n\n icon.className = 'me-2';\n icon.innerHTML = Icons.assignment;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.wordCount}:`;\n value.textContent = '0';\n value.className = 'text-primary';\n value.style.fontWeight = '600';\n value.style.fontSize = '14px';\n\n wordCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n wordCount.append(labelDiv, value);\n wordCount.style.fontSize = '12px';\n\n const observer = new MutationObserver(() => {\n const newText = status.textContent.trim();\n value.textContent = `${newText.replace('words', '')}`;\n });\n\n observer.observe(status, {\n characterData: true,\n subtree: true,\n childList: true\n });\n\n return wordCount;\n }\n\n\n timerCountDown(timer) {\n\n let warningDiv = document.querySelector('#user-notifications > div');\n if (warningDiv) {\n let clone = warningDiv.cloneNode(true);\n clone.querySelector('button')?.remove();\n this.editor.notificationManager.open({\n text: clone.textContent,\n type: 'error'\n });\n }\n\n\n const timerCount = this.create('div');\n timerCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n icon.innerHTML = Icons.time;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.timeleft}:`;\n value.textContent = '00:00:00';\n value.className = warningDiv ? 'text-danger' : 'text-primary';\n Object.assign(value.style, {\n fontWeight: '600',\n fontSize: '14px'\n });\n\n\n timerCount.append(labelDiv, value);\n timerCount.style.fontSize = '12px';\n if (timer) {\n const observer = new MutationObserver(() => {\n const newText = timer.textContent.trim();\n value.textContent = `${newText}`;\n });\n observer.observe(timer, {\n characterData: true,\n subtree: true,\n childList: true\n });\n } else {\n value.textContent = this.nolimit;\n }\n\n\n return timerCount;\n }\n\n\n generateStudentInfo(user, course) {\n\n const wrapper = this.create('div');\n\n const nameWrapper = this.create('div');\n const usernameWrapper = this.create('div');\n const courseWrapper = this.create('div');\n\n const nameLabel = this.create('strong');\n const nameValue = this.create('span');\n const usernameLabel = this.create('strong');\n const usernameValue = this.create('span');\n const courseLabel = this.create('strong');\n const courseValue = this.create('span');\n\n nameLabel.textContent = `${this.name}`;\n nameValue.textContent = user.fullname;\n\n usernameLabel.textContent = `${this.userename}: `;\n usernameValue.textContent = user.username;\n\n courseLabel.textContent = `${this.course}: `;\n courseValue.textContent = course.title;\n\n usernameLabel.className = 'cfw-bold me-2';\n usernameValue.className = 'cursiveFw-wrap';\n courseLabel.className = 'cfw-bold me-2';\n courseValue.className = 'cursiveFw-wrap';\n nameLabel.className = 'cfw-bold me-2';\n nameValue.className = 'cursiveFw-wrap';\n\n // nameWrapper.className = 'd-flex justify-content-between';\n // usernameWrapper.className = 'd-flex justify-content-between';\n // courseWrapper.className = 'd-flex justify-content-between';\n\n nameWrapper.append(nameLabel, nameValue);\n usernameWrapper.append(usernameLabel, usernameValue);\n courseWrapper.append(courseLabel, courseValue);\n\n wrapper.append(nameWrapper, usernameWrapper, courseWrapper);\n\n return wrapper.innerHTML;\n\n }\n\n generateImportantDates(open, due) {\n\n const wrapper = this.create('div');\n let openDate = null;\n let dueDate = null;\n\n const openedWrapper = this.create('div');\n const dueWrapper = this.create('div');\n const remainingWrapper = this.create('div');\n\n const openedLabel = this.create('span');\n const openedValue = this.create('span');\n const dueLabel = this.create('span');\n const dueValue = this.create('span');\n const remainingLabel = this.create('span');\n const remainingValue = this.create('span');\n if (this.module === 'quiz') {\n openDate = open * 1000;\n dueDate = due * 1000;\n } else {\n openDate = this.extractDate(open?.textContent);\n dueDate = this.extractDate(due?.textContent);\n }\n\n openedLabel.textContent = `${this.opened}: `;\n openedValue.textContent = this.formatDate(openDate ? new Date(openDate) : null);\n openedValue.className = 'text-dark';\n\n dueLabel.textContent = `${this.due}: `;\n dueValue.textContent = this.formatDate(dueDate ? new Date(dueDate) : null);\n dueValue.className = 'text-danger';\n\n remainingLabel.textContent = `${this.remaining}: `;\n remainingValue.textContent = this.calculateDate(dueDate);\n remainingValue.className = 'text-danger';\n\n openedWrapper.className = 'd-flex justify-content-between';\n dueWrapper.className = 'd-flex justify-content-between';\n remainingWrapper.className = 'd-flex align-items-center justify-content-between mt-2 pt-2 border-top';\n\n openedWrapper.append(openedLabel, openedValue);\n dueWrapper.append(dueLabel, dueValue);\n remainingWrapper.append(remainingLabel, remainingValue);\n\n wrapper.append(openedWrapper, dueWrapper, remainingWrapper);\n\n return wrapper.innerHTML;\n }\n\n formatDate(date) {\n if (!date) {\n return '-';\n }\n\n let options = {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true};\n return date.toLocaleString('en-US', options);\n }\n\n extractDate(text) {\n if (!text) {\n return '-';\n }\n // Split on first colon and return the right part\n const parts = text?.split(':');\n if (parts.length > 1) {\n return parts.slice(1).join(':').trim();\n }\n\n return text.trim();\n }\n\n\n calculateDate(date) {\n if (!date) {\n return '-';\n }\n const date1 = new Date(date); // Due date (local time)\n const now = new Date(); // Current date/time\n\n // Calculate the difference in milliseconds\n const diffMs = date1 - now;\n\n // Convert to days, hours, minutes\n if (diffMs <= 0) {\n return \"Overdue\";\n } else {\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n const diffHours = Math.floor((diffMs / (1000 * 60 * 60)) % 24);\n\n return `${diffDays} days, ${diffHours} hours`;\n }\n\n }\n\n fullPageModule(module) {\n let current = this.module === 'quiz' ?\n document.getElementById(`${module}_ifr`) : document.querySelector(`#${module}_ifr`);\n\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n let p3 = p2.parentElement;\n let p4 = p3.parentElement;\n\n let statusBar = document.querySelector('.tox-statusbar__right-container > button');\n let assignName = document.querySelector('.page-context-header');\n let header = this.create('div');\n let btn = null;\n\n assignName.classList.remove('mb-2');\n header.id = 'tiny_cursive-fullpage-custom-header';\n Object.assign(header.style, {\n backgroundColor: 'white',\n display: 'flex',\n justifyContent: 'space-between'\n });\n\n if (this.module === 'quiz') {\n btn = document.querySelector('#mod_quiz-next-nav').cloneNode(true);\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.style.margin = '.5rem';\n } else {\n btn = this.create('input');\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.value = this.savechanges;\n btn.type = 'submit';\n btn.style.margin = '.5rem';\n }\n\n if (this.module === 'pdfannotator') {\n const style = document.createElement('style');\n style.id = 'cursiveForceStyle';\n style.textContent = `\n .path-mod-pdfannotator #comment-wrapper h4,\n .path-mod-pdfannotator #comment-nav {\n margin: 0 !important;\n }\n `;\n document.head.appendChild(style);\n }\n\n\n\n const leftSide = this.create('div');\n const rightSide = this.create('div');\n let commonStyle = {\n display: 'flex',\n alignItems: 'center',\n margin: '0 1rem'\n };\n\n Object.assign(leftSide.style, commonStyle);\n rightSide.id = 'tiny_cursive-fullpage-right-wrapper';\n Object.assign(rightSide.style, commonStyle);\n\n rightSide.appendChild(btn);\n leftSide.appendChild(assignName.cloneNode(true));\n\n header.appendChild(leftSide);\n header.appendChild(rightSide);\n\n p4.insertBefore(header, p4.firstChild);\n p2.style.backgroundColor = '#efefef';\n Object.assign(current.style, {\n width: '750px',\n minWidth: '750px',\n boxShadow: '0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)'\n });\n\n Object.assign(p1.style, {\n display: 'flex',\n justifyContent: 'center',\n outline: 'none',\n margin: '2rem 0 0'\n });\n const style = this.create('style');\n style.id = 'tiny_cursive-fullpage-mode-style';\n style.textContent = `\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }`;\n document.head.appendChild(style);\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n\n if (iframeBody) {\n iframeBody.style.padding = '0.5in';\n }\n p2.style.position = 'relative';\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let toggle = this.create('div');\n toggle.id = 'cursive-fullpagemode-sidebar-toggle';\n toggle.innerHTML = Icons.hamburger;\n p2.appendChild(toggle);\n p2.appendChild(this.docSideBar(statusBar));\n }\n\n normalizePage(editorId) {\n document.getElementById('tiny_cursive-fullpage-custom-header')?.remove();\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let current = document.getElementById(editorId);\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n\n Object.assign(p2.style, {\n backgroundColor: \"\",\n position: \"\"\n });\n\n Object.assign(current.style, {\n width: '',\n minWidth: '',\n boxShadow: '',\n });\n\n Object.assign(p1.style, {\n display: '',\n justifyContent: '',\n outline: '',\n margin: ''\n });\n\n p1.classList.remove('tiny-cursive-editor-container');\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n if (iframeBody) {\n iframeBody.style.padding = '0';\n }\n document.head.querySelector('#tiny_cursive-fullpage-mode-style')?.remove();\n document.head.querySelector('#cursiveForceStyle')?.remove();\n }\n\n checkForumSubject() {\n const form = document.querySelector('#tiny_cursive-fullpage-right-wrapper > input');\n const msg = this.subjectnot;\n\n if (form) {\n form.addEventListener('click', (e) => {\n const subjectInput = document.getElementById('id_subject');\n let content = this.editor.getContent().trim();\n if (!subjectInput || subjectInput.value.trim() === '' || content === '') {\n e.preventDefault();\n e.stopPropagation();\n this.editor.windowManager.alert(msg);\n }\n });\n }\n }\n\n getSidebarTitle() {\n const [assign, discus, quiz, lesson] = this.getText('sbTitle');\n switch (this.module) {\n case 'assign':\n return {title: assign, icon: Icons.assignment};\n case 'forum':\n return {title: discus, icon: Icons.forum};\n case 'lesson':\n return {title: lesson, icon: Icons.forum};\n case 'quiz':\n return {title: quiz, icon: Icons.quiz};\n case 'pdfannotator':\n return {title: 'PDF Annotation', icon: Icons.pdfannotator};\n default:\n return {title: 'Page', icon: Icons.quiz};\n }\n }\n\n getTimerBlock(module) {\n switch (module) {\n case 'assign':\n return document.querySelector('#mod_assign_timelimit_block > div > div');\n case 'forum':\n return document.querySelector('#mod_forum_timelimit_block');\n case 'lesson':\n return document.querySelector('#lesson-timer');\n case 'quiz':\n return document.querySelector('#quiz-time-left');\n default:\n return null;\n }\n }\n\n getQuestionId(editoId) {\n try {\n if (!editoId || typeof editoId !== 'string') {\n return '';\n }\n return editoId.replace(/^q(\\d+):(\\d+)_.*$/, \"$1-$2\");\n } catch (error) {\n window.console.error('Error getting question ID:', error);\n return '';\n }\n }\n\n initStrings() {\n [\n this.details,\n this.studentInfo,\n this.progress,\n this.description,\n this.replyingto,\n this.answeringto,\n this.importantdates,\n this.rubrics,\n this.subStatus,\n this.status,\n this.draft,\n this.draftnot,\n this.lastModified,\n this.gradings,\n this.gradenot,\n this.wordCount,\n this.timeleft,\n this.nolimit,\n this.name,\n this.userename,\n this.course,\n this.opened,\n this.due,\n this.overdue,\n this.remaining,\n this.savechanges,\n this.subjectnot\n ] = this.getText('docSideBar');\n }\n\n getText(key) {\n return JSON.parse(localStorage.getItem(key)) || [];\n }\n\n create(tag) {\n return document.createElement(tag);\n }\n\n}"],"names":["constructor","User","Rubrics","submission","modulename","editor","quizInfo","module","moduleIcon","Icons","assignment","initStrings","normalMode","id","this","normalizePage","fullPageMode","fullPageModule","_this$editor2","forum","_this$editor3","_this$editor4","quiz","_this$editor5","lesson","_this$editor6","pdfannotator","_this$editor7","docSideBar","status","replyId","URL","window","location","href","searchParams","get","toggle","document","querySelector","timelimitBlock","getTimerBlock","headerInfo","getSidebarTitle","progressBar","courseName","courseDes","Dates","openDate","dueDate","container","create","Object","assign","className","style","width","overflow","crossBtn","innerHTML","close","addEventListener","transition","display","btnWrapper","padding","position","top","backgroundColor","append","header","headerTitle","textContent","title","details","fontWeight","headerIcon","prepend","cloneNode","wordCount","wordCounter","timerCountDown","content","createBox","bg","titleColor","icon","people","studentInfo","bodyHTML","generateStudentInfo","progress","trim","description","checkForumSubject","replyPost","replyingto","_this$editor8","questionId","getQuestionId","_this$editor9","question","intro","atob","answeringto","Number","open","time","importantdates","generateImportantDates","keys","length","rubrics","generateRubrics","subStatus","submissionStatus","box","heading","body","wrapper","forEach","rubric","rubricDiv","appendChild","values","levels","level","levelDiv","score","definition","statusWrapper","statusName","statusValue","isNew","current","draftnot","draft","modifiedWrapper","modifiedName","lastModified","modifiedValue","_submission$current2","timemodified","date","Date","formatDate","gradeWrapper","gradeName","gradings","gradeValue","grade","gradenot","labelDiv","label","value","fontSize","MutationObserver","newText","replace","observe","characterData","subtree","childList","timer","warningDiv","clone","remove","notificationManager","text","type","timerCount","timeleft","nolimit","user","course","nameWrapper","usernameWrapper","courseWrapper","nameLabel","nameValue","usernameLabel","usernameValue","courseLabel","courseValue","name","fullname","userename","username","due","openedWrapper","dueWrapper","remainingWrapper","openedLabel","openedValue","dueLabel","dueValue","remainingLabel","remainingValue","extractDate","opened","remaining","calculateDate","toLocaleString","year","month","day","hour","minute","hour12","parts","split","slice","join","diffMs","Math","floor","getElementById","p1","parentElement","p2","p4","statusBar","assignName","btn","classList","justifyContent","margin","savechanges","createElement","head","leftSide","rightSide","commonStyle","alignItems","insertBefore","firstChild","minWidth","boxShadow","outline","iframeBody","contentDocument","contentWindow","_current$contentWindo","_current$contentWindo2","hamburger","editorId","_current$contentWindo3","_current$contentWindo4","form","msg","subjectnot","e","subjectInput","getContent","preventDefault","stopPropagation","windowManager","alert","discus","getText","editoId","error","console","overdue","key","JSON","parse","localStorage","getItem","tag"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"document_view.min.js","sources":["../src/document_view.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module provides functionality for document view management in the Tiny editor,\n * including full page mode display and sidebar information\n * @module tiny_cursive/document_view\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Icons from 'tiny_cursive/svg_repo';\nexport default class DocumentView {\n\n constructor(User, Rubrics, submission, modulename, editor, quizInfo) {\n this.User = User;\n this.Rubrics = Rubrics;\n this.submission = submission;\n this.module = modulename;\n this.editor = editor;\n this.moduleIcon = Icons.assignment;\n this.quizInfo = quizInfo;\n this.initStrings();\n }\n\n normalMode() {\n let id = this.editor?.id + \"_ifr\";\n if (this.module === 'assign') {\n this.normalizePage(id);\n } else if (this.module === 'quiz') {\n this.normalizePage(id);\n } else if (this.module === 'forum') {\n this.normalizePage(id);\n } else if (this.module === 'lesson') {\n this.normalizePage(id);\n } else if(this.module === 'pdfannotator') {\n this.normalizePage(id);\n }\n }\n\n fullPageMode() {\n\n if (this.module === 'assign') {\n this.moduleIcon = Icons.assignment;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'forum') {\n this.moduleIcon = Icons.forum;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'quiz' && this.editor?.id) {\n this.moduleIcon = Icons.quiz;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'lesson') {\n this.moduleIcon = Icons.lesson;\n this.fullPageModule(this.editor?.id);\n } else if (this.module === 'pdfannotator') {\n this.moduleIcon = Icons.pdfannotator;\n this.fullPageModule(this.editor?.id);\n }\n }\n\n docSideBar(status) {\n\n const url = new URL(window.location.href);\n const replyId = url.searchParams.get(\"reply\");\n const toggle = document.querySelector('#cursive-fullpagemode-sidebar-toggle');\n const timelimitBlock = this.getTimerBlock(this.module);\n const headerInfo = this.getSidebarTitle();\n const progressBar = document.querySelector('.box.progress_bar');\n\n const courseName = document.querySelector('#page-navbar > nav > ol > li:nth-child(1) > a');\n const courseDes = document.querySelector('#intro');\n const Dates = document.querySelector('.activity-dates');\n\n let openDate = Dates?.querySelector('div:nth-child(1)');\n let dueDate = Dates?.querySelector('div:nth-child(2)');\n\n const container = this.create('div');\n Object.assign(container, {\n id: 'cursive-fullpagemode-sidebar',\n className: 'bg-white h-100 shadow'\n });\n Object.assign(container.style, {\n width: '300px',\n overflow: 'auto'\n });\n\n const crossBtn = this.create('span');\n Object.assign(crossBtn, {\n id: 'cursive-collapse-sidebar',\n className: 'btn p-2',\n innerHTML: Icons.close\n });\n\n crossBtn.addEventListener('click', () => {\n container.style.transition = 'width 0.3s ease';\n container.style.width = '0';\n toggle.style.display = 'flex';\n });\n toggle?.addEventListener('click', function() {\n toggle.style.display = 'none';\n container.style.width = '300px';\n });\n\n const btnWrapper = this.create('div');\n Object.assign(btnWrapper, {\n padding: '0 1rem',\n position: 'sticky',\n top: '0',\n backgroundColor: 'white'\n });\n btnWrapper.append(crossBtn);\n\n\n const header = this.create('div');\n header.className = 'border-bottom p-3 bg-light';\n Object.assign(header.style, {\n position: 'sticky',\n top: '0'\n });\n\n const headerTitle = this.create('h3');\n headerTitle.className = 'mb-3 d-flex align-items-center';\n headerTitle.textContent = `${headerInfo.title} ${this.details}`;\n headerTitle.style.fontWeight = '600';\n\n const headerIcon = document.querySelector('.page-header-image > div');\n if (headerIcon) {\n headerTitle.prepend(headerIcon.cloneNode(true));\n }\n\n let wordCount = this.wordCounter(status);\n if (timelimitBlock?.textContent) {\n header.append(headerTitle, wordCount, this.timerCountDown(timelimitBlock));\n } else {\n header.append(headerTitle, wordCount);\n }\n\n const content = this.create('div');\n content.className = 'p-3';\n\n content.append(\n this.createBox({\n bg: 'bg-info',\n titleColor: 'text-info',\n icon: Icons.people,\n title: this.studentInfo,\n bodyHTML: this.generateStudentInfo(this.User, courseName)\n })\n );\n\n if (this.module === 'lesson' && progressBar) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.progress,\n bodyHTML: progressBar.innerHTML\n })\n );\n }\n\n if (courseDes && courseDes?.textContent.trim() !== '') {\n let fileSubDiv = document.querySelectorAll('.fileuploadsubmission');\n if (fileSubDiv) {\n fileSubDiv.forEach(Element => {\n Element.style.verticalAlign = 'middle';\n });\n }\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.getSidebarTitle().title} ${this.description}`,\n bodyHTML: courseDes.innerHTML\n })\n );\n }\n\n if (this.module === 'forum' && replyId) {\n this.checkForumSubject();\n let replyPost = document.querySelector(`#post-content-${replyId}`);\n if (replyPost?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.replyingto,\n bodyHTML: replyPost.textContent.trim()\n })\n );\n }\n }\n\n if (this.module === 'quiz' && this.editor?.id) {\n\n let questionId = this.getQuestionId(this.editor?.id);\n let question = document.querySelector(`#question-${questionId} .qtext`);\n let intro = atob(this.quizInfo.intro);\n\n if (question?.textContent.trim()) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.answeringto,\n bodyHTML: question.textContent\n })\n );\n }\n\n if (intro && intro.trim() !== '') {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: `${this.quiz} ${this.description}`,\n bodyHTML: intro\n })\n );\n }\n\n if (Number(this.quizInfo.open)) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(Number(this.quizInfo.open), Number(this.quizInfo.close))\n })\n );\n }\n }\n\n if (Object.keys(this.Rubrics).length) {\n content.append(\n this.createBox({\n bg: 'bg-gray',\n titleColor: 'text-dark',\n icon: this.moduleIcon,\n title: this.rubrics,\n bodyHTML: this.generateRubrics(this.Rubrics)\n })\n );\n }\n\n if (Dates) {\n content.append(\n this.createBox({\n bg: 'bg-amber',\n titleColor: 'text-dark',\n icon: Icons.time,\n title: this.importantdates,\n bodyHTML: this.generateImportantDates(openDate, dueDate)\n })\n );\n }\n if (this.module === 'assign') {\n content.append(\n this.createBox({\n bg: 'bg-green',\n titleColor: 'text-success',\n icon: this.moduleIcon,\n title: this.subStatus,\n bodyHTML: this.submissionStatus(this.submission)\n })\n );\n }\n\n container.append(btnWrapper, header, content);\n return container;\n\n }\n // Helper to create info boxes\n createBox({bg, titleColor, icon, title, bodyHTML}) {\n const box = this.create('div');\n box.className = `tiny_cursive-fullpage-card ${bg}`;\n\n const heading = this.create('h4');\n heading.className = `tiny_cursive-fullpage-card-header ${titleColor} d-flex align-items-center`;\n heading.innerHTML = `${icon} ${title}`;\n\n const body = this.create('div');\n body.className = `tiny_cursive-fullpage-card-body`;\n body.innerHTML = bodyHTML;\n\n box.append(heading, body);\n return box;\n }\n\n generateRubrics(Rubrics) {\n const wrapper = this.create('div');\n\n Rubrics.forEach(rubric => {\n const rubricDiv = this.create('div');\n rubricDiv.className = 'tiny_cursive-rubric-card';\n\n const title = this.create('h3');\n title.className = 'tiny_cursive-rubric-title';\n title.textContent = rubric.description;\n rubricDiv.appendChild(title);\n\n Object.values(rubric.levels).forEach(level => {\n const levelDiv = this.create('div');\n const score = Number(level.score);\n\n // Assign background color class based on score\n if (score === 0) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-low';\n } else if (score <= 2) {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-mid';\n } else {\n levelDiv.className = 'tiny_cursive-rubric-level tiny_cursive-rubric-high';\n }\n\n levelDiv.textContent = `${level.definition} / ${level.score}`;\n rubricDiv.appendChild(levelDiv);\n });\n\n wrapper.appendChild(rubricDiv);\n });\n\n return wrapper.innerHTML;\n }\n\n submissionStatus(submission) {\n const wrapper = this.create('div');\n\n const statusWrapper = this.create('div');\n statusWrapper.className = 'tiny_cursive-status-row';\n\n const statusName = this.create('span');\n statusName.textContent = `${this.status}:`;\n\n const statusValue = this.create('span');\n const isNew = submission?.current?.status === 'new';\n statusValue.textContent = isNew ? this.draftnot : this.draft;\n statusValue.className = `tiny_cursive-status-value ${isNew ? 'tiny_cursive-status-red' : 'tiny_cursive-status-green'}`;\n\n statusWrapper.append(statusName, statusValue);\n\n const modifiedWrapper = this.create('div');\n modifiedWrapper.className = 'tiny_cursive-status-row';\n\n const modifiedName = this.create('span');\n modifiedName.textContent = `${this.lastModified}: `;\n\n const modifiedValue = this.create('span');\n if (submission?.current?.timemodified) {\n const date = new Date(submission.current.timemodified * 1000);\n modifiedValue.textContent = this.formatDate(date);\n } else {\n modifiedValue.textContent = 'N/A';\n }\n modifiedWrapper.append(modifiedName, modifiedValue);\n\n const gradeWrapper = this.create('div');\n gradeWrapper.className = 'tiny_cursive-status-row';\n\n const gradeName = this.create('span');\n gradeName.textContent = `${this.gradings}: `;\n\n const gradeValue = this.create('span');\n\n if (submission?.grade) {\n gradeValue.textContent = Number(submission.grade.grade) > 0\n ? submission.grade.grade\n : this.gradenot;\n } else {\n gradeValue.textContent = this.gradenot;\n }\n\n gradeWrapper.append(gradeName, gradeValue);\n wrapper.append(statusWrapper, gradeWrapper, modifiedWrapper);\n return wrapper.innerHTML;\n }\n\n wordCounter(status) {\n const wordCount = this.create('div');\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n\n icon.className = 'me-2';\n icon.innerHTML = Icons.assignment;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.wordCount}:`;\n value.textContent = '0';\n value.className = 'text-primary';\n value.style.fontWeight = '600';\n value.style.fontSize = '14px';\n\n wordCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n wordCount.append(labelDiv, value);\n wordCount.style.fontSize = '12px';\n\n const observer = new MutationObserver(() => {\n const newText = status.textContent.trim();\n value.textContent = `${newText.replace('words', '')}`;\n });\n\n observer.observe(status, {\n characterData: true,\n subtree: true,\n childList: true\n });\n\n return wordCount;\n }\n\n\n timerCountDown(timer) {\n\n let warningDiv = document.querySelector('#user-notifications > div');\n if (warningDiv) {\n let clone = warningDiv.cloneNode(true);\n clone.querySelector('button')?.remove();\n this.editor.notificationManager.open({\n text: clone.textContent,\n type: 'error'\n });\n }\n\n\n const timerCount = this.create('div');\n timerCount.className = 'bg-white rounded shadow-sm p-2 d-flex justify-content-between my-2';\n\n const labelDiv = this.create('div');\n const label = this.create('span');\n const value = this.create('span');\n const icon = this.create('span');\n icon.innerHTML = Icons.time;\n\n labelDiv.appendChild(icon);\n labelDiv.append(label);\n\n label.textContent = `${this.timeleft}:`;\n value.textContent = '00:00:00';\n value.className = warningDiv ? 'text-danger' : 'text-primary';\n Object.assign(value.style, {\n fontWeight: '600',\n fontSize: '14px'\n });\n\n\n timerCount.append(labelDiv, value);\n timerCount.style.fontSize = '12px';\n if (timer) {\n const observer = new MutationObserver(() => {\n const newText = timer.textContent.trim();\n value.textContent = `${newText}`;\n });\n observer.observe(timer, {\n characterData: true,\n subtree: true,\n childList: true\n });\n } else {\n value.textContent = this.nolimit;\n }\n\n\n return timerCount;\n }\n\n\n generateStudentInfo(user, course) {\n\n const wrapper = this.create('div');\n\n const nameWrapper = this.create('div');\n const usernameWrapper = this.create('div');\n const courseWrapper = this.create('div');\n\n const nameLabel = this.create('strong');\n const nameValue = this.create('span');\n const usernameLabel = this.create('strong');\n const usernameValue = this.create('span');\n const courseLabel = this.create('strong');\n const courseValue = this.create('span');\n\n nameLabel.textContent = `${this.name}`;\n nameValue.textContent = user.fullname;\n\n usernameLabel.textContent = `${this.userename}: `;\n usernameValue.textContent = user.username;\n\n courseLabel.textContent = `${this.course}: `;\n courseValue.textContent = course.title;\n\n usernameLabel.className = 'cfw-bold me-2';\n usernameValue.className = 'cursiveFw-wrap';\n courseLabel.className = 'cfw-bold me-2';\n courseValue.className = 'cursiveFw-wrap';\n nameLabel.className = 'cfw-bold me-2';\n nameValue.className = 'cursiveFw-wrap';\n\n nameWrapper.append(nameLabel, nameValue);\n usernameWrapper.append(usernameLabel, usernameValue);\n courseWrapper.append(courseLabel, courseValue);\n\n wrapper.append(nameWrapper, usernameWrapper, courseWrapper);\n\n return wrapper.innerHTML;\n\n }\n\n generateImportantDates(open, due) {\n\n const wrapper = this.create('div');\n let openDate = null;\n let dueDate = null;\n\n const openedWrapper = this.create('div');\n const dueWrapper = this.create('div');\n const remainingWrapper = this.create('div');\n\n const openedLabel = this.create('span');\n const openedValue = this.create('span');\n const dueLabel = this.create('span');\n const dueValue = this.create('span');\n const remainingLabel = this.create('span');\n const remainingValue = this.create('span');\n if (this.module === 'quiz') {\n openDate = open * 1000;\n dueDate = due * 1000;\n } else {\n openDate = this.extractDate(open?.textContent);\n dueDate = this.extractDate(due?.textContent);\n }\n\n openedLabel.textContent = `${this.opened}: `;\n openedValue.textContent = this.formatDate(openDate ? new Date(openDate) : null);\n openedValue.className = 'text-dark';\n\n dueLabel.textContent = `${this.due}: `;\n dueValue.textContent = this.formatDate(dueDate ? new Date(dueDate) : null);\n dueValue.className = 'text-danger';\n\n remainingLabel.textContent = `${this.remaining}: `;\n remainingValue.textContent = this.calculateDate(dueDate);\n remainingValue.className = 'text-danger';\n\n openedWrapper.className = 'd-flex justify-content-between';\n dueWrapper.className = 'd-flex justify-content-between';\n remainingWrapper.className = 'd-flex align-items-center justify-content-between mt-2 pt-2 border-top';\n\n openedWrapper.append(openedLabel, openedValue);\n dueWrapper.append(dueLabel, dueValue);\n remainingWrapper.append(remainingLabel, remainingValue);\n\n wrapper.append(openedWrapper, dueWrapper, remainingWrapper);\n\n return wrapper.innerHTML;\n }\n\n formatDate(date) {\n if (!date) {\n return '-';\n }\n\n let options = {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true};\n return date.toLocaleString('en-US', options);\n }\n\n extractDate(text) {\n if (!text) {\n return '-';\n }\n // Split on first colon and return the right part\n const parts = text?.split(':');\n if (parts.length > 1) {\n return parts.slice(1).join(':').trim();\n }\n\n return text.trim();\n }\n\n\n calculateDate(date) {\n if (!date) {\n return '-';\n }\n const date1 = new Date(date); // Due date (local time)\n const now = new Date(); // Current date/time\n\n // Calculate the difference in milliseconds\n const diffMs = date1 - now;\n\n // Convert to days, hours, minutes\n if (diffMs <= 0) {\n return \"Overdue\";\n } else {\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n const diffHours = Math.floor((diffMs / (1000 * 60 * 60)) % 24);\n\n return `${diffDays} days, ${diffHours} hours`;\n }\n\n }\n\n fullPageModule(module) {\n let current = this.module === 'quiz' ?\n document.getElementById(`${module}_ifr`) : document.querySelector(`#${module}_ifr`);\n\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n let p3 = p2.parentElement;\n let p4 = p3.parentElement;\n\n let statusBar = document.querySelector('.tox-statusbar__right-container > button');\n let assignName = document.querySelector('.page-context-header');\n let header = this.create('div');\n let btn = null;\n\n assignName.classList.remove('mb-2');\n header.id = 'tiny_cursive-fullpage-custom-header';\n Object.assign(header.style, {\n backgroundColor: 'white',\n display: 'flex',\n justifyContent: 'space-between'\n });\n\n if (this.module === 'quiz') {\n btn = document.querySelector('#mod_quiz-next-nav').cloneNode(true);\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.style.margin = '.5rem';\n } else {\n btn = this.create('input');\n btn.className = 'tiny_cursive-fullpage-submit-btn';\n btn.value = this.savechanges;\n btn.type = 'submit';\n btn.style.margin = '.5rem';\n }\n\n if (this.module === 'pdfannotator') {\n const style = document.createElement('style');\n style.id = 'cursiveForceStyle';\n style.textContent = `\n .path-mod-pdfannotator #comment-wrapper h4,\n .path-mod-pdfannotator #comment-nav {\n margin: 0 !important;\n }\n `;\n document.head.appendChild(style);\n }\n\n const leftSide = this.create('div');\n const rightSide = this.create('div');\n let commonStyle = {\n display: 'flex',\n alignItems: 'center',\n margin: '0 1rem'\n };\n\n Object.assign(leftSide.style, commonStyle);\n rightSide.id = 'tiny_cursive-fullpage-right-wrapper';\n Object.assign(rightSide.style, commonStyle);\n\n rightSide.appendChild(btn);\n leftSide.appendChild(assignName.cloneNode(true));\n\n header.appendChild(leftSide);\n header.appendChild(rightSide);\n\n p4.insertBefore(header, p4.firstChild);\n p2.style.backgroundColor = '#efefef';\n Object.assign(current.style, {\n width: '750px',\n minWidth: '750px',\n boxShadow: '0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1)'\n });\n\n Object.assign(p1.style, {\n display: 'flex',\n justifyContent: 'center',\n outline: 'none',\n margin: '2rem 0 0'\n });\n const style = this.create('style');\n style.id = 'tiny_cursive-fullpage-mode-style';\n style.textContent = `\n .tox.tox-edit-focus .tox-edit-area::before {\n opacity: 0;\n }`;\n document.head.appendChild(style);\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n\n if (iframeBody) {\n iframeBody.style.padding = '0.5in';\n }\n p2.style.position = 'relative';\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let toggle = this.create('div');\n toggle.id = 'cursive-fullpagemode-sidebar-toggle';\n toggle.innerHTML = Icons.hamburger;\n p2.appendChild(toggle);\n p2.appendChild(this.docSideBar(statusBar));\n }\n\n normalizePage(editorId) {\n document.getElementById('tiny_cursive-fullpage-custom-header')?.remove();\n document.getElementById('cursive-fullpagemode-sidebar')?.remove();\n\n let current = document.getElementById(editorId);\n let p1 = current.parentElement;\n let p2 = p1.parentElement;\n\n Object.assign(p2.style, {\n backgroundColor: \"\",\n position: \"\"\n });\n\n Object.assign(current.style, {\n width: '',\n minWidth: '',\n boxShadow: '',\n });\n\n Object.assign(p1.style, {\n display: '',\n justifyContent: '',\n outline: '',\n margin: ''\n });\n\n p1.classList.remove('tiny-cursive-editor-container');\n\n let iframeBody = current.contentDocument?.body || current.contentWindow?.document?.body;\n if (iframeBody) {\n iframeBody.style.padding = '0';\n }\n document.head.querySelector('#tiny_cursive-fullpage-mode-style')?.remove();\n document.head.querySelector('#cursiveForceStyle')?.remove();\n }\n\n checkForumSubject() {\n const form = document.querySelector('#tiny_cursive-fullpage-right-wrapper > input');\n const msg = this.subjectnot;\n\n if (form) {\n form.addEventListener('click', (e) => {\n const subjectInput = document.getElementById('id_subject');\n let content = this.editor.getContent().trim();\n if (!subjectInput || subjectInput.value.trim() === '' || content === '') {\n e.preventDefault();\n e.stopPropagation();\n this.editor.windowManager.alert(msg);\n }\n });\n }\n }\n\n getSidebarTitle() {\n const [assign, discus, quiz, lesson] = this.getText('sbTitle');\n switch (this.module) {\n case 'assign':\n return {title: assign, icon: Icons.assignment};\n case 'forum':\n return {title: discus, icon: Icons.forum};\n case 'lesson':\n return {title: lesson, icon: Icons.forum};\n case 'quiz':\n return {title: quiz, icon: Icons.quiz};\n case 'pdfannotator':\n return {title: 'PDF Annotation', icon: Icons.pdfannotator};\n default:\n return {title: 'Page', icon: Icons.quiz};\n }\n }\n\n getTimerBlock(module) {\n switch (module) {\n case 'assign':\n return document.querySelector('#mod_assign_timelimit_block > div > div');\n case 'forum':\n return document.querySelector('#mod_forum_timelimit_block');\n case 'lesson':\n return document.querySelector('#lesson-timer');\n case 'quiz':\n return document.querySelector('#quiz-time-left');\n default:\n return null;\n }\n }\n\n getQuestionId(editoId) {\n try {\n if (!editoId || typeof editoId !== 'string') {\n return '';\n }\n return editoId.replace(/^q(\\d+):(\\d+)_.*$/, \"$1-$2\");\n } catch (error) {\n window.console.error('Error getting question ID:', error);\n return '';\n }\n }\n\n initStrings() {\n [\n this.details,\n this.studentInfo,\n this.progress,\n this.description,\n this.replyingto,\n this.answeringto,\n this.importantdates,\n this.rubrics,\n this.subStatus,\n this.status,\n this.draft,\n this.draftnot,\n this.lastModified,\n this.gradings,\n this.gradenot,\n this.wordCount,\n this.timeleft,\n this.nolimit,\n this.name,\n this.userename,\n this.course,\n this.opened,\n this.due,\n this.overdue,\n this.remaining,\n this.savechanges,\n this.subjectnot\n ] = this.getText('docSideBar');\n }\n\n getText(key) {\n return JSON.parse(localStorage.getItem(key)) || [];\n }\n\n create(tag) {\n return document.createElement(tag);\n }\n\n}"],"names":["constructor","User","Rubrics","submission","modulename","editor","quizInfo","module","moduleIcon","Icons","assignment","initStrings","normalMode","id","this","normalizePage","fullPageMode","fullPageModule","_this$editor2","forum","_this$editor3","_this$editor4","quiz","_this$editor5","lesson","_this$editor6","pdfannotator","_this$editor7","docSideBar","status","replyId","URL","window","location","href","searchParams","get","toggle","document","querySelector","timelimitBlock","getTimerBlock","headerInfo","getSidebarTitle","progressBar","courseName","courseDes","Dates","openDate","dueDate","container","create","Object","assign","className","style","width","overflow","crossBtn","innerHTML","close","addEventListener","transition","display","btnWrapper","padding","position","top","backgroundColor","append","header","headerTitle","textContent","title","details","fontWeight","headerIcon","prepend","cloneNode","wordCount","wordCounter","timerCountDown","content","createBox","bg","titleColor","icon","people","studentInfo","bodyHTML","generateStudentInfo","progress","trim","fileSubDiv","querySelectorAll","forEach","Element","verticalAlign","description","checkForumSubject","replyPost","replyingto","_this$editor8","questionId","getQuestionId","_this$editor9","question","intro","atob","answeringto","Number","open","time","importantdates","generateImportantDates","keys","length","rubrics","generateRubrics","subStatus","submissionStatus","box","heading","body","wrapper","rubric","rubricDiv","appendChild","values","levels","level","levelDiv","score","definition","statusWrapper","statusName","statusValue","isNew","current","draftnot","draft","modifiedWrapper","modifiedName","lastModified","modifiedValue","_submission$current2","timemodified","date","Date","formatDate","gradeWrapper","gradeName","gradings","gradeValue","grade","gradenot","labelDiv","label","value","fontSize","MutationObserver","newText","replace","observe","characterData","subtree","childList","timer","warningDiv","clone","remove","notificationManager","text","type","timerCount","timeleft","nolimit","user","course","nameWrapper","usernameWrapper","courseWrapper","nameLabel","nameValue","usernameLabel","usernameValue","courseLabel","courseValue","name","fullname","userename","username","due","openedWrapper","dueWrapper","remainingWrapper","openedLabel","openedValue","dueLabel","dueValue","remainingLabel","remainingValue","extractDate","opened","remaining","calculateDate","toLocaleString","year","month","day","hour","minute","hour12","parts","split","slice","join","diffMs","Math","floor","getElementById","p1","parentElement","p2","p4","statusBar","assignName","btn","classList","justifyContent","margin","savechanges","createElement","head","leftSide","rightSide","commonStyle","alignItems","insertBefore","firstChild","minWidth","boxShadow","outline","iframeBody","contentDocument","contentWindow","_current$contentWindo","_current$contentWindo2","hamburger","editorId","_current$contentWindo3","_current$contentWindo4","form","msg","subjectnot","e","subjectInput","getContent","preventDefault","stopPropagation","windowManager","alert","discus","getText","editoId","error","console","overdue","key","JSON","parse","localStorage","getItem","tag"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/document_view.js b/amd/src/document_view.js index f469c26e..14eeb59a 100644 --- a/amd/src/document_view.js +++ b/amd/src/document_view.js @@ -173,6 +173,12 @@ export default class DocumentView { } if (courseDes && courseDes?.textContent.trim() !== '') { + let fileSubDiv = document.querySelectorAll('.fileuploadsubmission'); + if (fileSubDiv) { + fileSubDiv.forEach(Element => { + Element.style.verticalAlign = 'middle'; + }); + } content.append( this.createBox({ bg: 'bg-gray', diff --git a/styles.css b/styles.css index 4ed34912..0f2d9860 100644 --- a/styles.css +++ b/styles.css @@ -1924,4 +1924,8 @@ body.tox-fullscreen .tiny-cursive-modal .modal-dialog .close { } .cursiveFw-wrap { word-wrap: break-word; -} \ No newline at end of file +} + +#cursive-fullpagemode-sidebar .fileuploadsubmissiontime { + display: none !important; +} From f07b8ae34f1de7be465bd38d538288cb0026b1ea Mon Sep 17 00:00:00 2001 From: Tareq-Adnan Date: Mon, 26 Jan 2026 11:23:53 +0600 Subject: [PATCH 11/14] cmid mismatch issue fix --- amd/build/append_pdfannotator.min.js | 2 +- amd/build/append_pdfannotator.min.js.map | 2 +- amd/src/append_pdfannotator.js | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/amd/build/append_pdfannotator.min.js b/amd/build/append_pdfannotator.min.js index b6ba7407..c1578298 100644 --- a/amd/build/append_pdfannotator.min.js +++ b/amd/build/append_pdfannotator.min.js @@ -5,6 +5,6 @@ define("tiny_cursive/append_pdfannotator",["exports","core/ajax","tiny_cursive/a * @module tiny_cursive/append_pdfannotator * @copyright 2025 Cursive Technology, Inc. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_analytic_button=_interopRequireDefault(_analytic_button),_replay_button=_interopRequireDefault(_replay_button),_analytic_events=_interopRequireDefault(_analytic_events),_templates=_interopRequireDefault(_templates),_replay=_interopRequireDefault(_replay);_exports.init=(scoreSetting,comments,hasApiKey,userid)=>{const replayInstances={};window.video_playback=function(mid,filepath){if(""!==filepath){const replay=new _replay.default("content"+mid,filepath,10,!1,"player_"+mid);replayInstances[mid]=replay}else _templates.default.render("tiny_cursive/no_submission").then((html=>(document.getElementById("content"+mid).innerHTML=html,!0))).catch((e=>window.console.error(e)));return!1};let container=document.querySelector(".comment-list-container");const overviewTable=document.querySelector('table[id^="mod-pdfannotator-"]');document.addEventListener("click",(function(e){"commentSubmit"===e.target.id&&(localStorage.removeItem("isEditing"),buttonElement=e.target.value,pendingSubmit=!0);"commentCancel"===e.target.id&&(localStorage.removeItem("isEditing"),pendingSubmit=!1)}));const moduleName=document.body.id.split("-")[2];var pendingSubmit=!1,buttonElement="";if(container){new MutationObserver((()=>{var _container$lastChild;null!=container&&null!==(_container$lastChild=container.lastChild)&&void 0!==_container$lastChild&&_container$lastChild.id&&function(id){if("Save"===buttonElement)return void(pendingSubmit=!1);let resourceId=parseInt(null==id?void 0:id.split("_")[1]);if(resourceId&&pendingSubmit){pendingSubmit=!1,(async(methodname,args)=>{try{await(0,_ajax.call)([{methodname:methodname,args:args}])[0]}catch(error){throw window.console.error("updating Entries:",error),error}})("cursive_update_pdf_annote_id",{cmid:M.cfg.contextInstanceId,userid:M.cfg.userId??0,courseid:M.cfg.courseId,modulename:moduleName,resourceid:resourceId})}}(container.lastChild.id)})).observe(container,{subtree:!0,childList:!0})}if(overviewTable){let newChild=document.createElement("th");newChild.textContent="Analytics",overviewTable.querySelector("thead>tr>th:first-child").insertAdjacentElement("afterend",newChild),function(overviewTable){const rows=overviewTable.querySelectorAll("tbody > tr"),action=new URL(window.location.href).searchParams.get("action");rows.forEach((row=>{var _cols$col,_cols$col2,_cols$col3,_links$link,_userLink;const cols={col1:row.querySelector("td:nth-child(1)"),col2:row.querySelector("td:nth-child(2)"),col3:row.querySelector("td:nth-child(3)")},links={link1:null===(_cols$col=cols.col1)||void 0===_cols$col?void 0:_cols$col.querySelector("a"),link2:null===(_cols$col2=cols.col2)||void 0===_cols$col2?void 0:_cols$col2.querySelector("a"),link3:null===(_cols$col3=cols.col3)||void 0===_cols$col3?void 0:_cols$col3.querySelector("a")},commentId=null!==(_links$link=links.link1)&&void 0!==_links$link&&_links$link.href?new URL(links.link1.href).searchParams.get("commid"):null;let userId=null,userLink=null;switch(action){case"overviewquestions":userLink=links.link2;break;case"overviewanswers":userLink=links.link3;break;default:userId=userid}if(null!==(_userLink=userLink)&&void 0!==_userLink&&_userLink.href)try{userId=new URL(userLink.href).searchParams.get("id")}catch(e){window.console.warn("Error parsing user URL:",e)}!function(userid,resourceid,cmid,place){let args={id:resourceid,modulename:"pdfannotator",cmid:cmid},methodname="cursive_get_forum_comment_link",com=(0,_ajax.call)([{methodname:methodname,args:args}]);com[0].done((function(json){var data=JSON.parse(json),filepath="";data.data.filename&&(filepath=data.data.filename);let analyticButtonDiv=document.createElement("div"),analyticsColumn=document.createElement("td");hasApiKey?analyticButtonDiv.append((0,_analytic_button.default)(data.data.effort_ratio,resourceid)):analyticButtonDiv.append((0,_replay_button.default)(resourceid)),analyticButtonDiv.dataset.region="analytic-div"+userid,analyticsColumn.append(analyticButtonDiv),place.insertAdjacentElement("afterend",analyticsColumn);let myEvents=new _analytic_events.default;var context={tabledata:data.data,formattime:myEvents.formatedTime(data.data),page:scoreSetting,userid:resourceid,apikey:hasApiKey};let authIcon=myEvents.authorshipStatus(data.data.first_file,data.data.score,scoreSetting);myEvents.createModal(resourceid,context,"",replayInstances,authIcon),myEvents.analytics(resourceid,_templates.default,context,"",replayInstances,authIcon),myEvents.checkDiff(resourceid,data.data.file_id,"",replayInstances),myEvents.replyWriting(resourceid,filepath,"",replayInstances)})),com[0].fail((error=>{window.console.error("Error getting cursive config:",error)}))}(userId,commentId,M.cfg.contextInstanceId,cols.col1)}))}(overviewTable)}}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_analytic_button=_interopRequireDefault(_analytic_button),_replay_button=_interopRequireDefault(_replay_button),_analytic_events=_interopRequireDefault(_analytic_events),_templates=_interopRequireDefault(_templates),_replay=_interopRequireDefault(_replay);_exports.init=(scoreSetting,comments,hasApiKey,userid)=>{const replayInstances={};window.video_playback=function(mid,filepath){if(""!==filepath){const replay=new _replay.default("content"+mid,filepath,10,!1,"player_"+mid);replayInstances[mid]=replay}else _templates.default.render("tiny_cursive/no_submission").then((html=>(document.getElementById("content"+mid).innerHTML=html,!0))).catch((e=>window.console.error(e)));return!1};let container=document.querySelector(".comment-list-container");const overviewTable=document.querySelector('table[id^="mod-pdfannotator-"]');document.addEventListener("click",(function(e){"commentSubmit"===e.target.id&&(localStorage.removeItem("isEditing"),buttonElement=e.target.value,pendingSubmit=!0);"commentCancel"===e.target.id&&(localStorage.removeItem("isEditing"),pendingSubmit=!1)}));const moduleName=document.body.id.split("-")[2];var pendingSubmit=!1,buttonElement="";if(container){new MutationObserver((()=>{var _container$lastChild;null!=container&&null!==(_container$lastChild=container.lastChild)&&void 0!==_container$lastChild&&_container$lastChild.id&&function(id){if("Save"===buttonElement)return void(pendingSubmit=!1);let resourceId=parseInt(null==id?void 0:id.split("_")[1]);if(resourceId&&pendingSubmit){pendingSubmit=!1,(async(methodname,args)=>{try{await(0,_ajax.call)([{methodname:methodname,args:args}])[0]}catch(error){throw window.console.error("updating Entries:",error),error}})("cursive_update_pdf_annote_id",{cmid:M.cfg.contextInstanceId,userid:M.cfg.userId??0,courseid:M.cfg.courseId,modulename:moduleName,resourceid:resourceId})}}(container.lastChild.id)})).observe(container,{subtree:!0,childList:!0})}if(overviewTable){let newChild=document.createElement("th");newChild.textContent="Analytics",overviewTable.querySelector("thead>tr>th:first-child").insertAdjacentElement("afterend",newChild),function(overviewTable){const rows=overviewTable.querySelectorAll("tbody > tr"),action=new URL(window.location.href).searchParams.get("action");rows.forEach((row=>{var _cols$col,_cols$col2,_cols$col3,_links$link,_links$link2,_userLink;const cols={col1:row.querySelector("td:nth-child(1)"),col2:row.querySelector("td:nth-child(2)"),col3:row.querySelector("td:nth-child(3)")},links={link1:null===(_cols$col=cols.col1)||void 0===_cols$col?void 0:_cols$col.querySelector("a"),link2:null===(_cols$col2=cols.col2)||void 0===_cols$col2?void 0:_cols$col2.querySelector("a"),link3:null===(_cols$col3=cols.col3)||void 0===_cols$col3?void 0:_cols$col3.querySelector("a")},commentId=null!==(_links$link=links.link1)&&void 0!==_links$link&&_links$link.href?new URL(links.link1.href).searchParams.get("commid"):null,cmid=null!==(_links$link2=links.link1)&&void 0!==_links$link2&&_links$link2.href?new URL(links.link1.href).searchParams.get("id"):M.cfg.contextInstanceId;let userId=null,userLink=null;switch(action){case"overviewquestions":userLink=links.link2;break;case"overviewanswers":userLink=links.link3;break;default:userId=userid}if(null!==(_userLink=userLink)&&void 0!==_userLink&&_userLink.href)try{userId=new URL(userLink.href).searchParams.get("id")}catch(e){window.console.warn("Error parsing user URL:",e)}!function(userid,resourceid,cmid,place){let args={id:resourceid,modulename:"pdfannotator",cmid:cmid},methodname="cursive_get_forum_comment_link",com=(0,_ajax.call)([{methodname:methodname,args:args}]);com[0].done((function(json){var data=JSON.parse(json),filepath="";data.data.filename&&(filepath=data.data.filename);let analyticButtonDiv=document.createElement("div"),analyticsColumn=document.createElement("td");hasApiKey?analyticButtonDiv.append((0,_analytic_button.default)(data.data.effort_ratio,resourceid)):analyticButtonDiv.append((0,_replay_button.default)(resourceid)),analyticButtonDiv.dataset.region="analytic-div"+userid,analyticsColumn.append(analyticButtonDiv),place.insertAdjacentElement("afterend",analyticsColumn);let myEvents=new _analytic_events.default;var context={tabledata:data.data,formattime:myEvents.formatedTime(data.data),page:scoreSetting,userid:resourceid,apikey:hasApiKey};let authIcon=myEvents.authorshipStatus(data.data.first_file,data.data.score,scoreSetting);myEvents.createModal(resourceid,context,"",replayInstances,authIcon),myEvents.analytics(resourceid,_templates.default,context,"",replayInstances,authIcon),myEvents.checkDiff(resourceid,data.data.file_id,"",replayInstances),myEvents.replyWriting(resourceid,filepath,"",replayInstances)})),com[0].fail((error=>{window.console.error("Error getting cursive config:",error)}))}(userId,commentId,cmid,cols.col1)}))}(overviewTable)}}})); //# sourceMappingURL=append_pdfannotator.min.js.map \ No newline at end of file diff --git a/amd/build/append_pdfannotator.min.js.map b/amd/build/append_pdfannotator.min.js.map index 2e2adf54..cfb18cc9 100644 --- a/amd/build/append_pdfannotator.min.js.map +++ b/amd/build/append_pdfannotator.min.js.map @@ -1 +1 @@ -{"version":3,"file":"append_pdfannotator.min.js","sources":["../src/append_pdfannotator.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module for handling PDF annotator functionality,\n *\n * @module tiny_cursive/append_pdfannotator\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call} from 'core/ajax';\nimport analyticButton from 'tiny_cursive/analytic_button';\nimport replayButton from 'tiny_cursive/replay_button';\nimport AnalyticEvents from 'tiny_cursive/analytic_events';\nimport templates from 'core/templates';\nimport Replay from 'tiny_cursive/replay';\nexport const init = (scoreSetting, comments, hasApiKey, userid) => {\n const replayInstances = {};\n // eslint-disable-next-line camelcase\n window.video_playback = function(mid, filepath) {\n if (filepath !== '') {\n const replay = new Replay(\n 'content' + mid,\n filepath,\n 10,\n false,\n 'player_' + mid\n );\n replayInstances[mid] = replay;\n } else {\n templates.render('tiny_cursive/no_submission').then(html => {\n document.getElementById('content' + mid).innerHTML = html;\n return true;\n }).catch(e => window.console.error(e));\n }\n return false;\n };\n\n let container = document.querySelector('.comment-list-container');\n const overviewTable = document.querySelector('table[id^=\"mod-pdfannotator-\"]');\n\n document.addEventListener('click', handleSubmit);\n const moduleName = document.body.id.split('-')[2];\n var pendingSubmit = false;\n var buttonElement = \"\";\n\n if (container) {\n const observer = new MutationObserver(() => {\n if (container?.lastChild?.id) {\n extractResourceId(container.lastChild.id);\n }\n });\n\n observer.observe(container, {\n subtree: true,\n childList: true\n });\n }\n\n if (overviewTable) {\n let newChild = document.createElement('th');\n newChild.textContent = 'Analytics';\n let header = overviewTable.querySelector('thead>tr>th:first-child');\n header.insertAdjacentElement('afterend', newChild);\n setReplayButton(overviewTable);\n }\n\n /**\n * Sets up replay buttons and analytics for each row in the overview table\n * @param {HTMLTableElement} overviewTable - The table element containing the overview data\n * @description This function:\n * 1. Gets all rows from the table\n * 2. For each row:\n * - Extracts comment ID and user ID from relevant links\n * - Adds analytics column with replay/analytics buttons\n * - Sets up cursive analytics functionality\n */\n function setReplayButton(overviewTable) {\n const rows = overviewTable.querySelectorAll('tbody > tr');\n const action = new URL(window.location.href).searchParams.get('action');\n\n rows.forEach(row => {\n const cols = {\n col1: row.querySelector('td:nth-child(1)'),\n col2: row.querySelector('td:nth-child(2)'),\n col3: row.querySelector('td:nth-child(3)')\n };\n\n const links = {\n link1: cols.col1?.querySelector('a'),\n link2: cols.col2?.querySelector('a'),\n link3: cols.col3?.querySelector('a')\n };\n\n // Extract comment ID safely\n const commentId = links.link1?.href ?\n new URL(links.link1.href).searchParams.get('commid') : null;\n\n // Extract user ID based on action\n let userId = null;\n let userLink = null;\n\n switch(action) {\n case 'overviewquestions':\n userLink = links.link2;\n break;\n case 'overviewanswers':\n userLink = links.link3;\n break;\n default:\n userId = userid;\n }\n\n if (userLink?.href) {\n try {\n userId = new URL(userLink.href).searchParams.get('id');\n } catch (e) {\n window.console.warn('Error parsing user URL:', e);\n }\n }\n\n getCursiveAnalytics(userId, commentId, M.cfg.contextInstanceId, cols.col1);\n });\n }\n\n /**\n * Handles the submission and cancellation of comments\n * @param {Event} e - The click event object\n * @description When comment is submitted or cancelled:\n * - Removes 'isEditing' flag from localStorage\n * - Sets pendingSubmit flag appropriately (true for submit, false for cancel)\n */\n function handleSubmit(e) {\n if (e.target.id === 'commentSubmit') {\n localStorage.removeItem('isEditing');\n buttonElement = e.target.value;\n pendingSubmit = true;\n }\n if (e.target.id === 'commentCancel') {\n localStorage.removeItem('isEditing');\n pendingSubmit = false;\n }\n }\n\n const updateEntries = async (methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n return response;\n } catch (error) {\n window.console.error('updating Entries:', error);\n throw error;\n }\n };\n\n /**\n * Extracts the resource ID from a comment ID and updates entries if submission is pending\n * @param {string} id - The ID string to extract resource ID from, expected format: 'prefix_number'\n * @description This function:\n * 1. Parses the resource ID from the given ID string\n * 2. If resource ID exists and there's a pending submission:\n * - Resets the pending submission flag\n * - Constructs arguments with context info\n * - Calls updateEntries to process the PDF annotation\n */\n function extractResourceId(id) {\n\n // prevent updating ID while editing a existing entry.\n if (buttonElement === 'Save') {\n\n pendingSubmit = false;\n return;\n }\n\n let resourceId = parseInt(id?.split('_')[1]);\n if (resourceId && pendingSubmit) {\n pendingSubmit = false;\n let args = {\n cmid: M.cfg.contextInstanceId,\n userid: M.cfg.userId ?? 0,\n courseid: M.cfg.courseId,\n modulename: moduleName,\n resourceid: resourceId\n };\n updateEntries('cursive_update_pdf_annote_id', args);\n }\n }\n\n /**\n * Retrieves and displays cursive analytics for a given resource\n * @param {number} userid - The ID of the user\n * @param {number} resourceid - The ID of the resource to get analytics for\n * @param {number} cmid - The course module ID\n * @param {HTMLElement} place - The DOM element where analytics should be placed\n * @description This function:\n * 1. Makes an AJAX call to get forum comment data\n * 2. Creates and inserts analytics/replay buttons\n * 3. Sets up analytics events and modal functionality\n * 4. Handles both API key and non-API key scenarios\n */\n function getCursiveAnalytics(userid, resourceid, cmid, place) {\n let args = {id: resourceid, modulename: \"pdfannotator\", cmid: cmid};\n let methodname = 'cursive_get_forum_comment_link';\n let com = call([{methodname, args}]);\n com[0].done(function(json) {\n var data = JSON.parse(json);\n\n var filepath = '';\n if (data.data.filename) {\n filepath = data.data.filename;\n }\n\n let analyticButtonDiv = document.createElement('div');\n let analyticsColumn = document.createElement('td');\n\n if (!hasApiKey) {\n analyticButtonDiv.append(replayButton(resourceid));\n } else {\n analyticButtonDiv.append(analyticButton(data.data.effort_ratio, resourceid));\n }\n\n analyticButtonDiv.dataset.region = \"analytic-div\" + userid;\n analyticsColumn.append(analyticButtonDiv);\n place.insertAdjacentElement('afterend', analyticsColumn);\n\n let myEvents = new AnalyticEvents();\n var context = {\n tabledata: data.data,\n formattime: myEvents.formatedTime(data.data),\n page: scoreSetting,\n userid: resourceid,\n apikey: hasApiKey\n };\n\n let authIcon = myEvents.authorshipStatus(data.data.first_file, data.data.score, scoreSetting);\n myEvents.createModal(resourceid, context, '', replayInstances, authIcon);\n myEvents.analytics(resourceid, templates, context, '', replayInstances, authIcon);\n myEvents.checkDiff(resourceid, data.data.file_id, '', replayInstances);\n myEvents.replyWriting(resourceid, filepath, '', replayInstances);\n\n });\n com[0].fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n }\n};"],"names":["scoreSetting","comments","hasApiKey","userid","replayInstances","window","video_playback","mid","filepath","replay","Replay","render","then","html","document","getElementById","innerHTML","catch","e","console","error","container","querySelector","overviewTable","addEventListener","target","id","localStorage","removeItem","buttonElement","value","pendingSubmit","moduleName","body","split","MutationObserver","lastChild","_container$lastChild","resourceId","parseInt","async","methodname","args","updateEntries","cmid","M","cfg","contextInstanceId","userId","courseid","courseId","modulename","resourceid","extractResourceId","observe","subtree","childList","newChild","createElement","textContent","insertAdjacentElement","rows","querySelectorAll","action","URL","location","href","searchParams","get","forEach","row","cols","col1","col2","col3","links","link1","_cols$col","link2","_cols$col2","link3","_cols$col3","commentId","userLink","_userLink","warn","place","com","done","json","data","JSON","parse","filename","analyticButtonDiv","analyticsColumn","append","effort_ratio","dataset","region","myEvents","AnalyticEvents","context","tabledata","formattime","formatedTime","page","apikey","authIcon","authorshipStatus","first_file","score","createModal","analytics","templates","checkDiff","file_id","replyWriting","fail","getCursiveAnalytics","setReplayButton"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"append_pdfannotator.min.js","sources":["../src/append_pdfannotator.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module for handling PDF annotator functionality,\n *\n * @module tiny_cursive/append_pdfannotator\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call} from 'core/ajax';\nimport analyticButton from 'tiny_cursive/analytic_button';\nimport replayButton from 'tiny_cursive/replay_button';\nimport AnalyticEvents from 'tiny_cursive/analytic_events';\nimport templates from 'core/templates';\nimport Replay from 'tiny_cursive/replay';\nexport const init = (scoreSetting, comments, hasApiKey, userid) => {\n const replayInstances = {};\n // eslint-disable-next-line camelcase\n window.video_playback = function(mid, filepath) {\n if (filepath !== '') {\n const replay = new Replay(\n 'content' + mid,\n filepath,\n 10,\n false,\n 'player_' + mid\n );\n replayInstances[mid] = replay;\n } else {\n templates.render('tiny_cursive/no_submission').then(html => {\n document.getElementById('content' + mid).innerHTML = html;\n return true;\n }).catch(e => window.console.error(e));\n }\n return false;\n };\n\n let container = document.querySelector('.comment-list-container');\n const overviewTable = document.querySelector('table[id^=\"mod-pdfannotator-\"]');\n\n document.addEventListener('click', handleSubmit);\n const moduleName = document.body.id.split('-')[2];\n var pendingSubmit = false;\n var buttonElement = \"\";\n\n if (container) {\n const observer = new MutationObserver(() => {\n if (container?.lastChild?.id) {\n extractResourceId(container.lastChild.id);\n }\n });\n\n observer.observe(container, {\n subtree: true,\n childList: true\n });\n }\n\n if (overviewTable) {\n let newChild = document.createElement('th');\n newChild.textContent = 'Analytics';\n let header = overviewTable.querySelector('thead>tr>th:first-child');\n header.insertAdjacentElement('afterend', newChild);\n setReplayButton(overviewTable);\n }\n\n /**\n * Sets up replay buttons and analytics for each row in the overview table\n * @param {HTMLTableElement} overviewTable - The table element containing the overview data\n * @description This function:\n * 1. Gets all rows from the table\n * 2. For each row:\n * - Extracts comment ID and user ID from relevant links\n * - Adds analytics column with replay/analytics buttons\n * - Sets up cursive analytics functionality\n */\n function setReplayButton(overviewTable) {\n const rows = overviewTable.querySelectorAll('tbody > tr');\n const action = new URL(window.location.href).searchParams.get('action');\n\n rows.forEach(row => {\n const cols = {\n col1: row.querySelector('td:nth-child(1)'),\n col2: row.querySelector('td:nth-child(2)'),\n col3: row.querySelector('td:nth-child(3)')\n };\n\n const links = {\n link1: cols.col1?.querySelector('a'),\n link2: cols.col2?.querySelector('a'),\n link3: cols.col3?.querySelector('a')\n };\n\n // Extract comment ID safely\n const commentId = links.link1?.href ?\n new URL(links.link1.href).searchParams.get('commid') : null;\n const cmid = links.link1?.href ?\n new URL(links.link1.href).searchParams.get('id') : M.cfg.contextInstanceId;\n // Extract user ID based on action\n let userId = null;\n let userLink = null;\n\n switch(action) {\n case 'overviewquestions':\n userLink = links.link2;\n break;\n case 'overviewanswers':\n userLink = links.link3;\n break;\n default:\n userId = userid;\n }\n\n if (userLink?.href) {\n try {\n userId = new URL(userLink.href).searchParams.get('id');\n } catch (e) {\n window.console.warn('Error parsing user URL:', e);\n }\n }\n\n getCursiveAnalytics(userId, commentId, cmid, cols.col1);\n });\n }\n\n /**\n * Handles the submission and cancellation of comments\n * @param {Event} e - The click event object\n * @description When comment is submitted or cancelled:\n * - Removes 'isEditing' flag from localStorage\n * - Sets pendingSubmit flag appropriately (true for submit, false for cancel)\n */\n function handleSubmit(e) {\n if (e.target.id === 'commentSubmit') {\n localStorage.removeItem('isEditing');\n buttonElement = e.target.value;\n pendingSubmit = true;\n }\n if (e.target.id === 'commentCancel') {\n localStorage.removeItem('isEditing');\n pendingSubmit = false;\n }\n }\n\n const updateEntries = async (methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n return response;\n } catch (error) {\n window.console.error('updating Entries:', error);\n throw error;\n }\n };\n\n /**\n * Extracts the resource ID from a comment ID and updates entries if submission is pending\n * @param {string} id - The ID string to extract resource ID from, expected format: 'prefix_number'\n * @description This function:\n * 1. Parses the resource ID from the given ID string\n * 2. If resource ID exists and there's a pending submission:\n * - Resets the pending submission flag\n * - Constructs arguments with context info\n * - Calls updateEntries to process the PDF annotation\n */\n function extractResourceId(id) {\n\n // prevent updating ID while editing a existing entry.\n if (buttonElement === 'Save') {\n\n pendingSubmit = false;\n return;\n }\n\n let resourceId = parseInt(id?.split('_')[1]);\n if (resourceId && pendingSubmit) {\n pendingSubmit = false;\n let args = {\n cmid: M.cfg.contextInstanceId,\n userid: M.cfg.userId ?? 0,\n courseid: M.cfg.courseId,\n modulename: moduleName,\n resourceid: resourceId\n };\n updateEntries('cursive_update_pdf_annote_id', args);\n }\n }\n\n /**\n * Retrieves and displays cursive analytics for a given resource\n * @param {number} userid - The ID of the user\n * @param {number} resourceid - The ID of the resource to get analytics for\n * @param {number} cmid - The course module ID\n * @param {HTMLElement} place - The DOM element where analytics should be placed\n * @description This function:\n * 1. Makes an AJAX call to get forum comment data\n * 2. Creates and inserts analytics/replay buttons\n * 3. Sets up analytics events and modal functionality\n * 4. Handles both API key and non-API key scenarios\n */\n function getCursiveAnalytics(userid, resourceid, cmid, place) {\n let args = {id: resourceid, modulename: \"pdfannotator\", cmid: cmid};\n let methodname = 'cursive_get_forum_comment_link';\n let com = call([{methodname, args}]);\n com[0].done(function(json) {\n var data = JSON.parse(json);\n\n var filepath = '';\n if (data.data.filename) {\n filepath = data.data.filename;\n }\n\n let analyticButtonDiv = document.createElement('div');\n let analyticsColumn = document.createElement('td');\n\n if (!hasApiKey) {\n analyticButtonDiv.append(replayButton(resourceid));\n } else {\n analyticButtonDiv.append(analyticButton(data.data.effort_ratio, resourceid));\n }\n\n analyticButtonDiv.dataset.region = \"analytic-div\" + userid;\n analyticsColumn.append(analyticButtonDiv);\n place.insertAdjacentElement('afterend', analyticsColumn);\n\n let myEvents = new AnalyticEvents();\n var context = {\n tabledata: data.data,\n formattime: myEvents.formatedTime(data.data),\n page: scoreSetting,\n userid: resourceid,\n apikey: hasApiKey\n };\n\n let authIcon = myEvents.authorshipStatus(data.data.first_file, data.data.score, scoreSetting);\n myEvents.createModal(resourceid, context, '', replayInstances, authIcon);\n myEvents.analytics(resourceid, templates, context, '', replayInstances, authIcon);\n myEvents.checkDiff(resourceid, data.data.file_id, '', replayInstances);\n myEvents.replyWriting(resourceid, filepath, '', replayInstances);\n\n });\n com[0].fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n }\n};"],"names":["scoreSetting","comments","hasApiKey","userid","replayInstances","window","video_playback","mid","filepath","replay","Replay","render","then","html","document","getElementById","innerHTML","catch","e","console","error","container","querySelector","overviewTable","addEventListener","target","id","localStorage","removeItem","buttonElement","value","pendingSubmit","moduleName","body","split","MutationObserver","lastChild","_container$lastChild","resourceId","parseInt","async","methodname","args","updateEntries","cmid","M","cfg","contextInstanceId","userId","courseid","courseId","modulename","resourceid","extractResourceId","observe","subtree","childList","newChild","createElement","textContent","insertAdjacentElement","rows","querySelectorAll","action","URL","location","href","searchParams","get","forEach","row","cols","col1","col2","col3","links","link1","_cols$col","link2","_cols$col2","link3","_cols$col3","commentId","userLink","_userLink","warn","place","com","done","json","data","JSON","parse","filename","analyticButtonDiv","analyticsColumn","append","effort_ratio","dataset","region","myEvents","AnalyticEvents","context","tabledata","formattime","formatedTime","page","apikey","authIcon","authorshipStatus","first_file","score","createModal","analytics","templates","checkDiff","file_id","replyWriting","fail","getCursiveAnalytics","setReplayButton"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/append_pdfannotator.js b/amd/src/append_pdfannotator.js index 9980a1f8..30697561 100644 --- a/amd/src/append_pdfannotator.js +++ b/amd/src/append_pdfannotator.js @@ -108,7 +108,8 @@ export const init = (scoreSetting, comments, hasApiKey, userid) => { // Extract comment ID safely const commentId = links.link1?.href ? new URL(links.link1.href).searchParams.get('commid') : null; - + const cmid = links.link1?.href ? + new URL(links.link1.href).searchParams.get('id') : M.cfg.contextInstanceId; // Extract user ID based on action let userId = null; let userLink = null; @@ -132,7 +133,7 @@ export const init = (scoreSetting, comments, hasApiKey, userid) => { } } - getCursiveAnalytics(userId, commentId, M.cfg.contextInstanceId, cols.col1); + getCursiveAnalytics(userId, commentId, cmid, cols.col1); }); } From 816a1477b01f4023da842873249bcd7129ba3f5e Mon Sep 17 00:00:00 2001 From: Tareq-Adnan Date: Thu, 29 Jan 2026 20:17:23 +0600 Subject: [PATCH 12/14] moodle prechecks --- amd/build/append_pdfannotator.min.js.map | 2 +- amd/build/autosaver.min.js.map | 2 +- amd/src/append_pdfannotator.js | 6 +++--- amd/src/autosaver.js | 2 +- classes/helper.php | 10 ++++------ externallib.php | 11 +++++++---- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/amd/build/append_pdfannotator.min.js.map b/amd/build/append_pdfannotator.min.js.map index cfb18cc9..efbbc64c 100644 --- a/amd/build/append_pdfannotator.min.js.map +++ b/amd/build/append_pdfannotator.min.js.map @@ -1 +1 @@ -{"version":3,"file":"append_pdfannotator.min.js","sources":["../src/append_pdfannotator.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module for handling PDF annotator functionality,\n *\n * @module tiny_cursive/append_pdfannotator\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call} from 'core/ajax';\nimport analyticButton from 'tiny_cursive/analytic_button';\nimport replayButton from 'tiny_cursive/replay_button';\nimport AnalyticEvents from 'tiny_cursive/analytic_events';\nimport templates from 'core/templates';\nimport Replay from 'tiny_cursive/replay';\nexport const init = (scoreSetting, comments, hasApiKey, userid) => {\n const replayInstances = {};\n // eslint-disable-next-line camelcase\n window.video_playback = function(mid, filepath) {\n if (filepath !== '') {\n const replay = new Replay(\n 'content' + mid,\n filepath,\n 10,\n false,\n 'player_' + mid\n );\n replayInstances[mid] = replay;\n } else {\n templates.render('tiny_cursive/no_submission').then(html => {\n document.getElementById('content' + mid).innerHTML = html;\n return true;\n }).catch(e => window.console.error(e));\n }\n return false;\n };\n\n let container = document.querySelector('.comment-list-container');\n const overviewTable = document.querySelector('table[id^=\"mod-pdfannotator-\"]');\n\n document.addEventListener('click', handleSubmit);\n const moduleName = document.body.id.split('-')[2];\n var pendingSubmit = false;\n var buttonElement = \"\";\n\n if (container) {\n const observer = new MutationObserver(() => {\n if (container?.lastChild?.id) {\n extractResourceId(container.lastChild.id);\n }\n });\n\n observer.observe(container, {\n subtree: true,\n childList: true\n });\n }\n\n if (overviewTable) {\n let newChild = document.createElement('th');\n newChild.textContent = 'Analytics';\n let header = overviewTable.querySelector('thead>tr>th:first-child');\n header.insertAdjacentElement('afterend', newChild);\n setReplayButton(overviewTable);\n }\n\n /**\n * Sets up replay buttons and analytics for each row in the overview table\n * @param {HTMLTableElement} overviewTable - The table element containing the overview data\n * @description This function:\n * 1. Gets all rows from the table\n * 2. For each row:\n * - Extracts comment ID and user ID from relevant links\n * - Adds analytics column with replay/analytics buttons\n * - Sets up cursive analytics functionality\n */\n function setReplayButton(overviewTable) {\n const rows = overviewTable.querySelectorAll('tbody > tr');\n const action = new URL(window.location.href).searchParams.get('action');\n\n rows.forEach(row => {\n const cols = {\n col1: row.querySelector('td:nth-child(1)'),\n col2: row.querySelector('td:nth-child(2)'),\n col3: row.querySelector('td:nth-child(3)')\n };\n\n const links = {\n link1: cols.col1?.querySelector('a'),\n link2: cols.col2?.querySelector('a'),\n link3: cols.col3?.querySelector('a')\n };\n\n // Extract comment ID safely\n const commentId = links.link1?.href ?\n new URL(links.link1.href).searchParams.get('commid') : null;\n const cmid = links.link1?.href ?\n new URL(links.link1.href).searchParams.get('id') : M.cfg.contextInstanceId;\n // Extract user ID based on action\n let userId = null;\n let userLink = null;\n\n switch(action) {\n case 'overviewquestions':\n userLink = links.link2;\n break;\n case 'overviewanswers':\n userLink = links.link3;\n break;\n default:\n userId = userid;\n }\n\n if (userLink?.href) {\n try {\n userId = new URL(userLink.href).searchParams.get('id');\n } catch (e) {\n window.console.warn('Error parsing user URL:', e);\n }\n }\n\n getCursiveAnalytics(userId, commentId, cmid, cols.col1);\n });\n }\n\n /**\n * Handles the submission and cancellation of comments\n * @param {Event} e - The click event object\n * @description When comment is submitted or cancelled:\n * - Removes 'isEditing' flag from localStorage\n * - Sets pendingSubmit flag appropriately (true for submit, false for cancel)\n */\n function handleSubmit(e) {\n if (e.target.id === 'commentSubmit') {\n localStorage.removeItem('isEditing');\n buttonElement = e.target.value;\n pendingSubmit = true;\n }\n if (e.target.id === 'commentCancel') {\n localStorage.removeItem('isEditing');\n pendingSubmit = false;\n }\n }\n\n const updateEntries = async (methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n return response;\n } catch (error) {\n window.console.error('updating Entries:', error);\n throw error;\n }\n };\n\n /**\n * Extracts the resource ID from a comment ID and updates entries if submission is pending\n * @param {string} id - The ID string to extract resource ID from, expected format: 'prefix_number'\n * @description This function:\n * 1. Parses the resource ID from the given ID string\n * 2. If resource ID exists and there's a pending submission:\n * - Resets the pending submission flag\n * - Constructs arguments with context info\n * - Calls updateEntries to process the PDF annotation\n */\n function extractResourceId(id) {\n\n // prevent updating ID while editing a existing entry.\n if (buttonElement === 'Save') {\n\n pendingSubmit = false;\n return;\n }\n\n let resourceId = parseInt(id?.split('_')[1]);\n if (resourceId && pendingSubmit) {\n pendingSubmit = false;\n let args = {\n cmid: M.cfg.contextInstanceId,\n userid: M.cfg.userId ?? 0,\n courseid: M.cfg.courseId,\n modulename: moduleName,\n resourceid: resourceId\n };\n updateEntries('cursive_update_pdf_annote_id', args);\n }\n }\n\n /**\n * Retrieves and displays cursive analytics for a given resource\n * @param {number} userid - The ID of the user\n * @param {number} resourceid - The ID of the resource to get analytics for\n * @param {number} cmid - The course module ID\n * @param {HTMLElement} place - The DOM element where analytics should be placed\n * @description This function:\n * 1. Makes an AJAX call to get forum comment data\n * 2. Creates and inserts analytics/replay buttons\n * 3. Sets up analytics events and modal functionality\n * 4. Handles both API key and non-API key scenarios\n */\n function getCursiveAnalytics(userid, resourceid, cmid, place) {\n let args = {id: resourceid, modulename: \"pdfannotator\", cmid: cmid};\n let methodname = 'cursive_get_forum_comment_link';\n let com = call([{methodname, args}]);\n com[0].done(function(json) {\n var data = JSON.parse(json);\n\n var filepath = '';\n if (data.data.filename) {\n filepath = data.data.filename;\n }\n\n let analyticButtonDiv = document.createElement('div');\n let analyticsColumn = document.createElement('td');\n\n if (!hasApiKey) {\n analyticButtonDiv.append(replayButton(resourceid));\n } else {\n analyticButtonDiv.append(analyticButton(data.data.effort_ratio, resourceid));\n }\n\n analyticButtonDiv.dataset.region = \"analytic-div\" + userid;\n analyticsColumn.append(analyticButtonDiv);\n place.insertAdjacentElement('afterend', analyticsColumn);\n\n let myEvents = new AnalyticEvents();\n var context = {\n tabledata: data.data,\n formattime: myEvents.formatedTime(data.data),\n page: scoreSetting,\n userid: resourceid,\n apikey: hasApiKey\n };\n\n let authIcon = myEvents.authorshipStatus(data.data.first_file, data.data.score, scoreSetting);\n myEvents.createModal(resourceid, context, '', replayInstances, authIcon);\n myEvents.analytics(resourceid, templates, context, '', replayInstances, authIcon);\n myEvents.checkDiff(resourceid, data.data.file_id, '', replayInstances);\n myEvents.replyWriting(resourceid, filepath, '', replayInstances);\n\n });\n com[0].fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n }\n};"],"names":["scoreSetting","comments","hasApiKey","userid","replayInstances","window","video_playback","mid","filepath","replay","Replay","render","then","html","document","getElementById","innerHTML","catch","e","console","error","container","querySelector","overviewTable","addEventListener","target","id","localStorage","removeItem","buttonElement","value","pendingSubmit","moduleName","body","split","MutationObserver","lastChild","_container$lastChild","resourceId","parseInt","async","methodname","args","updateEntries","cmid","M","cfg","contextInstanceId","userId","courseid","courseId","modulename","resourceid","extractResourceId","observe","subtree","childList","newChild","createElement","textContent","insertAdjacentElement","rows","querySelectorAll","action","URL","location","href","searchParams","get","forEach","row","cols","col1","col2","col3","links","link1","_cols$col","link2","_cols$col2","link3","_cols$col3","commentId","userLink","_userLink","warn","place","com","done","json","data","JSON","parse","filename","analyticButtonDiv","analyticsColumn","append","effort_ratio","dataset","region","myEvents","AnalyticEvents","context","tabledata","formattime","formatedTime","page","apikey","authIcon","authorshipStatus","first_file","score","createModal","analytics","templates","checkDiff","file_id","replyWriting","fail","getCursiveAnalytics","setReplayButton"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"append_pdfannotator.min.js","sources":["../src/append_pdfannotator.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module for handling PDF annotator functionality,\n *\n * @module tiny_cursive/append_pdfannotator\n * @copyright 2025 Cursive Technology, Inc. \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call} from 'core/ajax';\nimport analyticButton from 'tiny_cursive/analytic_button';\nimport replayButton from 'tiny_cursive/replay_button';\nimport AnalyticEvents from 'tiny_cursive/analytic_events';\nimport templates from 'core/templates';\nimport Replay from 'tiny_cursive/replay';\nexport const init = (scoreSetting, comments, hasApiKey, userid) => {\n const replayInstances = {};\n // eslint-disable-next-line camelcase\n window.video_playback = function(mid, filepath) {\n if (filepath !== '') {\n const replay = new Replay(\n 'content' + mid,\n filepath,\n 10,\n false,\n 'player_' + mid\n );\n replayInstances[mid] = replay;\n } else {\n templates.render('tiny_cursive/no_submission').then(html => {\n document.getElementById('content' + mid).innerHTML = html;\n return true;\n }).catch(e => window.console.error(e));\n }\n return false;\n };\n\n let container = document.querySelector('.comment-list-container');\n const overviewTable = document.querySelector('table[id^=\"mod-pdfannotator-\"]');\n\n document.addEventListener('click', handleSubmit);\n const moduleName = document.body.id.split('-')[2];\n var pendingSubmit = false;\n var buttonElement = \"\";\n\n if (container) {\n const observer = new MutationObserver(() => {\n if (container?.lastChild?.id) {\n extractResourceId(container.lastChild.id);\n }\n });\n\n observer.observe(container, {\n subtree: true,\n childList: true\n });\n }\n\n if (overviewTable) {\n let newChild = document.createElement('th');\n newChild.textContent = 'Analytics';\n let header = overviewTable.querySelector('thead>tr>th:first-child');\n header.insertAdjacentElement('afterend', newChild);\n setReplayButton(overviewTable);\n }\n\n /**\n * Sets up replay buttons and analytics for each row in the overview table\n * @param {HTMLTableElement} overviewTable - The table element containing the overview data\n * @description This function:\n * 1. Gets all rows from the table\n * 2. For each row:\n * - Extracts comment ID and user ID from relevant links\n * - Adds analytics column with replay/analytics buttons\n * - Sets up cursive analytics functionality\n */\n function setReplayButton(overviewTable) {\n const rows = overviewTable.querySelectorAll('tbody > tr');\n const action = new URL(window.location.href).searchParams.get('action');\n\n rows.forEach(row => {\n const cols = {\n col1: row.querySelector('td:nth-child(1)'),\n col2: row.querySelector('td:nth-child(2)'),\n col3: row.querySelector('td:nth-child(3)')\n };\n\n const links = {\n link1: cols.col1?.querySelector('a'),\n link2: cols.col2?.querySelector('a'),\n link3: cols.col3?.querySelector('a')\n };\n\n // Extract comment ID safely\n const commentId = links.link1?.href ?\n new URL(links.link1.href).searchParams.get('commid') : null;\n const cmid = links.link1?.href ?\n new URL(links.link1.href).searchParams.get('id') : M.cfg.contextInstanceId;\n // Extract user ID based on action\n let userId = null;\n let userLink = null;\n\n switch (action) {\n case 'overviewquestions':\n userLink = links.link2;\n break;\n case 'overviewanswers':\n userLink = links.link3;\n break;\n default:\n userId = userid;\n }\n\n if (userLink?.href) {\n try {\n userId = new URL(userLink.href).searchParams.get('id');\n } catch (e) {\n window.console.warn('Error parsing user URL:', e);\n }\n }\n\n getCursiveAnalytics(userId, commentId, cmid, cols.col1);\n });\n }\n\n /**\n * Handles the submission and cancellation of comments\n * @param {Event} e - The click event object\n * @description When comment is submitted or cancelled:\n * - Removes 'isEditing' flag from localStorage\n * - Sets pendingSubmit flag appropriately (true for submit, false for cancel)\n */\n function handleSubmit(e) {\n if (e.target.id === 'commentSubmit') {\n localStorage.removeItem('isEditing');\n buttonElement = e.target.value;\n pendingSubmit = true;\n }\n if (e.target.id === 'commentCancel') {\n localStorage.removeItem('isEditing');\n pendingSubmit = false;\n }\n }\n\n const updateEntries = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n return response;\n } catch (error) {\n window.console.error('updating Entries:', error);\n throw error;\n }\n };\n\n /**\n * Extracts the resource ID from a comment ID and updates entries if submission is pending\n * @param {string} id - The ID string to extract resource ID from, expected format: 'prefix_number'\n * @description This function:\n * 1. Parses the resource ID from the given ID string\n * 2. If resource ID exists and there's a pending submission:\n * - Resets the pending submission flag\n * - Constructs arguments with context info\n * - Calls updateEntries to process the PDF annotation\n */\n function extractResourceId(id) {\n\n // Prevent updating ID while editing a existing entry.\n if (buttonElement === 'Save') {\n\n pendingSubmit = false;\n return;\n }\n\n let resourceId = parseInt(id?.split('_')[1]);\n if (resourceId && pendingSubmit) {\n pendingSubmit = false;\n let args = {\n cmid: M.cfg.contextInstanceId,\n userid: M.cfg.userId ?? 0,\n courseid: M.cfg.courseId,\n modulename: moduleName,\n resourceid: resourceId\n };\n updateEntries('cursive_update_pdf_annote_id', args);\n }\n }\n\n /**\n * Retrieves and displays cursive analytics for a given resource\n * @param {number} userid - The ID of the user\n * @param {number} resourceid - The ID of the resource to get analytics for\n * @param {number} cmid - The course module ID\n * @param {HTMLElement} place - The DOM element where analytics should be placed\n * @description This function:\n * 1. Makes an AJAX call to get forum comment data\n * 2. Creates and inserts analytics/replay buttons\n * 3. Sets up analytics events and modal functionality\n * 4. Handles both API key and non-API key scenarios\n */\n function getCursiveAnalytics(userid, resourceid, cmid, place) {\n let args = {id: resourceid, modulename: \"pdfannotator\", cmid: cmid};\n let methodname = 'cursive_get_forum_comment_link';\n let com = call([{methodname, args}]);\n com[0].done(function(json) {\n var data = JSON.parse(json);\n\n var filepath = '';\n if (data.data.filename) {\n filepath = data.data.filename;\n }\n\n let analyticButtonDiv = document.createElement('div');\n let analyticsColumn = document.createElement('td');\n\n if (!hasApiKey) {\n analyticButtonDiv.append(replayButton(resourceid));\n } else {\n analyticButtonDiv.append(analyticButton(data.data.effort_ratio, resourceid));\n }\n\n analyticButtonDiv.dataset.region = \"analytic-div\" + userid;\n analyticsColumn.append(analyticButtonDiv);\n place.insertAdjacentElement('afterend', analyticsColumn);\n\n let myEvents = new AnalyticEvents();\n var context = {\n tabledata: data.data,\n formattime: myEvents.formatedTime(data.data),\n page: scoreSetting,\n userid: resourceid,\n apikey: hasApiKey\n };\n\n let authIcon = myEvents.authorshipStatus(data.data.first_file, data.data.score, scoreSetting);\n myEvents.createModal(resourceid, context, '', replayInstances, authIcon);\n myEvents.analytics(resourceid, templates, context, '', replayInstances, authIcon);\n myEvents.checkDiff(resourceid, data.data.file_id, '', replayInstances);\n myEvents.replyWriting(resourceid, filepath, '', replayInstances);\n\n });\n com[0].fail((error) => {\n window.console.error('Error getting cursive config:', error);\n });\n }\n};"],"names":["scoreSetting","comments","hasApiKey","userid","replayInstances","window","video_playback","mid","filepath","replay","Replay","render","then","html","document","getElementById","innerHTML","catch","e","console","error","container","querySelector","overviewTable","addEventListener","target","id","localStorage","removeItem","buttonElement","value","pendingSubmit","moduleName","body","split","MutationObserver","lastChild","_container$lastChild","resourceId","parseInt","async","methodname","args","updateEntries","cmid","M","cfg","contextInstanceId","userId","courseid","courseId","modulename","resourceid","extractResourceId","observe","subtree","childList","newChild","createElement","textContent","insertAdjacentElement","rows","querySelectorAll","action","URL","location","href","searchParams","get","forEach","row","cols","col1","col2","col3","links","link1","_cols$col","link2","_cols$col2","link3","_cols$col3","commentId","userLink","_userLink","warn","place","com","done","json","data","JSON","parse","filename","analyticButtonDiv","analyticsColumn","append","effort_ratio","dataset","region","myEvents","AnalyticEvents","context","tabledata","formattime","formatedTime","page","apikey","authIcon","authorshipStatus","first_file","score","createModal","analytics","templates","checkDiff","file_id","replyWriting","fail","getCursiveAnalytics","setReplayButton"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/autosaver.min.js.map b/amd/build/autosaver.min.js.map index 971226ae..e0c9b4de 100644 --- a/amd/build/autosaver.min.js.map +++ b/amd/build/autosaver.min.js.map @@ -1 +1 @@ -{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n if (!originalText) {\n originalText = getRawText(editor);\n }\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n\n /**\n * Gets the raw text content from a TinyMCE editor iframe\n * @param {Object} editor - The TinyMCE editor instance\n * @returns {string} The raw text content of the editor body, or empty string if not found\n * @description Attempts to get the raw text content from the editor's iframe body by:\n * 1. Getting the editor ID\n * 2. Finding the associated iframe element\n * 3. Accessing the iframe's document body\n * 4. Returning the text content\n * Returns empty string if any step fails\n */\n function getRawText(editor) {\n let editorId = editor?.id;\n if (editorId) {\n let iframe = document.querySelector(`#${editorId}_ifr`);\n let iframeBody = iframe.contentDocument?.body || iframe.contentWindow?.document?.body;\n return iframeBody?.textContent;\n }\n return \"\";\n }\n\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","editorId","iframe","querySelector","iframeBody","contentDocument","contentWindow","_iframe$contentWindow","_iframe$contentWindow2","getRawText","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"autosaver.min.js","sources":["../src/autosaver.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module tiny_cursive/autosaver\n * @category TinyMCE Editor\n * @copyright CTI \n * @author Brain Station 23 \n */\n\nimport {call} from 'core/ajax';\nimport {create} from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport {save, cancel, hidden} from 'core/modal_events';\nimport $ from 'jquery';\nimport {iconUrl, iconGrayUrl, tooltipCss} from 'tiny_cursive/common';\nimport Autosave from 'tiny_cursive/cursive_autosave';\nimport DocumentView from 'tiny_cursive/document_view';\nimport {call as getUser} from \"core/ajax\";\n\nexport const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, submission, quizInfo, pasteSetting) => {\n\n var isStudent = !($('#body').hasClass('teacher_admin'));\n var intervention = $('#body').hasClass('intervention');\n var host = M.cfg.wwwroot;\n var userid = userId;\n var courseid = M.cfg.courseId;\n var editorid = editor?.id;\n var cmid = M.cfg.contextInstanceId;\n var ed = \"\";\n var event = \"\";\n var filename = \"\";\n var questionid = 0;\n var quizSubmit = $('#mod_quiz-next-nav');\n let assignSubmit = $('#id_submitbutton');\n var syncInterval = interval ? interval * 1000 : 10000; // Default: Sync Every 10s.\n var lastCaretPos = 1;\n let aiContents = [];\n var isFullScreen = false;\n var user = null;\n let ur = window.location.href;\n let parm = new URL(ur);\n let modulesInfo = getModulesInfo(ur, parm, MODULES);\n var resourceId = modulesInfo.resourceId;\n var modulename = modulesInfo.name;\n var errorAlert = true;\n let PASTE_SETTING = pasteSetting || 'allow';\n let shouldBlockPaste = false;\n let isPasteAllowed = false;\n\n if (ur.includes('pdfannotator')) {\n document.addEventListener('click', e => {\n if (e.target.className === \"dropdown-item comment-edit-a\") {\n let id = e.target.id;\n resourceId = id.replace('editButton', '');\n localStorage.setItem('isEditing', '1');\n }\n if (e.target.id === 'commentSubmit') {\n syncData();\n }\n });\n }\n\n const postOne = async(methodname, args) => {\n try {\n const response = await call([{\n methodname,\n args,\n }])[0];\n if (response) {\n setTimeout(() => {\n Autosave.updateSavingState('saved');\n }, 1000);\n }\n return response;\n } catch (error) {\n Autosave.updateSavingState('offline');\n window.console.error('Error in postOne:', error);\n throw error;\n }\n };\n\n getUser([{\n methodname: 'core_user_get_users_by_field',\n args: {field: 'id', values: [userid]},\n }])[0].done(response => {\n user = response[0];\n }).fail((ex) => {\n window.console.error('Error fetching user data:', ex);\n });\n\n assignSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n assignSubmit.off('click').click();\n });\n } else {\n assignSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n quizSubmit.on('click', async function(e) {\n e.preventDefault();\n if (filename) {\n // eslint-disable-next-line\n syncData().then(() => {\n quizSubmit.off('click').click();\n });\n } else {\n quizSubmit.off('click').click();\n }\n localStorage.removeItem('lastCopyCutContent');\n });\n\n const getModal = () => {\n\n Promise.all([\n getString('tiny_cursive_srcurl', 'tiny_cursive'),\n getString('tiny_cursive_srcurl_des', 'tiny_cursive'),\n getString('tiny_cursive_placeholder', 'tiny_cursive')\n ]).then(function([title, titledes, placeholder]) {\n\n return create({\n type: 'SAVE_CANCEL',\n title: `
${title}
\n ${titledes}
`,\n body: ``,\n removeOnClose: true,\n })\n .done(modal => {\n modal.getRoot().addClass('tiny-cursive-modal');\n modal.show();\n var lastEvent = '';\n\n modal.getRoot().on(save, function() {\n\n var number = document.getElementById(\"inputUrl\").value.trim();\n\n if (number === \"\" || number === null || number === undefined) {\n editor.execCommand('Undo');\n // eslint-disable-next-line\n getString('pastewarning', 'tiny_cursive').then(str => alert(str));\n } else {\n editor.execCommand('Paste');\n }\n\n postOne('cursive_user_comments', {\n modulename: modulename,\n cmid: cmid,\n resourceid: resourceId,\n courseid: courseid,\n usercomment: number,\n timemodified: Date.now(),\n editorid: editorid ? editorid : \"\"\n });\n\n lastEvent = 'save';\n modal.destroy();\n });\n modal.getRoot().on(cancel, function() {\n editor.execCommand('Undo');\n lastEvent = 'cancel';\n });\n\n modal.getRoot().on(hidden, function() {\n if (lastEvent != 'cancel' && lastEvent != 'save') {\n editor.execCommand('Undo');\n }\n });\n return modal;\n });\n }).catch(error => window.console.error(error));\n\n };\n\n const sendKeyEvent = (events, editor) => {\n ed = editor;\n event = events;\n\n filename = `${userid}_${resourceId}_${cmid}_${modulename}_attempt`;\n\n if (modulename === 'quiz') {\n questionid = editorid.split(':')[1].split('_')[0];\n filename = `${userid}_${resourceId}_${cmid}_${questionid}_${modulename}_attempt`;\n }\n\n if (localStorage.getItem(filename)) {\n let data = JSON.parse(localStorage.getItem(filename));\n data.push({\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n });\n localStorage.setItem(filename, JSON.stringify(data));\n } else {\n let data = [{\n resourceId: resourceId,\n key: editor.key,\n keyCode: editor.keyCode,\n event: event,\n courseId: courseid,\n unixTimestamp: Date.now(),\n clientId: host,\n personId: userid,\n position: ed.caretPosition,\n rePosition: ed.rePosition,\n pastedContent: editor.pastedContent,\n aiContent: editor.aiContent\n }];\n localStorage.setItem(filename, JSON.stringify(data));\n }\n };\n\n editor.on('keyUp', (editor) => {\n customTooltip();\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyUp\", editor);\n });\n editor.on('Paste', async(e) => {\n customTooltip();\n const pastedContent = (e.clipboardData || e.originalEvent.clipboardData).getData('text');\n if (!pastedContent) {\n return;\n }\n // Trim both values for consistent comparison\n const trimmedPastedContent = pastedContent.trim();\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && trimmedPastedContent === lastCopyCutContent;\n\n if (isStudent && intervention) {\n\n if (PASTE_SETTING === 'block') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n shouldBlockPaste = true;\n isPasteAllowed = false;\n e.stopPropagation();\n e.stopImmediatePropagation();\n getString('paste_blocked', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n setTimeout(() => {\n isPasteAllowed = true;\n shouldBlockPaste = false;\n }, 100);\n return;\n }\n shouldBlockPaste = false;\n isPasteAllowed = true;\n return;\n }\n if (PASTE_SETTING === 'cite_source') {\n if (!isFromOwnEditor) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n getModal(e);\n }\n isPasteAllowed = true;\n return;\n }\n }\n isPasteAllowed = true;\n });\n editor.on('Redo', async(e) => {\n customTooltip();\n if (isStudent && intervention) {\n getModal(e);\n }\n });\n editor.on('keyDown', (editor) => {\n customTooltip();\n const isPasteAttempt = (editor.key === 'v' || editor.key === 'V') &&\n (editor.ctrlKey || editor.metaKey);\n if (isPasteAttempt && isStudent && intervention && PASTE_SETTING === 'block' && !isPasteAllowed) {\n setTimeout(() => {\n isPasteAllowed = true;\n }, 100);\n return;\n }\n let position = getCaretPosition();\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n sendKeyEvent(\"keyDown\", editor);\n });\n editor.on('Cut', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('Copy', () => {\n const selectedContent = editor.selection.getContent({format: 'text'});\n localStorage.setItem('lastCopyCutContent', selectedContent.trim());\n });\n editor.on('mouseDown', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseDown\", editor);\n }, 0);\n });\n editor.on('mouseUp', async(editor) => {\n setTimeout(() => {\n constructMouseEvent(editor);\n sendKeyEvent(\"mouseUp\", editor);\n }, 10);\n });\n editor.on('init', () => {\n customTooltip();\n localStorage.removeItem('lastCopyCutContent');\n });\n editor.on('SetContent', () => {\n customTooltip();\n });\n editor.on('FullscreenStateChanged', (e) => {\n let view = new DocumentView(user, Rubrics, submission, modulename, editor, quizInfo);\n isFullScreen = e.state;\n try {\n if (!e.state) {\n view.normalMode();\n } else {\n view.fullPageMode();\n }\n } catch (error) {\n if (errorAlert) {\n errorAlert = false;\n getString('fullmodeerror', 'tiny_cursive').then(str => {\n return editor.windowManager.alert(str);\n }).catch(error => window.console.error(error));\n }\n view.normalMode();\n window.console.error('Error ResizeEditor event:', error);\n }\n });\n\n editor.on('execcommand', function(e) {\n if (e.command === \"mceInsertContent\") {\n const contentObj = e.value;\n\n const isPaste = contentObj && typeof contentObj === 'object' && contentObj.paste === true;\n\n let insertedContent = contentObj.content || contentObj;\n let tempDiv = document.createElement('div');\n tempDiv.innerHTML = insertedContent;\n let text = tempDiv.textContent || tempDiv.innerText || '';\n let pastedText = tempDiv.textContent || tempDiv.innerText || '';\n\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n\n if (isPaste) {\n if (shouldBlockPaste) {\n shouldBlockPaste = false;\n e.preventDefault();\n editor.undoManager.undo();\n return;\n }\n const lastCopyCutContent = localStorage.getItem('lastCopyCutContent');\n const isFromOwnEditor = lastCopyCutContent && pastedText.trim() === lastCopyCutContent;\n\n if (isStudent && intervention && PASTE_SETTING === 'block' && !isFromOwnEditor) {\n isPasteAllowed = false;\n editor.undoManager.undo();\n return;\n }\n\n sendKeyEvent(\"Paste\", {\n key: \"v\",\n keyCode: 86,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n pastedContent: pastedText,\n srcElement: {baseURI: window.location.href}\n });\n } else {\n aiContents.push(text);\n\n sendKeyEvent(\"aiInsert\", {\n key: \"ai\",\n keyCode: 0,\n caretPosition: editor.caretPosition,\n rePosition: editor.rePosition,\n aiContent: text,\n srcElement: {baseURI: window.location.href}\n });\n }\n }\n });\n\n editor.on('input', function(e) {\n let position = getCaretPosition(true);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n let aiContent = e.data;\n\n if (e.inputType === 'insertReplacementText' || (e.inputType === 'insertText' && aiContent && aiContent.length > 1)) {\n\n aiContents.push(aiContent);\n\n e.key = \"ai\";\n e.keyCode = 0;\n e.caretPosition = position.caretPosition;\n e.rePosition = position.rePosition;\n e.aiContent = aiContent;\n\n sendKeyEvent(\"aiInsert\", e);\n }\n });\n\n\n /**\n * Constructs a mouse event object with caret position and button information\n * @param {Object} editor - The TinyMCE editor instance\n * @function constructMouseEvent\n * @description Sets caret position, reposition, key and keyCode properties on the editor object based on current mouse state\n */\n function constructMouseEvent(editor) {\n let position = getCaretPosition(false);\n editor.caretPosition = position.caretPosition;\n editor.rePosition = position.rePosition;\n editor.key = getMouseButton(editor);\n editor.keyCode = editor.button;\n }\n\n /**\n * Gets the string representation of a mouse button based on its numeric value\n * @param {Object} editor - The editor object containing button information\n * @returns {string} The string representation of the mouse button ('left', 'middle', or 'right')\n */\n function getMouseButton(editor) {\n\n switch (editor.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n }\n return null;\n }\n\n /**\n * Gets the current caret position in the editor\n * @param {boolean} skip - If true, returns the last known caret position instead of calculating a new one\n * @returns {Object} Object containing:\n * - caretPosition: Sequential position number stored in session\n * - rePosition: Absolute character offset from start of content\n * @throws {Error} Logs warning to console if error occurs during calculation\n */\n function getCaretPosition(skip = false) {\n try {\n if (!editor || !editor.selection) {\n return {caretPosition: 0, rePosition: 0};\n }\n\n const range = editor.selection.getRng();\n const body = editor.getBody();\n\n // Create a range from start of document to current caret\n const preCaretRange = range.cloneRange();\n preCaretRange.selectNodeContents(body);\n preCaretRange.setEnd(range.endContainer, range.endOffset);\n\n const fragment = preCaretRange.cloneContents();\n const tempDiv = document.createElement('div');\n tempDiv.appendChild(fragment);\n let textBeforeCursor = tempDiv.innerText || '';\n\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n if (endOffset === 0 &&\n endContainer.nodeType === Node.ELEMENT_NODE &&\n editor.dom.isBlock(endContainer) &&\n endContainer.previousSibling) {\n textBeforeCursor += '\\n';\n }\n const blockElements = tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li');\n let emptyBlockCount = 0;\n blockElements.forEach(block => {\n const text = block.innerText || block.textContent || '';\n if (text.trim() === '' && block.childNodes.length === 1 &&\n block.childNodes[0].nodeName === 'BR') {\n emptyBlockCount++;\n }\n });\n\n // Add newlines for empty blocks (these represent Enter presses that created empty lines)\n if (emptyBlockCount > 0) {\n textBeforeCursor += '\\n'.repeat(emptyBlockCount);\n }\n\n const absolutePosition = textBeforeCursor.length;\n\n if (skip) {\n return {\n caretPosition: lastCaretPos,\n rePosition: absolutePosition\n };\n }\n // Increment sequential caretPosition\n const storageKey = `${userid}_${resourceId}_${cmid}_position`;\n let storedPos = parseInt(sessionStorage.getItem(storageKey), 10);\n if (isNaN(storedPos)) {\n storedPos = 0;\n }\n storedPos++;\n lastCaretPos = storedPos;\n sessionStorage.setItem(storageKey, storedPos);\n\n return {\n caretPosition: storedPos,\n rePosition: absolutePosition\n };\n\n } catch (e) {\n window.console.warn('Error getting caret position:', e);\n return {caretPosition: lastCaretPos || 1, rePosition: 0};\n }\n }\n\n\n /**\n * Synchronizes data from localStorage to server\n * @async\n * @function SyncData\n * @description Retrieves stored keypress data from localStorage and sends it to server\n * @returns {Promise} Returns response from server if data exists and is successfully sent\n * @throws {Error} Logs error to console if data submission fails\n */\n async function syncData() {\n checkIsPdfAnnotator();\n let data = localStorage.getItem(filename);\n\n if (!data || data.length === 0) {\n return;\n } else {\n localStorage.removeItem(filename);\n editor.fire('change');\n let originalText = editor.getContent({format: 'text'});\n if (!originalText) {\n originalText = getRawText(editor);\n }\n try {\n Autosave.updateSavingState('saving');\n // eslint-disable-next-line\n return await postOne('cursive_write_local_to_json', {\n key: ed.key,\n event: event,\n keyCode: ed.keyCode,\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n editorid: editorid,\n \"json_data\": data,\n originalText: originalText\n });\n } catch (error) {\n window.console.error('Error submitting data:', error);\n }\n }\n }\n\n /**\n * Gets the raw text content from a TinyMCE editor iframe\n * @param {Object} editor - The TinyMCE editor instance\n * @returns {string} The raw text content of the editor body, or empty string if not found\n * @description Attempts to get the raw text content from the editor's iframe body by:\n * 1. Getting the editor ID\n * 2. Finding the associated iframe element\n * 3. Accessing the iframe's document body\n * 4. Returning the text content\n * Returns empty string if any step fails\n */\n function getRawText(editor) {\n let editorId = editor?.id;\n if (editorId) {\n let iframe = document.querySelector(`#${editorId}_ifr`);\n let iframeBody = iframe.contentDocument?.body || iframe.contentWindow?.document?.body;\n return iframeBody?.textContent;\n }\n return \"\";\n }\n\n /**\n * Sets up custom tooltip functionality for the Cursive icon\n * Initializes tooltip text, positions the icon in the menubar,\n * and sets up mouse event handlers for showing/hiding the tooltip\n * @function customTooltip\n */\n function customTooltip() {\n try {\n const tooltipText = getTooltipText();\n const menubarDiv = document.querySelectorAll('div[role=\"menubar\"].tox-menubar');\n let classArray = [];\n\n if (menubarDiv.length) {\n menubarDiv.forEach(function(element, index) {\n index += 1;\n let className = 'cursive-menu-' + index;\n element.classList.add(className);\n classArray.push(className);\n });\n }\n\n const cursiveIcon = document.createElement('img');\n cursiveIcon.src = hasApiKey ? iconUrl : iconGrayUrl;\n\n cursiveIcon.setAttribute('class', 'tiny_cursive_StateButton');\n cursiveIcon.style.display = 'inline-block';\n\n cursiveState(cursiveIcon, menubarDiv, classArray);\n\n for (let index in classArray) {\n const elementId = \"tiny_cursive_StateIcon\" + index;\n const tooltipId = `tiny_cursive_tooltip${index}`;\n\n tooltipText.then((text) => {\n return setTooltip(text, document.querySelector(`#${elementId}`), tooltipId);\n }).catch(error => window.console.error(error));\n\n $(`#${elementId}`).on('mouseenter', function() {\n $(this).css('position', 'relative');\n $(`#${tooltipId}`).css(tooltipCss);\n });\n\n $(`#${elementId}`).on('mouseleave', function() {\n $(`#${tooltipId}`).css('display', 'none');\n });\n }\n } catch (error) {\n window.console.error('Error setting up custom tooltip:', error);\n }\n }\n\n /**\n * Retrieves tooltip text strings from language files\n * @async\n * @function getTooltipText\n * @returns {Promise} Object containing buttonTitle and buttonDes strings\n */\n async function getTooltipText() {\n const [\n buttonTitle,\n buttonDes,\n ] = await Promise.all([\n getString('cursive:state:active', 'tiny_cursive'),\n getString('cursive:state:active:des', 'tiny_cursive'),\n ]);\n return {buttonTitle, buttonDes};\n }\n\n /**\n * Updates the Cursive icon state and positions it in the menubar\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to modify\n * @param {HTMLElement} menubarDiv - The menubar div element\n * @param {Array} classArray - Array of class names for the menubar div elements\n */\n function cursiveState(cursiveIcon, menubarDiv, classArray) {\n if (!menubarDiv) {\n return;\n }\n\n for (let index in classArray) {\n const rightWrapper = document.createElement('div');\n const imgWrapper = document.createElement('span');\n const iconClone = cursiveIcon.cloneNode(true);\n const targetMenu = document.querySelector('.' + classArray[index]);\n let elementId = \"tiny_cursive_StateIcon\" + index;\n\n rightWrapper.style.cssText = `\n margin-left: auto;\n display: flex;\n align-items: center;\n `;\n\n imgWrapper.id = elementId;\n imgWrapper.style.marginLeft = '.2rem';\n imgWrapper.appendChild(iconClone);\n rightWrapper.appendChild(imgWrapper);\n\n let moduleIds = {\n resourceId: resourceId,\n cmid: cmid,\n modulename: modulename,\n questionid: questionid,\n userid: userid,\n courseid: courseid};\n // Document mode, other modules single editor instances\n if (isFullScreen && (modulename === 'assign' || modulename === 'forum'\n || modulename === 'lesson')) {\n let existsElement = document.querySelector('.tox-menubar[class*=\"cursive-menu-\"] > div');\n if (existsElement) {\n existsElement.remove();\n }\n\n if (!document.querySelector(`#${elementId}`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else if (isFullScreen && modulename === 'quiz') { // Document mode, quiz multiple editor instances\n let existingElement = editor.container?.childNodes[1]?.childNodes[0]?.childNodes[0]?.childNodes[7];\n let newHeader = editor.container?.childNodes[0];\n if (existingElement) {\n existingElement.remove();\n }\n\n if (newHeader && !newHeader.querySelector(`span[id*=tiny_cursive_StateIcon]`)) {\n rightWrapper.style.marginTop = '3px';\n document.querySelector('#tiny_cursive-fullpage-right-wrapper').prepend(rightWrapper);\n }\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n } else { // Regular view\n let menubar = editor?.container?.children[0]?.childNodes[0]?.childNodes[0];\n\n if (targetMenu && !targetMenu.querySelector(`#${elementId}`)) {\n targetMenu.appendChild(rightWrapper);\n }\n // Regular view, multiple editor instances\n if (modulename === 'quiz' && menubar) {\n let wrapper = menubar.querySelector('span[id*=\"tiny_cursive_StateIcon\"]');\n\n if (wrapper) {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, wrapper?.parentElement, moduleIds, isFullScreen);\n }\n } else {\n Autosave.destroyInstance();\n Autosave.getInstance(editor, rightWrapper, moduleIds, isFullScreen);\n }\n }\n }\n }\n\n /**\n * Sets up tooltip content and styling for the Cursive icon\n * @param {Object} text - Object containing tooltip text strings\n * @param {string} text.buttonTitle - Title text for the tooltip\n * @param {string} text.buttonDes - Description text for the tooltip\n * @param {HTMLElement} cursiveIcon - The Cursive icon element to attach tooltip to\n * @param {string} tooltipId - ID for the tooltip element\n */\n function setTooltip(text, cursiveIcon, tooltipId) {\n\n if (document.querySelector(`#${tooltipId}`)) {\n return;\n }\n if (cursiveIcon) {\n\n const tooltipSpan = document.createElement('span');\n const description = document.createElement('span');\n const linebreak = document.createElement('br');\n const tooltipTitle = document.createElement('strong');\n\n tooltipSpan.style.display = 'none';\n tooltipTitle.textContent = text.buttonTitle;\n tooltipTitle.style.fontSize = '16px';\n tooltipTitle.style.fontWeight = 'bold';\n description.textContent = text.buttonDes;\n description.style.fontSize = '14px';\n\n tooltipSpan.id = tooltipId;\n tooltipSpan.classList.add(`shadow`);\n tooltipSpan.appendChild(tooltipTitle);\n tooltipSpan.appendChild(linebreak);\n tooltipSpan.appendChild(description);\n cursiveIcon.appendChild(tooltipSpan);\n }\n }\n\n /**\n * Extracts module information from URL parameters\n * @param {string} ur - The base URL to analyze\n * @param {URL} parm - URL object containing search parameters\n * @param {Array} MODULES - Array of valid module names to check against\n * @returns {Object|boolean} Object containing resourceId and module name if found, false if no valid module\n */\n function getModulesInfo(ur, parm, MODULES) {\n fetchStrings();\n\n if (!MODULES.some(module => ur.includes(module))) {\n return false;\n }\n\n if (ur.includes(\"forum\") && !ur.includes(\"assign\")) {\n resourceId = parm.searchParams.get('edit');\n } else {\n resourceId = parm.searchParams.get('attempt');\n }\n\n if (resourceId === null) {\n resourceId = 0;\n }\n\n for (const module of MODULES) {\n if (ur.includes(module)) {\n modulename = module;\n if (module === \"lesson\" || module === \"assign\") {\n resourceId = cmid;\n } else if (module === \"oublog\") {\n resourceId = 0;\n }\n break;\n }\n }\n\n checkIsPdfAnnotator();\n\n return {resourceId: resourceId, name: modulename};\n }\n\n /**\n * Fetches and caches localized strings used in the UI\n * @function fetchStrings\n * @description Retrieves strings for sidebar titles and document sidebar elements if not already cached in localStorage\n * Uses Promise.all to fetch multiple strings in parallel for better performance\n * Stores the fetched strings in localStorage under 'sbTitle' and 'docSideBar' keys\n */\n function fetchStrings() {\n if (!localStorage.getItem('sbTitle')) {\n Promise.all([\n getString('assignment', 'tiny_cursive'),\n getString('discussion', 'tiny_cursive'),\n getString('pluginname', 'mod_quiz'),\n getString('pluginname', 'mod_lesson'),\n getString('description', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('sbTitle', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n if (!localStorage.getItem('docSideBar')) {\n Promise.all([\n getString('details', 'tiny_cursive'),\n getString('student_info', 'tiny_cursive'),\n getString('progress', 'tiny_cursive'),\n getString('description', 'tiny_cursive'),\n getString('replyingto', 'tiny_cursive'),\n getString('answeringto', 'tiny_cursive'),\n getString('importantdates', 'tiny_cursive'),\n getString('rubrics', 'tiny_cursive'),\n getString('submission_status', 'tiny_cursive'),\n getString('status', 'tiny_cursive'),\n getString('draft', 'tiny_cursive'),\n getString('draftnot', 'tiny_cursive'),\n getString('last_modified', 'tiny_cursive'),\n getString('gradings', 'tiny_cursive'),\n getString('gradenot', 'tiny_cursive'),\n getString('word_count', 'tiny_cursive'),\n getString('timeleft', 'tiny_cursive'),\n getString('nolimit', 'tiny_cursive'),\n getString('name', 'tiny_cursive'),\n getString('userename', 'tiny_cursive'),\n getString('course', 'tiny_cursive'),\n getString('opened', 'tiny_cursive'),\n getString('due', 'tiny_cursive'),\n getString('overdue', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n getString('savechanges', 'tiny_cursive'),\n getString('subjectnot', 'tiny_cursive'),\n getString('remaining', 'tiny_cursive'),\n ]).then(function(strings) {\n return localStorage.setItem('docSideBar', JSON.stringify(strings));\n }).catch(error => window.console.error(error));\n }\n\n }\n\n /**\n * Checks if the current page is a PDF annotator and updates the resourceId accordingly\n * @function checkIsPdfAnnotator\n * @description Checks if URL contains 'pdfannotator' and sets resourceId based on editor ID and editing state:\n * - If editing an existing annotation (editor.id !== 'id_pdfannotator_content' and isEditing is true):\n * Sets resourceId to the annotation ID extracted from editor.id\n * - Otherwise: Sets resourceId to 0\n */\n function checkIsPdfAnnotator() {\n if (ur.includes('pdfannotator')) {\n if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) {\n resourceId = parseInt(editor?.id.replace('editarea', ''));\n } else {\n resourceId = 0;\n }\n }\n }\n\n window.addEventListener('unload', () => {\n syncData();\n });\n\n setInterval(syncData, syncInterval);\n};\n"],"names":["editor","interval","userId","hasApiKey","MODULES","Rubrics","submission","quizInfo","pasteSetting","isStudent","hasClass","intervention","host","M","cfg","wwwroot","userid","courseid","courseId","editorid","id","cmid","contextInstanceId","ed","event","filename","questionid","quizSubmit","assignSubmit","syncInterval","lastCaretPos","aiContents","isFullScreen","user","ur","window","location","href","parm","URL","modulesInfo","localStorage","getItem","Promise","all","then","strings","setItem","JSON","stringify","catch","error","console","fetchStrings","some","module","includes","resourceId","searchParams","get","modulename","checkIsPdfAnnotator","name","getModulesInfo","errorAlert","PASTE_SETTING","shouldBlockPaste","isPasteAllowed","document","addEventListener","e","target","className","replace","syncData","postOne","async","methodname","args","response","setTimeout","updateSavingState","field","values","done","fail","ex","on","preventDefault","off","click","removeItem","getModal","title","titledes","placeholder","type","body","removeOnClose","modal","getRoot","addClass","show","lastEvent","save","number","getElementById","value","trim","execCommand","str","alert","resourceid","usercomment","timemodified","Date","now","destroy","cancel","hidden","sendKeyEvent","events","split","data","parse","push","key","keyCode","unixTimestamp","clientId","personId","position","caretPosition","rePosition","pastedContent","aiContent","constructMouseEvent","getCaretPosition","button","getMouseButton","skip","selection","range","getRng","getBody","preCaretRange","cloneRange","selectNodeContents","setEnd","endContainer","endOffset","fragment","cloneContents","tempDiv","createElement","appendChild","textBeforeCursor","innerText","nodeType","Node","ELEMENT_NODE","dom","isBlock","previousSibling","blockElements","querySelectorAll","emptyBlockCount","forEach","block","textContent","childNodes","length","nodeName","repeat","absolutePosition","storageKey","storedPos","parseInt","sessionStorage","isNaN","warn","fire","originalText","getContent","format","editorId","iframe","querySelector","iframeBody","contentDocument","contentWindow","_iframe$contentWindow","_iframe$contentWindow2","getRawText","customTooltip","tooltipText","buttonTitle","buttonDes","getTooltipText","menubarDiv","classArray","element","index","classList","add","cursiveIcon","src","iconUrl","iconGrayUrl","setAttribute","style","display","rightWrapper","imgWrapper","iconClone","cloneNode","targetMenu","elementId","cssText","marginLeft","moduleIds","existingElement","container","_editor$container","_editor$container$chi","_editor$container$chi2","_editor$container$chi3","newHeader","_editor$container2","remove","marginTop","prepend","destroyInstance","getInstance","menubar","_editor$container3","children","_editor$container3$ch","_editor$container3$ch2","wrapper","parentElement","existsElement","cursiveState","tooltipId","text","setTooltip","this","css","tooltipCss","tooltipSpan","description","linebreak","tooltipTitle","fontSize","fontWeight","clipboardData","originalEvent","getData","trimmedPastedContent","lastCopyCutContent","isFromOwnEditor","stopPropagation","stopImmediatePropagation","windowManager","ctrlKey","metaKey","selectedContent","view","DocumentView","state","fullPageMode","normalMode","command","contentObj","isPaste","paste","insertedContent","content","innerHTML","pastedText","undoManager","undo","srcElement","baseURI","inputType","setInterval"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/append_pdfannotator.js b/amd/src/append_pdfannotator.js index 30697561..f52c1ebd 100644 --- a/amd/src/append_pdfannotator.js +++ b/amd/src/append_pdfannotator.js @@ -114,7 +114,7 @@ export const init = (scoreSetting, comments, hasApiKey, userid) => { let userId = null; let userLink = null; - switch(action) { + switch (action) { case 'overviewquestions': userLink = links.link2; break; @@ -156,7 +156,7 @@ export const init = (scoreSetting, comments, hasApiKey, userid) => { } } - const updateEntries = async (methodname, args) => { + const updateEntries = async(methodname, args) => { try { const response = await call([{ methodname, @@ -181,7 +181,7 @@ export const init = (scoreSetting, comments, hasApiKey, userid) => { */ function extractResourceId(id) { - // prevent updating ID while editing a existing entry. + // Prevent updating ID while editing a existing entry. if (buttonElement === 'Save') { pendingSubmit = false; diff --git a/amd/src/autosaver.js b/amd/src/autosaver.js index 7e95adff..a18e5c2a 100644 --- a/amd/src/autosaver.js +++ b/amd/src/autosaver.js @@ -903,7 +903,7 @@ export const register = (editor, interval, userId, hasApiKey, MODULES, Rubrics, * Sets resourceId to the annotation ID extracted from editor.id * - Otherwise: Sets resourceId to 0 */ - function checkIsPdfAnnotator() { + function checkIsPdfAnnotator() { if (ur.includes('pdfannotator')) { if (editor.id !== 'id_pdfannotator_content' && parseInt(localStorage.getItem('isEditing'))) { resourceId = parseInt(editor?.id.replace('editarea', '')); diff --git a/classes/helper.php b/classes/helper.php index 7bc0a7ca..f7b413d2 100644 --- a/classes/helper.php +++ b/classes/helper.php @@ -37,9 +37,9 @@ public static function update_resource_id($data) { } /** - * Tiny cursive plugin update comment observer. + * Updates comments in the tiny_cursive_comments table. * - * @param \core\event\base $event The event object + * @param array $data Array containing userid, modulename, courseid, cmid and resourceid * @throws \dml_exception */ public static function update_comment($data) { @@ -64,10 +64,9 @@ public static function update_comment($data) { } /** - * Tiny cursive plugin update cursive files observer. + * Updates cursive file in the tiny_cursive_files table. * - * @param \core\event\base $event The event object - * @return void + * @param array $data Array containing userid, modulename, courseid, cmid and resourceid * @throws \dml_exception */ public static function update_cursive_files($data) { @@ -93,7 +92,6 @@ public static function update_cursive_files($data) { * * @param array $conditions The conditions to find records to update * @param string $table The database table name - * @param array $data The event data containing user, course and context info * @param int $postid The post ID to update the records with * @return void * @throws \dml_exception diff --git a/externallib.php b/externallib.php index 80d74390..6cadbf94 100644 --- a/externallib.php +++ b/externallib.php @@ -1659,9 +1659,12 @@ public static function write_local_to_json( ], ); - if ($params['resourceId'] == 0 && $params['modulename'] !== 'forum' && $params['modulename'] !== 'oublog' && $params['modulename'] !== 'pdfannotator') { - // For Quiz and Assignment there is no resourceid that's why cmid is resourceid. + if ( + $params['resourceId'] == 0 && $params['modulename'] !== 'forum' && $params['modulename'] !== 'oublog' && + $params['modulename'] !== 'pdfannotator' + ) { $params['resourceId'] = $params['cmid']; + // For Quiz and Assignment there is no resourceid that's why cmid is resourceid. } $courseid = 0; @@ -2185,11 +2188,11 @@ public static function update_pdf_annote_id_parameters() { /** * Updates the PDF annotation ID for a comment record * - * @param int $resourceid The PDF annotation comment ID - * @param string $modulename The name of the module * @param int $cmid The course module ID * @param int $userid The user ID (defaults to current user) * @param int $courseid The course ID + * @param string $modulename The name of the module + * @param int $resourceid The PDF annotation comment ID * @return bool * @throws coding_exception * @throws dml_exception From 7c29818d72c0df6da333785819ba6061b9c581ce Mon Sep 17 00:00:00 2001 From: Tareq-Adnan Date: Thu, 29 Jan 2026 20:24:59 +0600 Subject: [PATCH 13/14] version update --- version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.php b/version.php index 47e23930..19135c24 100644 --- a/version.php +++ b/version.php @@ -29,6 +29,6 @@ $plugin->component = 'tiny_cursive'; $plugin->release = '2.1.2'; -$plugin->version = 2025121901; +$plugin->version = 2025122000; $plugin->requires = 2022041912; $plugin->maturity = MATURITY_STABLE; From 2a1f664db28f2a5d66c2cff6f2bdf6f15e9429d3 Mon Sep 17 00:00:00 2001 From: Tareq-Adnan Date: Fri, 30 Jan 2026 15:32:08 +0600 Subject: [PATCH 14/14] Version v2.1.3 --- version.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.php b/version.php index 19135c24..d8027c3e 100644 --- a/version.php +++ b/version.php @@ -28,7 +28,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'tiny_cursive'; -$plugin->release = '2.1.2'; -$plugin->version = 2025122000; +$plugin->release = '2.1.3'; +$plugin->version = 2026013000; $plugin->requires = 2022041912; $plugin->maturity = MATURITY_STABLE;