1441 |
ariadna |
1 |
{"version":3,"file":"textmark.min.js","sources":["../src/textmark.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny AI Mark Changed text.\n *\n * This module marks text that was returned by the AI service\n * and that has been changed by a human prior to being inserted.\n *\n * @module tiny_aiplacement/textmark\n * @copyright 2023 Matt Porritt <matt.porritt@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class TinyAiTextMarker {\n /**\n * Finds the longest common subsequence of two strings.\n *\n * @param {string} a The first string.\n * @param {string} b The second string.\n * @returns {string} The longest common subsequence.\n */\n static longestCommonSubsequence(a, b) {\n const lengths = Array(a.length + 1)\n .fill(null)\n .map(() => Array(b.length + 1).fill(0));\n\n for (let i = 0; i < a.length; i++) {\n for (let j = 0; j < b.length; j++) {\n if (a[i] === b[j]) {\n lengths[i + 1][j + 1] = lengths[i][j] + 1;\n } else {\n lengths[i + 1][j + 1] = Math.max(lengths[i + 1][j], lengths[i][j + 1]);\n }\n }\n }\n\n let i = a.length;\n let j = b.length;\n let lcs = '';\n\n while (i > 0 && j > 0) {\n if (a[i - 1] === b[j - 1]) {\n lcs = a[i - 1] + lcs;\n i--;\n j--;\n } else if (lengths[i - 1][j] > lengths[i][j - 1]) {\n i--;\n } else {\n j--;\n }\n }\n\n return lcs;\n }\n\n /**\n * Finds the differences between the original and edited text using the LCS algorithm.\n *\n * @param {string} originalText The original text.\n * @param {string} editedText The edited text.\n * @returns {Array<Object>} An array of difference objects with start, end, and text properties.\n */\n static findDifferences(originalText, editedText) {\n const lcs = TinyAiTextMarker.longestCommonSubsequence(originalText, editedText);\n let differences = [];\n let i = 0;\n let j = 0;\n\n for (let k = 0; k < lcs.length; k++) {\n let commonChar = lcs[k];\n\n while (originalText[i] !== commonChar || editedText[j] !== commonChar) {\n let start = j;\n while (editedText[j] !== commonChar) {\n j++;\n }\n let editedSection = editedText.slice(start, j);\n differences.push({start, end: j, text: editedSection});\n\n while (originalText[i] !== commonChar) {\n i++;\n }\n }\n\n i++;\n j++;\n }\n\n if (j < editedText.length) {\n differences.push({start: j, end: editedText.length, text: editedText.slice(j)});\n }\n\n return differences;\n }\n\n /**\n * Wraps the given edited section in a span tag with a 'user-edited' class.\n *\n * @param {string} editedSection The edited section of the text.\n * @returns {Promise<string>} A promise that resolves with the wrapped edited section.\n */\n static async wrapInSpan(editedSection) {\n return new Promise((resolve, reject) => {\n try {\n let wrappedText = `<span class=\"user-edited\">${editedSection}</span>`;\n resolve(wrappedText);\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Wraps the edited sections of the text in span tags with a 'user-edited' class.\n *\n * @param {string} originalText The original text.\n * @param {string} editedText The edited text.\n * @returns {Promise<string>} A promise that resolves with the edited text, where edited sections are wrapped in span tags.\n */\n static async wrapEditedSections(originalText, editedText) {\n let differences = TinyAiTextMarker.findDifferences(originalText, editedText);\n let wrappedText = editedText;\n\n for (let i = differences.length - 1; i >= 0; i--) {\n let {start, end, text} = differences[i];\n let wrappedSection = await TinyAiTextMarker.wrapInSpan(text);\n wrappedText = wrappedText.slice(0, start) + wrappedSection + wrappedText.slice(end);\n }\n\n return wrappedText;\n }\n\n}\n"],"names":["TinyAiTextMarker","a","b","lengths","Array","length","fill","map","i","j","Math","max","lcs","originalText","editedText","longestCommonSubsequence","differences","k","commonChar","start","editedSection","slice","push","end","text","Promise","resolve","reject","error","findDifferences","wrappedText","wrappedSection","wrapInSpan"],"mappings":";;;;;;;;;;;MA0BqBA,iDAQeC,EAAGC,SACzBC,QAAUC,MAAMH,EAAEI,OAAS,GAC5BC,KAAK,MACLC,KAAI,IAAMH,MAAMF,EAAEG,OAAS,GAAGC,KAAK,SAEnC,IAAIE,EAAI,EAAGA,EAAIP,EAAEI,OAAQG,QACrB,IAAIC,EAAI,EAAGA,EAAIP,EAAEG,OAAQI,IACtBR,EAAEO,KAAON,EAAEO,GACXN,QAAQK,EAAI,GAAGC,EAAI,GAAKN,QAAQK,GAAGC,GAAK,EAExCN,QAAQK,EAAI,GAAGC,EAAI,GAAKC,KAAKC,IAAIR,QAAQK,EAAI,GAAGC,GAAIN,QAAQK,GAAGC,EAAI,QAK3ED,EAAIP,EAAEI,OACNI,EAAIP,EAAEG,OACNO,IAAM,QAEHJ,EAAI,GAAKC,EAAI,GACZR,EAAEO,EAAI,KAAON,EAAEO,EAAI,IACnBG,IAAMX,EAAEO,EAAI,GAAKI,IACjBJ,IACAC,KACON,QAAQK,EAAI,GAAGC,GAAKN,QAAQK,GAAGC,EAAI,GAC1CD,IAEAC,WAIDG,2BAUYC,aAAcC,kBAC3BF,IAAMZ,iBAAiBe,yBAAyBF,aAAcC,gBAChEE,YAAc,GACdR,EAAI,EACJC,EAAI,MAEH,IAAIQ,EAAI,EAAGA,EAAIL,IAAIP,OAAQY,IAAK,KAC7BC,WAAaN,IAAIK,QAEdJ,aAAaL,KAAOU,YAAcJ,WAAWL,KAAOS,YAAY,KAC/DC,MAAQV,OACLK,WAAWL,KAAOS,YACrBT,QAEAW,cAAgBN,WAAWO,MAAMF,MAAOV,OAC5CO,YAAYM,KAAK,CAACH,MAAAA,MAAOI,IAAKd,EAAGe,KAAMJ,gBAEhCP,aAAaL,KAAOU,YACvBV,IAIRA,IACAC,WAGAA,EAAIK,WAAWT,QACfW,YAAYM,KAAK,CAACH,MAAOV,EAAGc,IAAKT,WAAWT,OAAQmB,KAAMV,WAAWO,MAAMZ,KAGxEO,oCASaI,sBACb,IAAIK,SAAQ,CAACC,QAASC,cAGrBD,4CAD+CN,0BAEjD,MAAOQ,OACLD,OAAOC,2CAYaf,aAAcC,gBACtCE,YAAchB,iBAAiB6B,gBAAgBhB,aAAcC,YAC7DgB,YAAchB,eAEb,IAAIN,EAAIQ,YAAYX,OAAS,EAAGG,GAAK,EAAGA,IAAK,KAC1CW,MAACA,MAADI,IAAQA,IAARC,KAAaA,MAAQR,YAAYR,GACjCuB,qBAAuB/B,iBAAiBgC,WAAWR,MACvDM,YAAcA,YAAYT,MAAM,EAAGF,OAASY,eAAiBD,YAAYT,MAAME,YAG5EO"}
|