Index: lams_central/web/css/codemirror_simplescrollbars.css
===================================================================
diff -u
--- lams_central/web/css/codemirror_simplescrollbars.css (revision 0)
+++ lams_central/web/css/codemirror_simplescrollbars.css (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -0,0 +1,66 @@
+.CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
+ position: absolute;
+ background: #ccc;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ border: 1px solid #bbb;
+ border-radius: 2px;
+}
+
+.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
+ position: absolute;
+ z-index: 6;
+ background: #eee;
+}
+
+.CodeMirror-simplescroll-horizontal {
+ bottom: 0; left: 0;
+ height: 8px;
+}
+.CodeMirror-simplescroll-horizontal div {
+ bottom: 0;
+ height: 100%;
+}
+
+.CodeMirror-simplescroll-vertical {
+ right: 0; top: 0;
+ width: 8px;
+}
+.CodeMirror-simplescroll-vertical div {
+ right: 0;
+ width: 100%;
+}
+
+
+.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
+ display: none;
+}
+
+.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
+ position: absolute;
+ background: #bcd;
+ border-radius: 3px;
+}
+
+.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
+ position: absolute;
+ z-index: 6;
+}
+
+.CodeMirror-overlayscroll-horizontal {
+ bottom: 0; left: 0;
+ height: 6px;
+}
+.CodeMirror-overlayscroll-horizontal div {
+ bottom: 0;
+ height: 100%;
+}
+
+.CodeMirror-overlayscroll-vertical {
+ right: 0; top: 0;
+ width: 6px;
+}
+.CodeMirror-overlayscroll-vertical div {
+ right: 0;
+ width: 100%;
+}
Index: lams_central/web/includes/javascript/codemirror/addon/comment/continuecomment.js
===================================================================
diff -u
--- lams_central/web/includes/javascript/codemirror/addon/comment/continuecomment.js (revision 0)
+++ lams_central/web/includes/javascript/codemirror/addon/comment/continuecomment.js (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -0,0 +1,114 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var nonspace = /\S/g;
+ var repeat = String.prototype.repeat || function (n) { return Array(n + 1).join(this); };
+ function continueComment(cm) {
+ if (cm.getOption("disableInput")) return CodeMirror.Pass;
+ var ranges = cm.listSelections(), mode, inserts = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var pos = ranges[i].head
+ if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass;
+ var modeHere = cm.getModeAt(pos)
+ if (!mode) mode = modeHere;
+ else if (mode != modeHere) return CodeMirror.Pass;
+
+ var insert = null, line, found;
+ var blockStart = mode.blockCommentStart, lineCmt = mode.lineComment;
+ if (blockStart && mode.blockCommentContinue) {
+ line = cm.getLine(pos.line);
+ var end = line.lastIndexOf(mode.blockCommentEnd, pos.ch - mode.blockCommentEnd.length);
+ // 1. if this block comment ended
+ // 2. if this is actually inside a line comment
+ if (end != -1 && end == pos.ch - mode.blockCommentEnd.length ||
+ lineCmt && (found = line.lastIndexOf(lineCmt, pos.ch - 1)) > -1 &&
+ /\bcomment\b/.test(cm.getTokenTypeAt({line: pos.line, ch: found + 1}))) {
+ // ...then don't continue it
+ } else if (pos.ch >= blockStart.length &&
+ (found = line.lastIndexOf(blockStart, pos.ch - blockStart.length)) > -1 &&
+ found > end) {
+ // reuse the existing leading spaces/tabs/mixed
+ // or build the correct indent using CM's tab/indent options
+ if (nonspaceAfter(0, line) >= found) {
+ insert = line.slice(0, found);
+ } else {
+ var tabSize = cm.options.tabSize, numTabs;
+ found = CodeMirror.countColumn(line, found, tabSize);
+ insert = !cm.options.indentWithTabs ? repeat.call(" ", found) :
+ repeat.call("\t", (numTabs = Math.floor(found / tabSize))) +
+ repeat.call(" ", found - tabSize * numTabs);
+ }
+ } else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 &&
+ found <= pos.ch &&
+ found <= nonspaceAfter(0, line)) {
+ insert = line.slice(0, found);
+ }
+ if (insert != null) insert += mode.blockCommentContinue
+ }
+ if (insert == null && lineCmt && continueLineCommentEnabled(cm)) {
+ if (line == null) line = cm.getLine(pos.line);
+ found = line.indexOf(lineCmt);
+ // cursor at pos 0, line comment also at pos 0 => shift it down, don't continue
+ if (!pos.ch && !found) insert = "";
+ // continue only if the line starts with an optional space + line comment
+ else if (found > -1 && nonspaceAfter(0, line) >= found) {
+ // don't continue if there's only space(s) after cursor or the end of the line
+ insert = nonspaceAfter(pos.ch, line) > -1;
+ // but always continue if the next line starts with a line comment too
+ if (!insert) {
+ var next = cm.getLine(pos.line + 1) || '',
+ nextFound = next.indexOf(lineCmt);
+ insert = nextFound > -1 && nonspaceAfter(0, next) >= nextFound || null;
+ }
+ if (insert) {
+ insert = line.slice(0, found) + lineCmt +
+ line.slice(found + lineCmt.length).match(/^\s*/)[0];
+ }
+ }
+ }
+ if (insert == null) return CodeMirror.Pass;
+ inserts[i] = "\n" + insert;
+ }
+
+ cm.operation(function() {
+ for (var i = ranges.length - 1; i >= 0; i--)
+ cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert");
+ });
+ }
+
+ function nonspaceAfter(ch, str) {
+ nonspace.lastIndex = ch;
+ var m = nonspace.exec(str);
+ return m ? m.index : -1;
+ }
+
+ function continueLineCommentEnabled(cm) {
+ var opt = cm.getOption("continueComments");
+ if (opt && typeof opt == "object")
+ return opt.continueLineComment !== false;
+ return true;
+ }
+
+ CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
+ if (prev && prev != CodeMirror.Init)
+ cm.removeKeyMap("continueComment");
+ if (val) {
+ var key = "Enter";
+ if (typeof val == "string")
+ key = val;
+ else if (typeof val == "object" && val.key)
+ key = val.key;
+ var map = {name: "continueComment"};
+ map[key] = continueComment;
+ cm.addKeyMap(map);
+ }
+ });
+});
Index: lams_central/web/includes/javascript/codemirror/addon/edit/closebrackets.js
===================================================================
diff -u
--- lams_central/web/includes/javascript/codemirror/addon/edit/closebrackets.js (revision 0)
+++ lams_central/web/includes/javascript/codemirror/addon/edit/closebrackets.js (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -0,0 +1,201 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var defaults = {
+ pairs: "()[]{}''\"\"",
+ closeBefore: ")]}'\":;>",
+ triples: "",
+ explode: "[]{}"
+ };
+
+ var Pos = CodeMirror.Pos;
+
+ CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.removeKeyMap(keyMap);
+ cm.state.closeBrackets = null;
+ }
+ if (val) {
+ ensureBound(getOption(val, "pairs"))
+ cm.state.closeBrackets = val;
+ cm.addKeyMap(keyMap);
+ }
+ });
+
+ function getOption(conf, name) {
+ if (name == "pairs" && typeof conf == "string") return conf;
+ if (typeof conf == "object" && conf[name] != null) return conf[name];
+ return defaults[name];
+ }
+
+ var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
+ function ensureBound(chars) {
+ for (var i = 0; i < chars.length; i++) {
+ var ch = chars.charAt(i), key = "'" + ch + "'"
+ if (!keyMap[key]) keyMap[key] = handler(ch)
+ }
+ }
+ ensureBound(defaults.pairs + "`")
+
+ function handler(ch) {
+ return function(cm) { return handleChar(cm, ch); };
+ }
+
+ function getConfig(cm) {
+ var deflt = cm.state.closeBrackets;
+ if (!deflt || deflt.override) return deflt;
+ var mode = cm.getModeAt(cm.getCursor());
+ return mode.closeBrackets || deflt;
+ }
+
+ function handleBackspace(cm) {
+ var conf = getConfig(cm);
+ if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var pairs = getOption(conf, "pairs");
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var around = charsAround(cm, ranges[i].head);
+ if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ }
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var cur = ranges[i].head;
+ cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
+ }
+ }
+
+ function handleEnter(cm) {
+ var conf = getConfig(cm);
+ var explode = conf && getOption(conf, "explode");
+ if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var around = charsAround(cm, ranges[i].head);
+ if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ }
+ cm.operation(function() {
+ var linesep = cm.lineSeparator() || "\n";
+ cm.replaceSelection(linesep + linesep, null);
+ moveSel(cm, -1)
+ ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var line = ranges[i].head.line;
+ cm.indentLine(line, null, true);
+ cm.indentLine(line + 1, null, true);
+ }
+ });
+ }
+
+ function moveSel(cm, dir) {
+ var newRanges = [], ranges = cm.listSelections(), primary = 0
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i]
+ if (range.head == cm.getCursor()) primary = i
+ var pos = range.head.ch || dir > 0 ? {line: range.head.line, ch: range.head.ch + dir} : {line: range.head.line - 1}
+ newRanges.push({anchor: pos, head: pos})
+ }
+ cm.setSelections(newRanges, primary)
+ }
+
+ function contractSelection(sel) {
+ var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
+ return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
+ head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
+ }
+
+ function handleChar(cm, ch) {
+ var conf = getConfig(cm);
+ if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var pairs = getOption(conf, "pairs");
+ var pos = pairs.indexOf(ch);
+ if (pos == -1) return CodeMirror.Pass;
+
+ var closeBefore = getOption(conf,"closeBefore");
+
+ var triples = getOption(conf, "triples");
+
+ var identical = pairs.charAt(pos + 1) == ch;
+ var ranges = cm.listSelections();
+ var opening = pos % 2 == 0;
+
+ var type;
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], cur = range.head, curType;
+ var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
+ if (opening && !range.empty()) {
+ curType = "surround";
+ } else if ((identical || !opening) && next == ch) {
+ if (identical && stringStartsAfter(cm, cur))
+ curType = "both";
+ else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
+ curType = "skipThree";
+ else
+ curType = "skip";
+ } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+ cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
+ if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
+ curType = "addFour";
+ } else if (identical) {
+ var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
+ if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
+ else return CodeMirror.Pass;
+ } else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
+ curType = "both";
+ } else {
+ return CodeMirror.Pass;
+ }
+ if (!type) type = curType;
+ else if (type != curType) return CodeMirror.Pass;
+ }
+
+ var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
+ var right = pos % 2 ? ch : pairs.charAt(pos + 1);
+ cm.operation(function() {
+ if (type == "skip") {
+ moveSel(cm, 1)
+ } else if (type == "skipThree") {
+ moveSel(cm, 3)
+ } else if (type == "surround") {
+ var sels = cm.getSelections();
+ for (var i = 0; i < sels.length; i++)
+ sels[i] = left + sels[i] + right;
+ cm.replaceSelections(sels, "around");
+ sels = cm.listSelections().slice();
+ for (var i = 0; i < sels.length; i++)
+ sels[i] = contractSelection(sels[i]);
+ cm.setSelections(sels);
+ } else if (type == "both") {
+ cm.replaceSelection(left + right, null);
+ cm.triggerElectric(left + right);
+ moveSel(cm, -1)
+ } else if (type == "addFour") {
+ cm.replaceSelection(left + left + left + left, "before");
+ moveSel(cm, 1)
+ }
+ });
+ }
+
+ function charsAround(cm, pos) {
+ var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+ Pos(pos.line, pos.ch + 1));
+ return str.length == 2 ? str : null;
+ }
+
+ function stringStartsAfter(cm, pos) {
+ var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
+ return /\bstring/.test(token.type) && token.start == pos.ch &&
+ (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
+ }
+});
Index: lams_central/web/includes/javascript/codemirror/addon/edit/matchbrackets.js
===================================================================
diff -u
--- lams_central/web/includes/javascript/codemirror/addon/edit/matchbrackets.js (revision 0)
+++ lams_central/web/includes/javascript/codemirror/addon/edit/matchbrackets.js (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -0,0 +1,160 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
+ (document.documentMode == null || document.documentMode < 8);
+
+ var Pos = CodeMirror.Pos;
+
+ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
+
+ function bracketRegex(config) {
+ return config && config.bracketRegex || /[(){}[\]]/
+ }
+
+ function findMatchingBracket(cm, where, config) {
+ var line = cm.getLineHandle(where.line), pos = where.ch - 1;
+ var afterCursor = config && config.afterCursor
+ if (afterCursor == null)
+ afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
+ var re = bracketRegex(config)
+
+ // A cursor is defined as between two characters, but in in vim command mode
+ // (i.e. not insert mode), the cursor is visually represented as a
+ // highlighted box on top of the 2nd character. Otherwise, we allow matches
+ // from before or after the cursor.
+ var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
+ re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
+ if (!match) return null;
+ var dir = match.charAt(1) == ">" ? 1 : -1;
+ if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
+ var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
+
+ var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config);
+ if (found == null) return null;
+ return {from: Pos(where.line, pos), to: found && found.pos,
+ match: found && found.ch == match.charAt(0), forward: dir > 0};
+ }
+
+ // bracketRegex is used to specify which type of bracket to scan
+ // should be a regexp, e.g. /[[\]]/
+ //
+ // Note: If "where" is on an open bracket, then this bracket is ignored.
+ //
+ // Returns false when no bracket was found, null when it reached
+ // maxScanLines and gave up
+ function scanForBracket(cm, where, dir, style, config) {
+ var maxScanLen = (config && config.maxScanLineLength) || 10000;
+ var maxScanLines = (config && config.maxScanLines) || 1000;
+
+ var stack = [];
+ var re = bracketRegex(config)
+ var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
+ : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
+ for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
+ var line = cm.getLine(lineNo);
+ if (!line) continue;
+ var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
+ if (line.length > maxScanLen) continue;
+ if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
+ for (; pos != end; pos += dir) {
+ var ch = line.charAt(pos);
+ if (re.test(ch) && (style === undefined ||
+ (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) {
+ var match = matching[ch];
+ if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
+ else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
+ else stack.pop();
+ }
+ }
+ }
+ return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
+ }
+
+ function matchBrackets(cm, autoclear, config) {
+ // Disable brace matching in long lines, since it'll cause hugely slow updates
+ var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000,
+ highlightNonMatching = config && config.highlightNonMatching;
+ var marks = [], ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
+ if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) {
+ var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+ marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
+ if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
+ marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
+ }
+ }
+
+ if (marks.length) {
+ // Kludge to work around the IE bug from issue #1193, where text
+ // input stops going to the textarea whenever this fires.
+ if (ie_lt8 && cm.state.focused) cm.focus();
+
+ var clear = function() {
+ cm.operation(function() {
+ for (var i = 0; i < marks.length; i++) marks[i].clear();
+ });
+ };
+ if (autoclear) setTimeout(clear, 800);
+ else return clear;
+ }
+ }
+
+ function doMatchBrackets(cm) {
+ cm.operation(function() {
+ if (cm.state.matchBrackets.currentlyHighlighted) {
+ cm.state.matchBrackets.currentlyHighlighted();
+ cm.state.matchBrackets.currentlyHighlighted = null;
+ }
+ cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+ });
+ }
+
+ function clearHighlighted(cm) {
+ if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
+ cm.state.matchBrackets.currentlyHighlighted();
+ cm.state.matchBrackets.currentlyHighlighted = null;
+ }
+ }
+
+ CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.off("cursorActivity", doMatchBrackets);
+ cm.off("focus", doMatchBrackets)
+ cm.off("blur", clearHighlighted)
+ clearHighlighted(cm);
+ }
+ if (val) {
+ cm.state.matchBrackets = typeof val == "object" ? val : {};
+ cm.on("cursorActivity", doMatchBrackets);
+ cm.on("focus", doMatchBrackets)
+ cm.on("blur", clearHighlighted)
+ }
+ });
+
+ CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+ CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
+ // Backwards-compatibility kludge
+ if (oldConfig || typeof config == "boolean") {
+ if (!oldConfig) {
+ config = config ? {strict: true} : null
+ } else {
+ oldConfig.strict = config
+ config = oldConfig
+ }
+ }
+ return findMatchingBracket(this, pos, config)
+ });
+ CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
+ return scanForBracket(this, pos, dir, style, config);
+ });
+});
Index: lams_central/web/includes/javascript/codemirror/addon/runmode/colorize.js
===================================================================
diff -u
--- lams_central/web/includes/javascript/codemirror/addon/runmode/colorize.js (revision 0)
+++ lams_central/web/includes/javascript/codemirror/addon/runmode/colorize.js (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -0,0 +1,40 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("./runmode"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "./runmode"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/;
+
+ function textContent(node, out) {
+ if (node.nodeType == 3) return out.push(node.nodeValue);
+ for (var ch = node.firstChild; ch; ch = ch.nextSibling) {
+ textContent(ch, out);
+ if (isBlock.test(node.nodeType)) out.push("\n");
+ }
+ }
+
+ CodeMirror.colorize = function(collection, defaultMode) {
+ if (!collection) collection = document.body.getElementsByTagName("pre");
+
+ for (var i = 0; i < collection.length; ++i) {
+ var node = collection[i];
+ var mode = node.getAttribute("data-lang") || defaultMode;
+ if (!mode) continue;
+
+ var text = [];
+ textContent(node, text);
+ node.innerHTML = "";
+ CodeMirror.runMode(text.join(""), mode, node);
+
+ node.className += " cm-s-default";
+ }
+ };
+});
Index: lams_central/web/includes/javascript/codemirror/addon/runmode/runmode-standalone.js
===================================================================
diff -u
--- lams_central/web/includes/javascript/codemirror/addon/runmode/runmode-standalone.js (revision 0)
+++ lams_central/web/includes/javascript/codemirror/addon/runmode/runmode-standalone.js (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -0,0 +1,333 @@
+(function () {
+ 'use strict';
+
+ function copyObj(obj, target, overwrite) {
+ if (!target) { target = {}; }
+ for (var prop in obj)
+ { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
+ { target[prop] = obj[prop]; } }
+ return target
+ }
+
+ // Counts the column offset in a string, taking tabs into account.
+ // Used mostly to find indentation.
+ function countColumn(string, end, tabSize, startIndex, startValue) {
+ if (end == null) {
+ end = string.search(/[^\s\u00a0]/);
+ if (end == -1) { end = string.length; }
+ }
+ for (var i = startIndex || 0, n = startValue || 0;;) {
+ var nextTab = string.indexOf("\t", i);
+ if (nextTab < 0 || nextTab >= end)
+ { return n + (end - i) }
+ n += nextTab - i;
+ n += tabSize - (n % tabSize);
+ i = nextTab + 1;
+ }
+ }
+
+ function nothing() {}
+
+ function createObj(base, props) {
+ var inst;
+ if (Object.create) {
+ inst = Object.create(base);
+ } else {
+ nothing.prototype = base;
+ inst = new nothing();
+ }
+ if (props) { copyObj(props, inst); }
+ return inst
+ }
+
+ // STRING STREAM
+
+ // Fed to the mode parsers, provides helper functions to make
+ // parsers more succinct.
+
+ var StringStream = function(string, tabSize, lineOracle) {
+ this.pos = this.start = 0;
+ this.string = string;
+ this.tabSize = tabSize || 8;
+ this.lastColumnPos = this.lastColumnValue = 0;
+ this.lineStart = 0;
+ this.lineOracle = lineOracle;
+ };
+
+ StringStream.prototype.eol = function () {return this.pos >= this.string.length};
+ StringStream.prototype.sol = function () {return this.pos == this.lineStart};
+ StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
+ StringStream.prototype.next = function () {
+ if (this.pos < this.string.length)
+ { return this.string.charAt(this.pos++) }
+ };
+ StringStream.prototype.eat = function (match) {
+ var ch = this.string.charAt(this.pos);
+ var ok;
+ if (typeof match == "string") { ok = ch == match; }
+ else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
+ if (ok) {++this.pos; return ch}
+ };
+ StringStream.prototype.eatWhile = function (match) {
+ var start = this.pos;
+ while (this.eat(match)){}
+ return this.pos > start
+ };
+ StringStream.prototype.eatSpace = function () {
+ var start = this.pos;
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
+ return this.pos > start
+ };
+ StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
+ StringStream.prototype.skipTo = function (ch) {
+ var found = this.string.indexOf(ch, this.pos);
+ if (found > -1) {this.pos = found; return true}
+ };
+ StringStream.prototype.backUp = function (n) {this.pos -= n;};
+ StringStream.prototype.column = function () {
+ if (this.lastColumnPos < this.start) {
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
+ this.lastColumnPos = this.start;
+ }
+ return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+ };
+ StringStream.prototype.indentation = function () {
+ return countColumn(this.string, null, this.tabSize) -
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+ };
+ StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
+ if (typeof pattern == "string") {
+ var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
+ var substr = this.string.substr(this.pos, pattern.length);
+ if (cased(substr) == cased(pattern)) {
+ if (consume !== false) { this.pos += pattern.length; }
+ return true
+ }
+ } else {
+ var match = this.string.slice(this.pos).match(pattern);
+ if (match && match.index > 0) { return null }
+ if (match && consume !== false) { this.pos += match[0].length; }
+ return match
+ }
+ };
+ StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
+ StringStream.prototype.hideFirstChars = function (n, inner) {
+ this.lineStart += n;
+ try { return inner() }
+ finally { this.lineStart -= n; }
+ };
+ StringStream.prototype.lookAhead = function (n) {
+ var oracle = this.lineOracle;
+ return oracle && oracle.lookAhead(n)
+ };
+ StringStream.prototype.baseToken = function () {
+ var oracle = this.lineOracle;
+ return oracle && oracle.baseToken(this.pos)
+ };
+
+ // Known modes, by name and by MIME
+ var modes = {}, mimeModes = {};
+
+ // Extra arguments are stored as the mode's dependencies, which is
+ // used by (legacy) mechanisms like loadmode.js to automatically
+ // load a mode. (Preferred mechanism is the require/define calls.)
+ function defineMode(name, mode) {
+ if (arguments.length > 2)
+ { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
+ modes[name] = mode;
+ }
+
+ function defineMIME(mime, spec) {
+ mimeModes[mime] = spec;
+ }
+
+ // Given a MIME type, a {name, ...options} config object, or a name
+ // string, return a mode config object.
+ function resolveMode(spec) {
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
+ spec = mimeModes[spec];
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
+ var found = mimeModes[spec.name];
+ if (typeof found == "string") { found = {name: found}; }
+ spec = createObj(found, spec);
+ spec.name = found.name;
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
+ return resolveMode("application/xml")
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
+ return resolveMode("application/json")
+ }
+ if (typeof spec == "string") { return {name: spec} }
+ else { return spec || {name: "null"} }
+ }
+
+ // Given a mode spec (anything that resolveMode accepts), find and
+ // initialize an actual mode object.
+ function getMode(options, spec) {
+ spec = resolveMode(spec);
+ var mfactory = modes[spec.name];
+ if (!mfactory) { return getMode(options, "text/plain") }
+ var modeObj = mfactory(options, spec);
+ if (modeExtensions.hasOwnProperty(spec.name)) {
+ var exts = modeExtensions[spec.name];
+ for (var prop in exts) {
+ if (!exts.hasOwnProperty(prop)) { continue }
+ if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
+ modeObj[prop] = exts[prop];
+ }
+ }
+ modeObj.name = spec.name;
+ if (spec.helperType) { modeObj.helperType = spec.helperType; }
+ if (spec.modeProps) { for (var prop$1 in spec.modeProps)
+ { modeObj[prop$1] = spec.modeProps[prop$1]; } }
+
+ return modeObj
+ }
+
+ // This can be used to attach properties to mode objects from
+ // outside the actual mode definition.
+ var modeExtensions = {};
+ function extendMode(mode, properties) {
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
+ copyObj(properties, exts);
+ }
+
+ function copyState(mode, state) {
+ if (state === true) { return state }
+ if (mode.copyState) { return mode.copyState(state) }
+ var nstate = {};
+ for (var n in state) {
+ var val = state[n];
+ if (val instanceof Array) { val = val.concat([]); }
+ nstate[n] = val;
+ }
+ return nstate
+ }
+
+ // Given a mode and a state (for that mode), find the inner mode and
+ // state at the position that the state refers to.
+ function innerMode(mode, state) {
+ var info;
+ while (mode.innerMode) {
+ info = mode.innerMode(state);
+ if (!info || info.mode == mode) { break }
+ state = info.state;
+ mode = info.mode;
+ }
+ return info || {mode: mode, state: state}
+ }
+
+ function startState(mode, a1, a2) {
+ return mode.startState ? mode.startState(a1, a2) : true
+ }
+
+ var modeMethods = {
+ __proto__: null,
+ modes: modes,
+ mimeModes: mimeModes,
+ defineMode: defineMode,
+ defineMIME: defineMIME,
+ resolveMode: resolveMode,
+ getMode: getMode,
+ modeExtensions: modeExtensions,
+ extendMode: extendMode,
+ copyState: copyState,
+ innerMode: innerMode,
+ startState: startState
+ };
+
+ // declare global: globalThis, CodeMirror
+
+ // Create a minimal CodeMirror needed to use runMode, and assign to root.
+ var root = typeof globalThis !== 'undefined' ? globalThis : window;
+ root.CodeMirror = {};
+
+ // Copy StringStream and mode methods into CodeMirror object.
+ CodeMirror.StringStream = StringStream;
+ for (var exported in modeMethods) { CodeMirror[exported] = modeMethods[exported]; }
+
+ // Minimal default mode.
+ CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
+ CodeMirror.defineMIME("text/plain", "null");
+
+ CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min;
+ CodeMirror.splitLines = function(string) { return string.split(/\r?\n|\r/) };
+
+ CodeMirror.defaults = { indentUnit: 2 };
+
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
+ // Distributed under an MIT license: https://codemirror.net/LICENSE
+
+ (function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ { mod(require("../../lib/codemirror")); }
+ else if (typeof define == "function" && define.amd) // AMD
+ { define(["../../lib/codemirror"], mod); }
+ else // Plain browser env
+ { mod(CodeMirror); }
+ })(function(CodeMirror) {
+
+ CodeMirror.runMode = function(string, modespec, callback, options) {
+ var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
+ var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
+
+ // Create a tokenizing callback function if passed-in callback is a DOM element.
+ if (callback.appendChild) {
+ var ie = /MSIE \d/.test(navigator.userAgent);
+ var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
+ var node = callback, col = 0;
+ node.innerHTML = "";
+ callback = function(text, style) {
+ if (text == "\n") {
+ // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
+ // Emitting a carriage return makes everything ok.
+ node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
+ col = 0;
+ return;
+ }
+ var content = "";
+ // replace tabs
+ for (var pos = 0;;) {
+ var idx = text.indexOf("\t", pos);
+ if (idx == -1) {
+ content += text.slice(pos);
+ col += text.length - pos;
+ break;
+ } else {
+ col += idx - pos;
+ content += text.slice(pos, idx);
+ var size = tabSize - col % tabSize;
+ col += size;
+ for (var i = 0; i < size; ++i) { content += " "; }
+ pos = idx + 1;
+ }
+ }
+ // Create a node with token style and append it to the callback DOM element.
+ if (style) {
+ var sp = node.appendChild(document.createElement("span"));
+ sp.className = "cm-" + style.replace(/ +/g, " cm-");
+ sp.appendChild(document.createTextNode(content));
+ } else {
+ node.appendChild(document.createTextNode(content));
+ }
+ };
+ }
+
+ var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
+ for (var i = 0, e = lines.length; i < e; ++i) {
+ if (i) { callback("\n"); }
+ var stream = new CodeMirror.StringStream(lines[i], null, {
+ lookAhead: function(n) { return lines[i + n] },
+ baseToken: function() {}
+ });
+ if (!stream.string && mode.blankLine) { mode.blankLine(state); }
+ while (!stream.eol()) {
+ var style = mode.token(stream, state);
+ callback(stream.current(), style, i, stream.start, state);
+ stream.start = stream.pos;
+ }
+ }
+ };
+
+ });
+
+}());
Index: lams_central/web/includes/javascript/codemirror/addon/scroll/simplescrollbars.js
===================================================================
diff -u
--- lams_central/web/includes/javascript/codemirror/addon/scroll/simplescrollbars.js (revision 0)
+++ lams_central/web/includes/javascript/codemirror/addon/scroll/simplescrollbars.js (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -0,0 +1,152 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ function Bar(cls, orientation, scroll) {
+ this.orientation = orientation;
+ this.scroll = scroll;
+ this.screen = this.total = this.size = 1;
+ this.pos = 0;
+
+ this.node = document.createElement("div");
+ this.node.className = cls + "-" + orientation;
+ this.inner = this.node.appendChild(document.createElement("div"));
+
+ var self = this;
+ CodeMirror.on(this.inner, "mousedown", function(e) {
+ if (e.which != 1) return;
+ CodeMirror.e_preventDefault(e);
+ var axis = self.orientation == "horizontal" ? "pageX" : "pageY";
+ var start = e[axis], startpos = self.pos;
+ function done() {
+ CodeMirror.off(document, "mousemove", move);
+ CodeMirror.off(document, "mouseup", done);
+ }
+ function move(e) {
+ if (e.which != 1) return done();
+ self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));
+ }
+ CodeMirror.on(document, "mousemove", move);
+ CodeMirror.on(document, "mouseup", done);
+ });
+
+ CodeMirror.on(this.node, "click", function(e) {
+ CodeMirror.e_preventDefault(e);
+ var innerBox = self.inner.getBoundingClientRect(), where;
+ if (self.orientation == "horizontal")
+ where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;
+ else
+ where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;
+ self.moveTo(self.pos + where * self.screen);
+ });
+
+ function onWheel(e) {
+ var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"];
+ var oldPos = self.pos;
+ self.moveTo(self.pos + moved);
+ if (self.pos != oldPos) CodeMirror.e_preventDefault(e);
+ }
+ CodeMirror.on(this.node, "mousewheel", onWheel);
+ CodeMirror.on(this.node, "DOMMouseScroll", onWheel);
+ }
+
+ Bar.prototype.setPos = function(pos, force) {
+ if (pos < 0) pos = 0;
+ if (pos > this.total - this.screen) pos = this.total - this.screen;
+ if (!force && pos == this.pos) return false;
+ this.pos = pos;
+ this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
+ (pos * (this.size / this.total)) + "px";
+ return true
+ };
+
+ Bar.prototype.moveTo = function(pos) {
+ if (this.setPos(pos)) this.scroll(pos, this.orientation);
+ }
+
+ var minButtonSize = 10;
+
+ Bar.prototype.update = function(scrollSize, clientSize, barSize) {
+ var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize
+ if (sizeChanged) {
+ this.screen = clientSize;
+ this.total = scrollSize;
+ this.size = barSize;
+ }
+
+ var buttonSize = this.screen * (this.size / this.total);
+ if (buttonSize < minButtonSize) {
+ this.size -= minButtonSize - buttonSize;
+ buttonSize = minButtonSize;
+ }
+ this.inner.style[this.orientation == "horizontal" ? "width" : "height"] =
+ buttonSize + "px";
+ this.setPos(this.pos, sizeChanged);
+ };
+
+ function SimpleScrollbars(cls, place, scroll) {
+ this.addClass = cls;
+ this.horiz = new Bar(cls, "horizontal", scroll);
+ place(this.horiz.node);
+ this.vert = new Bar(cls, "vertical", scroll);
+ place(this.vert.node);
+ this.width = null;
+ }
+
+ SimpleScrollbars.prototype.update = function(measure) {
+ if (this.width == null) {
+ var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;
+ if (style) this.width = parseInt(style.height);
+ }
+ var width = this.width || 0;
+
+ var needsH = measure.scrollWidth > measure.clientWidth + 1;
+ var needsV = measure.scrollHeight > measure.clientHeight + 1;
+ this.vert.node.style.display = needsV ? "block" : "none";
+ this.horiz.node.style.display = needsH ? "block" : "none";
+
+ if (needsV) {
+ this.vert.update(measure.scrollHeight, measure.clientHeight,
+ measure.viewHeight - (needsH ? width : 0));
+ this.vert.node.style.bottom = needsH ? width + "px" : "0";
+ }
+ if (needsH) {
+ this.horiz.update(measure.scrollWidth, measure.clientWidth,
+ measure.viewWidth - (needsV ? width : 0) - measure.barLeft);
+ this.horiz.node.style.right = needsV ? width + "px" : "0";
+ this.horiz.node.style.left = measure.barLeft + "px";
+ }
+
+ return {right: needsV ? width : 0, bottom: needsH ? width : 0};
+ };
+
+ SimpleScrollbars.prototype.setScrollTop = function(pos) {
+ this.vert.setPos(pos);
+ };
+
+ SimpleScrollbars.prototype.setScrollLeft = function(pos) {
+ this.horiz.setPos(pos);
+ };
+
+ SimpleScrollbars.prototype.clear = function() {
+ var parent = this.horiz.node.parentNode;
+ parent.removeChild(this.horiz.node);
+ parent.removeChild(this.vert.node);
+ };
+
+ CodeMirror.scrollbarModel.simple = function(place, scroll) {
+ return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll);
+ };
+ CodeMirror.scrollbarModel.overlay = function(place, scroll) {
+ return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll);
+ };
+});
Index: lams_central/web/includes/javascript/codemirror/addon/selection/active-line.js
===================================================================
diff -u
--- lams_central/web/includes/javascript/codemirror/addon/selection/active-line.js (revision 0)
+++ lams_central/web/includes/javascript/codemirror/addon/selection/active-line.js (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -0,0 +1,72 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+ var WRAP_CLASS = "CodeMirror-activeline";
+ var BACK_CLASS = "CodeMirror-activeline-background";
+ var GUTT_CLASS = "CodeMirror-activeline-gutter";
+
+ CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
+ var prev = old == CodeMirror.Init ? false : old;
+ if (val == prev) return
+ if (prev) {
+ cm.off("beforeSelectionChange", selectionChange);
+ clearActiveLines(cm);
+ delete cm.state.activeLines;
+ }
+ if (val) {
+ cm.state.activeLines = [];
+ updateActiveLines(cm, cm.listSelections());
+ cm.on("beforeSelectionChange", selectionChange);
+ }
+ });
+
+ function clearActiveLines(cm) {
+ for (var i = 0; i < cm.state.activeLines.length; i++) {
+ cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
+ cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
+ cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
+ }
+ }
+
+ function sameArray(a, b) {
+ if (a.length != b.length) return false;
+ for (var i = 0; i < a.length; i++)
+ if (a[i] != b[i]) return false;
+ return true;
+ }
+
+ function updateActiveLines(cm, ranges) {
+ var active = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ var option = cm.getOption("styleActiveLine");
+ if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
+ continue
+ var line = cm.getLineHandleVisualStart(range.head.line);
+ if (active[active.length - 1] != line) active.push(line);
+ }
+ if (sameArray(cm.state.activeLines, active)) return;
+ cm.operation(function() {
+ clearActiveLines(cm);
+ for (var i = 0; i < active.length; i++) {
+ cm.addLineClass(active[i], "wrap", WRAP_CLASS);
+ cm.addLineClass(active[i], "background", BACK_CLASS);
+ cm.addLineClass(active[i], "gutter", GUTT_CLASS);
+ }
+ cm.state.activeLines = active;
+ });
+ }
+
+ function selectionChange(cm, sel) {
+ updateActiveLines(cm, sel.ranges);
+ }
+});
Index: lams_tool_assessment/web/pages/learning/learning.jsp
===================================================================
diff -u -r941959d3b2b6b95a6dbe873bafcd0e5ff5285511 -rc4ba84a2f40772b7efb1f1d734d19d153d84feff
--- lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision 941959d3b2b6b95a6dbe873bafcd0e5ff5285511)
+++ lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision c4ba84a2f40772b7efb1f1d734d19d153d84feff)
@@ -29,6 +29,7 @@
+