From c02059fc31513d79c6b1755561633ee0c02334da Mon Sep 17 00:00:00 2001 From: Peter Mayer Date: Sun, 21 Jul 2024 00:55:14 +0200 Subject: [PATCH] MBS-9255: Add ai spellcheck --- amd/build/diff.min.js | 3 + amd/build/diff.min.js.map | 1 + amd/build/editformhelper.min.js.map | 2 +- amd/build/getSpellcheck.min.js | 11 + amd/build/getSpellcheck.min.js.map | 1 + amd/build/showprompt.min.js.map | 2 +- amd/src/diff.js | 1730 +++++++++++++++++++++++++++ amd/src/editformhelper.js | 3 - amd/src/getSpellcheck.js | 97 ++ classes/form/edit_spellchek.php | 148 +++ db/install.xml | 5 +- db/upgrade.php | 16 + edit_aitext_form.php | 8 + lang/en/qtype_aitext.php | 6 + package-lock.json | 21 + package.json | 9 + question.php | 45 + questiontype.php | 2 + renderer.php | 47 +- styles.css | 13 +- version.php | 2 +- 21 files changed, 2161 insertions(+), 11 deletions(-) create mode 100644 amd/build/diff.min.js create mode 100644 amd/build/diff.min.js.map create mode 100644 amd/build/getSpellcheck.min.js create mode 100644 amd/build/getSpellcheck.min.js.map create mode 100644 amd/src/diff.js create mode 100644 amd/src/getSpellcheck.js create mode 100644 classes/form/edit_spellchek.php mode change 100755 => 100644 db/install.xml create mode 100644 package-lock.json create mode 100644 package.json diff --git a/amd/build/diff.min.js b/amd/build/diff.min.js new file mode 100644 index 0000000..ff975fa --- /dev/null +++ b/amd/build/diff.min.js @@ -0,0 +1,3 @@ +var global,factory;global=window,factory=function(exports){function Diff(){}function buildValues(diff,lastComponent,newString,oldString,useLongestToken){for(var nextComponent,components=[];lastComponent;)components.push(lastComponent),nextComponent=lastComponent.previousComponent,delete lastComponent.previousComponent,lastComponent=nextComponent;components.reverse();for(var componentPos=0,componentLen=components.length,newPos=0,oldPos=0;componentPosvalue.length?oldValue:value})),component.value=diff.join(value)}else component.value=diff.join(newString.slice(newPos,newPos+component.count));newPos+=component.count,component.added||(oldPos+=component.count)}}var finalComponent=components[componentLen-1];return componentLen>1&&"string"==typeof finalComponent.value&&(finalComponent.added||finalComponent.removed)&&diff.equals("",finalComponent.value)&&(components[componentLen-2].value+=finalComponent.value,components.pop()),components}Diff.prototype={diff:function(oldString,newString){var _options$timeout,options=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},callback=options.callback;"function"==typeof options&&(callback=options,options={}),this.options=options;var self=this;function done(value){return callback?(setTimeout((function(){callback(void 0,value)}),0),!0):value}oldString=this.castInput(oldString),newString=this.castInput(newString),oldString=this.removeEmpty(this.tokenize(oldString));var newLen=(newString=this.removeEmpty(this.tokenize(newString))).length,oldLen=oldString.length,editLength=1,maxEditLength=newLen+oldLen;options.maxEditLength&&(maxEditLength=Math.min(maxEditLength,options.maxEditLength));var maxExecutionTime=null!==(_options$timeout=options.timeout)&&void 0!==_options$timeout?_options$timeout:1/0,abortAfterTimestamp=Date.now()+maxExecutionTime,bestPath=[{oldPos:-1,lastComponent:void 0}],newPos=this.extractCommon(bestPath[0],newString,oldString,0);if(bestPath[0].oldPos+1>=oldLen&&newPos+1>=newLen)return done([{value:this.join(newString),count:newString.length}]);var minDiagonalToConsider=-1/0,maxDiagonalToConsider=1/0;function execEditLength(){for(var diagonalPath=Math.max(minDiagonalToConsider,-editLength);diagonalPath<=Math.min(maxDiagonalToConsider,editLength);diagonalPath+=2){var basePath=void 0,removePath=bestPath[diagonalPath-1],addPath=bestPath[diagonalPath+1];removePath&&(bestPath[diagonalPath-1]=void 0);var canAdd=!1;if(addPath){var addPathNewPos=addPath.oldPos-diagonalPath;canAdd=addPath&&0<=addPathNewPos&&addPathNewPos=oldLen&&newPos+1>=newLen)return done(buildValues(self,basePath.lastComponent,newString,oldString,self.useLongestToken));bestPath[diagonalPath]=basePath,basePath.oldPos+1>=oldLen&&(maxDiagonalToConsider=Math.min(maxDiagonalToConsider,diagonalPath-1)),newPos+1>=newLen&&(minDiagonalToConsider=Math.max(minDiagonalToConsider,diagonalPath+1))}else bestPath[diagonalPath]=void 0}editLength++}if(callback)!function exec(){setTimeout((function(){if(editLength>maxEditLength||Date.now()>abortAfterTimestamp)return callback();execEditLength()||exec()}),0)}();else for(;editLength<=maxEditLength&&Date.now()<=abortAfterTimestamp;){var ret=execEditLength();if(ret)return ret}},addToPath:function(path,added,removed,oldPosInc){var last=path.lastComponent;return last&&last.added===added&&last.removed===removed?{oldPos:path.oldPos+oldPosInc,lastComponent:{count:last.count+1,added:added,removed:removed,previousComponent:last.previousComponent}}:{oldPos:path.oldPos+oldPosInc,lastComponent:{count:1,added:added,removed:removed,previousComponent:last}}},extractCommon:function(basePath,newString,oldString,diagonalPath){for(var newLen=newString.length,oldLen=oldString.length,oldPos=basePath.oldPos,newPos=oldPos-diagonalPath,commonCount=0;newPos+1arr.length)&&(len=arr.length);for(var i=0,arr2=new Array(len);i1&&void 0!==arguments[1]?arguments[1]:{},diffstr=uniDiff.split(/\r\n|[\n\v\f\r\x85]/),delimiters=uniDiff.match(/\r\n|[\n\v\f\r\x85]/g)||[],list=[],i=0;function parseIndex(){var index={};for(list.push(index);i2&&void 0!==arguments[2]?arguments[2]:{};if("string"==typeof uniDiff&&(uniDiff=parsePatch(uniDiff)),Array.isArray(uniDiff)){if(uniDiff.length>1)throw new Error("applyPatch only works with a single input.");uniDiff=uniDiff[0]}var removeEOFNL,addEOFNL,lines=source.split(/\r\n|[\n\v\f\r\x85]/),delimiters=source.match(/\r\n|[\n\v\f\r\x85]/g)||[],hunks=uniDiff.hunks,compareLine=options.compareLine||function(lineNumber,line,operation,patchContent){return line===patchContent},errorCount=0,fuzzFactor=options.fuzzFactor||0,minLine=0,offset=0;function hunkFits(hunk,toPos){for(var j=0;j0?line[0]:" ",content=line.length>0?line.substr(1):line;if(" "===operation||"-"===operation){if(!compareLine(toPos+1,lines[toPos],operation,content)&&++errorCount>fuzzFactor)return!1;toPos++}}return!0}for(var i=0;i0?line[0]:" ",content=line.length>0?line.substr(1):line,delimiter=_hunk.linedelimiters&&_hunk.linedelimiters[j]||"\n";if(" "===operation)_toPos++;else if("-"===operation)lines.splice(_toPos,1),delimiters.splice(_toPos,1);else if("+"===operation)lines.splice(_toPos,0,content),delimiters.splice(_toPos,0,delimiter),_toPos++;else if("\\"===operation){var previousOperation=_hunk.lines[j-1]?_hunk.lines[j-1][0]:null;"+"===previousOperation?removeEOFNL=!0:"-"===previousOperation&&(addEOFNL=!0)}}}if(removeEOFNL)for(;!lines[lines.length-1];)lines.pop(),delimiters.pop();else addEOFNL&&(lines.push(""),delimiters.push("\n"));for(var _k=0;_k0?contextLines(prev.lines.slice(-options.context)):[],oldRangeStart-=curRange.length,newRangeStart-=curRange.length)}(_curRange=curRange).push.apply(_curRange,_toConsumableArray(lines.map((function(entry){return(current.added?"+":"-")+entry})))),current.added?newLine+=lines.length:oldLine+=lines.length}else{if(oldRangeStart)if(lines.length<=2*options.context&&i=diff.length-2&&lines.length<=options.context){var oldEOFNewline=/\n$/.test(oldStr),newEOFNewline=/\n$/.test(newStr),noNlBeforeAdds=0==lines.length&&curRange.length>hunk.oldLines;!oldEOFNewline&&noNlBeforeAdds&&oldStr.length>0&&curRange.splice(hunk.oldLines,0,"\\ No newline at end of file"),(oldEOFNewline||noNlBeforeAdds)&&newEOFNewline||curRange.push("\\ No newline at end of file")}hunks.push(hunk),oldRangeStart=0,newRangeStart=0,curRange=[]}oldLine+=lines.length,newLine+=lines.length}},i=0;iarray.length)return!1;for(var i=0;i"):change.removed&&ret.push(""),ret.push((s=change.value,void 0,s.replace(/&/g,"&").replace(//g,">").replace(/"/g,"""))),change.added?ret.push(""):change.removed&&ret.push("")}var s;return ret.join("")},exports.createPatch=function(fileName,oldStr,newStr,oldHeader,newHeader,options){return createTwoFilesPatch(fileName,fileName,oldStr,newStr,oldHeader,newHeader,options)},exports.createTwoFilesPatch=createTwoFilesPatch,exports.diffArrays=function(oldArr,newArr,callback){return arrayDiff.diff(oldArr,newArr,callback)},exports.diffChars=function(oldStr,newStr,options){return characterDiff.diff(oldStr,newStr,options)},exports.diffCss=function(oldStr,newStr,callback){return cssDiff.diff(oldStr,newStr,callback)},exports.diffJson=function(oldObj,newObj,options){return jsonDiff.diff(oldObj,newObj,options)},exports.diffLines=diffLines,exports.diffSentences=function(oldStr,newStr,callback){return sentenceDiff.diff(oldStr,newStr,callback)},exports.diffTrimmedLines=function(oldStr,newStr,callback){var options=generateOptions(callback,{ignoreWhitespace:!0});return lineDiff.diff(oldStr,newStr,options)},exports.diffWords=function(oldStr,newStr,options){return options=generateOptions(options,{ignoreWhitespace:!0}),wordDiff.diff(oldStr,newStr,options)},exports.diffWordsWithSpace=function(oldStr,newStr,options){return wordDiff.diff(oldStr,newStr,options)},exports.formatPatch=formatPatch,exports.merge=function(mine,theirs,base){mine=loadPatch(mine,base),theirs=loadPatch(theirs,base);var ret={};(mine.index||theirs.index)&&(ret.index=mine.index||theirs.index),(mine.newFileName||theirs.newFileName)&&(fileNameChanged(mine)?fileNameChanged(theirs)?(ret.oldFileName=selectField(ret,mine.oldFileName,theirs.oldFileName),ret.newFileName=selectField(ret,mine.newFileName,theirs.newFileName),ret.oldHeader=selectField(ret,mine.oldHeader,theirs.oldHeader),ret.newHeader=selectField(ret,mine.newHeader,theirs.newHeader)):(ret.oldFileName=mine.oldFileName,ret.newFileName=mine.newFileName,ret.oldHeader=mine.oldHeader,ret.newHeader=mine.newHeader):(ret.oldFileName=theirs.oldFileName||mine.oldFileName,ret.newFileName=theirs.newFileName||mine.newFileName,ret.oldHeader=theirs.oldHeader||mine.oldHeader,ret.newHeader=theirs.newHeader||mine.newHeader)),ret.hunks=[];for(var mineIndex=0,theirsIndex=0,mineOffset=0,theirsOffset=0;mineIndex 2 && arguments[2] !== undefined ? arguments[2] : {};\n var callback = options.callback;\n\n if (typeof options === 'function') {\n callback = options;\n options = {};\n }\n\n this.options = options;\n var self = this;\n\n function done(value) {\n if (callback) {\n setTimeout(function () {\n callback(undefined, value);\n }, 0);\n return true;\n } else {\n return value;\n }\n } // Allow subclasses to massage the input prior to running\n\n\n oldString = this.castInput(oldString);\n newString = this.castInput(newString);\n oldString = this.removeEmpty(this.tokenize(oldString));\n newString = this.removeEmpty(this.tokenize(newString));\n var newLen = newString.length,\n oldLen = oldString.length;\n var editLength = 1;\n var maxEditLength = newLen + oldLen;\n\n if (options.maxEditLength) {\n maxEditLength = Math.min(maxEditLength, options.maxEditLength);\n }\n\n var maxExecutionTime = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : Infinity;\n var abortAfterTimestamp = Date.now() + maxExecutionTime;\n var bestPath = [{\n oldPos: -1,\n lastComponent: undefined\n }]; // Seed editLength = 0, i.e. the content starts with the same values\n\n var newPos = this.extractCommon(bestPath[0], newString, oldString, 0);\n\n if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {\n // Identity per the equality and tokenizer\n return done([{\n value: this.join(newString),\n count: newString.length\n }]);\n } // Once we hit the right edge of the edit graph on some diagonal k, we can\n // definitely reach the end of the edit graph in no more than k edits, so\n // there's no point in considering any moves to diagonal k+1 any more (from\n // which we're guaranteed to need at least k+1 more edits).\n // Similarly, once we've reached the bottom of the edit graph, there's no\n // point considering moves to lower diagonals.\n // We record this fact by setting minDiagonalToConsider and\n // maxDiagonalToConsider to some finite value once we've hit the edge of\n // the edit graph.\n // This optimization is not faithful to the original algorithm presented in\n // Myers's paper, which instead pointlessly extends D-paths off the end of\n // the edit graph - see page 7 of Myers's paper which notes this point\n // explicitly and illustrates it with a diagram. This has major performance\n // implications for some common scenarios. For instance, to compute a diff\n // where the new text simply appends d characters on the end of the\n // original text of length n, the true Myers algorithm will take O(n+d^2)\n // time while this optimization needs only O(n+d) time.\n\n\n var minDiagonalToConsider = -Infinity,\n maxDiagonalToConsider = Infinity; // Main worker method. checks all permutations of a given edit length for acceptance.\n\n function execEditLength() {\n for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {\n var basePath = void 0;\n var removePath = bestPath[diagonalPath - 1],\n addPath = bestPath[diagonalPath + 1];\n\n if (removePath) {\n // No one else is going to attempt to use this value, clear it\n bestPath[diagonalPath - 1] = undefined;\n }\n\n var canAdd = false;\n\n if (addPath) {\n // what newPos will be after we do an insertion:\n var addPathNewPos = addPath.oldPos - diagonalPath;\n canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;\n }\n\n var canRemove = removePath && removePath.oldPos + 1 < oldLen;\n\n if (!canAdd && !canRemove) {\n // If this path is a terminal then prune\n bestPath[diagonalPath] = undefined;\n continue;\n } // Select the diagonal that we want to branch from. We select the prior\n // path whose position in the old string is the farthest from the origin\n // and does not pass the bounds of the diff graph\n // TODO: Remove the `+ 1` here to make behavior match Myers algorithm\n // and prefer to order removals before insertions.\n\n\n if (!canRemove || canAdd && removePath.oldPos + 1 < addPath.oldPos) {\n basePath = self.addToPath(addPath, true, undefined, 0);\n } else {\n basePath = self.addToPath(removePath, undefined, true, 1);\n }\n\n newPos = self.extractCommon(basePath, newString, oldString, diagonalPath);\n\n if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {\n // If we have hit the end of both strings, then we are done\n return done(buildValues(self, basePath.lastComponent, newString, oldString, self.useLongestToken));\n } else {\n bestPath[diagonalPath] = basePath;\n\n if (basePath.oldPos + 1 >= oldLen) {\n maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);\n }\n\n if (newPos + 1 >= newLen) {\n minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);\n }\n }\n }\n\n editLength++;\n } // Performs the length of edit iteration. Is a bit fugly as this has to support the\n // sync and async mode which is never fun. Loops over execEditLength until a value\n // is produced, or until the edit length exceeds options.maxEditLength (if given),\n // in which case it will return undefined.\n\n\n if (callback) {\n (function exec() {\n setTimeout(function () {\n if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {\n return callback();\n }\n\n if (!execEditLength()) {\n exec();\n }\n }, 0);\n })();\n } else {\n while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {\n var ret = execEditLength();\n\n if (ret) {\n return ret;\n }\n }\n }\n },\n addToPath: function addToPath(path, added, removed, oldPosInc) {\n var last = path.lastComponent;\n\n if (last && last.added === added && last.removed === removed) {\n return {\n oldPos: path.oldPos + oldPosInc,\n lastComponent: {\n count: last.count + 1,\n added: added,\n removed: removed,\n previousComponent: last.previousComponent\n }\n };\n } else {\n return {\n oldPos: path.oldPos + oldPosInc,\n lastComponent: {\n count: 1,\n added: added,\n removed: removed,\n previousComponent: last\n }\n };\n }\n },\n extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) {\n var newLen = newString.length,\n oldLen = oldString.length,\n oldPos = basePath.oldPos,\n newPos = oldPos - diagonalPath,\n commonCount = 0;\n\n while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) {\n newPos++;\n oldPos++;\n commonCount++;\n }\n\n if (commonCount) {\n basePath.lastComponent = {\n count: commonCount,\n previousComponent: basePath.lastComponent\n };\n }\n\n basePath.oldPos = oldPos;\n return newPos;\n },\n equals: function equals(left, right) {\n if (this.options.comparator) {\n return this.options.comparator(left, right);\n } else {\n return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase();\n }\n },\n removeEmpty: function removeEmpty(array) {\n var ret = [];\n\n for (var i = 0; i < array.length; i++) {\n if (array[i]) {\n ret.push(array[i]);\n }\n }\n\n return ret;\n },\n castInput: function castInput(value) {\n return value;\n },\n tokenize: function tokenize(value) {\n return value.split('');\n },\n join: function join(chars) {\n return chars.join('');\n }\n };\n\n function buildValues(diff, lastComponent, newString, oldString, useLongestToken) {\n // First we convert our linked list of components in reverse order to an\n // array in the right order:\n var components = [];\n var nextComponent;\n\n while (lastComponent) {\n components.push(lastComponent);\n nextComponent = lastComponent.previousComponent;\n delete lastComponent.previousComponent;\n lastComponent = nextComponent;\n }\n\n components.reverse();\n var componentPos = 0,\n componentLen = components.length,\n newPos = 0,\n oldPos = 0;\n\n for (; componentPos < componentLen; componentPos++) {\n var component = components[componentPos];\n\n if (!component.removed) {\n if (!component.added && useLongestToken) {\n var value = newString.slice(newPos, newPos + component.count);\n value = value.map(function (value, i) {\n var oldValue = oldString[oldPos + i];\n return oldValue.length > value.length ? oldValue : value;\n });\n component.value = diff.join(value);\n } else {\n component.value = diff.join(newString.slice(newPos, newPos + component.count));\n }\n\n newPos += component.count; // Common case\n\n if (!component.added) {\n oldPos += component.count;\n }\n } else {\n component.value = diff.join(oldString.slice(oldPos, oldPos + component.count));\n oldPos += component.count; // Reverse add and remove so removes are output first to match common convention\n // The diffing algorithm is tied to add then remove output and this is the simplest\n // route to get the desired output with minimal overhead.\n\n if (componentPos && components[componentPos - 1].added) {\n var tmp = components[componentPos - 1];\n components[componentPos - 1] = components[componentPos];\n components[componentPos] = tmp;\n }\n }\n } // Special case handle for when one terminal is ignored (i.e. whitespace).\n // For this case we merge the terminal into the prior string and drop the change.\n // This is only available for string mode.\n\n\n var finalComponent = components[componentLen - 1];\n\n if (componentLen > 1 && typeof finalComponent.value === 'string' && (finalComponent.added || finalComponent.removed) && diff.equals('', finalComponent.value)) {\n components[componentLen - 2].value += finalComponent.value;\n components.pop();\n }\n\n return components;\n }\n\n var characterDiff = new Diff();\n function diffChars(oldStr, newStr, options) {\n return characterDiff.diff(oldStr, newStr, options);\n }\n\n function generateOptions(options, defaults) {\n if (typeof options === 'function') {\n defaults.callback = options;\n } else if (options) {\n for (var name in options) {\n /* istanbul ignore else */\n if (options.hasOwnProperty(name)) {\n defaults[name] = options[name];\n }\n }\n }\n\n return defaults;\n }\n\n //\n // Ranges and exceptions:\n // Latin-1 Supplement, 0080–00FF\n // - U+00D7 × Multiplication sign\n // - U+00F7 ÷ Division sign\n // Latin Extended-A, 0100–017F\n // Latin Extended-B, 0180–024F\n // IPA Extensions, 0250–02AF\n // Spacing Modifier Letters, 02B0–02FF\n // - U+02C7 ˇ ˇ Caron\n // - U+02D8 ˘ ˘ Breve\n // - U+02D9 ˙ ˙ Dot Above\n // - U+02DA ˚ ˚ Ring Above\n // - U+02DB ˛ ˛ Ogonek\n // - U+02DC ˜ ˜ Small Tilde\n // - U+02DD ˝ ˝ Double Acute Accent\n // Latin Extended Additional, 1E00–1EFF\n\n var extendedWordChars = /^[A-Za-z\\xC0-\\u02C6\\u02C8-\\u02D7\\u02DE-\\u02FF\\u1E00-\\u1EFF]+$/;\n var reWhitespace = /\\S/;\n var wordDiff = new Diff();\n\n wordDiff.equals = function (left, right) {\n if (this.options.ignoreCase) {\n left = left.toLowerCase();\n right = right.toLowerCase();\n }\n\n return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right);\n };\n\n wordDiff.tokenize = function (value) {\n // All whitespace symbols except newline group into one token, each newline - in separate token\n var tokens = value.split(/([^\\S\\r\\n]+|[()[\\]{}'\"\\r\\n]|\\b)/); // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set.\n\n for (var i = 0; i < tokens.length - 1; i++) {\n // If we have an empty string in the next field and we have only word chars before and after, merge\n if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) {\n tokens[i] += tokens[i + 2];\n tokens.splice(i + 1, 2);\n i--;\n }\n }\n\n return tokens;\n };\n\n function diffWords(oldStr, newStr, options) {\n options = generateOptions(options, {\n ignoreWhitespace: true\n });\n return wordDiff.diff(oldStr, newStr, options);\n }\n function diffWordsWithSpace(oldStr, newStr, options) {\n return wordDiff.diff(oldStr, newStr, options);\n }\n\n var lineDiff = new Diff();\n\n lineDiff.tokenize = function (value) {\n if (this.options.stripTrailingCr) {\n // remove one \\r before \\n to match GNU diff's --strip-trailing-cr behavior\n value = value.replace(/\\r\\n/g, '\\n');\n }\n\n var retLines = [],\n linesAndNewlines = value.split(/(\\n|\\r\\n)/); // Ignore the final empty token that occurs if the string ends with a new line\n\n if (!linesAndNewlines[linesAndNewlines.length - 1]) {\n linesAndNewlines.pop();\n } // Merge the content and line separators into single tokens\n\n\n for (var i = 0; i < linesAndNewlines.length; i++) {\n var line = linesAndNewlines[i];\n\n if (i % 2 && !this.options.newlineIsToken) {\n retLines[retLines.length - 1] += line;\n } else {\n if (this.options.ignoreWhitespace) {\n line = line.trim();\n }\n\n retLines.push(line);\n }\n }\n\n return retLines;\n };\n\n function diffLines(oldStr, newStr, callback) {\n return lineDiff.diff(oldStr, newStr, callback);\n }\n function diffTrimmedLines(oldStr, newStr, callback) {\n var options = generateOptions(callback, {\n ignoreWhitespace: true\n });\n return lineDiff.diff(oldStr, newStr, options);\n }\n\n var sentenceDiff = new Diff();\n\n sentenceDiff.tokenize = function (value) {\n return value.split(/(\\S.+?[.!?])(?=\\s+|$)/);\n };\n\n function diffSentences(oldStr, newStr, callback) {\n return sentenceDiff.diff(oldStr, newStr, callback);\n }\n\n var cssDiff = new Diff();\n\n cssDiff.tokenize = function (value) {\n return value.split(/([{}:;,]|\\s+)/);\n };\n\n function diffCss(oldStr, newStr, callback) {\n return cssDiff.diff(oldStr, newStr, callback);\n }\n\n function _typeof(obj) {\n \"@babel/helpers - typeof\";\n\n if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n _typeof = function (obj) {\n return typeof obj;\n };\n } else {\n _typeof = function (obj) {\n return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n };\n }\n\n return _typeof(obj);\n }\n\n function _defineProperty(obj, key, value) {\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n\n return obj;\n }\n\n function ownKeys(object, enumerableOnly) {\n var keys = Object.keys(object);\n\n if (Object.getOwnPropertySymbols) {\n var symbols = Object.getOwnPropertySymbols(object);\n if (enumerableOnly) symbols = symbols.filter(function (sym) {\n return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n });\n keys.push.apply(keys, symbols);\n }\n\n return keys;\n }\n\n function _objectSpread2(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i] != null ? arguments[i] : {};\n\n if (i % 2) {\n ownKeys(Object(source), true).forEach(function (key) {\n _defineProperty(target, key, source[key]);\n });\n } else if (Object.getOwnPropertyDescriptors) {\n Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n } else {\n ownKeys(Object(source)).forEach(function (key) {\n Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n });\n }\n }\n\n return target;\n }\n\n function _toConsumableArray(arr) {\n return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();\n }\n\n function _arrayWithoutHoles(arr) {\n if (Array.isArray(arr)) return _arrayLikeToArray(arr);\n }\n\n function _iterableToArray(iter) {\n if (typeof Symbol !== \"undefined\" && Symbol.iterator in Object(iter)) return Array.from(iter);\n }\n\n function _unsupportedIterableToArray(o, minLen) {\n if (!o) return;\n if (typeof o === \"string\") return _arrayLikeToArray(o, minLen);\n var n = Object.prototype.toString.call(o).slice(8, -1);\n if (n === \"Object\" && o.constructor) n = o.constructor.name;\n if (n === \"Map\" || n === \"Set\") return Array.from(o);\n if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);\n }\n\n function _arrayLikeToArray(arr, len) {\n if (len == null || len > arr.length) len = arr.length;\n\n for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];\n\n return arr2;\n }\n\n function _nonIterableSpread() {\n throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n }\n\n var objectPrototypeToString = Object.prototype.toString;\n var jsonDiff = new Diff(); // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a\n // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output:\n\n jsonDiff.useLongestToken = true;\n jsonDiff.tokenize = lineDiff.tokenize;\n\n jsonDiff.castInput = function (value) {\n var _this$options = this.options,\n undefinedReplacement = _this$options.undefinedReplacement,\n _this$options$stringi = _this$options.stringifyReplacer,\n stringifyReplacer = _this$options$stringi === void 0 ? function (k, v) {\n return typeof v === 'undefined' ? undefinedReplacement : v;\n } : _this$options$stringi;\n return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' ');\n };\n\n jsonDiff.equals = function (left, right) {\n return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\\r\\n])/g, '$1'), right.replace(/,([\\r\\n])/g, '$1'));\n };\n\n function diffJson(oldObj, newObj, options) {\n return jsonDiff.diff(oldObj, newObj, options);\n } // This function handles the presence of circular references by bailing out when encountering an\n // object that is already on the \"stack\" of items being processed. Accepts an optional replacer\n\n function canonicalize(obj, stack, replacementStack, replacer, key) {\n stack = stack || [];\n replacementStack = replacementStack || [];\n\n if (replacer) {\n obj = replacer(key, obj);\n }\n\n var i;\n\n for (i = 0; i < stack.length; i += 1) {\n if (stack[i] === obj) {\n return replacementStack[i];\n }\n }\n\n var canonicalizedObj;\n\n if ('[object Array]' === objectPrototypeToString.call(obj)) {\n stack.push(obj);\n canonicalizedObj = new Array(obj.length);\n replacementStack.push(canonicalizedObj);\n\n for (i = 0; i < obj.length; i += 1) {\n canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key);\n }\n\n stack.pop();\n replacementStack.pop();\n return canonicalizedObj;\n }\n\n if (obj && obj.toJSON) {\n obj = obj.toJSON();\n }\n\n if (_typeof(obj) === 'object' && obj !== null) {\n stack.push(obj);\n canonicalizedObj = {};\n replacementStack.push(canonicalizedObj);\n\n var sortedKeys = [],\n _key;\n\n for (_key in obj) {\n /* istanbul ignore else */\n if (obj.hasOwnProperty(_key)) {\n sortedKeys.push(_key);\n }\n }\n\n sortedKeys.sort();\n\n for (i = 0; i < sortedKeys.length; i += 1) {\n _key = sortedKeys[i];\n canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key);\n }\n\n stack.pop();\n replacementStack.pop();\n } else {\n canonicalizedObj = obj;\n }\n\n return canonicalizedObj;\n }\n\n var arrayDiff = new Diff();\n\n arrayDiff.tokenize = function (value) {\n return value.slice();\n };\n\n arrayDiff.join = arrayDiff.removeEmpty = function (value) {\n return value;\n };\n\n function diffArrays(oldArr, newArr, callback) {\n return arrayDiff.diff(oldArr, newArr, callback);\n }\n\n function parsePatch(uniDiff) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var diffstr = uniDiff.split(/\\r\\n|[\\n\\v\\f\\r\\x85]/),\n delimiters = uniDiff.match(/\\r\\n|[\\n\\v\\f\\r\\x85]/g) || [],\n list = [],\n i = 0;\n\n function parseIndex() {\n var index = {};\n list.push(index); // Parse diff metadata\n\n while (i < diffstr.length) {\n var line = diffstr[i]; // File header found, end parsing diff metadata\n\n if (/^(\\-\\-\\-|\\+\\+\\+|@@)\\s/.test(line)) {\n break;\n } // Diff index\n\n\n var header = /^(?:Index:|diff(?: -r \\w+)+)\\s+(.+?)\\s*$/.exec(line);\n\n if (header) {\n index.index = header[1];\n }\n\n i++;\n } // Parse file headers if they are defined. Unified diff requires them, but\n // there's no technical issues to have an isolated hunk without file header\n\n\n parseFileHeader(index);\n parseFileHeader(index); // Parse hunks\n\n index.hunks = [];\n\n while (i < diffstr.length) {\n var _line = diffstr[i];\n\n if (/^(Index:|diff|\\-\\-\\-|\\+\\+\\+)\\s/.test(_line)) {\n break;\n } else if (/^@@/.test(_line)) {\n index.hunks.push(parseHunk());\n } else if (_line && options.strict) {\n // Ignore unexpected content unless in strict mode\n throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line));\n } else {\n i++;\n }\n }\n } // Parses the --- and +++ headers, if none are found, no lines\n // are consumed.\n\n\n function parseFileHeader(index) {\n var fileHeader = /^(---|\\+\\+\\+)\\s+(.*)$/.exec(diffstr[i]);\n\n if (fileHeader) {\n var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new';\n var data = fileHeader[2].split('\\t', 2);\n var fileName = data[0].replace(/\\\\\\\\/g, '\\\\');\n\n if (/^\".*\"$/.test(fileName)) {\n fileName = fileName.substr(1, fileName.length - 2);\n }\n\n index[keyPrefix + 'FileName'] = fileName;\n index[keyPrefix + 'Header'] = (data[1] || '').trim();\n i++;\n }\n } // Parses a hunk\n // This assumes that we are at the start of a hunk.\n\n\n function parseHunk() {\n var chunkHeaderIndex = i,\n chunkHeaderLine = diffstr[i++],\n chunkHeader = chunkHeaderLine.split(/@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@/);\n var hunk = {\n oldStart: +chunkHeader[1],\n oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2],\n newStart: +chunkHeader[3],\n newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4],\n lines: [],\n linedelimiters: []\n }; // Unified Diff Format quirk: If the chunk size is 0,\n // the first number is one lower than one would expect.\n // https://www.artima.com/weblogs/viewpost.jsp?thread=164293\n\n if (hunk.oldLines === 0) {\n hunk.oldStart += 1;\n }\n\n if (hunk.newLines === 0) {\n hunk.newStart += 1;\n }\n\n var addCount = 0,\n removeCount = 0;\n\n for (; i < diffstr.length; i++) {\n // Lines starting with '---' could be mistaken for the \"remove line\" operation\n // But they could be the header for the next file. Therefore prune such cases out.\n if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) {\n break;\n }\n\n var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0];\n\n if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\\\') {\n hunk.lines.push(diffstr[i]);\n hunk.linedelimiters.push(delimiters[i] || '\\n');\n\n if (operation === '+') {\n addCount++;\n } else if (operation === '-') {\n removeCount++;\n } else if (operation === ' ') {\n addCount++;\n removeCount++;\n }\n } else {\n break;\n }\n } // Handle the empty block count case\n\n\n if (!addCount && hunk.newLines === 1) {\n hunk.newLines = 0;\n }\n\n if (!removeCount && hunk.oldLines === 1) {\n hunk.oldLines = 0;\n } // Perform optional sanity checking\n\n\n if (options.strict) {\n if (addCount !== hunk.newLines) {\n throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1));\n }\n\n if (removeCount !== hunk.oldLines) {\n throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1));\n }\n }\n\n return hunk;\n }\n\n while (i < diffstr.length) {\n parseIndex();\n }\n\n return list;\n }\n\n // Iterator that traverses in the range of [min, max], stepping\n // by distance from a given start position. I.e. for [0, 4], with\n // start of 2, this will iterate 2, 3, 1, 4, 0.\n function distanceIterator (start, minLine, maxLine) {\n var wantForward = true,\n backwardExhausted = false,\n forwardExhausted = false,\n localOffset = 1;\n return function iterator() {\n if (wantForward && !forwardExhausted) {\n if (backwardExhausted) {\n localOffset++;\n } else {\n wantForward = false;\n } // Check if trying to fit beyond text length, and if not, check it fits\n // after offset location (or desired location on first iteration)\n\n\n if (start + localOffset <= maxLine) {\n return localOffset;\n }\n\n forwardExhausted = true;\n }\n\n if (!backwardExhausted) {\n if (!forwardExhausted) {\n wantForward = true;\n } // Check if trying to fit before text beginning, and if not, check it fits\n // before offset location\n\n\n if (minLine <= start - localOffset) {\n return -localOffset++;\n }\n\n backwardExhausted = true;\n return iterator();\n } // We tried to fit hunk before text beginning and beyond text length, then\n // hunk can't fit on the text. Return undefined\n\n };\n }\n\n function applyPatch(source, uniDiff) {\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n if (typeof uniDiff === 'string') {\n uniDiff = parsePatch(uniDiff);\n }\n\n if (Array.isArray(uniDiff)) {\n if (uniDiff.length > 1) {\n throw new Error('applyPatch only works with a single input.');\n }\n\n uniDiff = uniDiff[0];\n } // Apply the diff to the input\n\n\n var lines = source.split(/\\r\\n|[\\n\\v\\f\\r\\x85]/),\n delimiters = source.match(/\\r\\n|[\\n\\v\\f\\r\\x85]/g) || [],\n hunks = uniDiff.hunks,\n compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) {\n return line === patchContent;\n },\n errorCount = 0,\n fuzzFactor = options.fuzzFactor || 0,\n minLine = 0,\n offset = 0,\n removeEOFNL,\n addEOFNL;\n /**\n * Checks if the hunk exactly fits on the provided location\n */\n\n\n function hunkFits(hunk, toPos) {\n for (var j = 0; j < hunk.lines.length; j++) {\n var line = hunk.lines[j],\n operation = line.length > 0 ? line[0] : ' ',\n content = line.length > 0 ? line.substr(1) : line;\n\n if (operation === ' ' || operation === '-') {\n // Context sanity check\n if (!compareLine(toPos + 1, lines[toPos], operation, content)) {\n errorCount++;\n\n if (errorCount > fuzzFactor) {\n return false;\n }\n }\n\n toPos++;\n }\n }\n\n return true;\n } // Search best fit offsets for each hunk based on the previous ones\n\n\n for (var i = 0; i < hunks.length; i++) {\n var hunk = hunks[i],\n maxLine = lines.length - hunk.oldLines,\n localOffset = 0,\n toPos = offset + hunk.oldStart - 1;\n var iterator = distanceIterator(toPos, minLine, maxLine);\n\n for (; localOffset !== undefined; localOffset = iterator()) {\n if (hunkFits(hunk, toPos + localOffset)) {\n hunk.offset = offset += localOffset;\n break;\n }\n }\n\n if (localOffset === undefined) {\n return false;\n } // Set lower text limit to end of the current hunk, so next ones don't try\n // to fit over already patched text\n\n\n minLine = hunk.offset + hunk.oldStart + hunk.oldLines;\n } // Apply patch hunks\n\n\n var diffOffset = 0;\n\n for (var _i = 0; _i < hunks.length; _i++) {\n var _hunk = hunks[_i],\n _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1;\n\n diffOffset += _hunk.newLines - _hunk.oldLines;\n\n for (var j = 0; j < _hunk.lines.length; j++) {\n var line = _hunk.lines[j],\n operation = line.length > 0 ? line[0] : ' ',\n content = line.length > 0 ? line.substr(1) : line,\n delimiter = _hunk.linedelimiters && _hunk.linedelimiters[j] || '\\n';\n\n if (operation === ' ') {\n _toPos++;\n } else if (operation === '-') {\n lines.splice(_toPos, 1);\n delimiters.splice(_toPos, 1);\n /* istanbul ignore else */\n } else if (operation === '+') {\n lines.splice(_toPos, 0, content);\n delimiters.splice(_toPos, 0, delimiter);\n _toPos++;\n } else if (operation === '\\\\') {\n var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null;\n\n if (previousOperation === '+') {\n removeEOFNL = true;\n } else if (previousOperation === '-') {\n addEOFNL = true;\n }\n }\n }\n } // Handle EOFNL insertion/removal\n\n\n if (removeEOFNL) {\n while (!lines[lines.length - 1]) {\n lines.pop();\n delimiters.pop();\n }\n } else if (addEOFNL) {\n lines.push('');\n delimiters.push('\\n');\n }\n\n for (var _k = 0; _k < lines.length - 1; _k++) {\n lines[_k] = lines[_k] + delimiters[_k];\n }\n\n return lines.join('');\n } // Wrapper that supports multiple file patches via callbacks.\n\n function applyPatches(uniDiff, options) {\n if (typeof uniDiff === 'string') {\n uniDiff = parsePatch(uniDiff);\n }\n\n var currentIndex = 0;\n\n function processIndex() {\n var index = uniDiff[currentIndex++];\n\n if (!index) {\n return options.complete();\n }\n\n options.loadFile(index, function (err, data) {\n if (err) {\n return options.complete(err);\n }\n\n var updatedContent = applyPatch(data, index, options);\n options.patched(index, updatedContent, function (err) {\n if (err) {\n return options.complete(err);\n }\n\n processIndex();\n });\n });\n }\n\n processIndex();\n }\n\n function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {\n if (!options) {\n options = {};\n }\n\n if (typeof options.context === 'undefined') {\n options.context = 4;\n }\n\n var diff = diffLines(oldStr, newStr, options);\n\n if (!diff) {\n return;\n }\n\n diff.push({\n value: '',\n lines: []\n }); // Append an empty value to make cleanup easier\n\n function contextLines(lines) {\n return lines.map(function (entry) {\n return ' ' + entry;\n });\n }\n\n var hunks = [];\n var oldRangeStart = 0,\n newRangeStart = 0,\n curRange = [],\n oldLine = 1,\n newLine = 1;\n\n var _loop = function _loop(i) {\n var current = diff[i],\n lines = current.lines || current.value.replace(/\\n$/, '').split('\\n');\n current.lines = lines;\n\n if (current.added || current.removed) {\n var _curRange;\n\n // If we have previous context, start with that\n if (!oldRangeStart) {\n var prev = diff[i - 1];\n oldRangeStart = oldLine;\n newRangeStart = newLine;\n\n if (prev) {\n curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : [];\n oldRangeStart -= curRange.length;\n newRangeStart -= curRange.length;\n }\n } // Output our changes\n\n\n (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) {\n return (current.added ? '+' : '-') + entry;\n }))); // Track the updated file position\n\n\n if (current.added) {\n newLine += lines.length;\n } else {\n oldLine += lines.length;\n }\n } else {\n // Identical context lines. Track line changes\n if (oldRangeStart) {\n // Close out any changes that have been output (or join overlapping)\n if (lines.length <= options.context * 2 && i < diff.length - 2) {\n var _curRange2;\n\n // Overlapping\n (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines)));\n } else {\n var _curRange3;\n\n // end the range and output\n var contextSize = Math.min(lines.length, options.context);\n\n (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize))));\n\n var hunk = {\n oldStart: oldRangeStart,\n oldLines: oldLine - oldRangeStart + contextSize,\n newStart: newRangeStart,\n newLines: newLine - newRangeStart + contextSize,\n lines: curRange\n };\n\n if (i >= diff.length - 2 && lines.length <= options.context) {\n // EOF is inside this hunk\n var oldEOFNewline = /\\n$/.test(oldStr);\n var newEOFNewline = /\\n$/.test(newStr);\n var noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines;\n\n if (!oldEOFNewline && noNlBeforeAdds && oldStr.length > 0) {\n // special case: old has no eol and no trailing context; no-nl can end up before adds\n // however, if the old file is empty, do not output the no-nl line\n curRange.splice(hunk.oldLines, 0, '\\\\ No newline at end of file');\n }\n\n if (!oldEOFNewline && !noNlBeforeAdds || !newEOFNewline) {\n curRange.push('\\\\ No newline at end of file');\n }\n }\n\n hunks.push(hunk);\n oldRangeStart = 0;\n newRangeStart = 0;\n curRange = [];\n }\n }\n\n oldLine += lines.length;\n newLine += lines.length;\n }\n };\n\n for (var i = 0; i < diff.length; i++) {\n _loop(i);\n }\n\n return {\n oldFileName: oldFileName,\n newFileName: newFileName,\n oldHeader: oldHeader,\n newHeader: newHeader,\n hunks: hunks\n };\n }\n function formatPatch(diff) {\n if (Array.isArray(diff)) {\n return diff.map(formatPatch).join('\\n');\n }\n\n var ret = [];\n\n if (diff.oldFileName == diff.newFileName) {\n ret.push('Index: ' + diff.oldFileName);\n }\n\n ret.push('===================================================================');\n ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\\t' + diff.oldHeader));\n ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\\t' + diff.newHeader));\n\n for (var i = 0; i < diff.hunks.length; i++) {\n var hunk = diff.hunks[i]; // Unified Diff Format quirk: If the chunk size is 0,\n // the first number is one lower than one would expect.\n // https://www.artima.com/weblogs/viewpost.jsp?thread=164293\n\n if (hunk.oldLines === 0) {\n hunk.oldStart -= 1;\n }\n\n if (hunk.newLines === 0) {\n hunk.newStart -= 1;\n }\n\n ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@');\n ret.push.apply(ret, hunk.lines);\n }\n\n return ret.join('\\n') + '\\n';\n }\n function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {\n return formatPatch(structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options));\n }\n function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {\n return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);\n }\n\n function arrayEqual(a, b) {\n if (a.length !== b.length) {\n return false;\n }\n\n return arrayStartsWith(a, b);\n }\n function arrayStartsWith(array, start) {\n if (start.length > array.length) {\n return false;\n }\n\n for (var i = 0; i < start.length; i++) {\n if (start[i] !== array[i]) {\n return false;\n }\n }\n\n return true;\n }\n\n function calcLineCount(hunk) {\n var _calcOldNewLineCount = calcOldNewLineCount(hunk.lines),\n oldLines = _calcOldNewLineCount.oldLines,\n newLines = _calcOldNewLineCount.newLines;\n\n if (oldLines !== undefined) {\n hunk.oldLines = oldLines;\n } else {\n delete hunk.oldLines;\n }\n\n if (newLines !== undefined) {\n hunk.newLines = newLines;\n } else {\n delete hunk.newLines;\n }\n }\n function merge(mine, theirs, base) {\n mine = loadPatch(mine, base);\n theirs = loadPatch(theirs, base);\n var ret = {}; // For index we just let it pass through as it doesn't have any necessary meaning.\n // Leaving sanity checks on this to the API consumer that may know more about the\n // meaning in their own context.\n\n if (mine.index || theirs.index) {\n ret.index = mine.index || theirs.index;\n }\n\n if (mine.newFileName || theirs.newFileName) {\n if (!fileNameChanged(mine)) {\n // No header or no change in ours, use theirs (and ours if theirs does not exist)\n ret.oldFileName = theirs.oldFileName || mine.oldFileName;\n ret.newFileName = theirs.newFileName || mine.newFileName;\n ret.oldHeader = theirs.oldHeader || mine.oldHeader;\n ret.newHeader = theirs.newHeader || mine.newHeader;\n } else if (!fileNameChanged(theirs)) {\n // No header or no change in theirs, use ours\n ret.oldFileName = mine.oldFileName;\n ret.newFileName = mine.newFileName;\n ret.oldHeader = mine.oldHeader;\n ret.newHeader = mine.newHeader;\n } else {\n // Both changed... figure it out\n ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName);\n ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName);\n ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader);\n ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader);\n }\n }\n\n ret.hunks = [];\n var mineIndex = 0,\n theirsIndex = 0,\n mineOffset = 0,\n theirsOffset = 0;\n\n while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) {\n var mineCurrent = mine.hunks[mineIndex] || {\n oldStart: Infinity\n },\n theirsCurrent = theirs.hunks[theirsIndex] || {\n oldStart: Infinity\n };\n\n if (hunkBefore(mineCurrent, theirsCurrent)) {\n // This patch does not overlap with any of the others, yay.\n ret.hunks.push(cloneHunk(mineCurrent, mineOffset));\n mineIndex++;\n theirsOffset += mineCurrent.newLines - mineCurrent.oldLines;\n } else if (hunkBefore(theirsCurrent, mineCurrent)) {\n // This patch does not overlap with any of the others, yay.\n ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset));\n theirsIndex++;\n mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines;\n } else {\n // Overlap, merge as best we can\n var mergedHunk = {\n oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart),\n oldLines: 0,\n newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset),\n newLines: 0,\n lines: []\n };\n mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines);\n theirsIndex++;\n mineIndex++;\n ret.hunks.push(mergedHunk);\n }\n }\n\n return ret;\n }\n\n function loadPatch(param, base) {\n if (typeof param === 'string') {\n if (/^@@/m.test(param) || /^Index:/m.test(param)) {\n return parsePatch(param)[0];\n }\n\n if (!base) {\n throw new Error('Must provide a base reference or pass in a patch');\n }\n\n return structuredPatch(undefined, undefined, base, param);\n }\n\n return param;\n }\n\n function fileNameChanged(patch) {\n return patch.newFileName && patch.newFileName !== patch.oldFileName;\n }\n\n function selectField(index, mine, theirs) {\n if (mine === theirs) {\n return mine;\n } else {\n index.conflict = true;\n return {\n mine: mine,\n theirs: theirs\n };\n }\n }\n\n function hunkBefore(test, check) {\n return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart;\n }\n\n function cloneHunk(hunk, offset) {\n return {\n oldStart: hunk.oldStart,\n oldLines: hunk.oldLines,\n newStart: hunk.newStart + offset,\n newLines: hunk.newLines,\n lines: hunk.lines\n };\n }\n\n function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) {\n // This will generally result in a conflicted hunk, but there are cases where the context\n // is the only overlap where we can successfully merge the content here.\n var mine = {\n offset: mineOffset,\n lines: mineLines,\n index: 0\n },\n their = {\n offset: theirOffset,\n lines: theirLines,\n index: 0\n }; // Handle any leading content\n\n insertLeading(hunk, mine, their);\n insertLeading(hunk, their, mine); // Now in the overlap content. Scan through and select the best changes from each.\n\n while (mine.index < mine.lines.length && their.index < their.lines.length) {\n var mineCurrent = mine.lines[mine.index],\n theirCurrent = their.lines[their.index];\n\n if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) {\n // Both modified ...\n mutualChange(hunk, mine, their);\n } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') {\n var _hunk$lines;\n\n // Mine inserted\n (_hunk$lines = hunk.lines).push.apply(_hunk$lines, _toConsumableArray(collectChange(mine)));\n } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') {\n var _hunk$lines2;\n\n // Theirs inserted\n (_hunk$lines2 = hunk.lines).push.apply(_hunk$lines2, _toConsumableArray(collectChange(their)));\n } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') {\n // Mine removed or edited\n removal(hunk, mine, their);\n } else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') {\n // Their removed or edited\n removal(hunk, their, mine, true);\n } else if (mineCurrent === theirCurrent) {\n // Context identity\n hunk.lines.push(mineCurrent);\n mine.index++;\n their.index++;\n } else {\n // Context mismatch\n conflict(hunk, collectChange(mine), collectChange(their));\n }\n } // Now push anything that may be remaining\n\n\n insertTrailing(hunk, mine);\n insertTrailing(hunk, their);\n calcLineCount(hunk);\n }\n\n function mutualChange(hunk, mine, their) {\n var myChanges = collectChange(mine),\n theirChanges = collectChange(their);\n\n if (allRemoves(myChanges) && allRemoves(theirChanges)) {\n // Special case for remove changes that are supersets of one another\n if (arrayStartsWith(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) {\n var _hunk$lines3;\n\n (_hunk$lines3 = hunk.lines).push.apply(_hunk$lines3, _toConsumableArray(myChanges));\n\n return;\n } else if (arrayStartsWith(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) {\n var _hunk$lines4;\n\n (_hunk$lines4 = hunk.lines).push.apply(_hunk$lines4, _toConsumableArray(theirChanges));\n\n return;\n }\n } else if (arrayEqual(myChanges, theirChanges)) {\n var _hunk$lines5;\n\n (_hunk$lines5 = hunk.lines).push.apply(_hunk$lines5, _toConsumableArray(myChanges));\n\n return;\n }\n\n conflict(hunk, myChanges, theirChanges);\n }\n\n function removal(hunk, mine, their, swap) {\n var myChanges = collectChange(mine),\n theirChanges = collectContext(their, myChanges);\n\n if (theirChanges.merged) {\n var _hunk$lines6;\n\n (_hunk$lines6 = hunk.lines).push.apply(_hunk$lines6, _toConsumableArray(theirChanges.merged));\n } else {\n conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges);\n }\n }\n\n function conflict(hunk, mine, their) {\n hunk.conflict = true;\n hunk.lines.push({\n conflict: true,\n mine: mine,\n theirs: their\n });\n }\n\n function insertLeading(hunk, insert, their) {\n while (insert.offset < their.offset && insert.index < insert.lines.length) {\n var line = insert.lines[insert.index++];\n hunk.lines.push(line);\n insert.offset++;\n }\n }\n\n function insertTrailing(hunk, insert) {\n while (insert.index < insert.lines.length) {\n var line = insert.lines[insert.index++];\n hunk.lines.push(line);\n }\n }\n\n function collectChange(state) {\n var ret = [],\n operation = state.lines[state.index][0];\n\n while (state.index < state.lines.length) {\n var line = state.lines[state.index]; // Group additions that are immediately after subtractions and treat them as one \"atomic\" modify change.\n\n if (operation === '-' && line[0] === '+') {\n operation = '+';\n }\n\n if (operation === line[0]) {\n ret.push(line);\n state.index++;\n } else {\n break;\n }\n }\n\n return ret;\n }\n\n function collectContext(state, matchChanges) {\n var changes = [],\n merged = [],\n matchIndex = 0,\n contextChanges = false,\n conflicted = false;\n\n while (matchIndex < matchChanges.length && state.index < state.lines.length) {\n var change = state.lines[state.index],\n match = matchChanges[matchIndex]; // Once we've hit our add, then we are done\n\n if (match[0] === '+') {\n break;\n }\n\n contextChanges = contextChanges || change[0] !== ' ';\n merged.push(match);\n matchIndex++; // Consume any additions in the other block as a conflict to attempt\n // to pull in the remaining context after this\n\n if (change[0] === '+') {\n conflicted = true;\n\n while (change[0] === '+') {\n changes.push(change);\n change = state.lines[++state.index];\n }\n }\n\n if (match.substr(1) === change.substr(1)) {\n changes.push(change);\n state.index++;\n } else {\n conflicted = true;\n }\n }\n\n if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) {\n conflicted = true;\n }\n\n if (conflicted) {\n return changes;\n }\n\n while (matchIndex < matchChanges.length) {\n merged.push(matchChanges[matchIndex++]);\n }\n\n return {\n merged: merged,\n changes: changes\n };\n }\n\n function allRemoves(changes) {\n return changes.reduce(function (prev, change) {\n return prev && change[0] === '-';\n }, true);\n }\n\n function skipRemoveSuperset(state, removeChanges, delta) {\n for (var i = 0; i < delta; i++) {\n var changeContent = removeChanges[removeChanges.length - delta + i].substr(1);\n\n if (state.lines[state.index + i] !== ' ' + changeContent) {\n return false;\n }\n }\n\n state.index += delta;\n return true;\n }\n\n function calcOldNewLineCount(lines) {\n var oldLines = 0;\n var newLines = 0;\n lines.forEach(function (line) {\n if (typeof line !== 'string') {\n var myCount = calcOldNewLineCount(line.mine);\n var theirCount = calcOldNewLineCount(line.theirs);\n\n if (oldLines !== undefined) {\n if (myCount.oldLines === theirCount.oldLines) {\n oldLines += myCount.oldLines;\n } else {\n oldLines = undefined;\n }\n }\n\n if (newLines !== undefined) {\n if (myCount.newLines === theirCount.newLines) {\n newLines += myCount.newLines;\n } else {\n newLines = undefined;\n }\n }\n } else {\n if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) {\n newLines++;\n }\n\n if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) {\n oldLines++;\n }\n }\n });\n return {\n oldLines: oldLines,\n newLines: newLines\n };\n }\n\n function reversePatch(structuredPatch) {\n if (Array.isArray(structuredPatch)) {\n return structuredPatch.map(reversePatch).reverse();\n }\n\n return _objectSpread2(_objectSpread2({}, structuredPatch), {}, {\n oldFileName: structuredPatch.newFileName,\n oldHeader: structuredPatch.newHeader,\n newFileName: structuredPatch.oldFileName,\n newHeader: structuredPatch.oldHeader,\n hunks: structuredPatch.hunks.map(function (hunk) {\n return {\n oldLines: hunk.newLines,\n oldStart: hunk.newStart,\n newLines: hunk.oldLines,\n newStart: hunk.oldStart,\n linedelimiters: hunk.linedelimiters,\n lines: hunk.lines.map(function (l) {\n if (l.startsWith('-')) {\n return \"+\".concat(l.slice(1));\n }\n\n if (l.startsWith('+')) {\n return \"-\".concat(l.slice(1));\n }\n\n return l;\n })\n };\n })\n });\n }\n\n // See: http://code.google.com/p/google-diff-match-patch/wiki/API\n function convertChangesToDMP(changes) {\n var ret = [],\n change,\n operation;\n\n for (var i = 0; i < changes.length; i++) {\n change = changes[i];\n\n if (change.added) {\n operation = 1;\n } else if (change.removed) {\n operation = -1;\n } else {\n operation = 0;\n }\n\n ret.push([operation, change.value]);\n }\n\n return ret;\n }\n\n function convertChangesToXML(changes) {\n var ret = [];\n\n for (var i = 0; i < changes.length; i++) {\n var change = changes[i];\n\n if (change.added) {\n ret.push('');\n } else if (change.removed) {\n ret.push('');\n }\n\n ret.push(escapeHTML(change.value));\n\n if (change.added) {\n ret.push('');\n } else if (change.removed) {\n ret.push('');\n }\n }\n\n return ret.join('');\n }\n\n function escapeHTML(s) {\n var n = s;\n n = n.replace(/&/g, '&');\n n = n.replace(//g, '>');\n n = n.replace(/\"/g, '"');\n return n;\n }\n\n exports.Diff = Diff;\n exports.applyPatch = applyPatch;\n exports.applyPatches = applyPatches;\n exports.canonicalize = canonicalize;\n exports.convertChangesToDMP = convertChangesToDMP;\n exports.convertChangesToXML = convertChangesToXML;\n exports.createPatch = createPatch;\n exports.createTwoFilesPatch = createTwoFilesPatch;\n exports.diffArrays = diffArrays;\n exports.diffChars = diffChars;\n exports.diffCss = diffCss;\n exports.diffJson = diffJson;\n exports.diffLines = diffLines;\n exports.diffSentences = diffSentences;\n exports.diffTrimmedLines = diffTrimmedLines;\n exports.diffWords = diffWords;\n exports.diffWordsWithSpace = diffWordsWithSpace;\n exports.formatPatch = formatPatch;\n exports.merge = merge;\n exports.parsePatch = parsePatch;\n exports.reversePatch = reversePatch;\n exports.structuredPatch = structuredPatch;\n\n Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n"],"names":["global","factory","this","exports","Diff","buildValues","diff","lastComponent","newString","oldString","useLongestToken","nextComponent","components","push","previousComponent","reverse","componentPos","componentLen","length","newPos","oldPos","component","removed","value","join","slice","count","added","tmp","map","i","oldValue","finalComponent","equals","pop","prototype","_options$timeout","options","arguments","undefined","callback","self","done","setTimeout","castInput","removeEmpty","tokenize","newLen","oldLen","editLength","maxEditLength","Math","min","maxExecutionTime","timeout","Infinity","abortAfterTimestamp","Date","now","bestPath","extractCommon","minDiagonalToConsider","maxDiagonalToConsider","execEditLength","diagonalPath","max","basePath","removePath","addPath","canAdd","addPathNewPos","canRemove","addToPath","exec","ret","path","oldPosInc","last","commonCount","left","right","comparator","ignoreCase","toLowerCase","array","split","chars","characterDiff","generateOptions","defaults","name","hasOwnProperty","extendedWordChars","reWhitespace","wordDiff","ignoreWhitespace","test","tokens","splice","lineDiff","diffLines","oldStr","newStr","stripTrailingCr","replace","retLines","linesAndNewlines","line","newlineIsToken","trim","sentenceDiff","cssDiff","_typeof","obj","Symbol","iterator","constructor","_defineProperty","key","Object","defineProperty","enumerable","configurable","writable","ownKeys","object","enumerableOnly","keys","getOwnPropertySymbols","symbols","filter","sym","getOwnPropertyDescriptor","apply","_objectSpread2","target","source","forEach","getOwnPropertyDescriptors","defineProperties","_toConsumableArray","arr","Array","isArray","_arrayLikeToArray","_arrayWithoutHoles","iter","from","_iterableToArray","o","minLen","n","toString","call","_unsupportedIterableToArray","TypeError","_nonIterableSpread","len","arr2","objectPrototypeToString","jsonDiff","canonicalize","stack","replacementStack","replacer","canonicalizedObj","toJSON","_key","sortedKeys","sort","_this$options","undefinedReplacement","_this$options$stringi","stringifyReplacer","k","v","JSON","stringify","arrayDiff","parsePatch","uniDiff","diffstr","delimiters","match","list","parseIndex","index","header","parseFileHeader","hunks","_line","parseHunk","strict","Error","fileHeader","keyPrefix","data","fileName","substr","chunkHeaderIndex","chunkHeader","hunk","oldStart","oldLines","newStart","newLines","lines","linedelimiters","addCount","removeCount","indexOf","operation","distanceIterator","start","minLine","maxLine","wantForward","backwardExhausted","forwardExhausted","localOffset","applyPatch","removeEOFNL","addEOFNL","compareLine","lineNumber","patchContent","errorCount","fuzzFactor","offset","hunkFits","toPos","j","content","diffOffset","_i","_hunk","_toPos","delimiter","previousOperation","_k","structuredPatch","oldFileName","newFileName","oldHeader","newHeader","context","oldRangeStart","newRangeStart","curRange","oldLine","newLine","_loop","current","_curRange","prev","contextLines","entry","_curRange2","_curRange3","contextSize","oldEOFNewline","newEOFNewline","noNlBeforeAdds","formatPatch","createTwoFilesPatch","arrayStartsWith","calcLineCount","_calcOldNewLineCount","calcOldNewLineCount","loadPatch","param","base","fileNameChanged","patch","selectField","mine","theirs","conflict","hunkBefore","check","cloneHunk","mergeLines","mineOffset","mineLines","theirOffset","theirLines","their","insertLeading","mineCurrent","theirCurrent","_hunk$lines","collectChange","_hunk$lines2","removal","mutualChange","insertTrailing","a","b","myChanges","theirChanges","allRemoves","_hunk$lines3","_hunk$lines4","skipRemoveSuperset","_hunk$lines5","swap","_hunk$lines6","state","matchChanges","changes","merged","matchIndex","contextChanges","conflicted","change","collectContext","insert","reduce","removeChanges","delta","changeContent","myCount","theirCount","applyPatches","currentIndex","processIndex","complete","loadFile","err","updatedContent","patched","convertChangesToDMP","convertChangesToXML","s","createPatch","diffArrays","oldArr","newArr","diffChars","diffCss","diffJson","oldObj","newObj","diffSentences","diffTrimmedLines","diffWords","diffWordsWithSpace","merge","mineIndex","theirsIndex","theirsOffset","theirsCurrent","mergedHunk","reversePatch","l","startsWith","concat","module","define","amd"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/editformhelper.min.js.map b/amd/build/editformhelper.min.js.map index 10e9f7e..48785a6 100644 --- a/amd/build/editformhelper.min.js.map +++ b/amd/build/editformhelper.min.js.map @@ -1 +1 @@ -{"version":3,"file":"editformhelper.min.js","sources":["../src/editformhelper.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\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\nimport {get_strings} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Log from 'core/log';\nimport Notify from 'core/notification';\n\n/**\n * Question AI Text Edit Form Helper\n *\n * @module qtype_aitext/editformhelper\n * @copyright 2024 Justin Hunt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n fields: {\n sampleanswer: '#id_sampleanswer',\n sampleanswerbtn: '#id_sampleanswerbtn',\n sampleanswereval: '#id_sampleanswereval',\n aiprompt: '#id_aiprompt',\n markscheme: '#id_markscheme',\n defaultmark: '#id_defaultmark',\n },\n};\n\n/**\n * Initialise the format chooser.\n */\nexport const init = () => {\n\n // Set up strings\n var strings={};\n get_strings([\n { \"key\": \"prompttester\", \"component\": 'qtype_aitext'},\n { \"key\": \"sampleanswerempty\", \"component\": 'qtype_aitext'},\n\n ]).done(function (s) {\n var i = 0;\n strings.prompttester = s[i++];\n strings.sampleanswerempty = s[i++];\n });\n\n\n document.querySelector(Selectors.fields.sampleanswerbtn).addEventListener('click', e => {\n\n\n\n const form = e.target.closest('form');\n const sampleanswer = form.querySelector(Selectors.fields.sampleanswer);\n const sampleanswereval = form.querySelector(Selectors.fields.sampleanswereval);\n const aiprompt = form.querySelector(Selectors.fields.aiprompt);\n const marksscheme = form.querySelector(Selectors.fields.markscheme);\n const defaultmark = form.querySelector(Selectors.fields.defaultmark);\n\n if(sampleanswer.value===\"\" || aiprompt.value===\"\"){\n Notify.alert(strings.prompttester, strings.sampleanswerempty);\n return;\n }\n\n //put spinner in place\n sampleanswereval.innerHTML='';\n\n Ajax.call([{\n methodname: 'qtype_aitext_fetch_ai_grade',\n args: {\n response: sampleanswer.value,\n defaultmark: defaultmark.value,\n prompt: aiprompt.value,\n marksscheme: marksscheme.value\n },\n async: false\n }])[0].then(function(airesponse) {\n Log.debug(airesponse);\n if (airesponse.feedback) {\n sampleanswereval.textContent = airesponse.feedback + ' (GRADE: ' + airesponse.marks + '/' + defaultmark.value + ')';\n }\n });\n });//end of click\n};\n"],"names":["Selectors","sampleanswer","sampleanswerbtn","sampleanswereval","aiprompt","markscheme","defaultmark","strings","done","s","i","prompttester","sampleanswerempty","document","querySelector","addEventListener","e","form","target","closest","marksscheme","value","innerHTML","call","methodname","args","response","prompt","async","then","airesponse","debug","feedback","textContent","marks","alert"],"mappings":";;;;;;;kNA2BMA,iBACM,CACJC,aAAc,mBACdC,gBAAiB,sBACjBC,iBAAkB,uBAClBC,SAAU,eACVC,WAAY,iBACZC,YAAa,iCAOD,SAGZC,QAAQ,wBACA,CACR,KAAS,yBAA6B,gBACtC,KAAS,8BAAkC,kBAE5CC,MAAK,SAAUC,OACVC,EAAI,EACRH,QAAQI,aAAeF,EAAEC,KACzBH,QAAQK,kBAAoBH,EAAEC,QAIlCG,SAASC,cAAcd,iBAAiBE,iBAAiBa,iBAAiB,SAASC,UAIzEC,KAAOD,EAAEE,OAAOC,QAAQ,QACxBlB,aAAegB,KAAKH,cAAcd,iBAAiBC,cACnDE,iBAAmBc,KAAKH,cAAcd,iBAAiBG,kBACvDC,SAAWa,KAAKH,cAAcd,iBAAiBI,UAC/CgB,YAAcH,KAAKH,cAAcd,iBAAiBK,YAClDC,YAAcW,KAAKH,cAAcd,iBAAiBM,aAEhC,KAArBL,aAAaoB,OAA+B,KAAjBjB,SAASiB,OAMvClB,iBAAiBmB,UAAU,iEAEtBC,KAAK,CAAC,CACPC,WAAY,8BACZC,KAAM,CACFC,SAAUzB,aAAaoB,MACvBf,YAAaA,YAAYe,MACzBM,OAAQvB,SAASiB,MACjBD,YAAaA,YAAYC,OAE7BO,OAAO,KACP,GAAGC,MAAK,SAASC,yBACbC,MAAMD,YACNA,WAAWE,WACX7B,iBAAiB8B,YAAcH,WAAWE,SAAW,YAAcF,WAAWI,MAAQ,IAAM5B,YAAYe,MAAQ,+BAnB7Gc,MAAM5B,QAAQI,aAAcJ,QAAQK"} \ No newline at end of file +{"version":3,"file":"editformhelper.min.js","sources":["../src/editformhelper.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\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\nimport {get_strings} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Log from 'core/log';\nimport Notify from 'core/notification';\n\n/**\n * Question AI Text Edit Form Helper\n *\n * @module qtype_aitext/editformhelper\n * @copyright 2024 Justin Hunt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n fields: {\n sampleanswer: '#id_sampleanswer',\n sampleanswerbtn: '#id_sampleanswerbtn',\n sampleanswereval: '#id_sampleanswereval',\n aiprompt: '#id_aiprompt',\n markscheme: '#id_markscheme',\n defaultmark: '#id_defaultmark',\n },\n};\n\n/**\n * Initialise the format chooser.\n */\nexport const init = () => {\n\n // Set up strings\n var strings={};\n get_strings([\n { \"key\": \"prompttester\", \"component\": 'qtype_aitext'},\n { \"key\": \"sampleanswerempty\", \"component\": 'qtype_aitext'},\n\n ]).done(function (s) {\n var i = 0;\n strings.prompttester = s[i++];\n strings.sampleanswerempty = s[i++];\n });\n\n document.querySelector(Selectors.fields.sampleanswerbtn).addEventListener('click', e => {\n\n const form = e.target.closest('form');\n const sampleanswer = form.querySelector(Selectors.fields.sampleanswer);\n const sampleanswereval = form.querySelector(Selectors.fields.sampleanswereval);\n const aiprompt = form.querySelector(Selectors.fields.aiprompt);\n const marksscheme = form.querySelector(Selectors.fields.markscheme);\n const defaultmark = form.querySelector(Selectors.fields.defaultmark);\n\n if(sampleanswer.value===\"\" || aiprompt.value===\"\"){\n Notify.alert(strings.prompttester, strings.sampleanswerempty);\n return;\n }\n\n //put spinner in place\n sampleanswereval.innerHTML='';\n\n Ajax.call([{\n methodname: 'qtype_aitext_fetch_ai_grade',\n args: {\n response: sampleanswer.value,\n defaultmark: defaultmark.value,\n prompt: aiprompt.value,\n marksscheme: marksscheme.value\n },\n async: false\n }])[0].then(function(airesponse) {\n Log.debug(airesponse);\n if (airesponse.feedback) {\n sampleanswereval.textContent = airesponse.feedback + ' (GRADE: ' + airesponse.marks + '/' + defaultmark.value + ')';\n }\n });\n });//end of click\n};\n"],"names":["Selectors","sampleanswer","sampleanswerbtn","sampleanswereval","aiprompt","markscheme","defaultmark","strings","done","s","i","prompttester","sampleanswerempty","document","querySelector","addEventListener","e","form","target","closest","marksscheme","value","innerHTML","call","methodname","args","response","prompt","async","then","airesponse","debug","feedback","textContent","marks","alert"],"mappings":";;;;;;;kNA2BMA,iBACM,CACJC,aAAc,mBACdC,gBAAiB,sBACjBC,iBAAkB,uBAClBC,SAAU,eACVC,WAAY,iBACZC,YAAa,iCAOD,SAGZC,QAAQ,wBACA,CACR,KAAS,yBAA6B,gBACtC,KAAS,8BAAkC,kBAE5CC,MAAK,SAAUC,OACVC,EAAI,EACRH,QAAQI,aAAeF,EAAEC,KACzBH,QAAQK,kBAAoBH,EAAEC,QAGlCG,SAASC,cAAcd,iBAAiBE,iBAAiBa,iBAAiB,SAASC,UAEzEC,KAAOD,EAAEE,OAAOC,QAAQ,QACxBlB,aAAegB,KAAKH,cAAcd,iBAAiBC,cACnDE,iBAAmBc,KAAKH,cAAcd,iBAAiBG,kBACvDC,SAAWa,KAAKH,cAAcd,iBAAiBI,UAC/CgB,YAAcH,KAAKH,cAAcd,iBAAiBK,YAClDC,YAAcW,KAAKH,cAAcd,iBAAiBM,aAEhC,KAArBL,aAAaoB,OAA+B,KAAjBjB,SAASiB,OAMvClB,iBAAiBmB,UAAU,iEAEtBC,KAAK,CAAC,CACPC,WAAY,8BACZC,KAAM,CACFC,SAAUzB,aAAaoB,MACvBf,YAAaA,YAAYe,MACzBM,OAAQvB,SAASiB,MACjBD,YAAaA,YAAYC,OAE7BO,OAAO,KACP,GAAGC,MAAK,SAASC,yBACbC,MAAMD,YACNA,WAAWE,WACX7B,iBAAiB8B,YAAcH,WAAWE,SAAW,YAAcF,WAAWI,MAAQ,IAAM5B,YAAYe,MAAQ,+BAnB7Gc,MAAM5B,QAAQI,aAAcJ,QAAQK"} \ No newline at end of file diff --git a/amd/build/getSpellcheck.min.js b/amd/build/getSpellcheck.min.js new file mode 100644 index 0000000..e5e0aa2 --- /dev/null +++ b/amd/build/getSpellcheck.min.js @@ -0,0 +1,11 @@ +define("qtype_aitext/getSpellcheck",["exports","qtype_aitext/diff","core_form/modalform","core/str"],(function(_exports,Diff,_modalform,_str){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.showModalForm=_exports.renderDiff=_exports.init=void 0,Diff=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} +/** + * Generates the spellcheck diff view. + * + * @module qtype_aitext/getSpellcheck + * @copyright 2024, ISB Bayern + * @author Dr. Peter Mayer + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */(Diff),_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};_exports.init=()=>{renderDiff(),document.getElementById("aitext_spellcheckedit")&&document.getElementById("aitext_spellcheckedit").addEventListener("click",showModalForm)};const renderDiff=()=>{var studentanswer=document.getElementById("aitext_readonly_area").innerHTML,spellcheck=document.getElementById("aitext_readonly_area").dataset.spellcheck,span=null,diff=Diff.diffChars(studentanswer,spellcheck),fragment=document.createElement("div");let fullspellcheck="";diff.forEach((function(part){var cls=part.added?"qtype_aitext_spellcheck_new":part.removed?"qtype_aitext_spellcheck_wrong":"";part.added||part.removed?((span=document.createElement("span")).classList=cls,span.appendChild(document.createTextNode(part.value)),fullspellcheck+=span.outerHTML):fullspellcheck+=part.value})),fragment.innerHTML=fullspellcheck,document.getElementById("aitext_readonly_area").replaceChildren(fragment)};_exports.renderDiff=renderDiff;const showModalForm=()=>{const attemptstepid=document.getElementById("aitext_readonly_area").dataset.spellcheckattemptstepid,answerstepid=document.getElementById("aitext_readonly_area").dataset.spellcheckattemptstepanswerid,queryString=window.location.search,urlParams=new URLSearchParams(queryString),modalForm=new _modalform.default({formClass:"qtype_aitext\\form\\edit_spellchek",args:{attemptstepid:attemptstepid,answerstepid:answerstepid,cmid:urlParams.get("cmid")},modalConfig:{title:(0,_str.get_string)("spellcheckedit","qtype_aitext")}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,reloadpage),modalForm.show()};_exports.showModalForm=showModalForm;const reloadpage=()=>{location.reload()}})); + +//# sourceMappingURL=getSpellcheck.min.js.map \ No newline at end of file diff --git a/amd/build/getSpellcheck.min.js.map b/amd/build/getSpellcheck.min.js.map new file mode 100644 index 0000000..5932a78 --- /dev/null +++ b/amd/build/getSpellcheck.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"getSpellcheck.min.js","sources":["../src/getSpellcheck.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 * Generates the spellcheck diff view.\n *\n * @module qtype_aitext/getSpellcheck\n * @copyright 2024, ISB Bayern\n * @author Dr. Peter Mayer\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as Diff from \"qtype_aitext/diff\";\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Init\n */\nexport const init = () => {\n renderDiff();\n\n if (!document.getElementById('aitext_spellcheckedit')) {\n return;\n }\n document.getElementById('aitext_spellcheckedit').addEventListener(\"click\", showModalForm);\n};\n\n/**\n * Render the spellcheckdiff\n */\nexport const renderDiff = () => {\n var studentanswer = document.getElementById('aitext_readonly_area').innerHTML,\n spellcheck = document.getElementById('aitext_readonly_area').dataset.spellcheck,\n span = null;\n\n var diff = Diff.diffChars(studentanswer, spellcheck);\n var fragment = document.createElement('div');\n\n let fullspellcheck = '';\n\n diff.forEach(function (part) {\n var cls = part.added ? 'qtype_aitext_spellcheck_new' :\n part.removed ? 'qtype_aitext_spellcheck_wrong' : '';\n if (part.added || part.removed) {\n span = document.createElement('span');\n span.classList = cls;\n span.appendChild(document.createTextNode(part.value));\n fullspellcheck += span.outerHTML;\n } else {\n fullspellcheck += part.value;\n }\n });\n\n fragment.innerHTML = fullspellcheck;\n document.getElementById('aitext_readonly_area').replaceChildren(fragment);\n};\n\n/**\n * Show the dynamic spellcheck form.\n */\nexport const showModalForm = () => {\n const attemptstepid = document.getElementById('aitext_readonly_area').dataset.spellcheckattemptstepid;\n const answerstepid = document.getElementById('aitext_readonly_area').dataset.spellcheckattemptstepanswerid;\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n const modalForm = new ModalForm({\n formClass: \"qtype_aitext\\\\form\\\\edit_spellchek\",\n args: {\n attemptstepid: attemptstepid,\n answerstepid: answerstepid,\n cmid: urlParams.get('cmid'),\n },\n modalConfig: { title: getString('spellcheckedit', 'qtype_aitext') },\n });\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, reloadpage);\n modalForm.show();\n};\n\n/**\n * Reload the page. This is not nice, but easy :-)\n */\nconst reloadpage = () => {\n location.reload();\n}\n"],"names":["renderDiff","document","getElementById","addEventListener","showModalForm","studentanswer","innerHTML","spellcheck","dataset","span","diff","Diff","diffChars","fragment","createElement","fullspellcheck","forEach","part","cls","added","removed","classList","appendChild","createTextNode","value","outerHTML","replaceChildren","attemptstepid","spellcheckattemptstepid","answerstepid","spellcheckattemptstepanswerid","queryString","window","location","search","urlParams","URLSearchParams","modalForm","ModalForm","formClass","args","cmid","get","modalConfig","title","events","FORM_SUBMITTED","reloadpage","show","reload"],"mappings":";;;;;;;;wFA+BoB,KAChBA,aAEKC,SAASC,eAAe,0BAG7BD,SAASC,eAAe,yBAAyBC,iBAAiB,QAASC,sBAMlEJ,WAAa,SAClBK,cAAgBJ,SAASC,eAAe,wBAAwBI,UAChEC,WAAaN,SAASC,eAAe,wBAAwBM,QAAQD,WACrEE,KAAO,KAEPC,KAAOC,KAAKC,UAAUP,cAAeE,YACrCM,SAAWZ,SAASa,cAAc,WAElCC,eAAiB,GAErBL,KAAKM,SAAQ,SAAUC,UACfC,IAAMD,KAAKE,MAAQ,8BACnBF,KAAKG,QAAU,gCAAkC,GACjDH,KAAKE,OAASF,KAAKG,UACnBX,KAAOR,SAASa,cAAc,SACzBO,UAAYH,IACjBT,KAAKa,YAAYrB,SAASsB,eAAeN,KAAKO,QAC9CT,gBAAkBN,KAAKgB,WAEvBV,gBAAkBE,KAAKO,SAI/BX,SAASP,UAAYS,eACrBd,SAASC,eAAe,wBAAwBwB,gBAAgBb,gDAMvDT,cAAgB,WACnBuB,cAAgB1B,SAASC,eAAe,wBAAwBM,QAAQoB,wBACxEC,aAAe5B,SAASC,eAAe,wBAAwBM,QAAQsB,8BACvEC,YAAcC,OAAOC,SAASC,OAC9BC,UAAY,IAAIC,gBAAgBL,aAChCM,UAAY,IAAIC,mBAAU,CAC5BC,UAAW,qCACXC,KAAM,CACFb,cAAeA,cACfE,aAAcA,aACdY,KAAMN,UAAUO,IAAI,SAExBC,YAAa,CAAEC,OAAO,mBAAU,iBAAkB,mBAEtDP,UAAUlC,iBAAiBkC,UAAUQ,OAAOC,eAAgBC,YAC5DV,UAAUW,mDAMRD,WAAa,KACfd,SAASgB"} \ No newline at end of file diff --git a/amd/build/showprompt.min.js.map b/amd/build/showprompt.min.js.map index 83f2dcb..8307f39 100644 --- a/amd/build/showprompt.min.js.map +++ b/amd/build/showprompt.min.js.map @@ -1 +1 @@ -{"version":3,"file":"showprompt.min.js","sources":["../src/showprompt.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 * Display a button in testing to reveal the prompt that was sent\n *\n * @module qtype_aitext/showprompt\n * @copyright 2024 Marcus Green\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport const init = () => {\n var button = document.getElementById('showprompt');\n button.addEventListener('click', (event) => {\n toggleFullPrompt(event);\n });\n /**\n * Togle the visibility of the prompt that is sent to\n * the AI System\n * @param {*} event\n */\n function toggleFullPrompt(event) {\n event.preventDefault();\n var text = document.getElementById(\"fullprompt\");\n if (text.className === \"hidden\") {\n text.className = \"visible\";\n } else {\n text.className = \"hidden\";\n }\n }\n};\n"],"names":["document","getElementById","addEventListener","event","preventDefault","text","className","toggleFullPrompt"],"mappings":"4JAsBoB,KACHA,SAASC,eAAe,cAC9BC,iBAAiB,SAAUC,kBAQRA,OACtBA,MAAMC,qBACFC,KAAOL,SAASC,eAAe,cACZ,WAAnBI,KAAKC,UACLD,KAAKC,UAAY,UAEjBD,KAAKC,UAAY,SAbrBC,CAAiBJ"} \ No newline at end of file +{"version":3,"file":"showprompt.min.js","sources":["../src/showprompt.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 * Display a button in testing to reveal the prompt that was sent\n *\n * @module qtype_aitext/showprompt\n * @copyright 2024 Marcus Green\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport const init = () => {\n var button = document.getElementById('showprompt');\n button.addEventListener('click', (event) => {\n toggleFullPrompt(event);\n });\n\n /**\n * Togle the visibility of the prompt that is sent to\n * the AI System\n * @param {*} event\n */\n function toggleFullPrompt(event) {\n event.preventDefault();\n var text = document.getElementById(\"fullprompt\");\n if (text.className === \"hidden\") {\n text.className = \"visible\";\n } else {\n text.className = \"hidden\";\n }\n }\n};\n"],"names":["document","getElementById","addEventListener","event","preventDefault","text","className","toggleFullPrompt"],"mappings":"4JAsBoB,KACHA,SAASC,eAAe,cAC9BC,iBAAiB,SAAUC,kBASRA,OACtBA,MAAMC,qBACFC,KAAOL,SAASC,eAAe,cACZ,WAAnBI,KAAKC,UACLD,KAAKC,UAAY,UAEjBD,KAAKC,UAAY,SAdrBC,CAAiBJ"} \ No newline at end of file diff --git a/amd/src/diff.js b/amd/src/diff.js new file mode 100644 index 0000000..76232b2 --- /dev/null +++ b/amd/src/diff.js @@ -0,0 +1,1730 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = global || self, factory(global.Diff = {})); +}(this, (function (exports) { 'use strict'; + + function Diff() {} + Diff.prototype = { + diff: function diff(oldString, newString) { + var _options$timeout; + + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + var callback = options.callback; + + if (typeof options === 'function') { + callback = options; + options = {}; + } + + this.options = options; + var self = this; + + function done(value) { + if (callback) { + setTimeout(function () { + callback(undefined, value); + }, 0); + return true; + } else { + return value; + } + } // Allow subclasses to massage the input prior to running + + + oldString = this.castInput(oldString); + newString = this.castInput(newString); + oldString = this.removeEmpty(this.tokenize(oldString)); + newString = this.removeEmpty(this.tokenize(newString)); + var newLen = newString.length, + oldLen = oldString.length; + var editLength = 1; + var maxEditLength = newLen + oldLen; + + if (options.maxEditLength) { + maxEditLength = Math.min(maxEditLength, options.maxEditLength); + } + + var maxExecutionTime = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : Infinity; + var abortAfterTimestamp = Date.now() + maxExecutionTime; + var bestPath = [{ + oldPos: -1, + lastComponent: undefined + }]; // Seed editLength = 0, i.e. the content starts with the same values + + var newPos = this.extractCommon(bestPath[0], newString, oldString, 0); + + if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) { + // Identity per the equality and tokenizer + return done([{ + value: this.join(newString), + count: newString.length + }]); + } // Once we hit the right edge of the edit graph on some diagonal k, we can + // definitely reach the end of the edit graph in no more than k edits, so + // there's no point in considering any moves to diagonal k+1 any more (from + // which we're guaranteed to need at least k+1 more edits). + // Similarly, once we've reached the bottom of the edit graph, there's no + // point considering moves to lower diagonals. + // We record this fact by setting minDiagonalToConsider and + // maxDiagonalToConsider to some finite value once we've hit the edge of + // the edit graph. + // This optimization is not faithful to the original algorithm presented in + // Myers's paper, which instead pointlessly extends D-paths off the end of + // the edit graph - see page 7 of Myers's paper which notes this point + // explicitly and illustrates it with a diagram. This has major performance + // implications for some common scenarios. For instance, to compute a diff + // where the new text simply appends d characters on the end of the + // original text of length n, the true Myers algorithm will take O(n+d^2) + // time while this optimization needs only O(n+d) time. + + + var minDiagonalToConsider = -Infinity, + maxDiagonalToConsider = Infinity; // Main worker method. checks all permutations of a given edit length for acceptance. + + function execEditLength() { + for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) { + var basePath = void 0; + var removePath = bestPath[diagonalPath - 1], + addPath = bestPath[diagonalPath + 1]; + + if (removePath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath - 1] = undefined; + } + + var canAdd = false; + + if (addPath) { + // what newPos will be after we do an insertion: + var addPathNewPos = addPath.oldPos - diagonalPath; + canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen; + } + + var canRemove = removePath && removePath.oldPos + 1 < oldLen; + + if (!canAdd && !canRemove) { + // If this path is a terminal then prune + bestPath[diagonalPath] = undefined; + continue; + } // Select the diagonal that we want to branch from. We select the prior + // path whose position in the old string is the farthest from the origin + // and does not pass the bounds of the diff graph + // TODO: Remove the `+ 1` here to make behavior match Myers algorithm + // and prefer to order removals before insertions. + + + if (!canRemove || canAdd && removePath.oldPos + 1 < addPath.oldPos) { + basePath = self.addToPath(addPath, true, undefined, 0); + } else { + basePath = self.addToPath(removePath, undefined, true, 1); + } + + newPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + + if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) { + // If we have hit the end of both strings, then we are done + return done(buildValues(self, basePath.lastComponent, newString, oldString, self.useLongestToken)); + } else { + bestPath[diagonalPath] = basePath; + + if (basePath.oldPos + 1 >= oldLen) { + maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1); + } + + if (newPos + 1 >= newLen) { + minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1); + } + } + } + + editLength++; + } // Performs the length of edit iteration. Is a bit fugly as this has to support the + // sync and async mode which is never fun. Loops over execEditLength until a value + // is produced, or until the edit length exceeds options.maxEditLength (if given), + // in which case it will return undefined. + + + if (callback) { + (function exec() { + setTimeout(function () { + if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) { + return callback(); + } + + if (!execEditLength()) { + exec(); + } + }, 0); + })(); + } else { + while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) { + var ret = execEditLength(); + + if (ret) { + return ret; + } + } + } + }, + addToPath: function addToPath(path, added, removed, oldPosInc) { + var last = path.lastComponent; + + if (last && last.added === added && last.removed === removed) { + return { + oldPos: path.oldPos + oldPosInc, + lastComponent: { + count: last.count + 1, + added: added, + removed: removed, + previousComponent: last.previousComponent + } + }; + } else { + return { + oldPos: path.oldPos + oldPosInc, + lastComponent: { + count: 1, + added: added, + removed: removed, + previousComponent: last + } + }; + } + }, + extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + oldPos = basePath.oldPos, + newPos = oldPos - diagonalPath, + commonCount = 0; + + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + newPos++; + oldPos++; + commonCount++; + } + + if (commonCount) { + basePath.lastComponent = { + count: commonCount, + previousComponent: basePath.lastComponent + }; + } + + basePath.oldPos = oldPos; + return newPos; + }, + equals: function equals(left, right) { + if (this.options.comparator) { + return this.options.comparator(left, right); + } else { + return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); + } + }, + removeEmpty: function removeEmpty(array) { + var ret = []; + + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + + return ret; + }, + castInput: function castInput(value) { + return value; + }, + tokenize: function tokenize(value) { + return value.split(''); + }, + join: function join(chars) { + return chars.join(''); + } + }; + + function buildValues(diff, lastComponent, newString, oldString, useLongestToken) { + // First we convert our linked list of components in reverse order to an + // array in the right order: + var components = []; + var nextComponent; + + while (lastComponent) { + components.push(lastComponent); + nextComponent = lastComponent.previousComponent; + delete lastComponent.previousComponent; + lastComponent = nextComponent; + } + + components.reverse(); + var componentPos = 0, + componentLen = components.length, + newPos = 0, + oldPos = 0; + + for (; componentPos < componentLen; componentPos++) { + var component = components[componentPos]; + + if (!component.removed) { + if (!component.added && useLongestToken) { + var value = newString.slice(newPos, newPos + component.count); + value = value.map(function (value, i) { + var oldValue = oldString[oldPos + i]; + return oldValue.length > value.length ? oldValue : value; + }); + component.value = diff.join(value); + } else { + component.value = diff.join(newString.slice(newPos, newPos + component.count)); + } + + newPos += component.count; // Common case + + if (!component.added) { + oldPos += component.count; + } + } else { + component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); + oldPos += component.count; // Reverse add and remove so removes are output first to match common convention + // The diffing algorithm is tied to add then remove output and this is the simplest + // route to get the desired output with minimal overhead. + + if (componentPos && components[componentPos - 1].added) { + var tmp = components[componentPos - 1]; + components[componentPos - 1] = components[componentPos]; + components[componentPos] = tmp; + } + } + } // Special case handle for when one terminal is ignored (i.e. whitespace). + // For this case we merge the terminal into the prior string and drop the change. + // This is only available for string mode. + + + var finalComponent = components[componentLen - 1]; + + if (componentLen > 1 && typeof finalComponent.value === 'string' && (finalComponent.added || finalComponent.removed) && diff.equals('', finalComponent.value)) { + components[componentLen - 2].value += finalComponent.value; + components.pop(); + } + + return components; + } + + var characterDiff = new Diff(); + function diffChars(oldStr, newStr, options) { + return characterDiff.diff(oldStr, newStr, options); + } + + function generateOptions(options, defaults) { + if (typeof options === 'function') { + defaults.callback = options; + } else if (options) { + for (var name in options) { + /* istanbul ignore else */ + if (options.hasOwnProperty(name)) { + defaults[name] = options[name]; + } + } + } + + return defaults; + } + + // + // Ranges and exceptions: + // Latin-1 Supplement, 0080–00FF + // - U+00D7 × Multiplication sign + // - U+00F7 ÷ Division sign + // Latin Extended-A, 0100–017F + // Latin Extended-B, 0180–024F + // IPA Extensions, 0250–02AF + // Spacing Modifier Letters, 02B0–02FF + // - U+02C7 ˇ ˇ Caron + // - U+02D8 ˘ ˘ Breve + // - U+02D9 ˙ ˙ Dot Above + // - U+02DA ˚ ˚ Ring Above + // - U+02DB ˛ ˛ Ogonek + // - U+02DC ˜ ˜ Small Tilde + // - U+02DD ˝ ˝ Double Acute Accent + // Latin Extended Additional, 1E00–1EFF + + var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; + var reWhitespace = /\S/; + var wordDiff = new Diff(); + + wordDiff.equals = function (left, right) { + if (this.options.ignoreCase) { + left = left.toLowerCase(); + right = right.toLowerCase(); + } + + return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); + }; + + wordDiff.tokenize = function (value) { + // All whitespace symbols except newline group into one token, each newline - in separate token + var tokens = value.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/); // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. + + for (var i = 0; i < tokens.length - 1; i++) { + // If we have an empty string in the next field and we have only word chars before and after, merge + if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { + tokens[i] += tokens[i + 2]; + tokens.splice(i + 1, 2); + i--; + } + } + + return tokens; + }; + + function diffWords(oldStr, newStr, options) { + options = generateOptions(options, { + ignoreWhitespace: true + }); + return wordDiff.diff(oldStr, newStr, options); + } + function diffWordsWithSpace(oldStr, newStr, options) { + return wordDiff.diff(oldStr, newStr, options); + } + + var lineDiff = new Diff(); + + lineDiff.tokenize = function (value) { + if (this.options.stripTrailingCr) { + // remove one \r before \n to match GNU diff's --strip-trailing-cr behavior + value = value.replace(/\r\n/g, '\n'); + } + + var retLines = [], + linesAndNewlines = value.split(/(\n|\r\n)/); // Ignore the final empty token that occurs if the string ends with a new line + + if (!linesAndNewlines[linesAndNewlines.length - 1]) { + linesAndNewlines.pop(); + } // Merge the content and line separators into single tokens + + + for (var i = 0; i < linesAndNewlines.length; i++) { + var line = linesAndNewlines[i]; + + if (i % 2 && !this.options.newlineIsToken) { + retLines[retLines.length - 1] += line; + } else { + if (this.options.ignoreWhitespace) { + line = line.trim(); + } + + retLines.push(line); + } + } + + return retLines; + }; + + function diffLines(oldStr, newStr, callback) { + return lineDiff.diff(oldStr, newStr, callback); + } + function diffTrimmedLines(oldStr, newStr, callback) { + var options = generateOptions(callback, { + ignoreWhitespace: true + }); + return lineDiff.diff(oldStr, newStr, options); + } + + var sentenceDiff = new Diff(); + + sentenceDiff.tokenize = function (value) { + return value.split(/(\S.+?[.!?])(?=\s+|$)/); + }; + + function diffSentences(oldStr, newStr, callback) { + return sentenceDiff.diff(oldStr, newStr, callback); + } + + var cssDiff = new Diff(); + + cssDiff.tokenize = function (value) { + return value.split(/([{}:;,]|\s+)/); + }; + + function diffCss(oldStr, newStr, callback) { + return cssDiff.diff(oldStr, newStr, callback); + } + + function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; + } + + function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; + } + + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); + } + + function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); + } + + function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); + } + + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + var objectPrototypeToString = Object.prototype.toString; + var jsonDiff = new Diff(); // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a + // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: + + jsonDiff.useLongestToken = true; + jsonDiff.tokenize = lineDiff.tokenize; + + jsonDiff.castInput = function (value) { + var _this$options = this.options, + undefinedReplacement = _this$options.undefinedReplacement, + _this$options$stringi = _this$options.stringifyReplacer, + stringifyReplacer = _this$options$stringi === void 0 ? function (k, v) { + return typeof v === 'undefined' ? undefinedReplacement : v; + } : _this$options$stringi; + return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); + }; + + jsonDiff.equals = function (left, right) { + return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); + }; + + function diffJson(oldObj, newObj, options) { + return jsonDiff.diff(oldObj, newObj, options); + } // This function handles the presence of circular references by bailing out when encountering an + // object that is already on the "stack" of items being processed. Accepts an optional replacer + + function canonicalize(obj, stack, replacementStack, replacer, key) { + stack = stack || []; + replacementStack = replacementStack || []; + + if (replacer) { + obj = replacer(key, obj); + } + + var i; + + for (i = 0; i < stack.length; i += 1) { + if (stack[i] === obj) { + return replacementStack[i]; + } + } + + var canonicalizedObj; + + if ('[object Array]' === objectPrototypeToString.call(obj)) { + stack.push(obj); + canonicalizedObj = new Array(obj.length); + replacementStack.push(canonicalizedObj); + + for (i = 0; i < obj.length; i += 1) { + canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); + } + + stack.pop(); + replacementStack.pop(); + return canonicalizedObj; + } + + if (obj && obj.toJSON) { + obj = obj.toJSON(); + } + + if (_typeof(obj) === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + replacementStack.push(canonicalizedObj); + + var sortedKeys = [], + _key; + + for (_key in obj) { + /* istanbul ignore else */ + if (obj.hasOwnProperty(_key)) { + sortedKeys.push(_key); + } + } + + sortedKeys.sort(); + + for (i = 0; i < sortedKeys.length; i += 1) { + _key = sortedKeys[i]; + canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); + } + + stack.pop(); + replacementStack.pop(); + } else { + canonicalizedObj = obj; + } + + return canonicalizedObj; + } + + var arrayDiff = new Diff(); + + arrayDiff.tokenize = function (value) { + return value.slice(); + }; + + arrayDiff.join = arrayDiff.removeEmpty = function (value) { + return value; + }; + + function diffArrays(oldArr, newArr, callback) { + return arrayDiff.diff(oldArr, newArr, callback); + } + + function parsePatch(uniDiff) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], + list = [], + i = 0; + + function parseIndex() { + var index = {}; + list.push(index); // Parse diff metadata + + while (i < diffstr.length) { + var line = diffstr[i]; // File header found, end parsing diff metadata + + if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { + break; + } // Diff index + + + var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); + + if (header) { + index.index = header[1]; + } + + i++; + } // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + + + parseFileHeader(index); + parseFileHeader(index); // Parse hunks + + index.hunks = []; + + while (i < diffstr.length) { + var _line = diffstr[i]; + + if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + break; + } else if (/^@@/.test(_line)) { + index.hunks.push(parseHunk()); + } else if (_line && options.strict) { + // Ignore unexpected content unless in strict mode + throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); + } else { + i++; + } + } + } // Parses the --- and +++ headers, if none are found, no lines + // are consumed. + + + function parseFileHeader(index) { + var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); + + if (fileHeader) { + var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; + var data = fileHeader[2].split('\t', 2); + var fileName = data[0].replace(/\\\\/g, '\\'); + + if (/^".*"$/.test(fileName)) { + fileName = fileName.substr(1, fileName.length - 2); + } + + index[keyPrefix + 'FileName'] = fileName; + index[keyPrefix + 'Header'] = (data[1] || '').trim(); + i++; + } + } // Parses a hunk + // This assumes that we are at the start of a hunk. + + + function parseHunk() { + var chunkHeaderIndex = i, + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + var hunk = { + oldStart: +chunkHeader[1], + oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2], + newStart: +chunkHeader[3], + newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4], + lines: [], + linedelimiters: [] + }; // Unified Diff Format quirk: If the chunk size is 0, + // the first number is one lower than one would expect. + // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 + + if (hunk.oldLines === 0) { + hunk.oldStart += 1; + } + + if (hunk.newLines === 0) { + hunk.newStart += 1; + } + + var addCount = 0, + removeCount = 0; + + for (; i < diffstr.length; i++) { + // Lines starting with '---' could be mistaken for the "remove line" operation + // But they could be the header for the next file. Therefore prune such cases out. + if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { + break; + } + + var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; + + if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { + hunk.lines.push(diffstr[i]); + hunk.linedelimiters.push(delimiters[i] || '\n'); + + if (operation === '+') { + addCount++; + } else if (operation === '-') { + removeCount++; + } else if (operation === ' ') { + addCount++; + removeCount++; + } + } else { + break; + } + } // Handle the empty block count case + + + if (!addCount && hunk.newLines === 1) { + hunk.newLines = 0; + } + + if (!removeCount && hunk.oldLines === 1) { + hunk.oldLines = 0; + } // Perform optional sanity checking + + + if (options.strict) { + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + } + + return hunk; + } + + while (i < diffstr.length) { + parseIndex(); + } + + return list; + } + + // Iterator that traverses in the range of [min, max], stepping + // by distance from a given start position. I.e. for [0, 4], with + // start of 2, this will iterate 2, 3, 1, 4, 0. + function distanceIterator (start, minLine, maxLine) { + var wantForward = true, + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; + return function iterator() { + if (wantForward && !forwardExhausted) { + if (backwardExhausted) { + localOffset++; + } else { + wantForward = false; + } // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) + + + if (start + localOffset <= maxLine) { + return localOffset; + } + + forwardExhausted = true; + } + + if (!backwardExhausted) { + if (!forwardExhausted) { + wantForward = true; + } // Check if trying to fit before text beginning, and if not, check it fits + // before offset location + + + if (minLine <= start - localOffset) { + return -localOffset++; + } + + backwardExhausted = true; + return iterator(); + } // We tried to fit hunk before text beginning and beyond text length, then + // hunk can't fit on the text. Return undefined + + }; + } + + function applyPatch(source, uniDiff) { + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + if (typeof uniDiff === 'string') { + uniDiff = parsePatch(uniDiff); + } + + if (Array.isArray(uniDiff)) { + if (uniDiff.length > 1) { + throw new Error('applyPatch only works with a single input.'); + } + + uniDiff = uniDiff[0]; + } // Apply the diff to the input + + + var lines = source.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) { + return line === patchContent; + }, + errorCount = 0, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0, + offset = 0, + removeEOFNL, + addEOFNL; + /** + * Checks if the hunk exactly fits on the provided location + */ + + + function hunkFits(hunk, toPos) { + for (var j = 0; j < hunk.lines.length; j++) { + var line = hunk.lines[j], + operation = line.length > 0 ? line[0] : ' ', + content = line.length > 0 ? line.substr(1) : line; + + if (operation === ' ' || operation === '-') { + // Context sanity check + if (!compareLine(toPos + 1, lines[toPos], operation, content)) { + errorCount++; + + if (errorCount > fuzzFactor) { + return false; + } + } + + toPos++; + } + } + + return true; + } // Search best fit offsets for each hunk based on the previous ones + + + for (var i = 0; i < hunks.length; i++) { + var hunk = hunks[i], + maxLine = lines.length - hunk.oldLines, + localOffset = 0, + toPos = offset + hunk.oldStart - 1; + var iterator = distanceIterator(toPos, minLine, maxLine); + + for (; localOffset !== undefined; localOffset = iterator()) { + if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; + break; + } + } + + if (localOffset === undefined) { + return false; + } // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + + + minLine = hunk.offset + hunk.oldStart + hunk.oldLines; + } // Apply patch hunks + + + var diffOffset = 0; + + for (var _i = 0; _i < hunks.length; _i++) { + var _hunk = hunks[_i], + _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; + + diffOffset += _hunk.newLines - _hunk.oldLines; + + for (var j = 0; j < _hunk.lines.length; j++) { + var line = _hunk.lines[j], + operation = line.length > 0 ? line[0] : ' ', + content = line.length > 0 ? line.substr(1) : line, + delimiter = _hunk.linedelimiters && _hunk.linedelimiters[j] || '\n'; + + if (operation === ' ') { + _toPos++; + } else if (operation === '-') { + lines.splice(_toPos, 1); + delimiters.splice(_toPos, 1); + /* istanbul ignore else */ + } else if (operation === '+') { + lines.splice(_toPos, 0, content); + delimiters.splice(_toPos, 0, delimiter); + _toPos++; + } else if (operation === '\\') { + var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; + + if (previousOperation === '+') { + removeEOFNL = true; + } else if (previousOperation === '-') { + addEOFNL = true; + } + } + } + } // Handle EOFNL insertion/removal + + + if (removeEOFNL) { + while (!lines[lines.length - 1]) { + lines.pop(); + delimiters.pop(); + } + } else if (addEOFNL) { + lines.push(''); + delimiters.push('\n'); + } + + for (var _k = 0; _k < lines.length - 1; _k++) { + lines[_k] = lines[_k] + delimiters[_k]; + } + + return lines.join(''); + } // Wrapper that supports multiple file patches via callbacks. + + function applyPatches(uniDiff, options) { + if (typeof uniDiff === 'string') { + uniDiff = parsePatch(uniDiff); + } + + var currentIndex = 0; + + function processIndex() { + var index = uniDiff[currentIndex++]; + + if (!index) { + return options.complete(); + } + + options.loadFile(index, function (err, data) { + if (err) { + return options.complete(err); + } + + var updatedContent = applyPatch(data, index, options); + options.patched(index, updatedContent, function (err) { + if (err) { + return options.complete(err); + } + + processIndex(); + }); + }); + } + + processIndex(); + } + + function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + if (!options) { + options = {}; + } + + if (typeof options.context === 'undefined') { + options.context = 4; + } + + var diff = diffLines(oldStr, newStr, options); + + if (!diff) { + return; + } + + diff.push({ + value: '', + lines: [] + }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + + var hunks = []; + var oldRangeStart = 0, + newRangeStart = 0, + curRange = [], + oldLine = 1, + newLine = 1; + + var _loop = function _loop(i) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + var _curRange; + + // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } // Output our changes + + + (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); // Track the updated file position + + + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + var _curRange2; + + // Overlapping + (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines))); + } else { + var _curRange3; + + // end the range and output + var contextSize = Math.min(lines.length, options.context); + + (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize)))); + + var hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + + if (i >= diff.length - 2 && lines.length <= options.context) { + // EOF is inside this hunk + var oldEOFNewline = /\n$/.test(oldStr); + var newEOFNewline = /\n$/.test(newStr); + var noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines; + + if (!oldEOFNewline && noNlBeforeAdds && oldStr.length > 0) { + // special case: old has no eol and no trailing context; no-nl can end up before adds + // however, if the old file is empty, do not output the no-nl line + curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); + } + + if (!oldEOFNewline && !noNlBeforeAdds || !newEOFNewline) { + curRange.push('\\ No newline at end of file'); + } + } + + hunks.push(hunk); + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; + } + } + + oldLine += lines.length; + newLine += lines.length; + } + }; + + for (var i = 0; i < diff.length; i++) { + _loop(i); + } + + return { + oldFileName: oldFileName, + newFileName: newFileName, + oldHeader: oldHeader, + newHeader: newHeader, + hunks: hunks + }; + } + function formatPatch(diff) { + if (Array.isArray(diff)) { + return diff.map(formatPatch).join('\n'); + } + + var ret = []; + + if (diff.oldFileName == diff.newFileName) { + ret.push('Index: ' + diff.oldFileName); + } + + ret.push('==================================================================='); + ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); + ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); + + for (var i = 0; i < diff.hunks.length; i++) { + var hunk = diff.hunks[i]; // Unified Diff Format quirk: If the chunk size is 0, + // the first number is one lower than one would expect. + // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 + + if (hunk.oldLines === 0) { + hunk.oldStart -= 1; + } + + if (hunk.newLines === 0) { + hunk.newStart -= 1; + } + + ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); + ret.push.apply(ret, hunk.lines); + } + + return ret.join('\n') + '\n'; + } + function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + return formatPatch(structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)); + } + function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { + return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); + } + + function arrayEqual(a, b) { + if (a.length !== b.length) { + return false; + } + + return arrayStartsWith(a, b); + } + function arrayStartsWith(array, start) { + if (start.length > array.length) { + return false; + } + + for (var i = 0; i < start.length; i++) { + if (start[i] !== array[i]) { + return false; + } + } + + return true; + } + + function calcLineCount(hunk) { + var _calcOldNewLineCount = calcOldNewLineCount(hunk.lines), + oldLines = _calcOldNewLineCount.oldLines, + newLines = _calcOldNewLineCount.newLines; + + if (oldLines !== undefined) { + hunk.oldLines = oldLines; + } else { + delete hunk.oldLines; + } + + if (newLines !== undefined) { + hunk.newLines = newLines; + } else { + delete hunk.newLines; + } + } + function merge(mine, theirs, base) { + mine = loadPatch(mine, base); + theirs = loadPatch(theirs, base); + var ret = {}; // For index we just let it pass through as it doesn't have any necessary meaning. + // Leaving sanity checks on this to the API consumer that may know more about the + // meaning in their own context. + + if (mine.index || theirs.index) { + ret.index = mine.index || theirs.index; + } + + if (mine.newFileName || theirs.newFileName) { + if (!fileNameChanged(mine)) { + // No header or no change in ours, use theirs (and ours if theirs does not exist) + ret.oldFileName = theirs.oldFileName || mine.oldFileName; + ret.newFileName = theirs.newFileName || mine.newFileName; + ret.oldHeader = theirs.oldHeader || mine.oldHeader; + ret.newHeader = theirs.newHeader || mine.newHeader; + } else if (!fileNameChanged(theirs)) { + // No header or no change in theirs, use ours + ret.oldFileName = mine.oldFileName; + ret.newFileName = mine.newFileName; + ret.oldHeader = mine.oldHeader; + ret.newHeader = mine.newHeader; + } else { + // Both changed... figure it out + ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName); + ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName); + ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader); + ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); + } + } + + ret.hunks = []; + var mineIndex = 0, + theirsIndex = 0, + mineOffset = 0, + theirsOffset = 0; + + while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { + var mineCurrent = mine.hunks[mineIndex] || { + oldStart: Infinity + }, + theirsCurrent = theirs.hunks[theirsIndex] || { + oldStart: Infinity + }; + + if (hunkBefore(mineCurrent, theirsCurrent)) { + // This patch does not overlap with any of the others, yay. + ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); + mineIndex++; + theirsOffset += mineCurrent.newLines - mineCurrent.oldLines; + } else if (hunkBefore(theirsCurrent, mineCurrent)) { + // This patch does not overlap with any of the others, yay. + ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset)); + theirsIndex++; + mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines; + } else { + // Overlap, merge as best we can + var mergedHunk = { + oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart), + oldLines: 0, + newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset), + newLines: 0, + lines: [] + }; + mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines); + theirsIndex++; + mineIndex++; + ret.hunks.push(mergedHunk); + } + } + + return ret; + } + + function loadPatch(param, base) { + if (typeof param === 'string') { + if (/^@@/m.test(param) || /^Index:/m.test(param)) { + return parsePatch(param)[0]; + } + + if (!base) { + throw new Error('Must provide a base reference or pass in a patch'); + } + + return structuredPatch(undefined, undefined, base, param); + } + + return param; + } + + function fileNameChanged(patch) { + return patch.newFileName && patch.newFileName !== patch.oldFileName; + } + + function selectField(index, mine, theirs) { + if (mine === theirs) { + return mine; + } else { + index.conflict = true; + return { + mine: mine, + theirs: theirs + }; + } + } + + function hunkBefore(test, check) { + return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; + } + + function cloneHunk(hunk, offset) { + return { + oldStart: hunk.oldStart, + oldLines: hunk.oldLines, + newStart: hunk.newStart + offset, + newLines: hunk.newLines, + lines: hunk.lines + }; + } + + function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { + // This will generally result in a conflicted hunk, but there are cases where the context + // is the only overlap where we can successfully merge the content here. + var mine = { + offset: mineOffset, + lines: mineLines, + index: 0 + }, + their = { + offset: theirOffset, + lines: theirLines, + index: 0 + }; // Handle any leading content + + insertLeading(hunk, mine, their); + insertLeading(hunk, their, mine); // Now in the overlap content. Scan through and select the best changes from each. + + while (mine.index < mine.lines.length && their.index < their.lines.length) { + var mineCurrent = mine.lines[mine.index], + theirCurrent = their.lines[their.index]; + + if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { + // Both modified ... + mutualChange(hunk, mine, their); + } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { + var _hunk$lines; + + // Mine inserted + (_hunk$lines = hunk.lines).push.apply(_hunk$lines, _toConsumableArray(collectChange(mine))); + } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { + var _hunk$lines2; + + // Theirs inserted + (_hunk$lines2 = hunk.lines).push.apply(_hunk$lines2, _toConsumableArray(collectChange(their))); + } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { + // Mine removed or edited + removal(hunk, mine, their); + } else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') { + // Their removed or edited + removal(hunk, their, mine, true); + } else if (mineCurrent === theirCurrent) { + // Context identity + hunk.lines.push(mineCurrent); + mine.index++; + their.index++; + } else { + // Context mismatch + conflict(hunk, collectChange(mine), collectChange(their)); + } + } // Now push anything that may be remaining + + + insertTrailing(hunk, mine); + insertTrailing(hunk, their); + calcLineCount(hunk); + } + + function mutualChange(hunk, mine, their) { + var myChanges = collectChange(mine), + theirChanges = collectChange(their); + + if (allRemoves(myChanges) && allRemoves(theirChanges)) { + // Special case for remove changes that are supersets of one another + if (arrayStartsWith(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { + var _hunk$lines3; + + (_hunk$lines3 = hunk.lines).push.apply(_hunk$lines3, _toConsumableArray(myChanges)); + + return; + } else if (arrayStartsWith(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { + var _hunk$lines4; + + (_hunk$lines4 = hunk.lines).push.apply(_hunk$lines4, _toConsumableArray(theirChanges)); + + return; + } + } else if (arrayEqual(myChanges, theirChanges)) { + var _hunk$lines5; + + (_hunk$lines5 = hunk.lines).push.apply(_hunk$lines5, _toConsumableArray(myChanges)); + + return; + } + + conflict(hunk, myChanges, theirChanges); + } + + function removal(hunk, mine, their, swap) { + var myChanges = collectChange(mine), + theirChanges = collectContext(their, myChanges); + + if (theirChanges.merged) { + var _hunk$lines6; + + (_hunk$lines6 = hunk.lines).push.apply(_hunk$lines6, _toConsumableArray(theirChanges.merged)); + } else { + conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); + } + } + + function conflict(hunk, mine, their) { + hunk.conflict = true; + hunk.lines.push({ + conflict: true, + mine: mine, + theirs: their + }); + } + + function insertLeading(hunk, insert, their) { + while (insert.offset < their.offset && insert.index < insert.lines.length) { + var line = insert.lines[insert.index++]; + hunk.lines.push(line); + insert.offset++; + } + } + + function insertTrailing(hunk, insert) { + while (insert.index < insert.lines.length) { + var line = insert.lines[insert.index++]; + hunk.lines.push(line); + } + } + + function collectChange(state) { + var ret = [], + operation = state.lines[state.index][0]; + + while (state.index < state.lines.length) { + var line = state.lines[state.index]; // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. + + if (operation === '-' && line[0] === '+') { + operation = '+'; + } + + if (operation === line[0]) { + ret.push(line); + state.index++; + } else { + break; + } + } + + return ret; + } + + function collectContext(state, matchChanges) { + var changes = [], + merged = [], + matchIndex = 0, + contextChanges = false, + conflicted = false; + + while (matchIndex < matchChanges.length && state.index < state.lines.length) { + var change = state.lines[state.index], + match = matchChanges[matchIndex]; // Once we've hit our add, then we are done + + if (match[0] === '+') { + break; + } + + contextChanges = contextChanges || change[0] !== ' '; + merged.push(match); + matchIndex++; // Consume any additions in the other block as a conflict to attempt + // to pull in the remaining context after this + + if (change[0] === '+') { + conflicted = true; + + while (change[0] === '+') { + changes.push(change); + change = state.lines[++state.index]; + } + } + + if (match.substr(1) === change.substr(1)) { + changes.push(change); + state.index++; + } else { + conflicted = true; + } + } + + if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { + conflicted = true; + } + + if (conflicted) { + return changes; + } + + while (matchIndex < matchChanges.length) { + merged.push(matchChanges[matchIndex++]); + } + + return { + merged: merged, + changes: changes + }; + } + + function allRemoves(changes) { + return changes.reduce(function (prev, change) { + return prev && change[0] === '-'; + }, true); + } + + function skipRemoveSuperset(state, removeChanges, delta) { + for (var i = 0; i < delta; i++) { + var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); + + if (state.lines[state.index + i] !== ' ' + changeContent) { + return false; + } + } + + state.index += delta; + return true; + } + + function calcOldNewLineCount(lines) { + var oldLines = 0; + var newLines = 0; + lines.forEach(function (line) { + if (typeof line !== 'string') { + var myCount = calcOldNewLineCount(line.mine); + var theirCount = calcOldNewLineCount(line.theirs); + + if (oldLines !== undefined) { + if (myCount.oldLines === theirCount.oldLines) { + oldLines += myCount.oldLines; + } else { + oldLines = undefined; + } + } + + if (newLines !== undefined) { + if (myCount.newLines === theirCount.newLines) { + newLines += myCount.newLines; + } else { + newLines = undefined; + } + } + } else { + if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { + newLines++; + } + + if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { + oldLines++; + } + } + }); + return { + oldLines: oldLines, + newLines: newLines + }; + } + + function reversePatch(structuredPatch) { + if (Array.isArray(structuredPatch)) { + return structuredPatch.map(reversePatch).reverse(); + } + + return _objectSpread2(_objectSpread2({}, structuredPatch), {}, { + oldFileName: structuredPatch.newFileName, + oldHeader: structuredPatch.newHeader, + newFileName: structuredPatch.oldFileName, + newHeader: structuredPatch.oldHeader, + hunks: structuredPatch.hunks.map(function (hunk) { + return { + oldLines: hunk.newLines, + oldStart: hunk.newStart, + newLines: hunk.oldLines, + newStart: hunk.oldStart, + linedelimiters: hunk.linedelimiters, + lines: hunk.lines.map(function (l) { + if (l.startsWith('-')) { + return "+".concat(l.slice(1)); + } + + if (l.startsWith('+')) { + return "-".concat(l.slice(1)); + } + + return l; + }) + }; + }) + }); + } + + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + function convertChangesToDMP(changes) { + var ret = [], + change, + operation; + + for (var i = 0; i < changes.length; i++) { + change = changes[i]; + + if (change.added) { + operation = 1; + } else if (change.removed) { + operation = -1; + } else { + operation = 0; + } + + ret.push([operation, change.value]); + } + + return ret; + } + + function convertChangesToXML(changes) { + var ret = []; + + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + + return ret.join(''); + } + + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + return n; + } + + exports.Diff = Diff; + exports.applyPatch = applyPatch; + exports.applyPatches = applyPatches; + exports.canonicalize = canonicalize; + exports.convertChangesToDMP = convertChangesToDMP; + exports.convertChangesToXML = convertChangesToXML; + exports.createPatch = createPatch; + exports.createTwoFilesPatch = createTwoFilesPatch; + exports.diffArrays = diffArrays; + exports.diffChars = diffChars; + exports.diffCss = diffCss; + exports.diffJson = diffJson; + exports.diffLines = diffLines; + exports.diffSentences = diffSentences; + exports.diffTrimmedLines = diffTrimmedLines; + exports.diffWords = diffWords; + exports.diffWordsWithSpace = diffWordsWithSpace; + exports.formatPatch = formatPatch; + exports.merge = merge; + exports.parsePatch = parsePatch; + exports.reversePatch = reversePatch; + exports.structuredPatch = structuredPatch; + + Object.defineProperty(exports, '__esModule', { value: true }); + +}))); diff --git a/amd/src/editformhelper.js b/amd/src/editformhelper.js index 7238d62..1f6e000 100644 --- a/amd/src/editformhelper.js +++ b/amd/src/editformhelper.js @@ -53,11 +53,8 @@ export const init = () => { strings.sampleanswerempty = s[i++]; }); - document.querySelector(Selectors.fields.sampleanswerbtn).addEventListener('click', e => { - - const form = e.target.closest('form'); const sampleanswer = form.querySelector(Selectors.fields.sampleanswer); const sampleanswereval = form.querySelector(Selectors.fields.sampleanswereval); diff --git a/amd/src/getSpellcheck.js b/amd/src/getSpellcheck.js new file mode 100644 index 0000000..0e828f6 --- /dev/null +++ b/amd/src/getSpellcheck.js @@ -0,0 +1,97 @@ +// 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 . + +/** + * Generates the spellcheck diff view. + * + * @module qtype_aitext/getSpellcheck + * @copyright 2024, ISB Bayern + * @author Dr. Peter Mayer + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import * as Diff from "qtype_aitext/diff"; +import ModalForm from 'core_form/modalform'; +import {get_string as getString} from 'core/str'; + +/** + * Init + */ +export const init = () => { + renderDiff(); + + if (!document.getElementById('aitext_spellcheckedit')) { + return; + } + document.getElementById('aitext_spellcheckedit').addEventListener("click", showModalForm); +}; + +/** + * Render the spellcheckdiff + */ +export const renderDiff = () => { + var studentanswer = document.getElementById('aitext_readonly_area').innerHTML, + spellcheck = document.getElementById('aitext_readonly_area').dataset.spellcheck, + span = null; + + var diff = Diff.diffChars(studentanswer, spellcheck); + var fragment = document.createElement('div'); + + let fullspellcheck = ''; + + diff.forEach(function (part) { + var cls = part.added ? 'qtype_aitext_spellcheck_new' : + part.removed ? 'qtype_aitext_spellcheck_wrong' : ''; + if (part.added || part.removed) { + span = document.createElement('span'); + span.classList = cls; + span.appendChild(document.createTextNode(part.value)); + fullspellcheck += span.outerHTML; + } else { + fullspellcheck += part.value; + } + }); + + fragment.innerHTML = fullspellcheck; + document.getElementById('aitext_readonly_area').replaceChildren(fragment); +}; + +/** + * Show the dynamic spellcheck form. + */ +export const showModalForm = () => { + const attemptstepid = document.getElementById('aitext_readonly_area').dataset.spellcheckattemptstepid; + const answerstepid = document.getElementById('aitext_readonly_area').dataset.spellcheckattemptstepanswerid; + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const modalForm = new ModalForm({ + formClass: "qtype_aitext\\form\\edit_spellchek", + args: { + attemptstepid: attemptstepid, + answerstepid: answerstepid, + cmid: urlParams.get('cmid'), + }, + modalConfig: { title: getString('spellcheckedit', 'qtype_aitext') }, + }); + modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, reloadpage); + modalForm.show(); +}; + +/** + * Reload the page. This is not nice, but easy :-) + */ +const reloadpage = () => { + location.reload(); +} diff --git a/classes/form/edit_spellchek.php b/classes/form/edit_spellchek.php new file mode 100644 index 0000000..f11f0cd --- /dev/null +++ b/classes/form/edit_spellchek.php @@ -0,0 +1,148 @@ +. + +namespace qtype_aitext\form; + +use context; +use context_module; +use core_form\dynamic_form; +use moodle_url; + +/** + * From for editing a card. + * + * @package qtype_aitext + * @copyright 2024 ISB Bayern + * @author Dr. Peter Mayer + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class edit_spellchek extends dynamic_form { + + /** + * Define the form + */ + public function definition() { + + $mform = &$this->_form; + + $mform->addElement('hidden', 'cmid'); + $mform->setType('cmid', PARAM_INT); + + $mform->addElement('hidden', 'attemptstepid'); + $mform->setType('attemptstepid', PARAM_INT); + + $mform->addElement('hidden', 'answerstepid'); + $mform->setType('answerstepid', PARAM_INT); + + + $mform->addElement('static', 'student_answer', get_string('spellcheck_student_anser_desc', 'qtype_aitext')); + $mform->setType('student_answer', PARAM_RAW); + + + $mform->addElement( + 'editor', + 'spellcheck_editor', + get_string('spellcheck_editor_desc', 'qtype_aitext'), + null, + ['maxfiles' => -1] + ); + $mform->setType('spellcheck_editor', PARAM_RAW); + } + + /** + * Returns context where this form is used + * + * @return context + */ + protected function get_context_for_dynamic_submission(): context { + $cmid = $this->optional_param('cmid', null, PARAM_INT); + list($course, $cm) = get_course_and_cm_from_cmid($cmid); + return context_module::instance($cm->id); + } + + /** + * Checks if current user has access to this form, otherwise throws exception + * + * @throws \moodle_exception User does not have capability to access the form + */ + protected function check_access_for_dynamic_submission(): void { + $context = $this->get_context_for_dynamic_submission(); + if ( + !has_capability('mod/quiz:grade', $context) && + !has_capability('mod/quiz:regrade', $context) + ) { + throw new \moodle_exception('nocapabilitytousethisservice'); + } + } + + /** + * Process the form submission, used if form was submitted via AJAX + * + * @return object Returns the updated object. + */ + public function process_dynamic_submission(): object { + global $DB; + $formdata = $this->get_data(); + + $conditions = [ + 'attemptstepid' => $formdata->attemptstepid, + 'name' => '-spellcheckresponse', + ]; + + $record = $DB->get_record('question_attempt_step_data', $conditions, '*', MUST_EXIST); + + $record->value = $formdata->spellcheck_editor['text']; + $DB->update_record('question_attempt_step_data', $record); + + return $record; + } + + /** + * Load in existing data as form defaults + */ + public function set_data_for_dynamic_submission(): void { + global $DB; + + $conditions = ['attemptstepid' => $this->optional_param('attemptstepid', 0, PARAM_INT), 'name' => '-spellcheckresponse']; + $spellcheckrecord = $DB->get_record('question_attempt_step_data', $conditions); + + $conditions = ['attemptstepid' => $this->optional_param('answerstepid', 0, PARAM_INT), 'name' => 'answer']; + $answerrecord = $DB->get_record('question_attempt_step_data', $conditions); + + $draftitemid = file_get_submitted_draft_itemid('attachments'); + + $this->set_data((object)[ + 'test' => $spellcheckrecord->value, + 'spellcheck_editor' => ['text' => $spellcheckrecord->value, 'format' => FORMAT_HTML, 'itemid' => $draftitemid], + 'attemptstepid' => $this->optional_param('attemptstepid', 0, PARAM_INT), + 'cmid' => $this->optional_param('cmid', 0, PARAM_INT), + 'student_answer' => $answerrecord->value, + ]); + } + + /** + * Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX + * + * @return moodle_url + */ + protected function get_page_url_for_dynamic_submission(): moodle_url { + $params = [ + 'cmid' => $this->optional_param('cmid', null, PARAM_INT), + 'attempt' => $this->optional_param('attempt', null, PARAM_INT), + ]; + return new moodle_url('/mod/quiz/review.php', $params); + } +} diff --git a/db/install.xml b/db/install.xml old mode 100755 new mode 100644 index 9b43000..50f5a99 --- a/db/install.xml +++ b/db/install.xml @@ -1,5 +1,5 @@ - @@ -21,7 +21,8 @@ - + + diff --git a/db/upgrade.php b/db/upgrade.php index 6468b74..f753863 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -62,5 +62,21 @@ function xmldb_qtype_aitext_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2024051100, 'qtype', 'aitext'); } + if ($oldversion < 2024051101) { + + // Define field spellcheck to be added to qtype_aitext. + $table = new xmldb_table('qtype_aitext'); + $field = new xmldb_field('spellcheck', XMLDB_TYPE_INTEGER, '1', null, null, null, '0', 'model'); + + // Conditionally launch add field spellcheck. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Aitext savepoint reached. + upgrade_plugin_savepoint(true, 2024051101, 'qtype', 'aitext'); + } + + return true; } diff --git a/edit_aitext_form.php b/edit_aitext_form.php index 65214c2..95d1273 100755 --- a/edit_aitext_form.php +++ b/edit_aitext_form.php @@ -46,6 +46,11 @@ protected function definition_inner($mform) { $mform->addElement('editor', 'questiontext', get_string('questiontext', 'mod_quiz'), ['maxlen' => 50, 'rows' => 8, 'size' => 30], $this->editoroptions); + // Spelling correction. + $mform->addElement('checkbox', 'spellcheck', get_string('automatic_spellcheck', 'qtype_aitext')); + $mform->addRule('spellcheck', get_string('aipromptmissing', 'qtype_aitext'), 'required'); + + // Ai prompt. $mform->addElement('textarea', 'aiprompt', get_string('aiprompt', 'qtype_aitext'), ['maxlen' => 50, 'rows' => 5, 'size' => 30]); $mform->setType('aiprompt', PARAM_RAW); @@ -53,6 +58,7 @@ protected function definition_inner($mform) { $mform->addHelpButton('aiprompt', 'aiprompt', 'qtype_aitext'); $mform->addRule('aiprompt', get_string('aipromptmissing', 'qtype_aitext'), 'required'); + // Markscheme. $mform->addElement('textarea', 'markscheme', get_string('markscheme', 'qtype_aitext'), ['maxlen' => 50, 'rows' => 6, 'size' => 30]); $mform->setType('markscheme', PARAM_RAW); @@ -67,6 +73,7 @@ protected function definition_inner($mform) { $mform->addElement('hidden', 'model', $models[0]); } $mform->setType('model', PARAM_RAW); + // The question_edit_form that this class extends expects a general feedback field. $mform->addElement('html', '