'], RXstyle, RXmodifiers);
- }
- if (/[\^'"!~\/]/.test(c)) {
- eatSuffix(stream, 1);
- return tokenChain(stream, state, [stream.eat(c)], RXstyle, RXmodifiers);
- }
- } else if (c == 'q') {
- c = look(stream, 1);
- if (c == '(') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, [')'], 'string');
- }
- if (c == '[') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, [']'], 'string');
- }
- if (c == '{') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, ['}'], 'string');
- }
- if (c == '<') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, ['>'], 'string');
- }
- if (/[\^'"!~\/]/.test(c)) {
- eatSuffix(stream, 1);
- return tokenChain(stream, state, [stream.eat(c)], 'string');
- }
- } else if (c == 'w') {
- c = look(stream, 1);
- if (c == '(') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, [')'], 'bracket');
- }
- if (c == '[') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, [']'], 'bracket');
- }
- if (c == '{') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, ['}'], 'bracket');
- }
- if (c == '<') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, ['>'], 'bracket');
- }
- if (/[\^'"!~\/]/.test(c)) {
- eatSuffix(stream, 1);
- return tokenChain(stream, state, [stream.eat(c)], 'bracket');
- }
- } else if (c == 'r') {
- c = look(stream, 1);
- if (c == '(') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, [')'], RXstyle, RXmodifiers);
- }
- if (c == '[') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, [']'], RXstyle, RXmodifiers);
- }
- if (c == '{') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, ['}'], RXstyle, RXmodifiers);
- }
- if (c == '<') {
- eatSuffix(stream, 2);
- return tokenChain(stream, state, ['>'], RXstyle, RXmodifiers);
- }
- if (/[\^'"!~\/]/.test(c)) {
- eatSuffix(stream, 1);
- return tokenChain(stream, state, [stream.eat(c)], RXstyle, RXmodifiers);
- }
- } else if (/[\^'"!~\/(\[{<]/.test(c)) {
- if (c == '(') {
- eatSuffix(stream, 1);
- return tokenChain(stream, state, [')'], 'string');
- }
- if (c == '[') {
- eatSuffix(stream, 1);
- return tokenChain(stream, state, [']'], 'string');
- }
- if (c == '{') {
- eatSuffix(stream, 1);
- return tokenChain(stream, state, ['}'], 'string');
- }
- if (c == '<') {
- eatSuffix(stream, 1);
- return tokenChain(stream, state, ['>'], 'string');
- }
- if (/[\^'"!~\/]/.test(c)) {
- return tokenChain(stream, state, [stream.eat(c)], 'string');
- }
- }
- }
- }
- if (ch == 'm') {
- var c = look(stream, -2);
- if (!(c && /\w/.test(c))) {
- c = stream.eat(/[(\[{<\^'"!~\/]/);
- if (c) {
- if (/[\^'"!~\/]/.test(c)) {
- return tokenChain(stream, state, [c], RXstyle, RXmodifiers);
- }
- if (c == '(') {
- return tokenChain(stream, state, [')'], RXstyle, RXmodifiers);
- }
- if (c == '[') {
- return tokenChain(stream, state, [']'], RXstyle, RXmodifiers);
- }
- if (c == '{') {
- return tokenChain(stream, state, ['}'], RXstyle, RXmodifiers);
- }
- if (c == '<') {
- return tokenChain(stream, state, ['>'], RXstyle, RXmodifiers);
- }
- }
- }
- }
- if (ch == 's') {
- var c = /[\/>\]})\w]/.test(look(stream, -2));
- if (!c) {
- c = stream.eat(/[(\[{<\^'"!~\/]/);
- if (c) {
- if (c == '[') return tokenChain(stream, state, [']', ']'], RXstyle, RXmodifiers);
- if (c == '{') return tokenChain(stream, state, ['}', '}'], RXstyle, RXmodifiers);
- if (c == '<') return tokenChain(stream, state, ['>', '>'], RXstyle, RXmodifiers);
- if (c == '(') return tokenChain(stream, state, [')', ')'], RXstyle, RXmodifiers);
- return tokenChain(stream, state, [c, c], RXstyle, RXmodifiers);
- }
- }
- }
- if (ch == 'y') {
- var c = /[\/>\]})\w]/.test(look(stream, -2));
- if (!c) {
- c = stream.eat(/[(\[{<\^'"!~\/]/);
- if (c) {
- if (c == '[') return tokenChain(stream, state, [']', ']'], RXstyle, RXmodifiers);
- if (c == '{') return tokenChain(stream, state, ['}', '}'], RXstyle, RXmodifiers);
- if (c == '<') return tokenChain(stream, state, ['>', '>'], RXstyle, RXmodifiers);
- if (c == '(') return tokenChain(stream, state, [')', ')'], RXstyle, RXmodifiers);
- return tokenChain(stream, state, [c, c], RXstyle, RXmodifiers);
- }
- }
- }
- if (ch == 't') {
- var c = /[\/>\]})\w]/.test(look(stream, -2));
- if (!c) {
- c = stream.eat('r');
- if (c) {
- c = stream.eat(/[(\[{<\^'"!~\/]/);
- if (c) {
- if (c == '[') return tokenChain(stream, state, [']', ']'], RXstyle, RXmodifiers);
- if (c == '{') return tokenChain(stream, state, ['}', '}'], RXstyle, RXmodifiers);
- if (c == '<') return tokenChain(stream, state, ['>', '>'], RXstyle, RXmodifiers);
- if (c == '(') return tokenChain(stream, state, [')', ')'], RXstyle, RXmodifiers);
- return tokenChain(stream, state, [c, c], RXstyle, RXmodifiers);
- }
- }
- }
- }
- if (ch == '`') {
- return tokenChain(stream, state, [ch], 'variable-2');
- }
- if (ch == '/') {
- if (!/~\s*$/.test(prefix(stream))) return 'operator';
- else return tokenChain(stream, state, [ch], RXstyle, RXmodifiers);
- }
- if (ch == '$') {
- var p = stream.pos;
- if (stream.eatWhile(/\w/) && PGvars.has(stream.current().substring(1))) return PGstyle;
- else stream.pos = p;
- if (stream.eatWhile(/\d/) || (stream.eat('{') && stream.eatWhile(/\d/) && stream.eat('}')))
- return 'variable-2';
- else stream.pos = p;
- }
- if (/[$@%]/.test(ch)) {
- var p = stream.pos;
- if (
- (stream.eat('^') && stream.eat(/[A-Z]/)) ||
- (!/[@$%&]/.test(look(stream, -2)) && stream.eat(/[=|\\\-#?@;:&`~\^!\[\]*'"$+.,\/<>()]/))
- ) {
- var c = stream.current();
- if (PERL[c]) return 'variable-2';
- }
- stream.pos = p;
- }
- if (/[$@%&]/.test(ch)) {
- if (stream.eatWhile(/[\w$]/) || (stream.eat('{') && stream.eatWhile(/[\w$]/) && stream.eat('}'))) {
- var c = stream.current();
- if (PERL[c]) return 'variable-2';
- else return 'variable';
- }
- }
- if (ch == '#') {
- if (look(stream, -2) != '$') {
- stream.skipToEnd();
- return 'comment';
- }
- }
- if (ch == '-' && look(stream, -2) != ' ' && stream.match(/>\w+/)) return 'variable';
- if (/[:+\-\^*$&%@=<>!?|\/~\.]/.test(ch)) {
- var p = stream.pos;
- stream.eatWhile(/[:+\-\^*$&%@=<>!?|\/~\.]/);
- if (PERL[stream.current()]) return 'operator';
- else stream.pos = p;
- }
- if (ch == '_') {
- if (stream.pos == 1) {
- if (suffix(stream, 6) == '_END__') {
- return tokenChain(stream, state, ['\0'], 'comment');
- } else if (suffix(stream, 7) == '_DATA__') {
- return tokenChain(stream, state, ['\0'], 'variable-2');
- } else if (suffix(stream, 7) == '_C__') {
- return tokenChain(stream, state, ['\0'], 'string');
- }
- }
- }
- if (/\w/.test(ch)) {
- var p = stream.pos;
- if (
- look(stream, -2) == '{' &&
- (look(stream, 0) == '}' || (stream.eatWhile(/\w/) && look(stream, 0) == '}'))
- )
- return 'string';
- else stream.pos = p;
- if (stream.match(/\w* *=>/)) return 'string';
- }
- if (/[A-Z]/.test(ch)) {
- var l = look(stream, -2);
- var p = stream.pos;
- stream.eatWhile(/[A-Z_]/);
- if (/[\da-z]/.test(look(stream, 0))) {
- stream.pos = p;
- } else {
- var c = PERL[stream.current()];
- var isPG = PGcmds.has(stream.current());
- if (!c && !isPG) return 'meta';
- if (isPG) return PGkeyword;
- if (c[1]) c = c[0];
- if (l != ':') {
- if (c == 1) return 'keyword';
- else if (c == 2) return 'def';
- else if (c == 3) return 'atom';
- else if (c == 4) return 'operator';
- else if (c == 5) return 'variable-2';
- else return 'meta';
- } else return 'meta';
- }
- }
- if (/[a-zA-Z_]/.test(ch)) {
- var l = look(stream, -2);
- stream.eatWhile(/\w/);
- var c = PERL[stream.current()];
- var isPG = PGcmds.has(stream.current());
- if (!c && !isPG) return 'meta';
- if (isPG) return PGkeyword;
- if (c[1]) c = c[0];
- if (l != ':') {
- if (c == 1) return 'keyword';
- else if (c == 2) return 'def';
- else if (c == 3) return 'atom';
- else if (c == 4) return 'operator';
- else if (c == 5) return 'variable-2';
- else return 'meta';
- } else return 'meta';
- }
- return null;
- }
-
- return {
- startState: function () {
- return {
- tokenize: tokenPerl,
- chain: null,
- style: null,
- tail: null
- };
- },
- token: function (stream, state) {
- return (state.tokenize || tokenPerl)(stream, state);
- },
- lineComment: '#'
- };
- });
-
- CodeMirror.registerHelper('wordChars', 'perl', /[\w$]/);
-
- CodeMirror.registerHelper('fold', 'PG', (cm, start) => {
- const m1 =
- /^\s*BEGIN_(PGML|PGML_SOLUTION|PGML_HINT|TEXT)\s*$/.exec(cm.getLine(start.line)) ||
- /^\s*[$\w]*\s*->\s*BEGIN_(TIKZ|LATEX_IMAGE)\s*$/.exec(cm.getLine(start.line));
- const m2 = /^\s*(Section|Scaffold)::Begin/.exec(cm.getLine(start.line));
- if (m1 || m2) {
- for (let current_line = start.line + 1; current_line <= cm.lineCount(); ++current_line) {
- const end_re = m1 ? RegExp(`END_${m1[1]}`) : RegExp(`${m2[1]}::End`);
- if (end_re.test(cm.getLine(current_line))) {
- return {
- from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
- to: CodeMirror.Pos(current_line, cm.getLine(current_line).length)
- };
- }
- }
- }
- return;
- });
-
- CodeMirror.defineMIME('text/x-perl', 'perl');
-
- // it's like "peek", but need for look-ahead or look-behind if index < 0
- function look(stream, c) {
- return stream.string.charAt(stream.pos + (c || 0));
- }
-
- // return a part of prefix of current stream from current position
- function prefix(stream, c) {
- if (c) {
- var x = stream.pos - c;
- return stream.string.substr(x >= 0 ? x : 0, c);
- } else {
- return stream.string.substr(0, stream.pos - 1);
- }
- }
-
- // return a part of suffix of current stream from current position
- function suffix(stream, c) {
- var y = stream.string.length;
- var x = y - stream.pos + 1;
- return stream.string.substr(stream.pos, c && c < y ? c : x);
- }
-
- // eating and vomiting a part of stream from current position
- function eatSuffix(stream, c) {
- var x = stream.pos + c;
- var y;
- if (x <= 0) stream.pos = 0;
- else if (x >= (y = stream.string.length - 1)) stream.pos = y;
- else stream.pos = x;
- }
-})();
diff --git a/htdocs/js/PGCodeMirror/comment.js b/htdocs/js/PGCodeMirror/comment.js
deleted file mode 100644
index 356b9ce146..0000000000
--- a/htdocs/js/PGCodeMirror/comment.js
+++ /dev/null
@@ -1,287 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/5/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);
- // Plain browser env
- else mod(CodeMirror);
-})(function (CodeMirror) {
- 'use strict';
-
- var noOptions = {};
- var nonWS = /[^\s\u00a0]/;
- var Pos = CodeMirror.Pos,
- cmp = CodeMirror.cmpPos;
-
- function firstNonWS(str) {
- var found = str.search(nonWS);
- return found == -1 ? 0 : found;
- }
-
- CodeMirror.commands.toggleComment = function (cm) {
- cm.toggleComment();
- };
-
- CodeMirror.defineExtension('toggleComment', function (options) {
- if (!options) options = noOptions;
- var cm = this;
- var minLine = Infinity,
- ranges = this.listSelections(),
- mode = null;
- for (var i = ranges.length - 1; i >= 0; i--) {
- var from = ranges[i].from(),
- to = ranges[i].to();
- if (from.line >= minLine) continue;
- if (to.line >= minLine) to = Pos(minLine, 0);
- minLine = from.line;
- if (mode == null) {
- if (cm.uncomment(from, to, options)) mode = 'un';
- else {
- cm.lineComment(from, to, options);
- mode = 'line';
- }
- } else if (mode == 'un') {
- cm.uncomment(from, to, options);
- } else {
- cm.lineComment(from, to, options);
- }
- }
- });
-
- // Rough heuristic to try and detect lines that are part of multi-line string
- function probablyInsideString(cm, pos, line) {
- return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line);
- }
-
- const md_section = RegExp(
- 'DESCRIPTION|KEYWORDS|DBsubject|DBchapter|DBsection|Date|Author|Institution ' +
- '|MO|Static|TitleText|EditionText|AuthorText|Section|Problem|Language|Level'
- );
-
- // Custom version of getMode for PG files.
- function getMode(cm, pos) {
- const mode = cm.getModeAt(pos);
- // Clear any comment fields of mode.
- delete mode.lineComment;
- delete mode.blockCommentStart;
- delete mode.blockCommentEnd;
-
- if (md_section.test(cm.getLine(pos.line)) || insideDescriptionBlock(cm, pos)) {
- mode.lineComment = '##';
- mode.name = 'PG_meta';
- } else if (inPGMLBlock(cm, pos)) {
- mode.name = 'PGML';
- mode.blockCommentStart = '[%';
- mode.blockCommentEnd = '%]';
- } else if (inTikzBlock(cm, pos)) {
- mode.lineComment = '%';
- mode.name = 'tikz';
- } else {
- mode.name = 'perl';
- mode.lineComment = '#';
- }
- return mode;
- }
-
- function insideDescriptionBlock(cm, pos) {
- for (let line = pos.line; line >= 0; --line) {
- if (/ENDDESCRIPTION/.test(cm.getLine(line))) return false;
- if (/DESCRIPTION/.test(cm.getLine(line))) return true;
- }
- return false;
- }
-
- function inTikzBlock(cm, pos) {
- for (let line = pos.line; line >= 0; --line) {
- if (/BEGIN_TIKZ|BEGIN_LATEX_IMAGE/.test(cm.getLine(line))) return true;
- if (/END_PGML|END_TIKZ|END_LATEX_IMAGE/.test(cm.getLine(line))) return false;
- }
- return false;
- }
-
- function inPGMLBlock(cm, pos) {
- for (let line = pos.line; line >= 0; --line) {
- if (/BEGIN_PGML/.test(cm.getLine(line))) return true;
- if (/END_PGML/.test(cm.getLine(line))) return false;
- }
- return false;
- }
-
- CodeMirror.defineExtension('lineComment', function (from, to, options) {
- if (!options) options = noOptions;
- var self = this,
- mode = getMode(self, from);
- var firstLine = self.getLine(from.line);
- if (firstLine == null || probablyInsideString(self, from, firstLine)) return;
-
- var commentString = options.lineComment || mode.lineComment;
- if (!commentString) {
- if (options.blockCommentStart || mode.blockCommentStart) {
- options.fullLines = true;
- self.blockComment(from, to, options);
- }
- return;
- }
-
- var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
- var pad = options.padding == null ? ' ' : options.padding;
- var blankLines = options.commentBlankLines || from.line == to.line;
-
- self.operation(function () {
- if (options.indent) {
- var baseString = null;
- for (var i = from.line; i < end; ++i) {
- var line = self.getLine(i);
- var whitespace = line.search(nonWS) === -1 ? line : line.slice(0, firstNonWS(line));
- if (baseString == null || baseString.length > whitespace.length) {
- baseString = whitespace;
- }
- }
- for (var i = from.line; i < end; ++i) {
- var line = self.getLine(i),
- cut = baseString.length;
- if (!blankLines && !nonWS.test(line)) continue;
- if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
- self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
- }
- } else {
- for (var i = from.line; i < end; ++i) {
- if (blankLines || nonWS.test(self.getLine(i))) self.replaceRange(commentString + pad, Pos(i, 0));
- }
- }
- });
- });
-
- CodeMirror.defineExtension('blockComment', function (from, to, options) {
- if (!options) options = noOptions;
- var self = this,
- mode = getMode(self, from);
- var startString = options.blockCommentStart || mode.blockCommentStart;
- var endString = options.blockCommentEnd || mode.blockCommentEnd;
- if (!startString || !endString) {
- if ((options.lineComment || mode.lineComment) && options.fullLines != false)
- self.lineComment(from, to, options);
- return;
- }
- if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return;
-
- var end = Math.min(to.line, self.lastLine());
- if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
-
- var pad = options.padding == null ? ' ' : options.padding;
- if (from.line > end) return;
-
- self.operation(function () {
- if (options.fullLines != false) {
- var lastLineHasText = nonWS.test(self.getLine(end));
- self.replaceRange(pad + endString, Pos(end));
- self.replaceRange(startString + pad, Pos(from.line, 0));
- var lead = options.blockCommentLead || mode.blockCommentLead;
- if (lead != null)
- for (var i = from.line + 1; i <= end; ++i)
- if (i != end || lastLineHasText) self.replaceRange(lead + pad, Pos(i, 0));
- } else {
- var atCursor = cmp(self.getCursor('to'), to) == 0,
- empty = !self.somethingSelected();
- self.replaceRange(endString, to);
- if (atCursor) self.setSelection(empty ? to : self.getCursor('from'), to);
- self.replaceRange(startString, from);
- }
- });
- });
-
- CodeMirror.defineExtension('uncomment', function (from, to, options) {
- if (!options) options = noOptions;
- var self = this,
- mode = getMode(self, from);
- var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()),
- start = Math.min(from.line, end);
-
- // Try finding line comments
- var lineString = options.lineComment || mode.lineComment,
- lines = [];
- var pad = options.padding == null ? ' ' : options.padding,
- didSomething;
- lineComment: {
- if (!lineString) break lineComment;
- for (var i = start; i <= end; ++i) {
- var line = self.getLine(i);
- var found = line.indexOf(lineString);
- if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
- if (found == -1 && nonWS.test(line)) break lineComment;
- if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
- lines.push(line);
- }
- self.operation(function () {
- for (var i = start; i <= end; ++i) {
- var line = lines[i - start];
- var pos = line.indexOf(lineString),
- endPos = pos + lineString.length;
- if (pos < 0) continue;
- if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
- didSomething = true;
- self.replaceRange('', Pos(i, pos), Pos(i, endPos));
- }
- });
- if (didSomething) return true;
- }
-
- // Try block comments
- var startString = options.blockCommentStart || mode.blockCommentStart;
- var endString = options.blockCommentEnd || mode.blockCommentEnd;
- if (!startString || !endString) return false;
- var lead = options.blockCommentLead || mode.blockCommentLead;
- var startLine = self.getLine(start),
- open = startLine.indexOf(startString);
- if (open == -1) return false;
- var endLine = end == start ? startLine : self.getLine(end);
- var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
- var insideStart = Pos(start, open + 1),
- insideEnd = Pos(end, close + 1);
- if (
- close == -1 ||
- !/comment/.test(self.getTokenTypeAt(insideStart)) ||
- !/comment/.test(self.getTokenTypeAt(insideEnd)) ||
- self.getRange(insideStart, insideEnd, '\n').indexOf(endString) > -1
- )
- return false;
-
- // Avoid killing block comments completely outside the selection.
- // Positions of the last startString before the start of the selection, and the first endString after it.
- var lastStart = startLine.lastIndexOf(startString, from.ch);
- var firstEnd =
- lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
- if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
- // Positions of the first endString after the end of the selection, and the last startString before it.
- firstEnd = endLine.indexOf(endString, to.ch);
- var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
- lastStart = firstEnd == -1 || almostLastStart == -1 ? -1 : to.ch + almostLastStart;
- if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
-
- self.operation(function () {
- self.replaceRange(
- '',
- Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
- Pos(end, close + endString.length)
- );
- var openEnd = open + startString.length;
- if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
- self.replaceRange('', Pos(start, open), Pos(start, openEnd));
- if (lead)
- for (var i = start + 1; i <= end; ++i) {
- var line = self.getLine(i),
- found = line.indexOf(lead);
- if (found == -1 || nonWS.test(line.slice(0, found))) continue;
- var foundEnd = found + lead.length;
- if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
- self.replaceRange('', Pos(i, found), Pos(i, foundEnd));
- }
- });
- return true;
- });
-});
diff --git a/htdocs/js/PGCodeMirror/pgeditor.js b/htdocs/js/PGCodeMirror/pgeditor.js
index eee29e1233..28e91c4db7 100644
--- a/htdocs/js/PGCodeMirror/pgeditor.js
+++ b/htdocs/js/PGCodeMirror/pgeditor.js
@@ -1,148 +1,15 @@
-/* WeBWorK Online Homework Delivery System
- * Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of either: (a) the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version, or (b) the "Artistic License" which comes with this package.
- *
- * This program 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 either the GNU General Public License or the
- * Artistic License for more details.
- */
+(async () => {
+ const editorContainer = document.querySelector('.code-mirror-editor');
+ if (!PGCodeMirrorEditor || !editorContainer) return;
-(async function () {
- if (!CodeMirror) return;
+ const editorInput = document.getElementsByName(editorContainer.id)[0];
- const loadResource = async (src) => {
- return new Promise((resolve, reject) => {
- let shouldAppend = false;
- let el;
- if (/\.js(?:\?[0-9a-zA-Z=^.]*)?$/.exec(src)) {
- el = document.querySelector(`script[src="${src}"]`);
- if (!el) {
- el = document.createElement('script');
- el.async = false;
- el.src = src;
- shouldAppend = true;
- }
- } else if (/\.css(?:\?[0-9a-zA-Z=^.]*)?$/.exec(src)) {
- el = document.querySelector(`link[href="${src}"]`);
- if (!el) {
- el = document.createElement('link');
- el.rel = 'stylesheet';
- el.href = src;
- shouldAppend = true;
- }
- } else {
- reject();
- return;
- }
+ const cm = (webworkConfig.pgCodeMirror = new PGCodeMirrorEditor.View(editorContainer, {
+ source: editorInput?.value ?? '',
+ language: editorContainer.dataset.language ?? 'pg'
+ }));
- if (el.dataset.loaded) {
- resolve();
- return;
- }
+ new ResizeObserver(() => cm.refresh('window-resize')).observe(editorContainer);
- el.addEventListener('error', reject);
- el.addEventListener('abort', reject);
- el.addEventListener('load', () => {
- if (el) el.dataset.loaded = 'true';
- resolve();
- });
-
- if (shouldAppend) document.head.appendChild(el);
- });
- };
-
- const loadConfig = async (file) => {
- const configName =
- [...file.matchAll(/.*\/([^.]*?)(?:\.min)?\.(?:js|css)(?:\?[0-9a-zA-Z=^.]*)?$/g)][0]?.[1] ?? 'default';
- if (configName !== 'default') {
- try {
- await loadResource(file);
- } catch {
- return 'default';
- }
- }
- return configName;
- };
-
- const mode = document.querySelector('.codeMirrorEditor')?.dataset.mode ?? 'PG';
- const options = {
- mode,
- indentUnit: 4,
- tabMode: 'spaces',
- lineNumbers: true,
- lineWrapping: true,
- extraKeys: {
- Tab: (cm) => cm.execCommand('insertSoftTab'),
- 'Shift-Ctrl-[': (cm) => cm.foldCode(cm.getCursor(), { scanUp: true }),
- 'Cmd-Alt-[': (cm) => cm.foldCode(cm.getCursor(), { scanUp: true }),
- 'Ctrl-Alt-[': (cm) => CodeMirror.commands.foldAll(cm),
- 'Ctrl-Alt-]': (cm) => CodeMirror.commands.unfoldAll(cm)
- },
- highlightSelectionMatches: { annotateScrollbar: true },
- matchBrackets: true,
- inputStyle: 'contenteditable',
- spellcheck: localStorage.getItem('WW_PGEditor_spellcheck') === 'true',
- gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
- };
-
- if (mode === 'PG') {
- options.extraKeys['Ctrl-/'] = (cm) => cm.execCommand('toggleComment');
- options.extraKeys['Cmd-/'] = (cm) => cm.execCommand('toggleComment');
- options.foldGutter = { rangeFinder: new CodeMirror.fold.combine(CodeMirror.fold.PG) };
- options.fold = 'PG';
- } else {
- options.foldGutter = true;
- }
-
- const cm = (webworkConfig.pgCodeMirror = CodeMirror.fromTextArea(
- document.querySelector('.codeMirrorEditor'),
- options
- ));
- cm.setSize('100%', '550px');
-
- // Refresh the CodeMirror instance anytime the containing div resizes so that if line wrapping changes,
- // the mouse cursor will still go to the correct place when the user clicks on the CodeMirror window.
- new ResizeObserver(() => cm.refresh()).observe(document.querySelector('.CodeMirror'));
-
- const currentThemeFile = localStorage.getItem('WW_PGEditor_selected_theme') ?? 'default';
- const currentThemeName = await loadConfig(currentThemeFile);
- cm.setOption('theme', currentThemeName);
-
- const currentKeymapFile = localStorage.getItem('WW_PGEditor_selected_keymap') ?? 'default';
- const currentKeymapName = await loadConfig(currentKeymapFile);
- cm.setOption('keyMap', currentKeymapName);
-
- const selectTheme = document.getElementById('selectTheme');
- selectTheme.value = currentThemeName === 'default' ? 'default' : currentThemeFile;
- selectTheme.addEventListener('change', async () => {
- const themeName = await loadConfig(selectTheme.value);
- cm.setOption('theme', themeName);
- localStorage.setItem('WW_PGEditor_selected_theme', themeName === 'default' ? 'default' : selectTheme.value);
- });
-
- const selectKeymap = document.getElementById('selectKeymap');
- selectKeymap.value = currentKeymapName === 'default' ? 'default' : currentKeymapFile;
- selectKeymap.addEventListener('change', async () => {
- const keymapName = await loadConfig(selectKeymap.value);
- cm.setOption('keyMap', keymapName);
- localStorage.setItem('WW_PGEditor_selected_keymap', keymapName === 'default' ? 'default' : selectKeymap.value);
- });
-
- const enableSpell = document.getElementById('enableSpell');
- enableSpell.checked = localStorage.getItem('WW_PGEditor_spellcheck') === 'true';
- enableSpell.addEventListener('change', () => {
- cm.setOption('spellcheck', enableSpell.checked);
- localStorage.setItem('WW_PGEditor_spellcheck', enableSpell.checked);
- cm.focus();
- });
-
- const forceRTL = document.getElementById('forceRTL');
- forceRTL.addEventListener('change', () => {
- cm.setOption('direction', forceRTL.checked ? 'rtl' : 'ltr');
- });
+ editorInput?.form.addEventListener('submit', () => (editorInput.value = cm.source));
})();
diff --git a/htdocs/js/PGCodeMirror/pgeditor.scss b/htdocs/js/PGCodeMirror/pgeditor.scss
index 1fd132c9fc..3f1cd895c1 100644
--- a/htdocs/js/PGCodeMirror/pgeditor.scss
+++ b/htdocs/js/PGCodeMirror/pgeditor.scss
@@ -1,25 +1,30 @@
-/* WeBWorK Online Homework Delivery System
- * Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of either: (a) the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version, or (b) the "Artistic License" which comes with this package.
- *
- * This program 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 either the GNU General Public License or the
- * Artistic License for more details.
- */
-
-.CodeMirror {
+.code-mirror-editor {
border: 1px solid #ddd;
min-height: 400px;
+ overflow: auto;
resize: vertical;
+ height: 600px;
+
+ .cm-editor {
+ height: 100%;
+
+ .cm-scroller {
+ height: 100%;
+
+ .cm-content {
+ height: 100%;
+ min-height: 400px;
+ }
+ }
+
+ .cm-panels {
+ z-index: 18;
+ }
+ }
}
-// This style is only used if the CodeMirror editor is disabled in localOverrides.conf.
-.codeMirrorEditor {
+// This style is used if the CodeMirror editor is disabled in localOverrides.conf.
+.text-area-editor {
border: 1px solid #ddd;
padding: 2px;
height: 550px;
@@ -27,38 +32,3 @@
width: 100%;
resize: vertical;
}
-
-// Additional CSS for codemirror addons and overrides
-
-// CodeMirror overrides
-.CodeMirror-code {
- outline: none;
-}
-
-pre.CodeMirror-line {
- unicode-bidi: embed;
-}
-
-// Match Highligher CSS
-.CodeMirror-focused {
- .cm-matchhighlight {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
- background-position: bottom;
- background-repeat: repeat-x;
- }
-}
-
-.cm-matchhighlight {
- background-color: lightgreen;
-}
-
-.CodeMirror-selection-highlight-scrollbar {
- background-color: green;
-}
-
-// CSS to highlight trailing whitespace in PGML blocks
-.cm-trailingspace {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==);
- background-position: bottom left;
- background-repeat: repeat-x;
-}
diff --git a/htdocs/js/PGProblemEditor/pgproblemeditor.js b/htdocs/js/PGProblemEditor/pgproblemeditor.js
index f7e419d47b..f48214574f 100644
--- a/htdocs/js/PGProblemEditor/pgproblemeditor.js
+++ b/htdocs/js/PGProblemEditor/pgproblemeditor.js
@@ -1,4 +1,57 @@
(() => {
+ const fileChooserForm = document.forms['pg-editor-file-chooser'];
+ if (fileChooserForm) {
+ const newProblemRadio = document.getElementById('new_problem');
+
+ const sourceFilePathInput = fileChooserForm.elements['sourceFilePath'];
+ const filePathRadio = document.getElementById('file_path');
+
+ const sampleProblemFileSelect = fileChooserForm.elements['sampleProblemFile'];
+ const sampleProblemRadio = document.getElementById('sample_problem');
+
+ newProblemRadio?.addEventListener('change', () => {
+ if (newProblemRadio.checked) {
+ sampleProblemFileSelect.required = false;
+ sourceFilePathInput.required = false;
+ }
+ });
+
+ if (filePathRadio && sourceFilePathInput) {
+ const filePathSelected = () => {
+ sampleProblemFileSelect.required = false;
+ sourceFilePathInput.required = true;
+ filePathRadio.checked = true;
+ };
+ filePathRadio.addEventListener('change', () => {
+ if (filePathRadio.checked) filePathSelected();
+ });
+ sourceFilePathInput.addEventListener('focusin', filePathSelected);
+ }
+ if (sampleProblemRadio && sampleProblemFileSelect) {
+ const sampleProblemSelected = () => {
+ sampleProblemFileSelect.required = true;
+ sourceFilePathInput.required = false;
+ sampleProblemRadio.checked = true;
+ };
+ sampleProblemRadio.addEventListener('change', () => {
+ if (sampleProblemRadio.checked) sampleProblemSelected();
+ });
+ sampleProblemFileSelect.addEventListener('change', sampleProblemSelected);
+ sampleProblemFileSelect.addEventListener('focusin', sampleProblemSelected);
+ }
+
+ fileChooserForm.addEventListener('submit', (e) => {
+ if (!fileChooserForm.checkValidity()) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ fileChooserForm.classList.add('was-validated');
+ });
+
+ return;
+ }
+
// Add a container for message toasts.
const toastContainer = document.createElement('div');
toastContainer.classList.add('toast-container', 'position-fixed', 'bottom-0', 'end-0', 'p-3');
@@ -48,7 +101,7 @@
request_object.rpc_command = 'saveFile';
request_object.outputFilePath = document.getElementsByName('temp_file_path')[0]?.value ?? '';
request_object.fileContents =
- webworkConfig?.pgCodeMirror?.getValue() ?? document.getElementById('problemContents')?.value ?? '';
+ webworkConfig?.pgCodeMirror?.source ?? document.getElementById('problemContents')?.value ?? '';
if (!request_object.outputFilePath) return;
@@ -119,7 +172,7 @@
request_object.rpc_command = 'tidyPGCode';
request_object.pgCode =
- webworkConfig?.pgCodeMirror?.getValue() ?? document.getElementById('problemContents')?.value ?? '';
+ webworkConfig?.pgCodeMirror?.source ?? document.getElementById('problemContents')?.value ?? '';
fetch(webserviceURL, { method: 'post', mode: 'same-origin', body: new URLSearchParams(request_object) })
.then((response) => response.json())
@@ -141,7 +194,7 @@
if (request_object.pgCode === data.result_data.tidiedPGCode) {
showMessage('There were no changes to the code.', true);
} else {
- if (webworkConfig?.pgCodeMirror) webworkConfig.pgCodeMirror.setValue(data.result_data.tidiedPGCode);
+ if (webworkConfig?.pgCodeMirror) webworkConfig.pgCodeMirror.source = data.result_data.tidiedPGCode;
else document.getElementById('problemContents').value = data.result_data.tidiedPGCode;
saveTempFile();
showMessage('Successfuly perltidied code.', true);
@@ -161,7 +214,7 @@
request_object.rpc_command = 'convertCodeToPGML';
request_object.pgCode =
- webworkConfig?.pgCodeMirror?.getValue() ?? document.getElementById('problemContents')?.value ?? '';
+ webworkConfig?.pgCodeMirror?.source ?? document.getElementById('problemContents')?.value ?? '';
fetch(webserviceURL, { method: 'post', mode: 'same-origin', body: new URLSearchParams(request_object) })
.then((response) => response.json())
@@ -169,7 +222,7 @@
if (request_object.pgCode === data.result_data.pgmlCode) {
showMessage('There were no changes to the code.', true);
} else {
- if (webworkConfig?.pgCodeMirror) webworkConfig.pgCodeMirror.setValue(data.result_data.pgmlCode);
+ if (webworkConfig?.pgCodeMirror) webworkConfig.pgCodeMirror.source = data.result_data.pgmlCode;
else document.getElementById('problemContents').value = data.result_data.pgmlCode;
saveTempFile();
showMessage('Successfully converted code to PGML', true);
@@ -252,9 +305,9 @@
const renderArea = document.getElementById('pgedit-render-area');
const fileType = document.getElementsByName('file_type')[0]?.value;
- // This is either the div created by the CodeMirror editor or the problemContents textarea in the case that
+ // This is either the div containing the CodeMirror editor or the problemContents textarea in the case that
// CodeMirror is disabled in localOverrides.conf.
- const editorArea = document.querySelector('.CodeMirror') ?? document.getElementById('problemContents');
+ const editorArea = document.querySelector('.code-mirror-editor') ?? document.getElementById('problemContents');
// Add hot key, ctrl-enter, to render the problem
editorArea.addEventListener('keydown', async (e) => {
@@ -279,8 +332,7 @@
if (window.getComputedStyle(renderArea).getPropertyValue('height') !== `${height}px`)
renderArea.style.height = `${height}px`;
if (window.getComputedStyle(editorArea).getPropertyValue('height') !== `${height}px`) {
- if (webworkConfig?.pgCodeMirror) webworkConfig.pgCodeMirror.setSize('100%', `${height}px`);
- else editorArea.style.height = `${height}px`;
+ editorArea.style.height = `${height}px`;
}
}
}
@@ -321,7 +373,7 @@
const requestData = new URLSearchParams(new FormData(problemForm));
requestData.set(
'rawProblemSource',
- webworkConfig?.pgCodeMirror?.getValue() ?? document.getElementById('problemContents')?.value ?? ''
+ webworkConfig?.pgCodeMirror?.source ?? document.getElementById('problemContents')?.value ?? ''
);
requestData.set('send_pg_flags', 1);
requestData.set(button.name, button.value);
@@ -352,7 +404,7 @@
}
if (fileType === 'course_info') {
- const contents = webworkConfig?.pgCodeMirror?.getValue();
+ const contents = webworkConfig?.pgCodeMirror?.source;
if (contents) renderArea.innerHTML = `${contents}
`;
else
renderArea.innerHTML =
@@ -370,7 +422,7 @@
}
if (fileType === 'hardcopy_theme') {
- const contents = webworkConfig?.pgCodeMirror?.getValue();
+ const contents = webworkConfig?.pgCodeMirror?.source;
if (contents) {
renderArea.innerHTML = '' + contents.replace(/&/g, '&').replace(/';
} else
@@ -396,10 +448,8 @@
problemSeed: document.getElementById('action_view_seed_id')?.value ?? 1,
sourceFilePath: document.getElementsByName('edit_file_path')[0]?.value,
rawProblemSource:
- webworkConfig?.pgCodeMirror?.getValue() ??
- document.getElementById('problemContents')?.value ??
- '',
- outputformat: 'simple',
+ webworkConfig?.pgCodeMirror?.source ?? document.getElementById('problemContents')?.value ?? '',
+ outputformat: 'debug',
showAnswerNumbers: 0,
// The set id is really only needed by set headers to get the correct dates for the set.
set_id: document.getElementsByName('hidden_set_id')[0]?.value ?? 'Unknown Set',
@@ -508,9 +558,7 @@
problemSeed: document.getElementById('action_hardcopy_seed_id')?.value ?? 1,
sourceFilePath: document.getElementsByName('edit_file_path')[0]?.value,
rawProblemSource:
- webworkConfig?.pgCodeMirror?.getValue() ??
- document.getElementById('problemContents')?.value ??
- '',
+ webworkConfig?.pgCodeMirror?.source ?? document.getElementById('problemContents')?.value ?? '',
outputformat: document.getElementById('action_hardcopy_format_id')?.value ?? 'pdf',
hardcopy_theme: document.getElementById('action_hardcopy_theme_id')?.value ?? 'oneColumn',
// The set id is really only needed by set headers to get the correct dates for the set.
diff --git a/htdocs/js/Problem/problem.js b/htdocs/js/Problem/problem.js
index 26baf236f1..a3b8777ac3 100644
--- a/htdocs/js/Problem/problem.js
+++ b/htdocs/js/Problem/problem.js
@@ -4,9 +4,4 @@
const bsToast = new bootstrap.Toast(toast, { delay: 5000 });
bsToast.show();
});
-
- // Prevent problems which are disabled from acting as links
- $('.problem-list .disabled-problem')
- .addClass('disabled')
- .on('click', (e) => e.preventDefault());
})();
diff --git a/htdocs/js/ProblemSetList/problemsetlist.js b/htdocs/js/ProblemSetList/problemsetlist.js
index c2802ebc9e..03eacc7b09 100644
--- a/htdocs/js/ProblemSetList/problemsetlist.js
+++ b/htdocs/js/ProblemSetList/problemsetlist.js
@@ -158,6 +158,7 @@
const datetimeFormats = {
en: 'L/d/yy, h:mm a',
'en-US': 'L/d/yy, h:mm a',
+ 'en-GB': 'dd/LL/yyyy, HH:mm',
'cs-CZ': 'dd.LL.yy H:mm',
de: 'dd.LL.yy, HH:mm',
el: 'd/L/yy, h:mm a',
@@ -178,17 +179,24 @@
if (importDateShift) {
luxon.Settings.defaultLocale = importDateShift.dataset.locale ?? 'en';
- // Compute the time difference between the current browser timezone and the course timezone.
+ // Compute the time difference between a time in the browser timezone and the same time in the course timezone.
// flatpickr gives the time in the browser's timezone, and this is used to adjust to the course timezone.
- // Note that this is in seconds.
- const timezoneAdjustment =
- new Date(new Date().toLocaleString('en-US')).getTime() -
- new Date(
- new Date().toLocaleString('en-US', { timeZone: importDateShift.dataset.timezone ?? 'America/New_York' })
- ).getTime();
+ // Note that the input time is in seconds and output times is in milliseconds.
+ const timezoneAdjustment = (time) => {
+ const dateTime = new Date(0);
+ dateTime.setUTCSeconds(time);
+ return (
+ new Date(dateTime.toLocaleString('en-US')).getTime() -
+ new Date(
+ dateTime.toLocaleString('en-US', {
+ timeZone: importDateShift.dataset.timezone ?? 'America/New_York'
+ })
+ ).getTime()
+ );
+ };
let fallbackDate = importDateShift.value
- ? new Date(parseInt(importDateShift.value) * 1000 - timezoneAdjustment)
+ ? new Date(parseInt(importDateShift.value) * 1000 - timezoneAdjustment(parseInt(importDateShift.value)))
: new Date();
const fp = flatpickr(importDateShift.parentNode, {
@@ -247,7 +255,7 @@
parseDate(datestr, format) {
// Deal with the case of a unix timestamp. The timezone needs to be adjusted back as this is for
// the unix timestamp stored in the hidden input whose value will be sent to the server.
- if (format === 'U') return new Date(parseInt(datestr) * 1000 - timezoneAdjustment);
+ if (format === 'U') return new Date(parseInt(datestr) * 1000 - timezoneAdjustment(parseInt(datestr)));
// Next attempt to parse the datestr with the current format. This should not be adjusted. It is
// for display only.
@@ -262,7 +270,7 @@
formatDate(date, format) {
// In this case the date provided is in the browser's time zone. So it needs to be adjusted to the
// timezone of the course.
- if (format === 'U') return (date.getTime() + timezoneAdjustment) / 1000;
+ if (format === 'U') return (date.getTime() + timezoneAdjustment(date.getTime() / 1000)) / 1000;
return luxon.DateTime.fromMillis(date.getTime()).toFormat(
datetimeFormats[luxon.Settings.defaultLocale]
diff --git a/htdocs/js/SampleProblemViewer/sample-problem.js b/htdocs/js/SampleProblemViewer/sample-problem.js
index 7907504d2f..aea4fd72f7 100644
--- a/htdocs/js/SampleProblemViewer/sample-problem.js
+++ b/htdocs/js/SampleProblemViewer/sample-problem.js
@@ -1,5 +1,5 @@
-for (const pre of document.body.querySelectorAll('pre.CodeMirror')) {
- CodeMirror.runMode(pre.textContent, 'PG', pre);
+for (const pre of document.body.querySelectorAll('pre.PGCodeMirror')) {
+ PGCodeMirrorEditor.runMode(pre.textContent, pre);
}
for (const btn of document.querySelectorAll('.clipboard-btn')) {
diff --git a/htdocs/js/SampleProblemViewer/sample-problem.scss b/htdocs/js/SampleProblemViewer/sample-problem.scss
index 4d42527785..f94287b838 100644
--- a/htdocs/js/SampleProblemViewer/sample-problem.scss
+++ b/htdocs/js/SampleProblemViewer/sample-problem.scss
@@ -1,4 +1,4 @@
-pre.CodeMirror {
+pre.PGCodeMirror {
background-color: #fcfaf1;
}
diff --git a/htdocs/js/System/system.js b/htdocs/js/System/system.js
index 9e4aff02bc..634f07a87c 100644
--- a/htdocs/js/System/system.js
+++ b/htdocs/js/System/system.js
@@ -1,17 +1,3 @@
-/* WeBWorK Online Homework Delivery System
- * Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of either: (a) the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version, or (b) the "Artistic License" which comes with this package.
- *
- * This program 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 either the GNU General Public License or the
- * Artistic License for more details.
- */
-
(() => {
// Enable site-navigation menu toggling if the page has a site-navigation element.
const navigation_element = document.getElementById('site-navigation');
@@ -117,18 +103,4 @@
messages.forEach((message) => bootstrap.Alert.getOrCreateInstance(message)?.close())
);
}
-
- // Accessibility
- // Present the contents of the data-alt attribute as alternative content for screen reader users.
- // The icon should be formatted as
- // FIXME: Don't add these by javascript. Just add these in place instead.
- document.querySelectorAll('i.icon').forEach((icon) => {
- if (typeof icon.dataset.alt !== 'undefined') {
- const glyph = document.createElement('span');
- glyph.classList.add('visually-hidden');
- glyph.style.fontSize = icon.style.fontSize;
- glyph.textContent = icon.dataset.alt;
- icon.after(glyph);
- }
- });
})();
diff --git a/htdocs/js/System/system.scss b/htdocs/js/System/system.scss
index 6f60b1b4ab..c6a9468d58 100644
--- a/htdocs/js/System/system.scss
+++ b/htdocs/js/System/system.scss
@@ -1,17 +1,3 @@
-/* WeBWorK Online Homework Delivery System
- * Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of either: (a) the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version, or (b) the "Artistic License" which comes with this package.
- *
- * This program 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 either the GNU General Public License or the
- * Artistic License for more details.
- */
-
/* General styles */
table caption {
@@ -809,7 +795,7 @@ input.changed[type='text'] {
#pgedit-render-area {
border: 1px solid #ddd;
min-height: 400px;
- height: 550px;
+ height: 600px;
resize: vertical;
display: flex;
flex-direction: column;
diff --git a/htdocs/js/UserList/userlist.js b/htdocs/js/UserList/userlist.js
index 74f16a1596..617594371f 100644
--- a/htdocs/js/UserList/userlist.js
+++ b/htdocs/js/UserList/userlist.js
@@ -113,7 +113,10 @@
e.preventDefault();
e.stopPropagation();
show_errors(['select_user_err_msg'], [export_select]);
- } else if (export_select_target?.value === 'new' && export_filename.value === '') {
+ } else if (
+ export_select_target?.value === 'new' &&
+ (export_filename.value === '' || /\//.test(export_filename.value))
+ ) {
e.preventDefault();
e.stopPropagation();
show_errors(['export_file_err_msg'], [export_filename, export_select_target]);
diff --git a/htdocs/package-lock.json b/htdocs/package-lock.json
index 3b186ab181..1680182cef 100644
--- a/htdocs/package-lock.json
+++ b/htdocs/package-lock.json
@@ -1,2777 +1,3582 @@
{
- "name": "webwork.javascript_package_manager",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {
- "": {
- "name": "webwork.javascript_package_manager",
- "license": "GPL-2.0+",
- "dependencies": {
- "@fortawesome/fontawesome-free": "^6.5.2",
- "bootstrap": "~5.3.3",
- "codemirror": "^5.65.15",
- "flatpickr": "^4.6.13",
- "iframe-resizer": "^4.3.11",
- "jquery": "^3.7.1",
- "jquery-ui-dist": "^1.13.2",
- "luxon": "^3.4.4",
- "mathjax": "^3.2.2",
- "shortcut-buttons-flatpickr": "^0.4.0",
- "sortablejs": "^1.15.2"
- },
- "devDependencies": {
- "autoprefixer": "^10.4.19",
- "chokidar": "^3.6.0",
- "cssnano": "^6.1.2",
- "postcss": "^8.4.38",
- "prettier": "^3.2.5",
- "rtlcss": "^4.1.1",
- "sass": "^1.75.0",
- "terser": "^5.30.4",
- "yargs": "^17.7.2"
- }
- },
- "node_modules/@fortawesome/fontawesome-free": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
- "integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==",
- "hasInstallScript": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
- "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
- "dev": true,
- "dependencies": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true,
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "dev": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.4.15",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
- "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
- "dev": true
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@popperjs/core": {
- "version": "2.11.8",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
- "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
- "peer": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
- "node_modules/@trysound/sax": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
- "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
- "dev": true,
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
- "dev": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dev": true,
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/autoprefixer": {
- "version": "10.4.19",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
- "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/autoprefixer"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "browserslist": "^4.23.0",
- "caniuse-lite": "^1.0.30001599",
- "fraction.js": "^4.3.7",
- "normalize-range": "^0.1.2",
- "picocolors": "^1.0.0",
- "postcss-value-parser": "^4.2.0"
- },
- "bin": {
- "autoprefixer": "bin/autoprefixer"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- },
- "peerDependencies": {
- "postcss": "^8.1.0"
- }
- },
- "node_modules/binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/boolbase": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
- "dev": true
- },
- "node_modules/bootstrap": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
- "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/twbs"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/bootstrap"
- }
- ],
- "peerDependencies": {
- "@popperjs/core": "^2.11.8"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/browserslist": {
- "version": "4.23.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
- "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "caniuse-lite": "^1.0.30001587",
- "electron-to-chromium": "^1.4.668",
- "node-releases": "^2.0.14",
- "update-browserslist-db": "^1.0.13"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true
- },
- "node_modules/caniuse-api": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
- "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.0.0",
- "caniuse-lite": "^1.0.0",
- "lodash.memoize": "^4.1.2",
- "lodash.uniq": "^4.5.0"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001612",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
- "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ]
- },
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dev": true,
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/codemirror": {
- "version": "5.65.16",
- "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz",
- "integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg=="
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "node_modules/colord": {
- "version": "2.9.3",
- "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
- "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
- "dev": true
- },
- "node_modules/commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
- "dev": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/css-declaration-sorter": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
- "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==",
- "dev": true,
- "engines": {
- "node": "^14 || ^16 || >=18"
- },
- "peerDependencies": {
- "postcss": "^8.0.9"
- }
- },
- "node_modules/css-select": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
- "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
- "dev": true,
- "dependencies": {
- "boolbase": "^1.0.0",
- "css-what": "^6.1.0",
- "domhandler": "^5.0.2",
- "domutils": "^3.0.1",
- "nth-check": "^2.0.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/fb55"
- }
- },
- "node_modules/css-tree": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
- "dev": true,
- "dependencies": {
- "mdn-data": "2.0.30",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
- }
- },
- "node_modules/css-what": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
- "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
- "dev": true,
- "engines": {
- "node": ">= 6"
- },
- "funding": {
- "url": "https://github.com/sponsors/fb55"
- }
- },
- "node_modules/cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "dev": true,
- "bin": {
- "cssesc": "bin/cssesc"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/cssnano": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz",
- "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==",
- "dev": true,
- "dependencies": {
- "cssnano-preset-default": "^6.1.2",
- "lilconfig": "^3.1.1"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/cssnano"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/cssnano-preset-default": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz",
- "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.23.0",
- "css-declaration-sorter": "^7.2.0",
- "cssnano-utils": "^4.0.2",
- "postcss-calc": "^9.0.1",
- "postcss-colormin": "^6.1.0",
- "postcss-convert-values": "^6.1.0",
- "postcss-discard-comments": "^6.0.2",
- "postcss-discard-duplicates": "^6.0.3",
- "postcss-discard-empty": "^6.0.3",
- "postcss-discard-overridden": "^6.0.2",
- "postcss-merge-longhand": "^6.0.5",
- "postcss-merge-rules": "^6.1.1",
- "postcss-minify-font-values": "^6.1.0",
- "postcss-minify-gradients": "^6.0.3",
- "postcss-minify-params": "^6.1.0",
- "postcss-minify-selectors": "^6.0.4",
- "postcss-normalize-charset": "^6.0.2",
- "postcss-normalize-display-values": "^6.0.2",
- "postcss-normalize-positions": "^6.0.2",
- "postcss-normalize-repeat-style": "^6.0.2",
- "postcss-normalize-string": "^6.0.2",
- "postcss-normalize-timing-functions": "^6.0.2",
- "postcss-normalize-unicode": "^6.1.0",
- "postcss-normalize-url": "^6.0.2",
- "postcss-normalize-whitespace": "^6.0.2",
- "postcss-ordered-values": "^6.0.2",
- "postcss-reduce-initial": "^6.1.0",
- "postcss-reduce-transforms": "^6.0.2",
- "postcss-svgo": "^6.0.3",
- "postcss-unique-selectors": "^6.0.4"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/cssnano-utils": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz",
- "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==",
- "dev": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/csso": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
- "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
- "dev": true,
- "dependencies": {
- "css-tree": "~2.2.0"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
- "npm": ">=7.0.0"
- }
- },
- "node_modules/csso/node_modules/css-tree": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
- "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
- "dev": true,
- "dependencies": {
- "mdn-data": "2.0.28",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
- "npm": ">=7.0.0"
- }
- },
- "node_modules/csso/node_modules/mdn-data": {
- "version": "2.0.28",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
- "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
- "dev": true
- },
- "node_modules/dom-serializer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
- "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
- "dev": true,
- "dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.2",
- "entities": "^4.2.0"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
- }
- },
- "node_modules/domelementtype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ]
- },
- "node_modules/domhandler": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
- "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
- "dev": true,
- "dependencies": {
- "domelementtype": "^2.3.0"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "node_modules/domutils": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
- "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
- "dev": true,
- "dependencies": {
- "dom-serializer": "^2.0.0",
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3"
- },
- "funding": {
- "url": "https://github.com/fb55/domutils?sponsor=1"
- }
- },
- "node_modules/electron-to-chromium": {
- "version": "1.4.747",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.747.tgz",
- "integrity": "sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw==",
- "dev": true
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "dev": true,
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/flatpickr": {
- "version": "4.6.13",
- "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
- "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw=="
- },
- "node_modules/fraction.js": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
- "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
- "dev": true,
- "engines": {
- "node": "*"
- },
- "funding": {
- "type": "patreon",
- "url": "https://github.com/sponsors/rawify"
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true,
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/iframe-resizer": {
- "version": "4.3.11",
- "resolved": "https://registry.npmjs.org/iframe-resizer/-/iframe-resizer-4.3.11.tgz",
- "integrity": "sha512-5QtnsmfH11GDsuC7Gxd/eNzojudX3346Gb0E+Ku8ln8AtfSq+cWCZtnhCrthrtE7f1CI2/kwHkZ9G4sFYzHP7A==",
- "engines": {
- "node": ">=0.8.0"
- },
- "funding": {
- "type": "individual",
- "url": "https://github.com/davidjbradshaw/iframe-resizer/blob/master/FUNDING.md"
- }
- },
- "node_modules/immutable": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
- "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
- "dev": true
- },
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/jquery": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
- "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
- },
- "node_modules/jquery-ui-dist": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.13.2.tgz",
- "integrity": "sha512-oVDRd1NLtTbBwpRKAYdIRgpWVDzeBhfy7Gu0RmY6JEaZtmBq6kDn1pm5SgDiAotrnDS+RoTRXO6xvcNTxA9tOA==",
- "dependencies": {
- "jquery": ">=1.8.0 <4.0.0"
- }
- },
- "node_modules/lilconfig": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
- "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
- "dev": true,
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/antonk52"
- }
- },
- "node_modules/lodash.memoize": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
- "dev": true
- },
- "node_modules/lodash.uniq": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
- "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
- "dev": true
- },
- "node_modules/luxon": {
- "version": "3.4.4",
- "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz",
- "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/mathjax": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-3.2.2.tgz",
- "integrity": "sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw=="
- },
- "node_modules/mdn-data": {
- "version": "2.0.30",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
- "dev": true
- },
- "node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/node-releases": {
- "version": "2.0.14",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
- "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
- "dev": true
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nth-check": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "dev": true,
- "dependencies": {
- "boolbase": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/fb55/nth-check?sponsor=1"
- }
- },
- "node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/postcss": {
- "version": "8.4.38",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
- "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.2.0"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/postcss-calc": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz",
- "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
- "dev": true,
- "dependencies": {
- "postcss-selector-parser": "^6.0.11",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.2"
- }
- },
- "node_modules/postcss-colormin": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz",
- "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.23.0",
- "caniuse-api": "^3.0.0",
- "colord": "^2.9.3",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-convert-values": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz",
- "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.23.0",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-discard-comments": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz",
- "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==",
- "dev": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-discard-duplicates": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz",
- "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==",
- "dev": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-discard-empty": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz",
- "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==",
- "dev": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-discard-overridden": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz",
- "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==",
- "dev": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-merge-longhand": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz",
- "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0",
- "stylehacks": "^6.1.1"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-merge-rules": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz",
- "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.23.0",
- "caniuse-api": "^3.0.0",
- "cssnano-utils": "^4.0.2",
- "postcss-selector-parser": "^6.0.16"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-minify-font-values": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz",
- "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-minify-gradients": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz",
- "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==",
- "dev": true,
- "dependencies": {
- "colord": "^2.9.3",
- "cssnano-utils": "^4.0.2",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-minify-params": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz",
- "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.23.0",
- "cssnano-utils": "^4.0.2",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-minify-selectors": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz",
- "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==",
- "dev": true,
- "dependencies": {
- "postcss-selector-parser": "^6.0.16"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-charset": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz",
- "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==",
- "dev": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-display-values": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz",
- "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-positions": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz",
- "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-repeat-style": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz",
- "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-string": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz",
- "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-timing-functions": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz",
- "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-unicode": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz",
- "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.23.0",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-url": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz",
- "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-whitespace": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz",
- "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-ordered-values": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz",
- "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==",
- "dev": true,
- "dependencies": {
- "cssnano-utils": "^4.0.2",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-reduce-initial": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz",
- "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.23.0",
- "caniuse-api": "^3.0.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-reduce-transforms": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz",
- "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-selector-parser": {
- "version": "6.0.16",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
- "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
- "dev": true,
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/postcss-svgo": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz",
- "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==",
- "dev": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0",
- "svgo": "^3.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >= 18"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-unique-selectors": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz",
- "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==",
- "dev": true,
- "dependencies": {
- "postcss-selector-parser": "^6.0.16"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "dev": true
- },
- "node_modules/prettier": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
- "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
- "dev": true,
- "bin": {
- "prettier": "bin/prettier.cjs"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/rtlcss": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz",
- "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==",
- "dev": true,
- "dependencies": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0",
- "postcss": "^8.4.21",
- "strip-json-comments": "^3.1.1"
- },
- "bin": {
- "rtlcss": "bin/rtlcss.js"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/sass": {
- "version": "1.75.0",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
- "integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
- "dev": true,
- "dependencies": {
- "chokidar": ">=3.0.0 <4.0.0",
- "immutable": "^4.0.0",
- "source-map-js": ">=0.6.2 <2.0.0"
- },
- "bin": {
- "sass": "sass.js"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/shortcut-buttons-flatpickr": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/shortcut-buttons-flatpickr/-/shortcut-buttons-flatpickr-0.4.0.tgz",
- "integrity": "sha512-JKmT4my3Hm1e18OvG4Q6RcFhN4WRqqpTMkHrvZ7fup/dp6aTIWGVCHdRYtASkp/FCzDlJh6iCLQ/VcwwNpAMoQ=="
- },
- "node_modules/sortablejs": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz",
- "integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA=="
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
- "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/stylehacks": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz",
- "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.23.0",
- "postcss-selector-parser": "^6.0.16"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/svgo": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz",
- "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==",
- "dev": true,
- "dependencies": {
- "@trysound/sax": "0.2.0",
- "commander": "^7.2.0",
- "css-select": "^5.1.0",
- "css-tree": "^2.3.1",
- "css-what": "^6.1.0",
- "csso": "^5.0.5",
- "picocolors": "^1.0.0"
- },
- "bin": {
- "svgo": "bin/svgo"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/svgo"
- }
- },
- "node_modules/terser": {
- "version": "5.30.4",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
- "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
- "dev": true,
- "dependencies": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "bin": {
- "terser": "bin/terser"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/terser/node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/update-browserslist-db": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
- "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
- },
- "bin": {
- "update-browserslist-db": "cli.js"
- },
- "peerDependencies": {
- "browserslist": ">= 4.21.0"
- }
- },
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true
- },
- "node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "dev": true,
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- }
- },
- "dependencies": {
- "@fortawesome/fontawesome-free": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
- "integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q=="
- },
- "@jridgewell/gen-mapping": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
- "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
- "dev": true,
- "requires": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true
- },
- "@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true
- },
- "@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "dev": true,
- "requires": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25"
- }
- },
- "@jridgewell/sourcemap-codec": {
- "version": "1.4.15",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
- "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
- "dev": true
- },
- "@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
- "requires": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "@popperjs/core": {
- "version": "2.11.8",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
- "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
- "peer": true
- },
- "@trysound/sax": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
- "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
- "dev": true
- },
- "acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
- "dev": true
- },
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
- "autoprefixer": {
- "version": "10.4.19",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
- "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "caniuse-lite": "^1.0.30001599",
- "fraction.js": "^4.3.7",
- "normalize-range": "^0.1.2",
- "picocolors": "^1.0.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "dev": true
- },
- "boolbase": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
- "dev": true
- },
- "bootstrap": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
- "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
- "requires": {}
- },
- "braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
- "requires": {
- "fill-range": "^7.1.1"
- }
- },
- "browserslist": {
- "version": "4.23.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
- "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001587",
- "electron-to-chromium": "^1.4.668",
- "node-releases": "^2.0.14",
- "update-browserslist-db": "^1.0.13"
- }
- },
- "buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true
- },
- "caniuse-api": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
- "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
- "dev": true,
- "requires": {
- "browserslist": "^4.0.0",
- "caniuse-lite": "^1.0.0",
- "lodash.memoize": "^4.1.2",
- "lodash.uniq": "^4.5.0"
- }
- },
- "caniuse-lite": {
- "version": "1.0.30001612",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
- "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
- "dev": true
- },
- "chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
- "requires": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "fsevents": "~2.3.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- }
- },
- "cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dev": true,
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- }
- },
- "codemirror": {
- "version": "5.65.16",
- "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz",
- "integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg=="
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "colord": {
- "version": "2.9.3",
- "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
- "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
- "dev": true
- },
- "commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
- "dev": true
- },
- "css-declaration-sorter": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
- "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==",
- "dev": true,
- "requires": {}
- },
- "css-select": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
- "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
- "dev": true,
- "requires": {
- "boolbase": "^1.0.0",
- "css-what": "^6.1.0",
- "domhandler": "^5.0.2",
- "domutils": "^3.0.1",
- "nth-check": "^2.0.1"
- }
- },
- "css-tree": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
- "dev": true,
- "requires": {
- "mdn-data": "2.0.30",
- "source-map-js": "^1.0.1"
- }
- },
- "css-what": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
- "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
- "dev": true
- },
- "cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "dev": true
- },
- "cssnano": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz",
- "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==",
- "dev": true,
- "requires": {
- "cssnano-preset-default": "^6.1.2",
- "lilconfig": "^3.1.1"
- }
- },
- "cssnano-preset-default": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz",
- "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "css-declaration-sorter": "^7.2.0",
- "cssnano-utils": "^4.0.2",
- "postcss-calc": "^9.0.1",
- "postcss-colormin": "^6.1.0",
- "postcss-convert-values": "^6.1.0",
- "postcss-discard-comments": "^6.0.2",
- "postcss-discard-duplicates": "^6.0.3",
- "postcss-discard-empty": "^6.0.3",
- "postcss-discard-overridden": "^6.0.2",
- "postcss-merge-longhand": "^6.0.5",
- "postcss-merge-rules": "^6.1.1",
- "postcss-minify-font-values": "^6.1.0",
- "postcss-minify-gradients": "^6.0.3",
- "postcss-minify-params": "^6.1.0",
- "postcss-minify-selectors": "^6.0.4",
- "postcss-normalize-charset": "^6.0.2",
- "postcss-normalize-display-values": "^6.0.2",
- "postcss-normalize-positions": "^6.0.2",
- "postcss-normalize-repeat-style": "^6.0.2",
- "postcss-normalize-string": "^6.0.2",
- "postcss-normalize-timing-functions": "^6.0.2",
- "postcss-normalize-unicode": "^6.1.0",
- "postcss-normalize-url": "^6.0.2",
- "postcss-normalize-whitespace": "^6.0.2",
- "postcss-ordered-values": "^6.0.2",
- "postcss-reduce-initial": "^6.1.0",
- "postcss-reduce-transforms": "^6.0.2",
- "postcss-svgo": "^6.0.3",
- "postcss-unique-selectors": "^6.0.4"
- }
- },
- "cssnano-utils": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz",
- "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==",
- "dev": true,
- "requires": {}
- },
- "csso": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
- "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
- "dev": true,
- "requires": {
- "css-tree": "~2.2.0"
- },
- "dependencies": {
- "css-tree": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
- "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
- "dev": true,
- "requires": {
- "mdn-data": "2.0.28",
- "source-map-js": "^1.0.1"
- }
- },
- "mdn-data": {
- "version": "2.0.28",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
- "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
- "dev": true
- }
- }
- },
- "dom-serializer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
- "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.2",
- "entities": "^4.2.0"
- }
- },
- "domelementtype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
- "dev": true
- },
- "domhandler": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
- "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.3.0"
- }
- },
- "domutils": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
- "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
- "dev": true,
- "requires": {
- "dom-serializer": "^2.0.0",
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3"
- }
- },
- "electron-to-chromium": {
- "version": "1.4.747",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.747.tgz",
- "integrity": "sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw==",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "dev": true
- },
- "escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
- "dev": true
- },
- "fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "flatpickr": {
- "version": "4.6.13",
- "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
- "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw=="
- },
- "fraction.js": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
- "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
- "dev": true
- },
- "fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "optional": true
- },
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
- },
- "glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "iframe-resizer": {
- "version": "4.3.11",
- "resolved": "https://registry.npmjs.org/iframe-resizer/-/iframe-resizer-4.3.11.tgz",
- "integrity": "sha512-5QtnsmfH11GDsuC7Gxd/eNzojudX3346Gb0E+Ku8ln8AtfSq+cWCZtnhCrthrtE7f1CI2/kwHkZ9G4sFYzHP7A=="
- },
- "immutable": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
- "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
- "dev": true
- },
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
- "is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "requires": {
- "is-extglob": "^2.1.1"
- }
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "jquery": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
- "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
- },
- "jquery-ui-dist": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.13.2.tgz",
- "integrity": "sha512-oVDRd1NLtTbBwpRKAYdIRgpWVDzeBhfy7Gu0RmY6JEaZtmBq6kDn1pm5SgDiAotrnDS+RoTRXO6xvcNTxA9tOA==",
- "requires": {
- "jquery": ">=1.8.0 <4.0.0"
- }
- },
- "lilconfig": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
- "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
- "dev": true
- },
- "lodash.memoize": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
- "dev": true
- },
- "lodash.uniq": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
- "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
- "dev": true
- },
- "luxon": {
- "version": "3.4.4",
- "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz",
- "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA=="
- },
- "mathjax": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-3.2.2.tgz",
- "integrity": "sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw=="
- },
- "mdn-data": {
- "version": "2.0.30",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
- "dev": true
- },
- "nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "dev": true
- },
- "node-releases": {
- "version": "2.0.14",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
- "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
- "dev": true
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- },
- "normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
- "dev": true
- },
- "nth-check": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "dev": true,
- "requires": {
- "boolbase": "^1.0.0"
- }
- },
- "picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true
- },
- "postcss": {
- "version": "8.4.38",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
- "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
- "dev": true,
- "requires": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.2.0"
- }
- },
- "postcss-calc": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz",
- "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
- "dev": true,
- "requires": {
- "postcss-selector-parser": "^6.0.11",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-colormin": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz",
- "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "caniuse-api": "^3.0.0",
- "colord": "^2.9.3",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-convert-values": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz",
- "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-discard-comments": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz",
- "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==",
- "dev": true,
- "requires": {}
- },
- "postcss-discard-duplicates": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz",
- "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==",
- "dev": true,
- "requires": {}
- },
- "postcss-discard-empty": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz",
- "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==",
- "dev": true,
- "requires": {}
- },
- "postcss-discard-overridden": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz",
- "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==",
- "dev": true,
- "requires": {}
- },
- "postcss-merge-longhand": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz",
- "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0",
- "stylehacks": "^6.1.1"
- }
- },
- "postcss-merge-rules": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz",
- "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "caniuse-api": "^3.0.0",
- "cssnano-utils": "^4.0.2",
- "postcss-selector-parser": "^6.0.16"
- }
- },
- "postcss-minify-font-values": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz",
- "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-gradients": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz",
- "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==",
- "dev": true,
- "requires": {
- "colord": "^2.9.3",
- "cssnano-utils": "^4.0.2",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-params": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz",
- "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "cssnano-utils": "^4.0.2",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-selectors": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz",
- "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==",
- "dev": true,
- "requires": {
- "postcss-selector-parser": "^6.0.16"
- }
- },
- "postcss-normalize-charset": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz",
- "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==",
- "dev": true,
- "requires": {}
- },
- "postcss-normalize-display-values": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz",
- "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-positions": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz",
- "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-repeat-style": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz",
- "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-string": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz",
- "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-timing-functions": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz",
- "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-unicode": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz",
- "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-url": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz",
- "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-whitespace": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz",
- "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-ordered-values": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz",
- "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==",
- "dev": true,
- "requires": {
- "cssnano-utils": "^4.0.2",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-reduce-initial": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz",
- "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "caniuse-api": "^3.0.0"
- }
- },
- "postcss-reduce-transforms": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz",
- "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-selector-parser": {
- "version": "6.0.16",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
- "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
- "dev": true,
- "requires": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- }
- },
- "postcss-svgo": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz",
- "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0",
- "svgo": "^3.2.0"
- }
- },
- "postcss-unique-selectors": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz",
- "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==",
- "dev": true,
- "requires": {
- "postcss-selector-parser": "^6.0.16"
- }
- },
- "postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "dev": true
- },
- "prettier": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
- "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
- "dev": true
- },
- "readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "requires": {
- "picomatch": "^2.2.1"
- }
- },
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true
- },
- "rtlcss": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz",
- "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==",
- "dev": true,
- "requires": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0",
- "postcss": "^8.4.21",
- "strip-json-comments": "^3.1.1"
- }
- },
- "sass": {
- "version": "1.75.0",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
- "integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
- "dev": true,
- "requires": {
- "chokidar": ">=3.0.0 <4.0.0",
- "immutable": "^4.0.0",
- "source-map-js": ">=0.6.2 <2.0.0"
- }
- },
- "shortcut-buttons-flatpickr": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/shortcut-buttons-flatpickr/-/shortcut-buttons-flatpickr-0.4.0.tgz",
- "integrity": "sha512-JKmT4my3Hm1e18OvG4Q6RcFhN4WRqqpTMkHrvZ7fup/dp6aTIWGVCHdRYtASkp/FCzDlJh6iCLQ/VcwwNpAMoQ=="
- },
- "sortablejs": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz",
- "integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA=="
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- },
- "source-map-js": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
- "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
- "dev": true
- },
- "source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- }
- },
- "strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- },
- "strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true
- },
- "stylehacks": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz",
- "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==",
- "dev": true,
- "requires": {
- "browserslist": "^4.23.0",
- "postcss-selector-parser": "^6.0.16"
- }
- },
- "svgo": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz",
- "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==",
- "dev": true,
- "requires": {
- "@trysound/sax": "0.2.0",
- "commander": "^7.2.0",
- "css-select": "^5.1.0",
- "css-tree": "^2.3.1",
- "css-what": "^6.1.0",
- "csso": "^5.0.5",
- "picocolors": "^1.0.0"
- }
- },
- "terser": {
- "version": "5.30.4",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
- "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
- "dev": true,
- "requires": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
- }
- }
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- },
- "update-browserslist-db": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
- "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
- "dev": true,
- "requires": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true
- },
- "wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "dev": true
- },
- "yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "dev": true,
- "requires": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- }
- },
- "yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true
- }
- }
+ "name": "webwork.javascript_package_manager",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "webwork.javascript_package_manager",
+ "license": "GPL-2.0+",
+ "dependencies": {
+ "@fortawesome/fontawesome-free": "^6.5.2",
+ "@openwebwork/pg-codemirror-editor": "^0.0.4",
+ "bootstrap": "~5.3.3",
+ "flatpickr": "^4.6.13",
+ "iframe-resizer": "^4.3.11",
+ "jquery": "^3.7.1",
+ "jquery-ui-dist": "^1.13.2",
+ "luxon": "^3.4.4",
+ "mathjax": "^3.2.2",
+ "shortcut-buttons-flatpickr": "^0.4.0",
+ "sortablejs": "^1.15.2"
+ },
+ "devDependencies": {
+ "autoprefixer": "^10.4.19",
+ "chokidar": "^3.6.0",
+ "cssnano": "^6.1.2",
+ "postcss": "^8.4.38",
+ "prettier": "^3.2.5",
+ "rtlcss": "^4.1.1",
+ "sass": "^1.75.0",
+ "terser": "^5.30.4",
+ "yargs": "^17.7.2"
+ }
+ },
+ "node_modules/@codemirror/autocomplete": {
+ "version": "6.18.6",
+ "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
+ "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/commands": {
+ "version": "6.8.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz",
+ "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.4.0",
+ "@codemirror/view": "^6.27.0",
+ "@lezer/common": "^1.1.0"
+ }
+ },
+ "node_modules/@codemirror/lang-css": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
+ "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@lezer/common": "^1.0.2",
+ "@lezer/css": "^1.1.7"
+ }
+ },
+ "node_modules/@codemirror/lang-html": {
+ "version": "6.4.9",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
+ "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/lang-css": "^6.0.0",
+ "@codemirror/lang-javascript": "^6.0.0",
+ "@codemirror/language": "^6.4.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0",
+ "@lezer/css": "^1.1.0",
+ "@lezer/html": "^1.3.0"
+ }
+ },
+ "node_modules/@codemirror/lang-javascript": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz",
+ "integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/language": "^6.6.0",
+ "@codemirror/lint": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0",
+ "@lezer/javascript": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/lang-xml": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz",
+ "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/language": "^6.4.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/common": "^1.0.0",
+ "@lezer/xml": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/language": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz",
+ "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.23.0",
+ "@lezer/common": "^1.1.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0",
+ "style-mod": "^4.0.0"
+ }
+ },
+ "node_modules/@codemirror/lint": {
+ "version": "6.8.5",
+ "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz",
+ "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.35.0",
+ "crelt": "^1.0.5"
+ }
+ },
+ "node_modules/@codemirror/search": {
+ "version": "6.5.10",
+ "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz",
+ "integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "crelt": "^1.0.5"
+ }
+ },
+ "node_modules/@codemirror/state": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
+ "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
+ "license": "MIT",
+ "dependencies": {
+ "@marijn/find-cluster-break": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/theme-one-dark": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
+ "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/view": {
+ "version": "6.36.7",
+ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.7.tgz",
+ "integrity": "sha512-kCWGW/chWGPgZqfZ36Um9Iz0X2IVpmCjg1P/qY6B6a2ecXtWRRAigmpJ6YgUQ5lTWXMyyVdfmpzhLZmsZQMbtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/state": "^6.5.0",
+ "style-mod": "^4.1.0",
+ "w3c-keyname": "^2.2.4"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-free": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
+ "integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@lezer/common": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz",
+ "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==",
+ "license": "MIT"
+ },
+ "node_modules/@lezer/css": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.11.tgz",
+ "integrity": "sha512-FuAnusbLBl1SEAtfN8NdShxYJiESKw9LAFysfea1T96jD3ydBn12oYjaSG1a04BQRIUd93/0D8e5CV1cUMkmQg==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
+ },
+ "node_modules/@lezer/highlight": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
+ "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "node_modules/@lezer/html": {
+ "version": "1.3.10",
+ "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz",
+ "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
+ },
+ "node_modules/@lezer/javascript": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz",
+ "integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.1.3",
+ "@lezer/lr": "^1.3.0"
+ }
+ },
+ "node_modules/@lezer/lr": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
+ "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "node_modules/@lezer/xml": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz",
+ "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==",
+ "license": "MIT",
+ "dependencies": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
+ },
+ "node_modules/@marijn/find-cluster-break": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
+ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
+ "license": "MIT"
+ },
+ "node_modules/@openwebwork/codemirror-lang-pg": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/@openwebwork/codemirror-lang-pg/-/codemirror-lang-pg-0.0.2.tgz",
+ "integrity": "sha512-xnipB2bydjDbcRVO9XpFNkhI9muoA6FzPqx6gRxQk10cXj8nzOn694gl7ATpn/xn5J5Y+7YpFAFTDqs0atgI6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.11.0",
+ "@lezer/highlight": "^1.2.1",
+ "@lezer/lr": "^1.4.2"
+ }
+ },
+ "node_modules/@openwebwork/pg-codemirror-editor": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@openwebwork/pg-codemirror-editor/-/pg-codemirror-editor-0.0.4.tgz",
+ "integrity": "sha512-aJ3/AmKc1Ck/6zKctETP29QnuKn4PHIWhEZNdCzi49zAI+Ls2wH0JQdPadoj3z4savAxhabsdYOu8WWV7vwx5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/lang-html": "^6.4.9",
+ "@codemirror/lang-xml": "^6.1.0",
+ "@codemirror/theme-one-dark": "^6.1.2",
+ "@openwebwork/codemirror-lang-pg": "^0.0.2",
+ "@replit/codemirror-emacs": "^6.1.0",
+ "@replit/codemirror-vim": "^6.3.0",
+ "cm6-theme-basic-dark": "^0.2.0",
+ "cm6-theme-basic-light": "^0.2.0",
+ "cm6-theme-gruvbox-dark": "^0.2.0",
+ "cm6-theme-gruvbox-light": "^0.2.0",
+ "cm6-theme-material-dark": "^0.2.0",
+ "cm6-theme-nord": "^0.2.0",
+ "cm6-theme-solarized-dark": "^0.2.0",
+ "cm6-theme-solarized-light": "^0.2.0",
+ "codemirror": "^6.0.1",
+ "codemirror-lang-mt": "^0.0.2",
+ "codemirror-lang-perl": "^0.1.5",
+ "style-mod": "^4.1.2",
+ "thememirror": "^2.0.1"
+ }
+ },
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
+ "node_modules/@replit/codemirror-emacs": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz",
+ "integrity": "sha512-74DITnht6Cs6sHg02PQ169IKb1XgtyhI9sLD0JeOFco6Ds18PT+dkD8+DgXBDokne9UIFKsBbKPnpFRAz60/Lw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/autocomplete": "^6.0.2",
+ "@codemirror/commands": "^6.0.0",
+ "@codemirror/search": "^6.0.0",
+ "@codemirror/state": "^6.0.1",
+ "@codemirror/view": "^6.3.0"
+ }
+ },
+ "node_modules/@replit/codemirror-vim": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.3.0.tgz",
+ "integrity": "sha512-aTx931ULAMuJx6xLf7KQDOL7CxD+Sa05FktTDrtLaSy53uj01ll3Zf17JdKsriER248oS55GBzg0CfCTjEneAQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/commands": "6.x.x",
+ "@codemirror/language": "6.x.x",
+ "@codemirror/search": "6.x.x",
+ "@codemirror/state": "6.x.x",
+ "@codemirror/view": "6.x.x"
+ }
+ },
+ "node_modules/@trysound/sax": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
+ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.19",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
+ "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-lite": "^1.0.30001599",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true
+ },
+ "node_modules/bootstrap": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
+ "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/twbs"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/bootstrap"
+ }
+ ],
+ "peerDependencies": {
+ "@popperjs/core": "^2.11.8"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.23.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+ "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001587",
+ "electron-to-chromium": "^1.4.668",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.13"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "node_modules/caniuse-api": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
+ "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.0.0",
+ "caniuse-lite": "^1.0.0",
+ "lodash.memoize": "^4.1.2",
+ "lodash.uniq": "^4.5.0"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001723",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz",
+ "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cm6-theme-basic-dark": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-basic-dark/-/cm6-theme-basic-dark-0.2.0.tgz",
+ "integrity": "sha512-+mNNJecRtxS/KkloMDCQF0oTrT6aFGRZTjnBcdT5UG1pcDO4Brq8l1+0KR/8dZ7hub2gOGOzoi3rGFD8GzlH7Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/cm6-theme-basic-light": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-basic-light/-/cm6-theme-basic-light-0.2.0.tgz",
+ "integrity": "sha512-1prg2gv44sYfpHscP26uLT/ePrh0mlmVwMSoSd3zYKQ92Ab3jPRLzyCnpyOCQLJbK+YdNs4HvMRqMNYdy4pMhA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/cm6-theme-gruvbox-dark": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-gruvbox-dark/-/cm6-theme-gruvbox-dark-0.2.0.tgz",
+ "integrity": "sha512-xyqsG19qV+nb7ZHTMocSNWwZHMExfQxDm0FlbNMqEGKeQR96WryssXJH/IZZQudwrPpWU2dCoyOgMFhti2UTYA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/cm6-theme-gruvbox-light": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-gruvbox-light/-/cm6-theme-gruvbox-light-0.2.0.tgz",
+ "integrity": "sha512-sc4dEMLU5y4F3QGLjwMQs1H3Q0a0ooXA1EvyWnknxLEGQVXwJrxkkV67gs1TqWASl2i63iomt4zyz5pkbfO1yg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/cm6-theme-material-dark": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-material-dark/-/cm6-theme-material-dark-0.2.0.tgz",
+ "integrity": "sha512-H09JZihzg4w0mTtOqo5bQdxItkQWw+ergKlk7BSfwYjaR2nOi+wIN0R+ByAo7bON8GbFODvjTxH3EIqdhovFeA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/cm6-theme-nord": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-nord/-/cm6-theme-nord-0.2.0.tgz",
+ "integrity": "sha512-jTh+5nvl+N/5CtTK7UVcrxDCj2AOStvbNM8uP6tx6amq4QaaLDlapjMw+MNzEkvxcPnHY+YM91tbklS2KNlR2w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/cm6-theme-solarized-dark": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-solarized-dark/-/cm6-theme-solarized-dark-0.2.0.tgz",
+ "integrity": "sha512-FWtYHcX8NLzNSs21yGbkLF+q/5m2u80ug0JytKoI9nMZWPP5dcnsFYp1iZBEegLehiZnpv1qcmTsLTUG2KD39w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/cm6-theme-solarized-light": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-solarized-light/-/cm6-theme-solarized-light-0.2.0.tgz",
+ "integrity": "sha512-Iw7Xv+9A6NlT7sRGlM2pOwD3ZBETkAqpb7c6O0LPj5kjwcK6C3k+mvjzaQt1gzfBErMmhL1HHuK07zICeXkE+w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "node_modules/codemirror": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
+ "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/commands": "^6.0.0",
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/lint": "^6.0.0",
+ "@codemirror/search": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0"
+ }
+ },
+ "node_modules/codemirror-lang-mt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/codemirror-lang-mt/-/codemirror-lang-mt-0.0.2.tgz",
+ "integrity": "sha512-m2q+EVgNeDxq3A8gCnoUGZvTuXvvZKlZliiqif4VAMPiu7dKJsaopvXZo8S1KH6cb2x9fJuKr5yUTnkxSLQZIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/lang-css": "^6.3.1",
+ "@codemirror/lang-html": "^6.4.9",
+ "@codemirror/lang-javascript": "^6.2.3",
+ "@codemirror/language": "^6.11.0",
+ "@lezer/highlight": "^1.2.1",
+ "@lezer/lr": "^1.4.2"
+ }
+ },
+ "node_modules/codemirror-lang-perl": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/codemirror-lang-perl/-/codemirror-lang-perl-0.1.5.tgz",
+ "integrity": "sha512-o0QBzsO4z+ZWaN7ueYnFVYWoFlFvvfgcgNA/dQLxYUDiKGSUY0R52UL/NqTO6swUVrR+O6JI3Xh1j/ed81JIwA==",
+ "license": "MIT",
+ "dependencies": {
+ "@codemirror/language": "^6.11.0",
+ "@lezer/highlight": "^1.2.1",
+ "@lezer/lr": "^1.4.2"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/colord": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+ "dev": true
+ },
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/crelt": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
+ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
+ "license": "MIT"
+ },
+ "node_modules/css-declaration-sorter": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
+ "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.9"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+ "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+ "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+ "dev": true,
+ "dependencies": {
+ "mdn-data": "2.0.30",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cssnano": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz",
+ "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==",
+ "dev": true,
+ "dependencies": {
+ "cssnano-preset-default": "^6.1.2",
+ "lilconfig": "^3.1.1"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/cssnano"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/cssnano-preset-default": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz",
+ "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "css-declaration-sorter": "^7.2.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-calc": "^9.0.1",
+ "postcss-colormin": "^6.1.0",
+ "postcss-convert-values": "^6.1.0",
+ "postcss-discard-comments": "^6.0.2",
+ "postcss-discard-duplicates": "^6.0.3",
+ "postcss-discard-empty": "^6.0.3",
+ "postcss-discard-overridden": "^6.0.2",
+ "postcss-merge-longhand": "^6.0.5",
+ "postcss-merge-rules": "^6.1.1",
+ "postcss-minify-font-values": "^6.1.0",
+ "postcss-minify-gradients": "^6.0.3",
+ "postcss-minify-params": "^6.1.0",
+ "postcss-minify-selectors": "^6.0.4",
+ "postcss-normalize-charset": "^6.0.2",
+ "postcss-normalize-display-values": "^6.0.2",
+ "postcss-normalize-positions": "^6.0.2",
+ "postcss-normalize-repeat-style": "^6.0.2",
+ "postcss-normalize-string": "^6.0.2",
+ "postcss-normalize-timing-functions": "^6.0.2",
+ "postcss-normalize-unicode": "^6.1.0",
+ "postcss-normalize-url": "^6.0.2",
+ "postcss-normalize-whitespace": "^6.0.2",
+ "postcss-ordered-values": "^6.0.2",
+ "postcss-reduce-initial": "^6.1.0",
+ "postcss-reduce-transforms": "^6.0.2",
+ "postcss-svgo": "^6.0.3",
+ "postcss-unique-selectors": "^6.0.4"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/cssnano-utils": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz",
+ "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/csso": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+ "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+ "dev": true,
+ "dependencies": {
+ "css-tree": "~2.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/css-tree": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+ "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+ "dev": true,
+ "dependencies": {
+ "mdn-data": "2.0.28",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/mdn-data": {
+ "version": "2.0.28",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+ "dev": true
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "dev": true,
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.747",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.747.tgz",
+ "integrity": "sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw==",
+ "dev": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/flatpickr": {
+ "version": "4.6.13",
+ "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
+ "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw=="
+ },
+ "node_modules/fraction.js": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/iframe-resizer": {
+ "version": "4.3.11",
+ "resolved": "https://registry.npmjs.org/iframe-resizer/-/iframe-resizer-4.3.11.tgz",
+ "integrity": "sha512-5QtnsmfH11GDsuC7Gxd/eNzojudX3346Gb0E+Ku8ln8AtfSq+cWCZtnhCrthrtE7f1CI2/kwHkZ9G4sFYzHP7A==",
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/davidjbradshaw/iframe-resizer/blob/master/FUNDING.md"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
+ "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
+ "dev": true
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jquery": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
+ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
+ },
+ "node_modules/jquery-ui-dist": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.13.2.tgz",
+ "integrity": "sha512-oVDRd1NLtTbBwpRKAYdIRgpWVDzeBhfy7Gu0RmY6JEaZtmBq6kDn1pm5SgDiAotrnDS+RoTRXO6xvcNTxA9tOA==",
+ "dependencies": {
+ "jquery": ">=1.8.0 <4.0.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
+ "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
+ },
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "dev": true
+ },
+ "node_modules/luxon": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz",
+ "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mathjax": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-3.2.2.tgz",
+ "integrity": "sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw=="
+ },
+ "node_modules/mdn-data": {
+ "version": "2.0.30",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-calc": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz",
+ "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.11",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.2"
+ }
+ },
+ "node_modules/postcss-colormin": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz",
+ "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0",
+ "colord": "^2.9.3",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-convert-values": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz",
+ "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-comments": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz",
+ "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-duplicates": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz",
+ "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-empty": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz",
+ "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-overridden": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz",
+ "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-merge-longhand": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz",
+ "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0",
+ "stylehacks": "^6.1.1"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-merge-rules": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz",
+ "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-font-values": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz",
+ "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-gradients": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz",
+ "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==",
+ "dev": true,
+ "dependencies": {
+ "colord": "^2.9.3",
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-params": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz",
+ "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-selectors": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz",
+ "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-charset": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz",
+ "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-display-values": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz",
+ "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-positions": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz",
+ "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-repeat-style": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz",
+ "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-string": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz",
+ "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-timing-functions": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz",
+ "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-unicode": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz",
+ "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-url": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz",
+ "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-whitespace": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz",
+ "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-ordered-values": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz",
+ "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==",
+ "dev": true,
+ "dependencies": {
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-reduce-initial": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz",
+ "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-reduce-transforms": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz",
+ "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.16",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
+ "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-svgo": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz",
+ "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0",
+ "svgo": "^3.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >= 18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-unique-selectors": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz",
+ "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
+ "node_modules/prettier": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
+ "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rtlcss": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz",
+ "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==",
+ "dev": true,
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.21",
+ "strip-json-comments": "^3.1.1"
+ },
+ "bin": {
+ "rtlcss": "bin/rtlcss.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/sass": {
+ "version": "1.75.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
+ "integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/shortcut-buttons-flatpickr": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/shortcut-buttons-flatpickr/-/shortcut-buttons-flatpickr-0.4.0.tgz",
+ "integrity": "sha512-JKmT4my3Hm1e18OvG4Q6RcFhN4WRqqpTMkHrvZ7fup/dp6aTIWGVCHdRYtASkp/FCzDlJh6iCLQ/VcwwNpAMoQ=="
+ },
+ "node_modules/sortablejs": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz",
+ "integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA=="
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/style-mod": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
+ "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
+ "license": "MIT"
+ },
+ "node_modules/stylehacks": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz",
+ "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/svgo": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz",
+ "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==",
+ "dev": true,
+ "dependencies": {
+ "@trysound/sax": "0.2.0",
+ "commander": "^7.2.0",
+ "css-select": "^5.1.0",
+ "css-tree": "^2.3.1",
+ "css-what": "^6.1.0",
+ "csso": "^5.0.5",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "svgo": "bin/svgo"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/svgo"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.30.4",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
+ "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/thememirror": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/thememirror/-/thememirror-2.0.1.tgz",
+ "integrity": "sha512-d5i6FVvWWPkwrm4cHLI3t9AT1OrkAt7Ig8dtdYSofgF7C/eiyNuq6zQzSTusWTde3jpW9WLvA9J/fzNKMUsd0w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+ "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "node_modules/w3c-keyname": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ }
+ },
+ "dependencies": {
+ "@codemirror/autocomplete": {
+ "version": "6.18.6",
+ "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
+ "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
+ "requires": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "@codemirror/commands": {
+ "version": "6.8.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz",
+ "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
+ "requires": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.4.0",
+ "@codemirror/view": "^6.27.0",
+ "@lezer/common": "^1.1.0"
+ }
+ },
+ "@codemirror/lang-css": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
+ "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
+ "requires": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@lezer/common": "^1.0.2",
+ "@lezer/css": "^1.1.7"
+ }
+ },
+ "@codemirror/lang-html": {
+ "version": "6.4.9",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
+ "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
+ "requires": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/lang-css": "^6.0.0",
+ "@codemirror/lang-javascript": "^6.0.0",
+ "@codemirror/language": "^6.4.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0",
+ "@lezer/css": "^1.1.0",
+ "@lezer/html": "^1.3.0"
+ }
+ },
+ "@codemirror/lang-javascript": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz",
+ "integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==",
+ "requires": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/language": "^6.6.0",
+ "@codemirror/lint": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0",
+ "@lezer/javascript": "^1.0.0"
+ }
+ },
+ "@codemirror/lang-xml": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz",
+ "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==",
+ "requires": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/language": "^6.4.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/common": "^1.0.0",
+ "@lezer/xml": "^1.0.0"
+ }
+ },
+ "@codemirror/language": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz",
+ "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==",
+ "requires": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.23.0",
+ "@lezer/common": "^1.1.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0",
+ "style-mod": "^4.0.0"
+ }
+ },
+ "@codemirror/lint": {
+ "version": "6.8.5",
+ "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz",
+ "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
+ "requires": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.35.0",
+ "crelt": "^1.0.5"
+ }
+ },
+ "@codemirror/search": {
+ "version": "6.5.10",
+ "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz",
+ "integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==",
+ "requires": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "crelt": "^1.0.5"
+ }
+ },
+ "@codemirror/state": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
+ "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
+ "requires": {
+ "@marijn/find-cluster-break": "^1.0.0"
+ }
+ },
+ "@codemirror/theme-one-dark": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
+ "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
+ "requires": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
+ "@codemirror/view": {
+ "version": "6.36.7",
+ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.7.tgz",
+ "integrity": "sha512-kCWGW/chWGPgZqfZ36Um9Iz0X2IVpmCjg1P/qY6B6a2ecXtWRRAigmpJ6YgUQ5lTWXMyyVdfmpzhLZmsZQMbtg==",
+ "requires": {
+ "@codemirror/state": "^6.5.0",
+ "style-mod": "^4.1.0",
+ "w3c-keyname": "^2.2.4"
+ }
+ },
+ "@fortawesome/fontawesome-free": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
+ "integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q=="
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true
+ },
+ "@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "@lezer/common": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz",
+ "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="
+ },
+ "@lezer/css": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.11.tgz",
+ "integrity": "sha512-FuAnusbLBl1SEAtfN8NdShxYJiESKw9LAFysfea1T96jD3ydBn12oYjaSG1a04BQRIUd93/0D8e5CV1cUMkmQg==",
+ "requires": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
+ },
+ "@lezer/highlight": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
+ "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
+ "requires": {
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "@lezer/html": {
+ "version": "1.3.10",
+ "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz",
+ "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
+ "requires": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
+ },
+ "@lezer/javascript": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz",
+ "integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
+ "requires": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.1.3",
+ "@lezer/lr": "^1.3.0"
+ }
+ },
+ "@lezer/lr": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
+ "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
+ "requires": {
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "@lezer/xml": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz",
+ "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==",
+ "requires": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
+ },
+ "@marijn/find-cluster-break": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
+ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
+ },
+ "@openwebwork/codemirror-lang-pg": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/@openwebwork/codemirror-lang-pg/-/codemirror-lang-pg-0.0.2.tgz",
+ "integrity": "sha512-xnipB2bydjDbcRVO9XpFNkhI9muoA6FzPqx6gRxQk10cXj8nzOn694gl7ATpn/xn5J5Y+7YpFAFTDqs0atgI6g==",
+ "requires": {
+ "@codemirror/language": "^6.11.0",
+ "@lezer/highlight": "^1.2.1",
+ "@lezer/lr": "^1.4.2"
+ }
+ },
+ "@openwebwork/pg-codemirror-editor": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@openwebwork/pg-codemirror-editor/-/pg-codemirror-editor-0.0.4.tgz",
+ "integrity": "sha512-aJ3/AmKc1Ck/6zKctETP29QnuKn4PHIWhEZNdCzi49zAI+Ls2wH0JQdPadoj3z4savAxhabsdYOu8WWV7vwx5g==",
+ "requires": {
+ "@codemirror/lang-html": "^6.4.9",
+ "@codemirror/lang-xml": "^6.1.0",
+ "@codemirror/theme-one-dark": "^6.1.2",
+ "@openwebwork/codemirror-lang-pg": "^0.0.2",
+ "@replit/codemirror-emacs": "^6.1.0",
+ "@replit/codemirror-vim": "^6.3.0",
+ "cm6-theme-basic-dark": "^0.2.0",
+ "cm6-theme-basic-light": "^0.2.0",
+ "cm6-theme-gruvbox-dark": "^0.2.0",
+ "cm6-theme-gruvbox-light": "^0.2.0",
+ "cm6-theme-material-dark": "^0.2.0",
+ "cm6-theme-nord": "^0.2.0",
+ "cm6-theme-solarized-dark": "^0.2.0",
+ "cm6-theme-solarized-light": "^0.2.0",
+ "codemirror": "^6.0.1",
+ "codemirror-lang-mt": "^0.0.2",
+ "codemirror-lang-perl": "^0.1.5",
+ "style-mod": "^4.1.2",
+ "thememirror": "^2.0.1"
+ }
+ },
+ "@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "peer": true
+ },
+ "@replit/codemirror-emacs": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz",
+ "integrity": "sha512-74DITnht6Cs6sHg02PQ169IKb1XgtyhI9sLD0JeOFco6Ds18PT+dkD8+DgXBDokne9UIFKsBbKPnpFRAz60/Lw==",
+ "requires": {}
+ },
+ "@replit/codemirror-vim": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.3.0.tgz",
+ "integrity": "sha512-aTx931ULAMuJx6xLf7KQDOL7CxD+Sa05FktTDrtLaSy53uj01ll3Zf17JdKsriER248oS55GBzg0CfCTjEneAQ==",
+ "requires": {}
+ },
+ "@trysound/sax": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
+ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "autoprefixer": {
+ "version": "10.4.19",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
+ "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "caniuse-lite": "^1.0.30001599",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true
+ },
+ "bootstrap": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
+ "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
+ "requires": {}
+ },
+ "braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.1.1"
+ }
+ },
+ "browserslist": {
+ "version": "4.23.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+ "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001587",
+ "electron-to-chromium": "^1.4.668",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.13"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "caniuse-api": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
+ "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.0.0",
+ "caniuse-lite": "^1.0.0",
+ "lodash.memoize": "^4.1.2",
+ "lodash.uniq": "^4.5.0"
+ }
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001723",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz",
+ "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "cm6-theme-basic-dark": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-basic-dark/-/cm6-theme-basic-dark-0.2.0.tgz",
+ "integrity": "sha512-+mNNJecRtxS/KkloMDCQF0oTrT6aFGRZTjnBcdT5UG1pcDO4Brq8l1+0KR/8dZ7hub2gOGOzoi3rGFD8GzlH7Q==",
+ "requires": {}
+ },
+ "cm6-theme-basic-light": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-basic-light/-/cm6-theme-basic-light-0.2.0.tgz",
+ "integrity": "sha512-1prg2gv44sYfpHscP26uLT/ePrh0mlmVwMSoSd3zYKQ92Ab3jPRLzyCnpyOCQLJbK+YdNs4HvMRqMNYdy4pMhA==",
+ "requires": {}
+ },
+ "cm6-theme-gruvbox-dark": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-gruvbox-dark/-/cm6-theme-gruvbox-dark-0.2.0.tgz",
+ "integrity": "sha512-xyqsG19qV+nb7ZHTMocSNWwZHMExfQxDm0FlbNMqEGKeQR96WryssXJH/IZZQudwrPpWU2dCoyOgMFhti2UTYA==",
+ "requires": {}
+ },
+ "cm6-theme-gruvbox-light": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-gruvbox-light/-/cm6-theme-gruvbox-light-0.2.0.tgz",
+ "integrity": "sha512-sc4dEMLU5y4F3QGLjwMQs1H3Q0a0ooXA1EvyWnknxLEGQVXwJrxkkV67gs1TqWASl2i63iomt4zyz5pkbfO1yg==",
+ "requires": {}
+ },
+ "cm6-theme-material-dark": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-material-dark/-/cm6-theme-material-dark-0.2.0.tgz",
+ "integrity": "sha512-H09JZihzg4w0mTtOqo5bQdxItkQWw+ergKlk7BSfwYjaR2nOi+wIN0R+ByAo7bON8GbFODvjTxH3EIqdhovFeA==",
+ "requires": {}
+ },
+ "cm6-theme-nord": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-nord/-/cm6-theme-nord-0.2.0.tgz",
+ "integrity": "sha512-jTh+5nvl+N/5CtTK7UVcrxDCj2AOStvbNM8uP6tx6amq4QaaLDlapjMw+MNzEkvxcPnHY+YM91tbklS2KNlR2w==",
+ "requires": {}
+ },
+ "cm6-theme-solarized-dark": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-solarized-dark/-/cm6-theme-solarized-dark-0.2.0.tgz",
+ "integrity": "sha512-FWtYHcX8NLzNSs21yGbkLF+q/5m2u80ug0JytKoI9nMZWPP5dcnsFYp1iZBEegLehiZnpv1qcmTsLTUG2KD39w==",
+ "requires": {}
+ },
+ "cm6-theme-solarized-light": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/cm6-theme-solarized-light/-/cm6-theme-solarized-light-0.2.0.tgz",
+ "integrity": "sha512-Iw7Xv+9A6NlT7sRGlM2pOwD3ZBETkAqpb7c6O0LPj5kjwcK6C3k+mvjzaQt1gzfBErMmhL1HHuK07zICeXkE+w==",
+ "requires": {}
+ },
+ "codemirror": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
+ "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
+ "requires": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/commands": "^6.0.0",
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/lint": "^6.0.0",
+ "@codemirror/search": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0"
+ }
+ },
+ "codemirror-lang-mt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/codemirror-lang-mt/-/codemirror-lang-mt-0.0.2.tgz",
+ "integrity": "sha512-m2q+EVgNeDxq3A8gCnoUGZvTuXvvZKlZliiqif4VAMPiu7dKJsaopvXZo8S1KH6cb2x9fJuKr5yUTnkxSLQZIQ==",
+ "requires": {
+ "@codemirror/lang-css": "^6.3.1",
+ "@codemirror/lang-html": "^6.4.9",
+ "@codemirror/lang-javascript": "^6.2.3",
+ "@codemirror/language": "^6.11.0",
+ "@lezer/highlight": "^1.2.1",
+ "@lezer/lr": "^1.4.2"
+ }
+ },
+ "codemirror-lang-perl": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/codemirror-lang-perl/-/codemirror-lang-perl-0.1.5.tgz",
+ "integrity": "sha512-o0QBzsO4z+ZWaN7ueYnFVYWoFlFvvfgcgNA/dQLxYUDiKGSUY0R52UL/NqTO6swUVrR+O6JI3Xh1j/ed81JIwA==",
+ "requires": {
+ "@codemirror/language": "^6.11.0",
+ "@lezer/highlight": "^1.2.1",
+ "@lezer/lr": "^1.4.2"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "colord": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+ "dev": true
+ },
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true
+ },
+ "crelt": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
+ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
+ },
+ "css-declaration-sorter": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
+ "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==",
+ "dev": true,
+ "requires": {}
+ },
+ "css-select": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+ "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ }
+ },
+ "css-tree": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+ "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+ "dev": true,
+ "requires": {
+ "mdn-data": "2.0.30",
+ "source-map-js": "^1.0.1"
+ }
+ },
+ "css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "dev": true
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true
+ },
+ "cssnano": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz",
+ "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==",
+ "dev": true,
+ "requires": {
+ "cssnano-preset-default": "^6.1.2",
+ "lilconfig": "^3.1.1"
+ }
+ },
+ "cssnano-preset-default": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz",
+ "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "css-declaration-sorter": "^7.2.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-calc": "^9.0.1",
+ "postcss-colormin": "^6.1.0",
+ "postcss-convert-values": "^6.1.0",
+ "postcss-discard-comments": "^6.0.2",
+ "postcss-discard-duplicates": "^6.0.3",
+ "postcss-discard-empty": "^6.0.3",
+ "postcss-discard-overridden": "^6.0.2",
+ "postcss-merge-longhand": "^6.0.5",
+ "postcss-merge-rules": "^6.1.1",
+ "postcss-minify-font-values": "^6.1.0",
+ "postcss-minify-gradients": "^6.0.3",
+ "postcss-minify-params": "^6.1.0",
+ "postcss-minify-selectors": "^6.0.4",
+ "postcss-normalize-charset": "^6.0.2",
+ "postcss-normalize-display-values": "^6.0.2",
+ "postcss-normalize-positions": "^6.0.2",
+ "postcss-normalize-repeat-style": "^6.0.2",
+ "postcss-normalize-string": "^6.0.2",
+ "postcss-normalize-timing-functions": "^6.0.2",
+ "postcss-normalize-unicode": "^6.1.0",
+ "postcss-normalize-url": "^6.0.2",
+ "postcss-normalize-whitespace": "^6.0.2",
+ "postcss-ordered-values": "^6.0.2",
+ "postcss-reduce-initial": "^6.1.0",
+ "postcss-reduce-transforms": "^6.0.2",
+ "postcss-svgo": "^6.0.3",
+ "postcss-unique-selectors": "^6.0.4"
+ }
+ },
+ "cssnano-utils": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz",
+ "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "csso": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+ "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+ "dev": true,
+ "requires": {
+ "css-tree": "~2.2.0"
+ },
+ "dependencies": {
+ "css-tree": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+ "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+ "dev": true,
+ "requires": {
+ "mdn-data": "2.0.28",
+ "source-map-js": "^1.0.1"
+ }
+ },
+ "mdn-data": {
+ "version": "2.0.28",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+ "dev": true
+ }
+ }
+ },
+ "dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.3.0"
+ }
+ },
+ "domutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.4.747",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.747.tgz",
+ "integrity": "sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "flatpickr": {
+ "version": "4.6.13",
+ "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
+ "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw=="
+ },
+ "fraction.js": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "iframe-resizer": {
+ "version": "4.3.11",
+ "resolved": "https://registry.npmjs.org/iframe-resizer/-/iframe-resizer-4.3.11.tgz",
+ "integrity": "sha512-5QtnsmfH11GDsuC7Gxd/eNzojudX3346Gb0E+Ku8ln8AtfSq+cWCZtnhCrthrtE7f1CI2/kwHkZ9G4sFYzHP7A=="
+ },
+ "immutable": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
+ "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "jquery": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
+ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
+ },
+ "jquery-ui-dist": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.13.2.tgz",
+ "integrity": "sha512-oVDRd1NLtTbBwpRKAYdIRgpWVDzeBhfy7Gu0RmY6JEaZtmBq6kDn1pm5SgDiAotrnDS+RoTRXO6xvcNTxA9tOA==",
+ "requires": {
+ "jquery": ">=1.8.0 <4.0.0"
+ }
+ },
+ "lilconfig": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
+ "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
+ "dev": true
+ },
+ "lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
+ },
+ "lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "dev": true
+ },
+ "luxon": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz",
+ "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA=="
+ },
+ "mathjax": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-3.2.2.tgz",
+ "integrity": "sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw=="
+ },
+ "mdn-data": {
+ "version": "2.0.30",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true
+ },
+ "nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "postcss-calc": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz",
+ "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.11",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-colormin": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz",
+ "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0",
+ "colord": "^2.9.3",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-convert-values": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz",
+ "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-discard-comments": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz",
+ "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-discard-duplicates": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz",
+ "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-discard-empty": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz",
+ "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-discard-overridden": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz",
+ "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-merge-longhand": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz",
+ "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0",
+ "stylehacks": "^6.1.1"
+ }
+ },
+ "postcss-merge-rules": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz",
+ "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-selector-parser": "^6.0.16"
+ }
+ },
+ "postcss-minify-font-values": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz",
+ "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-minify-gradients": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz",
+ "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==",
+ "dev": true,
+ "requires": {
+ "colord": "^2.9.3",
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-minify-params": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz",
+ "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-minify-selectors": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz",
+ "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.16"
+ }
+ },
+ "postcss-normalize-charset": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz",
+ "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-normalize-display-values": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz",
+ "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-positions": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz",
+ "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-repeat-style": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz",
+ "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-string": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz",
+ "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-timing-functions": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz",
+ "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-unicode": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz",
+ "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-url": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz",
+ "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-whitespace": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz",
+ "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-ordered-values": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz",
+ "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==",
+ "dev": true,
+ "requires": {
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-reduce-initial": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz",
+ "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0"
+ }
+ },
+ "postcss-reduce-transforms": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz",
+ "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.0.16",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
+ "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "postcss-svgo": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz",
+ "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0",
+ "svgo": "^3.2.0"
+ }
+ },
+ "postcss-unique-selectors": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz",
+ "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.16"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
+ "prettier": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
+ "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
+ "dev": true
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true
+ },
+ "rtlcss": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz",
+ "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.21",
+ "strip-json-comments": "^3.1.1"
+ }
+ },
+ "sass": {
+ "version": "1.75.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
+ "integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ }
+ },
+ "shortcut-buttons-flatpickr": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/shortcut-buttons-flatpickr/-/shortcut-buttons-flatpickr-0.4.0.tgz",
+ "integrity": "sha512-JKmT4my3Hm1e18OvG4Q6RcFhN4WRqqpTMkHrvZ7fup/dp6aTIWGVCHdRYtASkp/FCzDlJh6iCLQ/VcwwNpAMoQ=="
+ },
+ "sortablejs": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz",
+ "integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA=="
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "style-mod": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
+ "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
+ },
+ "stylehacks": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz",
+ "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.23.0",
+ "postcss-selector-parser": "^6.0.16"
+ }
+ },
+ "svgo": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz",
+ "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==",
+ "dev": true,
+ "requires": {
+ "@trysound/sax": "0.2.0",
+ "commander": "^7.2.0",
+ "css-select": "^5.1.0",
+ "css-tree": "^2.3.1",
+ "css-what": "^6.1.0",
+ "csso": "^5.0.5",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "terser": {
+ "version": "5.30.4",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
+ "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ }
+ }
+ },
+ "thememirror": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/thememirror/-/thememirror-2.0.1.tgz",
+ "integrity": "sha512-d5i6FVvWWPkwrm4cHLI3t9AT1OrkAt7Ig8dtdYSofgF7C/eiyNuq6zQzSTusWTde3jpW9WLvA9J/fzNKMUsd0w==",
+ "requires": {}
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "update-browserslist-db": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+ "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "w3c-keyname": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true
+ }
+ }
}
diff --git a/htdocs/package.json b/htdocs/package.json
index ddbb13e751..fb477d5362 100644
--- a/htdocs/package.json
+++ b/htdocs/package.json
@@ -1,50 +1,50 @@
{
- "name": "webwork.javascript_package_manager",
- "description": "Third party javascript for WeBWorK",
- "license": "GPL-2.0+",
- "scripts": {
- "generate-assets": "node generate-assets",
- "prepare": "npm run generate-assets",
- "prettier-format": "prettier --ignore-path=../.gitignore --write \"**/*.{js,css,scss,html}\" \"../**/*.dist.yml\"",
- "prettier-check": "prettier --ignore-path=../.gitignore --check \"**/*.{js,css,scss,html}\" \"../**/*.dist.yml\""
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/openwebwork/webwork2"
- },
- "dependencies": {
- "@fortawesome/fontawesome-free": "^6.5.2",
- "bootstrap": "~5.3.3",
- "codemirror": "^5.65.15",
- "flatpickr": "^4.6.13",
- "iframe-resizer": "^4.3.11",
- "jquery": "^3.7.1",
- "jquery-ui-dist": "^1.13.2",
- "luxon": "^3.4.4",
- "mathjax": "^3.2.2",
- "shortcut-buttons-flatpickr": "^0.4.0",
- "sortablejs": "^1.15.2"
- },
- "devDependencies": {
- "autoprefixer": "^10.4.19",
- "chokidar": "^3.6.0",
- "cssnano": "^6.1.2",
- "postcss": "^8.4.38",
- "prettier": "^3.2.5",
- "rtlcss": "^4.1.1",
- "sass": "^1.75.0",
- "terser": "^5.30.4",
- "yargs": "^17.7.2"
- },
- "browserslist": [
- "last 10 Chrome versions",
- "last 10 Firefox versions",
- "last 4 Edge versions",
- "last 7 Safari versions",
- "last 8 Android versions",
- "last 8 ChromeAndroid versions",
- "last 8 FirefoxAndroid versions",
- "last 10 iOS versions",
- "last 5 Opera versions"
- ]
+ "name": "webwork.javascript_package_manager",
+ "description": "Third party javascript for WeBWorK",
+ "license": "GPL-2.0+",
+ "scripts": {
+ "generate-assets": "node generate-assets",
+ "prepare": "npm run generate-assets",
+ "prettier-format": "prettier --ignore-path=../.gitignore --write \"**/*.{js,css,scss,html}\" \"../**/*.dist.yml\"",
+ "prettier-check": "prettier --ignore-path=../.gitignore --check \"**/*.{js,css,scss,html}\" \"../**/*.dist.yml\""
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/openwebwork/webwork2"
+ },
+ "dependencies": {
+ "@fortawesome/fontawesome-free": "^6.5.2",
+ "@openwebwork/pg-codemirror-editor": "^0.0.4",
+ "bootstrap": "~5.3.3",
+ "flatpickr": "^4.6.13",
+ "iframe-resizer": "^4.3.11",
+ "jquery": "^3.7.1",
+ "jquery-ui-dist": "^1.13.2",
+ "luxon": "^3.4.4",
+ "mathjax": "^3.2.2",
+ "shortcut-buttons-flatpickr": "^0.4.0",
+ "sortablejs": "^1.15.2"
+ },
+ "devDependencies": {
+ "autoprefixer": "^10.4.19",
+ "chokidar": "^3.6.0",
+ "cssnano": "^6.1.2",
+ "postcss": "^8.4.38",
+ "prettier": "^3.2.5",
+ "rtlcss": "^4.1.1",
+ "sass": "^1.75.0",
+ "terser": "^5.30.4",
+ "yargs": "^17.7.2"
+ },
+ "browserslist": [
+ "last 10 Chrome versions",
+ "last 10 Firefox versions",
+ "last 4 Edge versions",
+ "last 7 Safari versions",
+ "last 8 Android versions",
+ "last 8 ChromeAndroid versions",
+ "last 8 FirefoxAndroid versions",
+ "last 10 iOS versions",
+ "last 5 Opera versions"
+ ]
}
diff --git a/htdocs/themes/math4-green/_theme-colors.scss b/htdocs/themes/math4-green/_theme-colors.scss
index f577945b9f..89fa61322f 100644
--- a/htdocs/themes/math4-green/_theme-colors.scss
+++ b/htdocs/themes/math4-green/_theme-colors.scss
@@ -1,17 +1,3 @@
-/* WeBWorK Online Homework Delivery System
- * Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of either: (a) the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version, or (b) the "Artistic License" which comes with this package.
- *
- * This program 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 either the GNU General Public License or the
- * Artistic License for more details.
- */
-
// Include bootstrap scss functions (so you can manipulate colors, SVGs, calc, etc)
@import '../../node_modules/bootstrap/scss/functions';
diff --git a/htdocs/themes/math4-red/_theme-colors.scss b/htdocs/themes/math4-red/_theme-colors.scss
index e1f057edb6..b34e6f2682 100644
--- a/htdocs/themes/math4-red/_theme-colors.scss
+++ b/htdocs/themes/math4-red/_theme-colors.scss
@@ -1,17 +1,3 @@
-/* WeBWorK Online Homework Delivery System
- * Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of either: (a) the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version, or (b) the "Artistic License" which comes with this package.
- *
- * This program 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 either the GNU General Public License or the
- * Artistic License for more details.
- */
-
// Include bootstrap scss functions (so you can manipulate colors, SVGs, calc, etc)
@import '../../node_modules/bootstrap/scss/functions';
diff --git a/htdocs/themes/math4-yellow/_theme-colors.scss b/htdocs/themes/math4-yellow/_theme-colors.scss
index b718e81b27..cbc8f078bb 100644
--- a/htdocs/themes/math4-yellow/_theme-colors.scss
+++ b/htdocs/themes/math4-yellow/_theme-colors.scss
@@ -1,17 +1,3 @@
-/* WeBWorK Online Homework Delivery System
- * Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of either: (a) the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version, or (b) the "Artistic License" which comes with this package.
- *
- * This program 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 either the GNU General Public License or the
- * Artistic License for more details.
- */
-
// Include bootstrap scss functions (so you can manipulate colors, SVGs, calc, etc)
@import '../../node_modules/bootstrap/scss/functions';
diff --git a/htdocs/themes/math4/_theme-colors.scss b/htdocs/themes/math4/_theme-colors.scss
index ad5d04eef1..bd57046fb7 100644
--- a/htdocs/themes/math4/_theme-colors.scss
+++ b/htdocs/themes/math4/_theme-colors.scss
@@ -1,17 +1,3 @@
-/* WeBWorK Online Homework Delivery System
-* Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-*
-* This program is free software; you can redistribute it and/or modify it under
-* the terms of either: (a) the GNU General Public License as published by the
-* Free Software Foundation; either version 2, or (at your option) any later
-* version, or (b) the "Artistic License" which comes with this package.
-*
-* This program 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 either the GNU General Public License or the
-* Artistic License for more details.
-*/
-
// Include bootstrap scss functions (so you can manipulate colors, SVGs, calc, etc)
@import '../../node_modules/bootstrap/scss/functions';
diff --git a/htdocs/themes/math4/bootstrap.scss b/htdocs/themes/math4/bootstrap.scss
index 4e94098660..72cbc9cbd0 100644
--- a/htdocs/themes/math4/bootstrap.scss
+++ b/htdocs/themes/math4/bootstrap.scss
@@ -1,17 +1,3 @@
-/* WeBWorK Online Homework Delivery System
- * Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of either: (a) the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version, or (b) the "Artistic License" which comes with this package.
- *
- * This program 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 either the GNU General Public License or the
- * Artistic License for more details.
- */
-
// Include functions first (so you can manipulate colors, SVGs, calc, etc)
@import '../../node_modules/bootstrap/scss/functions';
diff --git a/lib/Caliper/Entity.pm b/lib/Caliper/Entity.pm
index 13d9561a79..78883b765e 100644
--- a/lib/Caliper/Entity.pm
+++ b/lib/Caliper/Entity.pm
@@ -372,7 +372,7 @@ sub problem_set_attempt {
my $extensions = { 'attempt_score' => $score, };
if ($version_id) {
- $extensions->{'gateway_score'} = grade_gateway($db, $problem_set_user, $problem_set_user->set_id, $user_id);
+ $extensions->{'gateway_score'} = grade_gateway($db, $problem_set_user->set_id, $user_id);
}
my $problem_set_attempt = {
diff --git a/lib/Caliper/Sensor.pm b/lib/Caliper/Sensor.pm
index 84c3b80cda..0cee244c4d 100644
--- a/lib/Caliper/Sensor.pm
+++ b/lib/Caliper/Sensor.pm
@@ -7,7 +7,7 @@ use WeBWorK::CourseEnvironment;
use WeBWorK::DB;
use WeBWorK::Debug;
use Data::Dumper;
-use JSON;
+use Mojo::JSON qw(encode_json);
use Time::HiRes qw/gettimeofday/;
use Date::Format;
@@ -67,7 +67,7 @@ sub sendEvents {
'data' => $event_chunk,
};
- my $json_payload = JSON->new->canonical->encode($envelope);
+ my $json_payload = encode_json($envelope);
# debug("Caliper event json_payload: " . $json_payload);
my $HTTPRequest = HTTP::Request->new(
diff --git a/lib/FormatRenderedProblem.pm b/lib/FormatRenderedProblem.pm
index c681e25249..1c7f0194f3 100644
--- a/lib/FormatRenderedProblem.pm
+++ b/lib/FormatRenderedProblem.pm
@@ -1,17 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
=head1 NAME
@@ -24,12 +10,12 @@ package FormatRenderedProblem;
use strict;
use warnings;
-use JSON;
use Digest::SHA qw(sha1_base64);
-use Mojo::Util qw(xml_escape);
+use Mojo::Util qw(xml_escape);
+use Mojo::JSON qw(encode_json);
use Mojo::DOM;
-use WeBWorK::Utils qw(getAssetURL);
+use WeBWorK::Utils qw(getAssetURL);
use WeBWorK::Utils::LanguageAndDirection qw(get_lang_and_dir get_problem_lang_and_dir);
sub formatRenderedProblem {
@@ -187,8 +173,8 @@ sub formatRenderedProblem {
if $ws->c->current_route eq 'render_rpc' && ($ws->c->param('displayMode') // '') eq 'PTX';
}
- # Make sure this is defined and is an array reference as saveGradeToLTI might add to it.
- $rh_result->{debug_messages} = [] unless defined $rh_result && ref $rh_result eq 'ARRAY';
+ # Make sure $rh_result->{debug_messages} an array reference as saveGradeToLTI might add to it.
+ $rh_result->{debug_messages} = [] unless ref $rh_result->{debug_messages} eq 'ARRAY';
$forbidGradePassback = 1 if !$forbidGradePassback && !$submitMode;
@@ -227,7 +213,7 @@ sub formatRenderedProblem {
$output->{pg_version} = $ce->{PG_VERSION};
# Convert to JSON and render.
- return $ws->c->render(data => JSON->new->utf8(1)->encode($output));
+ return $ws->c->render(data => encode_json($output));
}
# Setup arnd render the appropriate template in the templates/RPCRenderFormats folder depending on the outputformat.
@@ -280,6 +266,7 @@ sub formatRenderedProblem {
showCorrectAnswersButton => $ws->{inputs_ref}{showCorrectAnswersButton} // '',
showCorrectAnswersOnlyButton => $ws->{inputs_ref}{showCorrectAnswersOnlyButton} // 0,
showFooter => $ws->{inputs_ref}{showFooter} // '',
+ problem_data => encode_json($rh_result->{PERSISTENCE_HASH}),
pretty_print => \&pretty_print
);
@@ -429,7 +416,6 @@ sub pretty_print {
# certain internals of the CourseEnvironment in case one slips in.
next
if (($key =~ /database/)
- || ($key =~ /dbLayout/)
|| ($key eq "ConfigValues")
|| ($key eq "ENV")
|| ($key eq "externalPrograms")
diff --git a/lib/HardcopyRenderedProblem.pm b/lib/HardcopyRenderedProblem.pm
index 76380647d0..8786cd033d 100644
--- a/lib/HardcopyRenderedProblem.pm
+++ b/lib/HardcopyRenderedProblem.pm
@@ -1,17 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
=head1 NAME
@@ -29,7 +15,7 @@ use warnings;
use File::Path;
use String::ShellQuote;
use Archive::Zip qw(:ERROR_CODES);
-use Mojo::File qw(path tempdir);
+use Mojo::File qw(path tempdir);
use XML::LibXML;
sub hardcopyRenderedProblem {
@@ -265,8 +251,7 @@ sub write_problem_tex {
$correctTeX .=
"\\item\n\$\\displaystyle "
. ($rh_result->{answers}{$_}{correct_ans_latex_string}
- || "\\text{$rh_result->{answers}{$_}{correct_ans}}")
- . "\$\n";
+ || "\\text{$rh_result->{answers}{$_}{correct_ans}}") . "\$\n";
}
$correctTeX .= "\\end{itemize}}\\par\n";
diff --git a/lib/Mojolicious/WeBWorK.pm b/lib/Mojolicious/WeBWorK.pm
index 0e6eeeb016..c9315547c4 100644
--- a/lib/Mojolicious/WeBWorK.pm
+++ b/lib/Mojolicious/WeBWorK.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package Mojolicious::WeBWorK;
use Mojo::Base 'Mojolicious', -signatures, -async_await;
@@ -28,9 +13,9 @@ use Mojo::JSON qw(encode_json);
use WeBWorK;
use WeBWorK::CourseEnvironment;
-use WeBWorK::Utils::Logs qw(writeTimingLogEntry);
+use WeBWorK::Utils::Logs qw(writeTimingLogEntry);
use WeBWorK::Utils::Routes qw(setup_content_generator_routes);
-use WeBWorK::Utils::Files qw(path_is_subdir);
+use WeBWorK::Utils::Files qw(path_is_subdir);
sub startup ($app) {
# Set up logging.
@@ -171,15 +156,14 @@ sub startup ($app) {
$app->hook(
after_dispatch => sub ($c) {
- $SIG{__WARN__} = $c->stash->{orig_sig_warn} if defined $c->stash->{orig_sig_warn};
+ $SIG{__WARN__} = ref($c->stash->{orig_sig_warn}) eq 'CODE' ? $c->stash->{orig_sig_warn} : 'DEFAULT';
if ($c->isa('WeBWorK::ContentGenerator') && $c->ce) {
$c->authen->store_session if $c->authen;
writeTimingLogEntry(
$c->ce,
'[' . $c->url_for . ']',
- sprintf('runTime = %.3f sec', $c->timing->elapsed('content_generator_rendering')) . ' '
- . $c->ce->{dbLayoutName}
+ sprintf('runTime = %.3f sec', $c->timing->elapsed('content_generator_rendering'))
);
}
}
diff --git a/lib/Mojolicious/WeBWorK/Tasks/AchievementNotification.pm b/lib/Mojolicious/WeBWorK/Tasks/AchievementNotification.pm
index f4975d65aa..00640afa51 100644
--- a/lib/Mojolicious/WeBWorK/Tasks/AchievementNotification.pm
+++ b/lib/Mojolicious/WeBWorK/Tasks/AchievementNotification.pm
@@ -1,18 +1,3 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package Mojolicious::WeBWorK::Tasks::AchievementNotification;
use Mojo::Base 'Minion::Job', -signatures;
@@ -39,7 +24,7 @@ sub run ($job, $mail_data) {
$job->{language_handle} = WeBWorK::Localize::getLoc($ce->{language} || 'en');
- my $db = WeBWorK::DB->new($ce->{dbLayout});
+ my $db = WeBWorK::DB->new($ce);
return $job->fail($job->maketext('Could not obtain database connection for [_1].', $courseID))
unless $db;
@@ -71,7 +56,7 @@ sub send_achievement_notification ($job, $ce, $db, $mail_data) {
$compartment->share_from('main',
[qw(%Encode:: %Mojo::Base:: %Mojo::Exception:: %Mojo::Template:: %WeBWorK::SafeTemplate::)]);
- # Since the WeBWorK::SafeTemplate module can not add "no warnings 'ambiguous'", those warnings must be prevented
+ # Since the WeBWorK::SafeTemplate module cannot add "no warnings 'ambiguous'", those warnings must be prevented
# with the following $SIG{__WARN__} handler.
local $SIG{__WARN__} = sub {
my $warning = shift;
diff --git a/lib/Mojolicious/WeBWorK/Tasks/LTIMassUpdate.pm b/lib/Mojolicious/WeBWorK/Tasks/LTIMassUpdate.pm
index 83d3001a9e..e0d9e5b866 100644
--- a/lib/Mojolicious/WeBWorK/Tasks/LTIMassUpdate.pm
+++ b/lib/Mojolicious/WeBWorK/Tasks/LTIMassUpdate.pm
@@ -1,18 +1,3 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package Mojolicious::WeBWorK::Tasks::LTIMassUpdate;
use Mojo::Base 'Minion::Job', -signatures;
@@ -36,7 +21,7 @@ sub run ($job, $userID = '', $setID = '') {
$job->{language_handle} = WeBWorK::Localize::getLoc($ce->{language} || 'en');
- my $db = WeBWorK::DB->new($ce->{dbLayout});
+ my $db = WeBWorK::DB->new($ce);
return $job->fail($job->maketext('Could not obtain database connection.')) unless $db;
my @messages;
diff --git a/lib/Mojolicious/WeBWorK/Tasks/SendInstructorEmail.pm b/lib/Mojolicious/WeBWorK/Tasks/SendInstructorEmail.pm
index 3f9c177e19..388d2338ff 100644
--- a/lib/Mojolicious/WeBWorK/Tasks/SendInstructorEmail.pm
+++ b/lib/Mojolicious/WeBWorK/Tasks/SendInstructorEmail.pm
@@ -1,18 +1,3 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package Mojolicious::WeBWorK::Tasks::SendInstructorEmail;
use Mojo::Base 'Minion::Job', -signatures;
@@ -36,7 +21,7 @@ sub run ($job, $mail_data) {
$job->{language_handle} = WeBWorK::Localize::getLoc($ce->{language} || 'en');
- my $db = WeBWorK::DB->new($ce->{dbLayout});
+ my $db = WeBWorK::DB->new($ce);
return $job->fail($job->maketext('Could not obtain database connection.')) unless $db;
my @result_messages = eval { $job->mail_message_to_recipients($ce, $db, $mail_data) };
diff --git a/lib/WeBWorK.pm b/lib/WeBWorK.pm
index 0ac5eeba66..a80dd817ce 100644
--- a/lib/WeBWorK.pm
+++ b/lib/WeBWorK.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK;
use Mojo::Base -signatures, -async_await;
@@ -62,28 +47,19 @@ async sub dispatch ($c) {
# Note that this is Time::HiRes's time, which gives floating point values.
$c->submitTime(time);
- my $method = $c->req->method;
- my $location = $c->location;
- my $uri = $c->url_for;
- my $args = $c->req->params->to_string || '';
+ my $method = $c->req->method;
+ my $uri = $c->url_for;
+ my $args = $c->req->params->to_string || '';
debug("\n\n===> Begin " . __PACKAGE__ . "::dispatch() <===\n\n");
- debug("Hi, I'm the new dispatcher!\n");
debug(("-" x 80) . "\n");
- debug("Okay, I got some basic information:\n");
- debug("The site location is $location\n");
debug("The request method is $method\n");
debug("The URI is $uri\n");
debug("The argument string is $args\n");
debug(('-' x 80) . "\n");
- my ($path) = $uri =~ m/$location(.*)/;
- $path .= '/' if $path !~ m(/$);
- debug("The path is $path\n");
-
debug("The current route is " . $c->current_route . "\n");
- debug("Here is some information about this route:\n");
my $displayModule = ref $c;
my %routeCaptures = %{ $c->stash->{'mojo.captures'} };
@@ -96,8 +72,6 @@ async sub dispatch ($c) {
debug(('-' x 80) . "\n");
- debug("Now we want to look at the parameters we got.\n");
-
debug("The raw params:\n");
for my $key ($c->param) {
# Make it so we dont debug plain text passwords
@@ -122,7 +96,6 @@ async sub dispatch ($c) {
$c->initializeRoute(\%routeCaptures) if $c->can('initializeRoute');
# Create Course Environment
- debug("We need to get a course environment (with or without a courseID!)\n");
my $ce = eval { WeBWorK::CourseEnvironment->new({ courseName => $routeCaptures{courseID} }) };
$@ and die "Failed to initialize course environment: $@\n";
debug("Here's the course environment: $ce\n");
@@ -134,17 +107,17 @@ async sub dispatch ($c) {
my @uploads = @{ $c->req->uploads };
- foreach my $u (@uploads) {
+ for my $u (@uploads) {
# Make sure it's a "real" upload.
next unless $u->filename;
# Store the upload.
- my $upload = WeBWorK::Upload->store($u, dir => $ce->{webworkDirs}{uploadCache});
+ my $upload = WeBWorK::Upload->store($u, $ce->{webworkDirs}{uploadCache});
- # Store the upload ID and hash in the file upload field.
- my $id = $upload->id;
- my $hash = $upload->hash;
- $c->param($u->name => "$id $hash");
+ # Store the upload temporary file location and hash in the file upload field.
+ my $tmpFile = $upload->tmpFile;
+ my $hash = $upload->hash;
+ $c->param($u->name => "$tmpFile $hash");
}
# Create these out here. They should fail if they don't have the right information.
@@ -164,13 +137,15 @@ async sub dispatch ($c) {
if ($routeCaptures{courseID}) {
debug("We got a courseID from the route, now we can do some stuff:\n");
+ # This route could have the courseID set, but does not need authentication.
+ return 1 if $c->current_route eq 'saml2_metadata';
+
return (0, 'This course does not exist.')
unless (-e $ce->{courseDirs}{root}
|| -e "$ce->{webwork_courses_dir}/$ce->{admin_course_id}/archives/$routeCaptures{courseID}.tar.gz");
return (0, 'This course has been archived and closed.') unless -e $ce->{courseDirs}{root};
- debug("...we can create a database object...\n");
- my $db = WeBWorK::DB->new($ce->{dbLayout});
+ my $db = WeBWorK::DB->new($ce);
debug("(here's the DB handle: $db)\n");
$c->db($db);
@@ -227,6 +202,7 @@ async sub dispatch ($c) {
# current server time during a gateway quiz, and that definitely should not revoke proctor
# authorization.
delete $c->authen->session->{proctor_authorization_granted};
+ delete $c->authen->session->{acting_proctor};
}
return 1;
} else {
diff --git a/lib/WeBWorK/AchievementEvaluator.pm b/lib/WeBWorK/AchievementEvaluator.pm
index fc21a716ce..249d34ea22 100644
--- a/lib/WeBWorK/AchievementEvaluator.pm
+++ b/lib/WeBWorK/AchievementEvaluator.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementEvaluator;
use Mojo::Base 'Exporter', -signatures;
@@ -24,7 +9,7 @@ use Mojo::Base 'Exporter', -signatures;
use DateTime;
-use WeBWorK::Utils qw(sortAchievements nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(sortAchievements nfreeze_base64 thaw_base64);
use WeBWorK::Utils::ProblemProcessing qw(compute_unreduced_score);
use WeBWorK::Utils::Tags;
use WeBWorK::WWSafe;
diff --git a/lib/WeBWorK/AchievementItems.pm b/lib/WeBWorK/AchievementItems.pm
index 8081c7bd6e..f54d407e67 100644
--- a/lib/WeBWorK/AchievementItems.pm
+++ b/lib/WeBWorK/AchievementItems.pm
@@ -1,22 +1,7 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems;
use Mojo::Base -signatures;
-use WeBWorK::Utils qw(thaw_base64);
+use WeBWorK::Utils qw(nfreeze_base64 thaw_base64);
# List of available achievement items. Make sure to add any new items to this list. Furthermore, the elements in this
# list have to match the class name of the achievement item classes loaded below.
@@ -27,11 +12,14 @@ use constant ITEMS => [ qw(
HalfCreditProb
FullCreditProb
ReducedCred
+ NoReducedCred
ExtendDueDate
+ ExtendReducedDate
DoubleSet
ResurrectHW
Surprise
SuperExtendDueDate
+ SuperExtendReducedDate
HalfCreditSet
FullCreditSet
AddNewTestGW
@@ -41,65 +29,134 @@ use constant ITEMS => [ qw(
=head2 NAME
-This is the base class for achievement times. This defines an interface for all of the achievement items. Each
-achievement item will have a name, a description, a method for creating an html form to get its inputs called print_form
-and a method for applying those inputs called use_item.
+This is the base class for achievement times. This defines an interface for all of the achievement items.
+Each achievement item will have an id, a name, a description, and the three methods can_use (checks if the
+item can be used on the given set), print_form (prints the form to use the item), and use_item.
Note: the ID has to match the name of the class.
+The global method UserItems returns an array of all achievement items available to the given user. If no
+set is included, a list of all earned achievement items is return. If provided a set and corresponding problem
+or test version records, a list of items usable on the current set and records paired with an input form to
+use the item is returned. This method will also process any posts to use the achievement item.
+
=cut
-sub id ($c) { return $c->{id}; }
-sub name ($c) { return $c->{name}; }
-sub description ($c) { return $c->{description}; }
+sub id ($self) { return $self->{id}; }
+sub name ($self) { return $self->{name}; }
+sub count ($self) { return $self->{count}; }
+sub description ($self) { return $self->{description}; }
+
+# Method to find all achievement items available to the given user.
+# If $set is undefined return an array reference of all earned items.
+# If $set is defined, return an array reference of the usable items
+# for the given $set and problem or test versions records. Each item
+# is paired with its input form to use the item.
+sub UserItems ($c, $userName, $set, $records) {
+ my $db = $c->db;
+ my $ce = $c->ce;
-# This is a global method that returns all of the provided users items.
-sub UserItems ($userName, $db, $ce) {
- # return unless the user has global achievement data
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
+ # Return unless achievement items are enabled.
+ return unless $ce->{achievementsEnabled} && $ce->{achievementItemsEnabled};
- return unless ($globalUserAchievement->frozen_hash);
+ # When acting as another user, achievement items can be listed but not used.
+ return if $set && $userName ne $c->param('user');
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
+ # Return unless the user has global achievement data.
+ my $globalUserAchievement = $c->{globalData} // $db->getGlobalUserAchievement($userName);
+ return unless $globalUserAchievement && $globalUserAchievement->frozen_hash;
+
+ my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
+ my $use_item_id = $c->param('use_achievement_item_id') // '';
my @items;
- # Get a new item object for each type of item.
for my $item (@{ +ITEMS }) {
- push(@items, [ "WeBWorK::AchievementItems::$item"->new, $globalData->{$item} ])
- if ($globalData->{$item});
+ next unless $globalData->{$item};
+ my $achievementItem = "WeBWorK::AchievementItems::$item"->new;
+ $achievementItem->{count} = $globalData->{$item};
+
+ # Return list of achievements items if $set is not defined.
+ unless ($set) {
+ push(@items, $achievementItem);
+ next;
+ }
+ next unless $achievementItem->can_use($set, $records);
+
+ # Use the achievement item.
+ if ($use_item_id eq $item) {
+ my $message = $achievementItem->use_item($set, $records, $c);
+ if ($message) {
+ $globalData->{$item}--;
+ $achievementItem->{count}--;
+ $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
+ $db->putGlobalUserAchievement($globalUserAchievement);
+ $c->addgoodmessage($c->maketext('[_1] successfuly used. [_2]', $achievementItem->name, $message));
+ }
+ }
+
+ push(@items, [ $achievementItem, $use_item_id ? '' : $achievementItem->print_form($set, $records, $c) ]);
}
+ # If an achievement item has been used, double check if the achievement items can still be used
+ # since the item count could now be zero or an achievement item has altered the set/records.
+ # Input forms are also built here to account for any possible change.
+ if ($set && $use_item_id) {
+ my @new_items;
+ for (@items) {
+ my $item = $_->[0];
+ next unless $item->{count} && $item->can_use($set, $records);
+ push(@new_items, [ $item, $item->print_form($set, $records, $c) ]);
+ }
+ return \@new_items;
+ }
return \@items;
}
+# Method that returns a string with the achievement name and number of remaining items.
+# This should only be called if count != 0.
+sub remaining_title ($self, $c) {
+ if ($self->count > 0) {
+ return $c->maketext('[_1] ([_2] remaining)', $c->maketext($self->name), $self->count);
+ } else {
+ return $c->maketext('[_1] (unlimited reusability)', $c->maketext($self->name));
+ }
+}
+
# Utility method for outputing a form row with a label and popup menu.
# The id, label_text, and values are required parameters.
sub form_popup_menu_row ($c, %options) {
my %params = (
- id => '',
- label_text => '',
- label_attr => {},
- values => [],
- menu_attr => {},
- menu_container_attr => {},
- add_container => 1,
+ id => '',
+ first_item => '',
+ label_text => '',
+ label_attr => {},
+ values => [],
+ menu_attr => {},
+ add_container => 1,
%options
);
- $params{label_attr}{class} //= 'col-4 col-form-label';
- $params{menu_attr}{class} //= 'form-select';
- $params{menu_container_attr}{class} //= 'col-8';
-
- my $row_contents = $c->c(
- $c->label_for($params{id} => $params{label_text}, %{ $params{label_attr} }),
- $c->tag(
- 'div',
- %{ $params{menu_container_attr} },
- $c->select_field($params{id} => $params{values}, id => $params{id}, %{ $params{menu_attr} })
- )
- )->join('');
+ $params{label_attr}{class} //= 'col-form-label';
+ $params{menu_attr}{class} //= 'form-select';
+
+ unshift(@{ $params{values} }, [ $params{first_item} => '', disabled => undef, selected => undef ])
+ if $params{first_item};
+
+ my $row_contents = $c->tag(
+ 'div',
+ class => 'form-floating',
+ $c->c(
+ $c->select_field(
+ $params{id} => $params{values},
+ id => $params{id},
+ required => undef,
+ %{ $params{menu_attr} }
+ ),
+ $c->label_for($params{id} => $params{label_text}, %{ $params{label_attr} })
+ )->join('')
+ );
- return $params{add_container} ? $c->tag('div', class => 'row mb-3', $row_contents) : $row_contents;
+ return $params{add_container} ? $c->tag('div', class => 'my-3', $row_contents) : $row_contents;
}
END {
@@ -110,15 +167,18 @@ END {
use WeBWorK::AchievementItems::DuplicateProb;
use WeBWorK::AchievementItems::ExtendDueDateGW;
use WeBWorK::AchievementItems::ExtendDueDate;
+ use WeBWorK::AchievementItems::ExtendReducedDate;
use WeBWorK::AchievementItems::FullCreditProb;
use WeBWorK::AchievementItems::FullCreditSet;
use WeBWorK::AchievementItems::HalfCreditProb;
use WeBWorK::AchievementItems::HalfCreditSet;
use WeBWorK::AchievementItems::ReducedCred;
+ use WeBWorK::AchievementItems::NoReducedCred;
use WeBWorK::AchievementItems::ResetIncorrectAttempts;
use WeBWorK::AchievementItems::ResurrectGW;
use WeBWorK::AchievementItems::ResurrectHW;
use WeBWorK::AchievementItems::SuperExtendDueDate;
+ use WeBWorK::AchievementItems::SuperExtendReducedDate;
use WeBWorK::AchievementItems::Surprise;
}
diff --git a/lib/WeBWorK/AchievementItems/AddNewTestGW.pm b/lib/WeBWorK/AchievementItems/AddNewTestGW.pm
index 6674cb17f6..9eadafbe69 100644
--- a/lib/WeBWorK/AchievementItems/AddNewTestGW.pm
+++ b/lib/WeBWorK/AchievementItems/AddNewTestGW.pm
@@ -1,26 +1,10 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::AddNewTestGW;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to allow students to take an additional version of a test within its test version interval
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
-use WeBWorK::Utils::DateTime qw(before between);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
+use WeBWorK::Utils qw(x);
+use WeBWorK::Utils::DateTime qw(between);
sub new ($class) {
return bless {
@@ -33,60 +17,34 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my $db = $c->db;
-
- my @openGateways;
-
- # Find the template sets of open gateway quizzes.
- for my $set (@$sets) {
- push(@openGateways, [ format_set_name_display($set->set_id) => $set->set_id ])
- if $set->assignment_type =~ /gateway/
- && $set->set_id !~ /,v\d+$/
- && between($set->open_date, $set->due_date);
- }
-
- return unless @openGateways;
+sub can_use ($self, $set, $records) {
+ return
+ $set->assignment_type =~ /gateway/
+ && $set->set_id !~ /,v\d+$/
+ && between($set->open_date, $set->due_date)
+ && $set->versions_per_interval > 0;
+}
- return $c->c(
- $c->tag('p', $c->maketext('Add a new version for which test?')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'adtgw_gw_id',
- label_text => $c->maketext('Test Name'),
- values => \@openGateways,
- menu_attr => { dir => 'ltr' }
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Increase the number of versions from [_1] to [_2] for this test.',
+ $set->versions_per_interval,
+ $set->versions_per_interval + 1
)
- )->join('');
+ );
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('adtgw_gw_id');
- return 'You need to input a Test Name' unless defined $setID;
-
- my $set = $db->getMergedSet($userName, $setID);
- my $userSet = $db->getUserSet($userName, $setID);
- return q{Couldn't find that set!} unless $set && $userSet;
-
- # Add an additional version per interval to the set.
- $userSet->versions_per_interval($set->versions_per_interval + 1) unless $set->versions_per_interval == 0;
+sub use_item ($self, $set, $records, $c) {
+ # Increase the number of versions per interval by 1.
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
+ $set->versions_per_interval($set->versions_per_interval + 1);
+ $userSet->versions_per_interval($set->versions_per_interval);
$db->putUserSet($userSet);
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ return $c->maketext('One additional test version added to this test.');
}
1;
diff --git a/lib/WeBWorK/AchievementItems/DoubleProb.pm b/lib/WeBWorK/AchievementItems/DoubleProb.pm
index 57f6e3b948..15ca7dc072 100644
--- a/lib/WeBWorK/AchievementItems/DoubleProb.pm
+++ b/lib/WeBWorK/AchievementItems/DoubleProb.pm
@@ -1,28 +1,10 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::DoubleProb;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to make a problem worth double.
-use Mojo::JSON qw(encode_json);
-
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x);
use WeBWorK::Utils::DateTime qw(after);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
sub new ($class) {
return bless {
@@ -32,84 +14,48 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- # Construct a dropdown with open sets and another with problems.
- # Javascript ensures the appropriate problems are shown for the selected set.
-
- my (@openSets, @initialProblemIDs);
-
- for my $i (0 .. $#$sets) {
- if (after($sets->[$i]->open_date)
- && $sets->[$i]->assignment_type eq 'default'
- && @{ $setProblemIds->{ $sets->[$i]->set_id } })
- {
- push(
- @openSets,
- [
- format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id,
- data => { problem_ids => encode_json($setProblemIds->{ $sets->[$i]->set_id }) }
- ]
- );
- @initialProblemIDs = @{ $setProblemIds->{ $sets->[$i]->set_id } } unless @initialProblemIDs;
- }
- }
-
- return unless @openSets;
-
- return $c->c(
- $c->tag(
- 'p',
- $c->maketext(
- 'Please choose the set name and problem number of the question which should have its weight doubled.')
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'dbp_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr', data => { problems => 'dbp_problem_id' } }
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'dbp_problem_id',
- label_text => $c->maketext('Problem Number'),
- values => \@initialProblemIDs,
- menu_container_attr => { class => 'col-3' }
- )
- )->join('');
+sub can_use ($self, $set, $records) {
+ return $set->assignment_type eq 'default' && after($set->open_date);
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('dbp_set_id');
- return 'You need to input a Set Name' unless defined $setID;
+sub print_form ($self, $set, $records, $c) {
+ return WeBWorK::AchievementItems::form_popup_menu_row(
+ $c,
+ id => 'dbp_problem_id',
+ label_text => $c->maketext('Problem number to double weight'),
+ first_item => $c->maketext('Choose problem to double its weight.'),
+ values => [
+ map { [ $c->maketext('Problem [_1] ([_2] to [_3])', $_->problem_id, $_->value, 2 * $_->value) =>
+ $_->problem_id ] } @$records
+ ],
+ );
+}
+sub use_item ($self, $set, $records, $c) {
my $problemID = $c->param('dbp_problem_id');
- return 'You need to input a Problem Number' unless $problemID;
+ unless ($problemID) {
+ $c->addbadmessage($c->maketext('Select problem to double its weight with the [_1].', $self->name));
+ return '';
+ }
- my $globalproblem = $db->getMergedProblem($userName, $setID, $problemID);
- my $problem = $db->getUserProblem($userName, $setID, $problemID);
- return 'There was an error accessing that problem.' unless $globalproblem && $problem;
+ my $problem;
+ for (@$records) {
+ if ($_->problem_id == $problemID) {
+ $problem = $_;
+ last;
+ }
+ }
+ return '' unless $problem;
# Double the value of the problem.
- $problem->value($globalproblem->value * 2);
- $db->putUserProblem($problem);
-
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ my $db = $c->db;
+ my $userProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id);
+ my $orig_value = $problem->value;
+ $problem->value($orig_value * 2);
+ $userProblem->value($problem->value);
+ $db->putUserProblem($userProblem);
+
+ return $c->maketext('Problem [_1] weight increased from [_2] to [_3].', $problemID, $orig_value, $problem->value);
}
1;
diff --git a/lib/WeBWorK/AchievementItems/DoubleSet.pm b/lib/WeBWorK/AchievementItems/DoubleSet.pm
index bde65cd065..b91629f717 100644
--- a/lib/WeBWorK/AchievementItems/DoubleSet.pm
+++ b/lib/WeBWorK/AchievementItems/DoubleSet.pm
@@ -1,26 +1,10 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::DoubleSet;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to make a homework set worth twice as much
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x);
use WeBWorK::Utils::DateTime qw(after);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
sub new ($class) {
return bless {
@@ -30,62 +14,37 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my @openSets;
+sub can_use ($self, $set, $records) {
+ return $set->assignment_type eq 'default' && after($set->open_date);
+}
- for my $i (0 .. $#$sets) {
- push(@openSets, [ format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id ])
- if (after($sets->[$i]->open_date) && $sets->[$i]->assignment_type eq 'default');
+sub print_form ($self, $set, $records, $c) {
+ my $total = 0;
+ for my $problem (@$records) {
+ $total += $problem->value;
}
-
- return unless @openSets;
-
- return $c->c(
- $c->tag('p', $c->maketext('Choose the set which you would like to be worth twice as much.')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'dub_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr' }
- )
- )->join('');
+ return $c->tag('p',
+ $c->maketext(q(Increase this assignment's total number of points from [_1] to [_2].), $total, 2 * $total));
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('dub_set_id');
- return 'You need to input a Set Name' unless defined $setID;
-
- my $set = $db->getMergedSet($userName, $setID);
- return q{Couldn't find that set!} unless $set;
-
- my @probIDs = $db->listUserProblems($userName, $setID);
-
- for my $probID (@probIDs) {
- my $globalproblem = $db->getMergedProblem($userName, $setID, $probID);
- my $problem = $db->getUserProblem($userName, $setID, $probID);
-
- # Double the problem value.
- $problem->value($globalproblem->value * 2);
- $db->putUserProblem($problem);
+sub use_item ($self, $set, $records, $c) {
+ my $db = $c->db;
+ my $old_value = 0;
+ my $new_value = 0;
+
+ my %userProblems =
+ map { $_->problem_id => $_ } $db->getUserProblemsWhere({ user_id => $set->user_id, set_id => $set->set_id });
+ for my $problem (@$records) {
+ my $userProblem = $userProblems{ $problem->problem_id };
+ $old_value += $problem->value;
+ $problem->value(2 * $problem->value);
+ $userProblem->value($problem->value);
+ $new_value += $userProblem->value;
+ $db->putUserProblem($userProblem);
}
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ return $c->maketext(q(Assignment's total point value increased from [_1] points to [_2] points),
+ $old_value, $new_value);
}
1;
diff --git a/lib/WeBWorK/AchievementItems/DuplicateProb.pm b/lib/WeBWorK/AchievementItems/DuplicateProb.pm
index e7841b86a8..20c90d726d 100644
--- a/lib/WeBWorK/AchievementItems/DuplicateProb.pm
+++ b/lib/WeBWorK/AchievementItems/DuplicateProb.pm
@@ -1,28 +1,10 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::DuplicateProb;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to turn one problem into another problem
-use Mojo::JSON qw(encode_json);
-
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x);
use WeBWorK::Utils::DateTime qw(between);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
sub new ($class) {
return bless {
@@ -32,109 +14,57 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- # Show open sets and allow for a choice of two problems from the set.
- # Javascript ensures the appropriate problems are shown for the selected set.
-
- my (@openSets, @initialProblemIDs);
-
- for my $i (0 .. $#$sets) {
- if (between($sets->[$i]->open_date, $sets->[$i]->due_date)
- && $sets->[$i]->assignment_type eq 'default'
- && @{ $setProblemIds->{ $sets->[$i]->set_id } })
- {
- push(
- @openSets,
- [
- format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id,
- data => { problem_ids => encode_json($setProblemIds->{ $sets->[$i]->set_id }) }
- ]
- );
- @initialProblemIDs = @{ $setProblemIds->{ $sets->[$i]->set_id } } unless @initialProblemIDs;
- }
- }
-
- return unless @openSets;
+sub can_use ($self, $set, $records) {
+ return $set->assignment_type eq 'default' && between($set->open_date, $set->due_date);
+}
+sub print_form ($self, $set, $records, $c) {
return $c->c(
- $c->tag(
- 'p',
- $c->maketext(
- 'Please choose the set, the problem you would like to copy, '
- . 'and the problem you would like to copy it to.'
- )
+ $c->tag('p', $c->maketext('Replaces the second problem with a copy of the first.')),
+ WeBWorK::AchievementItems::form_popup_menu_row(
+ $c,
+ id => 'clone_source_problem_id',
+ label_text => $c->maketext('Problem number to copy'),
+ first_item => $c->maketext('Choose problem to copy from.'),
+ values => [ map { [ $c->maketext('Problem [_1]', $_->problem_id) => $_->problem_id ] } @$records ],
),
WeBWorK::AchievementItems::form_popup_menu_row(
$c,
- id => 'tran_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => {
- dir => 'ltr',
- data => { problems => 'tran_problem_id', problems2 => 'tran_problem_id2' }
- }
+ id => 'clone_dest_problem_id',
+ label_text => $c->maketext('Problem number to replace'),
+ first_item => $c->maketext('Choose problem to replace.'),
+ values => [ map { [ $c->maketext('Problem [_1]', $_->problem_id) => $_->problem_id ] } @$records ],
),
- $c->tag(
- 'div',
- class => 'row mb-3',
- $c->c(
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'tran_problem_id',
- values => \@initialProblemIDs,
- label_text => $c->maketext('Copy this Problem'),
- menu_container_attr => { class => 'col-2 ps-0' },
- add_container => 0
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'tran_problem_id2',
- values => \@initialProblemIDs,
- label_text => $c->maketext('To this Problem'),
- menu_container_attr => { class => 'col-2 ps-0' },
- add_container => 0
- )
- )->join('')
- )
)->join('');
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('tran_set_id');
- return 'You need to input a Set Name' unless defined $setID;
-
- my $problemID = $c->param('tran_problem_id');
- return 'You need to input a Problem Number' unless $problemID;
-
- my $problemID2 = $c->param('tran_problem_id2');
- return 'You need to input a Problem Number' unless $problemID2;
-
- return 'You need to pick 2 different problems!' if $problemID == $problemID2;
-
- my $problem = $db->getMergedProblem($userName, $setID, $problemID);
- my $problem2 = $db->getUserProblem($userName, $setID, $problemID2);
- return 'There was an error accessing those problems.' unless $problem && $problem2;
+sub use_item ($self, $set, $records, $c) {
+ my $sourceID = $c->param('clone_source_problem_id');
+ my $destID = $c->param('clone_dest_problem_id');
+ unless ($sourceID) {
+ $c->addbadmessage($c->maketext('Select problem to clone with the [_1].', $self->name));
+ return '';
+ }
+ unless ($destID) {
+ $c->addbadmessage($c->maketext('Select problem to replace with the [_1].', $self->name));
+ return '';
+ }
- # Set the source of the second problem to that of the first problem.
- $problem2->source_file($problem->source_file);
- $db->putUserProblem($problem2);
+ my ($sourceProblem, $destProblem);
+ for (@$records) {
+ $sourceProblem = $_ if $_->problem_id == $sourceID;
+ $destProblem = $_ if $_->problem_id == $destID;
+ last if $sourceProblem && $destProblem;
+ }
+ return '' unless $sourceProblem && $destProblem;
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
+ my $db = $c->db;
+ my $userProblem = $db->getUserProblem($destProblem->user_id, $destProblem->set_id, $destProblem->problem_id);
+ $destProblem->source_file($sourceProblem->source_file);
+ $userProblem->source_file($destProblem->source_file);
+ $db->putUserProblem($userProblem);
- return;
+ return $c->maketext("Problem [_1] replaced with problem [_2].", $destID, $sourceID);
}
1;
diff --git a/lib/WeBWorK/AchievementItems/ExtendDueDate.pm b/lib/WeBWorK/AchievementItems/ExtendDueDate.pm
index 7b3214021f..2391ab916d 100644
--- a/lib/WeBWorK/AchievementItems/ExtendDueDate.pm
+++ b/lib/WeBWorK/AchievementItems/ExtendDueDate.pm
@@ -1,87 +1,76 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::ExtendDueDate;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to extend a close date by 24 hours.
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
-use WeBWorK::Utils::DateTime qw(between);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
+use WeBWorK::Utils qw(x);
+use WeBWorK::Utils::DateTime qw(after between);
+
+use constant ONE_DAY => 86400;
sub new ($class) {
return bless {
id => 'ExtendDueDate',
name => x('Tunic of Extension'),
- description => x('Adds 24 hours to the close date of a homework.')
+ description => x(
+ 'Adds 24 hours to the close date of a homework. '
+ . 'This will randomize problem details if used after the original close date.'
+ )
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my @openSets;
-
- for my $i (0 .. $#$sets) {
- push(@openSets, [ format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id ])
- if (between($sets->[$i]->open_date, $sets->[$i]->due_date) && $sets->[$i]->assignment_type eq 'default');
- }
-
- return unless @openSets;
+sub can_use ($self, $set, $records) {
+ return $set->assignment_type eq 'default' && between($set->open_date, $set->due_date + ONE_DAY);
+}
- return $c->c(
- $c->tag('p', $c->maketext('Choose the set whose close date you would like to extend.')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'ext_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr' }
+sub print_form ($self, $set, $records, $c) {
+ my $randomization_statement = after($set->due_date) ? $c->maketext('All problems will be rerandomized.') : '';
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Extend the close date of this assignment to [_1] (an additional 24 hours). [_2]',
+ $c->formatDateTime($set->due_date + ONE_DAY, $c->ce->{studentDateDisplayFormat}),
+ $randomization_statement
)
- )->join('');
+ );
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('ext_set_id');
- return 'You need to input a Set Name' unless defined $setID;
-
- my $set = $db->getMergedSet($userName, $setID);
- my $userSet = $db->getUserSet($userName, $setID);
- return q{Couldn't find that set!} unless $set && $userSet;
+sub use_item ($self, $set, $records, $c) {
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
+
+ # Change the seed for all of the problems if the set is currently closed.
+ if (after($set->due_date)) {
+ my %userProblems =
+ map { $_->problem_id => $_ }
+ $db->getUserProblemsWhere({ user_id => $set->user_id, set_id => $set->set_id });
+ for my $problem (@$records) {
+ my $userProblem = $userProblems{ $problem->problem_id };
+ $userProblem->problem_seed($userProblem->problem_seed % 2**31 + 1);
+ $problem->problem_seed($userProblem->problem_seed);
+ $db->putUserProblem($userProblem);
+ }
+ }
- # Add time to the reduced scoring date, due date, and answer date.
- $userSet->reduced_scoring_date($set->reduced_scoring_date() + 86400) if $set->reduced_scoring_date;
- $userSet->due_date($set->due_date() + 86400);
- $userSet->answer_date($set->answer_date() + 86400);
+ # Add time to the reduced scoring date if it was defined in the first place
+ if ($set->reduced_scoring_date) {
+ $set->reduced_scoring_date($set->reduced_scoring_date + ONE_DAY);
+ $userSet->reduced_scoring_date($set->reduced_scoring_date);
+ }
+ # Add time to the close date
+ $set->due_date($set->due_date + ONE_DAY);
+ $userSet->due_date($set->due_date);
+ # This may require also extending the answer date.
+ if ($set->due_date > $set->answer_date) {
+ $set->answer_date($set->due_date);
+ $userSet->answer_date($set->answer_date);
+ }
$db->putUserSet($userSet);
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ return $c->maketext(
+ 'Close date of this assignment extended by 24 hours to [_1].',
+ $c->formatDateTime($set->due_date, $c->ce->{studentDateDisplayFormat})
+ );
}
1;
diff --git a/lib/WeBWorK/AchievementItems/ExtendDueDateGW.pm b/lib/WeBWorK/AchievementItems/ExtendDueDateGW.pm
index 710824605e..6900d1f828 100644
--- a/lib/WeBWorK/AchievementItems/ExtendDueDateGW.pm
+++ b/lib/WeBWorK/AchievementItems/ExtendDueDateGW.pm
@@ -1,26 +1,12 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::ExtendDueDateGW;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to extend the close date on a test
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x);
use WeBWorK::Utils::DateTime qw(between);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
+
+use constant ONE_DAY => 86400;
sub new ($class) {
return bless {
@@ -31,76 +17,53 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my $db = $c->db;
-
- my @openGateways;
-
- # Find the template sets for open tests.
- for my $set (@$sets) {
- push(@openGateways, [ format_set_name_display($set->set_id) => $set->set_id ])
- if $set->assignment_type =~ /gateway/
- && $set->set_id !~ /,v\d+$/
- && between($set->open_date, $set->due_date);
- }
-
- return unless @openGateways;
+sub can_use ($self, $set, $records) {
+ return
+ $set->assignment_type =~ /gateway/
+ && $set->set_id !~ /,v\d+$/
+ && between($set->open_date, $set->due_date + ONE_DAY);
+}
- return $c->c(
- $c->tag('p', $c->maketext('Extend the close date for which test?')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'eddgw_gw_id',
- label_text => $c->maketext('Test Name'),
- values => \@openGateways,
- menu_attr => { dir => 'ltr' }
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Extend the close date of this test to [_1] (an additional 24 hours).',
+ $c->formatDateTime($set->due_date + ONE_DAY, $c->ce->{studentDateDisplayFormat})
)
- )->join('');
+ );
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('eddgw_gw_id');
- return 'You need to input a Test Name' unless defined $setID;
-
- my $set = $db->getMergedSet($userName, $setID);
- my $userSet = $db->getUserSet($userName, $setID);
- return q{Couldn't find that set!} unless $set && $userSet;
+sub use_item ($self, $set, $records, $c) {
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
# Add time to the reduced scoring date, due date, and answer date.
- $userSet->reduced_scoring_date($set->reduced_scoring_date() + 86400)
- if defined($set->reduced_scoring_date()) && $set->reduced_scoring_date();
- $userSet->due_date($set->due_date() + 86400);
- $userSet->answer_date($set->answer_date() + 86400);
+ if ($set->reduced_scoring_date) {
+ $set->reduced_scoring_date($set->reduced_scoring_date + ONE_DAY);
+ $userSet->reduced_scoring_date($set->reduced_scoring_date);
+ }
+ $set->due_date($set->due_date + ONE_DAY);
+ $userSet->due_date($set->due_date);
+ $set->answer_date($set->answer_date + ONE_DAY);
+ $userSet->answer_date($set->answer_date);
$db->putUserSet($userSet);
+ # FIXME: Should we add time to each test version, as adding 24 hours to a 1 hour long test
+ # isn't reasonable. Disabling this for now, will revisit later.
# Add time to the reduced scoring date, due date, and answer date for all versions.
- my @versions = $db->listSetVersions($userName, $setID);
-
- for my $version (@versions) {
- $set = $db->getSetVersion($userName, $setID, $version);
- $set->reduced_scoring_date($set->reduced_scoring_date() + 86400)
- if defined($set->reduced_scoring_date()) && $set->reduced_scoring_date();
- $set->due_date($set->due_date() + 86400);
- $set->answer_date($set->answer_date() + 86400);
- $db->putSetVersion($set);
- }
-
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ #my @versions = $db->listSetVersions($userName, $setID);
+ #for my $version (@versions) {
+ # $set = $db->getSetVersion($userName, $setID, $version);
+ # $set->reduced_scoring_date($set->reduced_scoring_date() + ONE_DAY)
+ # if defined($set->reduced_scoring_date()) && $set->reduced_scoring_date();
+ # $set->due_date($set->due_date() + ONE_DAY);
+ # $set->answer_date($set->answer_date() + ONE_DAY);
+ # $db->putSetVersion($set);
+ #}
+
+ return $c->maketext('Close date of this test extended by 24 hours to [_1].',
+ $c->formatDateTime($set->due_date, $c->ce->{studentDateDisplayFormat}));
}
1;
diff --git a/lib/WeBWorK/AchievementItems/ExtendReducedDate.pm b/lib/WeBWorK/AchievementItems/ExtendReducedDate.pm
new file mode 100644
index 0000000000..f6e24bb3b3
--- /dev/null
+++ b/lib/WeBWorK/AchievementItems/ExtendReducedDate.pm
@@ -0,0 +1,69 @@
+package WeBWorK::AchievementItems::ExtendReducedDate;
+use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
+
+# Item to extend a close date by 24 hours.
+
+use WeBWorK::Utils qw(x);
+use WeBWorK::Utils::DateTime qw(between);
+
+use constant ONE_DAY => 86400;
+
+sub new ($class) {
+ return bless {
+ id => 'ExtendReducedDate',
+ name => x('Scroll of Extension'),
+ description => x(
+ 'Adds 24 hours to the reduced scoring date of an assignment. You will have to resubmit '
+ . 'any problems that have already been penalized to earn full credit. You cannot '
+ . 'extend the reduced scoring date beyond the due date of an assignment.'
+ )
+ }, $class;
+}
+
+sub can_use ($self, $set, $records) {
+ return 0
+ unless $set->assignment_type eq 'default'
+ && $set->enable_reduced_scoring
+ && $set->reduced_scoring_date
+ && $set->reduced_scoring_date < $set->due_date;
+
+ $self->{new_date} = $set->reduced_scoring_date + ONE_DAY;
+ $self->{new_date} = $set->due_date if $set->due_date < $self->{new_date};
+ return between($set->open_date, $self->{new_date});
+}
+
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ q{This item won't work unless your instructor enables the reduced scoring feature. }
+ . 'Let your instructor know that you recieved this message.'
+ )
+ ) unless $c->{ce}->{pg}{ansEvalDefaults}{enableReducedScoring};
+
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Extend the reduced scoring date to [_1] (an additional 24 hours).',
+ $c->formatDateTime($self->{new_date}, $c->ce->{studentDateDisplayFormat})
+ )
+ );
+}
+
+sub use_item ($self, $set, $records, $c) {
+ return '' unless $c->{ce}->{pg}{ansEvalDefaults}{enableReducedScoring};
+
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
+
+ $set->reduced_scoring_date($self->{new_date});
+ $userSet->reduced_scoring_date($set->reduced_scoring_date);
+ $db->putUserSet($userSet);
+
+ return $c->maketext(
+ 'Reduced scoring date of this assignment extended by 24 hours to [_1].',
+ $c->formatDateTime($self->{new_date}, $c->ce->{studentDateDisplayFormat})
+ );
+}
+
+1;
diff --git a/lib/WeBWorK/AchievementItems/FullCreditProb.pm b/lib/WeBWorK/AchievementItems/FullCreditProb.pm
index 3bb8b38758..50e15639b5 100644
--- a/lib/WeBWorK/AchievementItems/FullCreditProb.pm
+++ b/lib/WeBWorK/AchievementItems/FullCreditProb.pm
@@ -1,28 +1,10 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::FullCreditProb;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to give full credit on a single problem
-use Mojo::JSON qw(encode_json);
-
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x wwRound);
use WeBWorK::Utils::DateTime qw(after);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
sub new ($class) {
return bless {
@@ -32,84 +14,57 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- # Construct a dropdown with open sets and another with problems.
- # Javascript ensures the appropriate problems are shown for the selected set.
-
- my (@openSets, @initialProblemIDs);
-
- for my $i (0 .. $#$sets) {
- if (after($sets->[$i]->open_date)
- && $sets->[$i]->assignment_type eq 'default'
- && @{ $setProblemIds->{ $sets->[$i]->set_id } })
- {
- push(
- @openSets,
- [
- format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id,
- data => { problem_ids => encode_json($setProblemIds->{ $sets->[$i]->set_id }) }
- ]
- );
- @initialProblemIDs = @{ $setProblemIds->{ $sets->[$i]->set_id } } unless @initialProblemIDs;
- }
- }
+sub can_use ($self, $set, $records) {
+ return 0
+ unless $set->assignment_type eq 'default'
+ && after($set->open_date);
- return unless @openSets;
+ my @problems = grep { $_->status < 1 } @$records;
+ return 0 unless @problems;
- return $c->c(
- $c->tag(
- 'p',
- $c->maketext(
- 'Please choose the set name and problem number of the question which should be given full credit.')
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'fcp_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr', data => { problems => 'fcp_problem_id' } }
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'fcp_problem_id',
- label_text => $c->maketext('Problem Number'),
- values => \@initialProblemIDs,
- menu_container_attr => { class => 'col-3' }
- )
- )->join('');
+ $self->{usableProblems} = \@problems;
+ return 1;
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('fcp_set_id');
- return 'You need to input a Set Name' unless defined $setID;
+sub print_form ($self, $set, $records, $c) {
+ return WeBWorK::AchievementItems::form_popup_menu_row(
+ $c,
+ id => 'full_cred_problem_id',
+ label_text => $c->maketext('Problem number to give full credit'),
+ first_item => $c->maketext('Choose problem to give full credit.'),
+ values => [
+ map { [ $c->maketext('Problem [_1] ([_2]% to 100%)', $_->problem_id, 100 * wwRound(2, $_->status)) =>
+ $_->problem_id ] } @{ $self->{usableProblems} }
+ ],
+ );
+}
- my $problemID = $c->param('fcp_problem_id');
- return 'You need to input a Problem Number' unless $problemID;
+sub use_item ($self, $set, $records, $c) {
+ my $problemID = $c->param('full_cred_problem_id');
+ unless ($problemID) {
+ $c->addbadmessage($c->maketext('Select problem to give full credit with the [_1].', $self->name));
+ return '';
+ }
- my $problem = $db->getUserProblem($userName, $setID, $problemID);
- return 'There was an error accessing that problem.' unless $problem;
+ my $problem;
+ for (@$records) {
+ if ($_->problem_id == $problemID) {
+ $problem = $_;
+ last;
+ }
+ }
+ return '' unless $problem;
- # Set the status and sub_status of the problem to one.
+ # Increase status to 100%.
+ my $db = $c->db;
+ my $userProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id);
$problem->status(1);
$problem->sub_status(1);
- $db->putUserProblem($problem);
-
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
+ $userProblem->status(1);
+ $userProblem->sub_status(1);
+ $db->putUserProblem($userProblem);
- return;
+ return $c->maketext('Problem number [_1] given full credit.', $problemID);
}
1;
diff --git a/lib/WeBWorK/AchievementItems/FullCreditSet.pm b/lib/WeBWorK/AchievementItems/FullCreditSet.pm
index b29469c69f..6c9065fa48 100644
--- a/lib/WeBWorK/AchievementItems/FullCreditSet.pm
+++ b/lib/WeBWorK/AchievementItems/FullCreditSet.pm
@@ -1,26 +1,10 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::FullCreditSet;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to give half credit on all problems in a homework set.
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x wwRound);
use WeBWorK::Utils::DateTime qw(after);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
sub new ($class) {
return bless {
@@ -30,59 +14,40 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my @openSets;
+sub can_use ($self, $set, $records) {
+ return 0
+ unless $set->assignment_type eq 'default'
+ && after($set->open_date);
- for my $i (0 .. $#$sets) {
- push(@openSets, [ format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id ])
- if (after($sets->[$i]->open_date) && $sets->[$i]->assignment_type eq 'default');
+ my $total = 0;
+ my $grade = 0;
+ for my $problem (@$records) {
+ $grade += $problem->status * $problem->value;
+ $total += $problem->value;
}
+ $self->{old_grade} = 100 * wwRound(2, $grade / $total);
+ return $self->{old_grade} == 100 ? 0 : 1;
+}
- return unless @openSets;
-
- return $c->c(
- $c->tag('p', $c->maketext('Please choose the set for which all problems should be given full credit.')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'fcs_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr' }
- )
- )->join('');
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag('p', $c->maketext(q(Increase this assignment's grade from [_1]% to 100%.), $self->{old_grade}));
}
-sub use_item ($self, $userName, $c) {
+sub use_item ($self, $set, $records, $c) {
my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('fcs_set_id');
- return 'You need to input a Set Name' unless defined $setID;
-
- my @probIDs = $db->listUserProblems($userName, $setID);
-
- for my $probID (@probIDs) {
- my $problem = $db->getUserProblem($userName, $setID, $probID);
-
- # Set status and sub_status to 1.
+ my %userProblems =
+ map { $_->problem_id => $_ } $db->getUserProblemsWhere({ user_id => $set->user_id, set_id => $set->set_id });
+ for my $problem (@$records) {
+ my $userProblem = $userProblems{ $problem->problem_id };
$problem->status(1);
$problem->sub_status(1);
- $db->putUserProblem($problem);
+ $userProblem->status(1);
+ $userProblem->sub_status(1);
+ $db->putUserProblem($userProblem);
}
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ return $c->maketext(q(Assignment's grade increased from [_1]% to 100%.), $self->{old_grade});
}
1;
diff --git a/lib/WeBWorK/AchievementItems/HalfCreditProb.pm b/lib/WeBWorK/AchievementItems/HalfCreditProb.pm
index 748ce57619..307e3d32af 100644
--- a/lib/WeBWorK/AchievementItems/HalfCreditProb.pm
+++ b/lib/WeBWorK/AchievementItems/HalfCreditProb.pm
@@ -1,118 +1,74 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::HalfCreditProb;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to give half credit on a single problem.
-use Mojo::JSON qw(encode_json);
-
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x wwRound);
use WeBWorK::Utils::DateTime qw(after);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
sub new ($class) {
return bless {
id => 'HalfCreditProb',
name => x('Lesser Rod of Revelation'),
- description => x('Increases the score of a single problem by 50%, to a maximum of 100%.')
+ description => x('Increases the grade of a single problem by 50%, to a maximum of 100%.')
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- # Construct a dropdown with open sets and another with problems.
- # Javascript ensures the appropriate problems are shown for the selected set.
-
- my (@openSets, @initialProblemIDs);
-
- for my $i (0 .. $#$sets) {
- if (after($sets->[$i]->open_date)
- && $sets->[$i]->assignment_type eq 'default'
- && @{ $setProblemIds->{ $sets->[$i]->set_id } })
- {
- push(
- @openSets,
- [
- format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id,
- data => { problem_ids => encode_json($setProblemIds->{ $sets->[$i]->set_id }) }
- ]
- );
- @initialProblemIDs = @{ $setProblemIds->{ $sets->[$i]->set_id } } unless @initialProblemIDs;
- }
- }
-
- return unless @openSets;
+sub can_use($self, $set, $records) {
+ return 0
+ unless $set->assignment_type eq 'default'
+ && after($set->open_date);
- return $c->c(
- $c->tag(
- 'p',
- $c->maketext(
- 'Please choose the assignment name and problem number of the question to add half credit to.')
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'hcp_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr', data => { problems => 'hcp_problem_id' } }
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'hcp_problem_id',
- values => \@initialProblemIDs,
- label_text => $c->maketext('Problem Number'),
- menu_container_attr => { class => 'col-3' }
- )
- )->join('');
+ $self->{unfinishedProblems} = [ grep { $_->status < 1 } @$records ];
+ return @{ $self->{unfinishedProblems} } ? 1 : 0;
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('hcp_set_id');
- return 'You need to input a Set Name' unless defined $setID;
-
- my $problemID = $c->param('hcp_problem_id');
- return 'You need to input a Problem Number' unless $problemID;
-
- my $problem = $db->getUserProblem($userName, $setID, $problemID);
- return 'There was an error accessing that problem.' unless $problem;
-
- # Add .5 to grade with max of 1
- my $new_status = $problem->status + 0.5;
- $new_status = 1 if $new_status > 1;
- $problem->status($new_status);
- $problem->sub_status($new_status);
-
- $db->putUserProblem($problem);
+sub print_form ($self, $set, $records, $c) {
+ return WeBWorK::AchievementItems::form_popup_menu_row(
+ $c,
+ id => 'half_cred_problem_id',
+ label_text => $c->maketext('Problem number to increase grade by 50%'),
+ first_item => $c->maketext('Choose problem to increase grade by 50%.'),
+ values => [
+ map { [
+ $c->maketext(
+ 'Problem [_1] ([_2]% to [_3]%)',
+ $_->problem_id,
+ 100 * wwRound(2, $_->status),
+ 100 * wwRound(2, $_->status < 0.5 ? $_->status + 0.5 : 1)
+ ) => $_->problem_id
+ ] } @{ $self->{unfinishedProblems} }
+ ],
+ );
+}
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
+sub use_item ($self, $set, $records, $c) {
+ my $problemID = $c->param('half_cred_problem_id');
+ unless ($problemID) {
+ $c->addbadmessage($c->maketext('Select problem to increase its grade by 50% with the [_1].', $self->name));
+ return '';
+ }
- return;
+ my $problem;
+ for (@$records) {
+ if ($_->problem_id == $problemID) {
+ $problem = $_;
+ last;
+ }
+ }
+ return '' unless $problem;
+
+ # Increase status to 100%.
+ my $db = $c->db;
+ my $userProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id);
+ $problem->status($problem->status > 0.5 ? 1 : $problem->status + 0.5);
+ $problem->sub_status($problem->status);
+ $userProblem->status($problem->status);
+ $userProblem->sub_status($problem->status);
+ $db->putUserProblem($userProblem);
+
+ return $c->maketext('Problem number [_1] grade increased to [_2]%.', $problemID,
+ 100 * wwRound(2, $problem->status));
}
1;
diff --git a/lib/WeBWorK/AchievementItems/HalfCreditSet.pm b/lib/WeBWorK/AchievementItems/HalfCreditSet.pm
index ba01a455bd..37496e9bdd 100644
--- a/lib/WeBWorK/AchievementItems/HalfCreditSet.pm
+++ b/lib/WeBWorK/AchievementItems/HalfCreditSet.pm
@@ -1,26 +1,10 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::HalfCreditSet;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to give half credit on all problems in a homework set.
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x wwRound);
use WeBWorK::Utils::DateTime qw(after);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
sub new ($class) {
return bless {
@@ -30,64 +14,49 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my @openSets;
-
- for my $i (0 .. $#$sets) {
- push(@openSets, [ format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id ])
- if (after($sets->[$i]->open_date) && $sets->[$i]->assignment_type eq 'default');
+sub can_use($self, $set, $records) {
+ return 0
+ unless $set->assignment_type eq 'default'
+ && after($set->open_date);
+
+ my $total = 0;
+ my $old_grade = 0;
+ my $new_grade = 0;
+ for my $problem (@$records) {
+ $old_grade += $problem->status * $problem->value;
+ $new_grade += ($problem->status > 0.5 ? 1 : $problem->status + 0.5) * $problem->value;
+ $total += $problem->value;
}
+ $self->{old_grade} = 100 * wwRound(2, $old_grade / $total);
+ $self->{new_grade} = 100 * wwRound(2, $new_grade / $total);
+ return $self->{old_grade} == 100 ? 0 : 1;
+}
- return unless @openSets;
-
- return $c->c(
- $c->tag(
- 'p', $c->maketext('Please choose the assignment for which all problems should have half credit added.')
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'hcs_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr' }
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ q(Increase this assignment's grade from [_1]% to [_2]%.),
+ $self->{old_grade}, $self->{new_grade}
)
- )->join('');
+ );
}
-sub use_item ($self, $userName, $c) {
+sub use_item ($self, $set, $records, $c) {
my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('hcs_set_id');
- return 'You need to input a Set Name' unless defined $setID;
-
- my @probIDs = $db->listUserProblems($userName, $setID);
-
- for my $probID (@probIDs) {
- my $problem = $db->getUserProblem($userName, $setID, $probID);
-
- # Add .5 to grade with max of 1.
- my $new_status = $problem->status + 0.5;
- $new_status = 1 if $new_status > 1;
- $problem->status($new_status);
- $problem->sub_status($new_status);
-
- $db->putUserProblem($problem);
+ my %userProblems =
+ map { $_->problem_id => $_ } $db->getUserProblemsWhere({ user_id => $set->user_id, set_id => $set->set_id });
+ for my $problem (@$records) {
+ my $userProblem = $userProblems{ $problem->problem_id };
+ $problem->status($problem->status > 0.5 ? 1 : $problem->status + 0.5);
+ $problem->sub_status($problem->status);
+ $userProblem->status($problem->status);
+ $userProblem->sub_status($problem->status);
+ $db->putUserProblem($userProblem);
}
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ return $c->maketext(q(Assignment's grade increased from [_1] to [_2].), $self->{old_grade}, $self->{new_grade});
}
1;
diff --git a/lib/WeBWorK/AchievementItems/NoReducedCred.pm b/lib/WeBWorK/AchievementItems/NoReducedCred.pm
new file mode 100644
index 0000000000..9a8b124a78
--- /dev/null
+++ b/lib/WeBWorK/AchievementItems/NoReducedCred.pm
@@ -0,0 +1,65 @@
+package WeBWorK::AchievementItems::NoReducedCred;
+use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
+
+# Item to remove reduce credit scoring period from a set.
+# Reduced scoring needs to be enabled for this item to be useful.
+
+use WeBWorK::Utils qw(x);
+use WeBWorK::Utils::DateTime qw(between);
+
+sub new ($class) {
+ return bless {
+ id => 'NoReducedCred',
+ name => x('Potion of Power'),
+ description => x(
+ 'Remove reduced scoring penalties from an open assignemnt. You will have to resubmit '
+ . 'any problems that have already been penalized to earn full credit on them.'
+ )
+ }, $class;
+}
+
+sub can_use ($self, $set, $records) {
+ return 0
+ unless $set->assignment_type eq 'default'
+ && $set->enable_reduced_scoring
+ && $set->reduced_scoring_date
+ && $set->reduced_scoring_date < $set->due_date
+ && between($set->open_date, $set->due_date);
+}
+
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ q{This item won't work unless your instructor enables the reduced scoring feature. }
+ . 'Let your instructor know that you recieved this message.'
+ )
+ ) unless $c->{ce}->{pg}{ansEvalDefaults}{enableReducedScoring};
+
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Remove the reduced scoring penalty from this assignment. Problems submitted before '
+ . 'the close date on [_1] will earn full credit. Any problems that have already been '
+ . 'penalized will have to be resubmitted for full credit.',
+ $c->formatDateTime($set->due_date, $c->ce->{studentDateDisplayFormat})
+ )
+ );
+}
+
+sub use_item ($self, $set, $records, $c) {
+ return '' unless $c->{ce}->{pg}{ansEvalDefaults}{enableReducedScoring};
+
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
+
+ $set->enable_reduced_scoring(0);
+ $set->reduced_scoring_date($set->due_date);
+ $userSet->enable_reduced_scoring(0);
+ $userSet->reduced_scoring_date($set->due_date);
+ $db->putUserSet($userSet);
+
+ return $c->maketext('Reduced scoring penalty removed.');
+}
+
+1;
diff --git a/lib/WeBWorK/AchievementItems/ReducedCred.pm b/lib/WeBWorK/AchievementItems/ReducedCred.pm
index 089dbfe410..340d960815 100644
--- a/lib/WeBWorK/AchievementItems/ReducedCred.pm
+++ b/lib/WeBWorK/AchievementItems/ReducedCred.pm
@@ -1,27 +1,13 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::ReducedCred;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to extend a close date by 24 hours for reduced credit
# Reduced scoring needs to be enabled for this item to work.
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
-use WeBWorK::Utils::DateTime qw(between);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
+use WeBWorK::Utils qw(x);
+use WeBWorK::Utils::DateTime qw(after between);
+
+use constant ONE_DAY => 86400;
sub new ($class) {
return bless {
@@ -29,69 +15,83 @@ sub new ($class) {
name => x('Ring of Reduction'),
description => x(
'Enable reduced scoring for a homework set. This will allow you to submit answers '
- . 'for partial credit for 24 hours after the close date.'
+ . 'for partial credit for 24 hours after the close date. '
+ . 'This will randomize problem details if used after the original close date.'
)
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my @openSets;
-
- for my $i (0 .. $#$sets) {
- push(@openSets, [ format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id ])
- if (between($sets->[$i]->open_date, $sets->[$i]->due_date) && $sets->[$i]->assignment_type eq 'default');
- }
+sub can_use ($self, $set, $records) {
+ return $set->assignment_type eq 'default' && between($set->open_date, $set->due_date + ONE_DAY);
+}
- return unless @openSets;
+sub print_form ($self, $set, $records, $c) {
+ my $ce = $c->ce;
- return $c->c(
- $c->tag('p', $c->maketext('Choose the set which you would like to enable partial credit for.')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'red_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr' }
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ q{This item won't work unless your instructor enables the reduced scoring feature. }
+ . 'Let your instructor know that you received this message.'
+ )
+ ) unless $ce->{pg}{ansEvalDefaults}{enableReducedScoring};
+
+ my $randomization_statement = after($set->due_date) ? $c->maketext('All problems will be rerandomized.') : '';
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Extend the close date of this assignment to [_1] (an additional 24 hours). Any submissions during '
+ . 'this additional time will be reduced and are worth [_2]% of their full value. [_3]',
+ $c->formatDateTime($set->due_date + ONE_DAY, $ce->{studentDateDisplayFormat}),
+ 100 * $ce->{pg}{ansEvalDefaults}{reducedScoringValue},
+ $randomization_statement
)
- )->join('');
+ );
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
+sub use_item ($self, $set, $records, $c) {
my $ce = $c->ce;
+ my $db = $c->db;
- # Validate data
-
- return q{This item won't work unless your instructor enables the reduced scoring feature. }
- . 'Let your instructor know that you recieved this message.'
- unless $ce->{pg}{ansEvalDefaults}{reducedScoringPeriod};
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return "No achievement data?!?!?!" unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('red_set_id');
- return "You need to input a Set Name" unless defined $setID;
-
- my $set = $db->getMergedSet($userName, $setID);
- my $userSet = $db->getUserSet($userName, $setID);
- return "Couldn't find that set!" unless $set && $userSet;
+ # Still need to double check reduced scoring is enabled.
+ return '' unless $ce->{pg}{ansEvalDefaults}{enableReducedScoring};
+
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
+
+ # Change the seed for all of the problems if the set is currently closed.
+ if (after($set->due_date)) {
+ my %userProblems =
+ map { $_->problem_id => $_ }
+ $db->getUserProblemsWhere({ user_id => $set->user_id, set_id => $set->set_id });
+ for my $problem (@$records) {
+ my $userProblem = $userProblems{ $problem->problem_id };
+ $userProblem->problem_seed($userProblem->problem_seed % 2**31 + 1);
+ $problem->problem_seed($userProblem->problem_seed);
+ $db->putUserProblem($userProblem);
+ }
+ }
- # Enable reduced scoring on the set and add the reduced scoring period to the due date.
- my $additionalTime = 60 * $ce->{pg}{ansEvalDefaults}{reducedScoringPeriod};
+ # Either there is already a valid reduced scoring date, or set the reduced scoring date to the close date.
+ unless ($set->reduced_scoring_date && $set->reduced_scoring_date < $set->due_date) {
+ $set->reduced_scoring_date($set->due_date);
+ $userSet->reduced_scoring_date($set->reduced_scoring_date);
+ }
+ $set->enable_reduced_scoring(1);
$userSet->enable_reduced_scoring(1);
- $userSet->reduced_scoring_date($set->due_date());
- $userSet->due_date($set->due_date() + $additionalTime);
- $userSet->answer_date($set->answer_date() + $additionalTime);
+ # Add time to the close date
+ $set->due_date($set->due_date + ONE_DAY);
+ $userSet->due_date($set->due_date);
+ # This may require also extending the answer date.
+ if ($set->due_date > $set->answer_date) {
+ $set->answer_date($set->due_date);
+ $userSet->answer_date($set->answer_date);
+ }
$db->putUserSet($userSet);
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ return $c->maketext(
+ 'Close date of this assignment extended by 24 hours to [_1].',
+ $c->formatDateTime($set->due_date, $ce->{studentDateDisplayFormat})
+ );
}
1;
diff --git a/lib/WeBWorK/AchievementItems/ResetIncorrectAttempts.pm b/lib/WeBWorK/AchievementItems/ResetIncorrectAttempts.pm
index 7f63b37e1f..cd0ef5a330 100644
--- a/lib/WeBWorK/AchievementItems/ResetIncorrectAttempts.pm
+++ b/lib/WeBWorK/AchievementItems/ResetIncorrectAttempts.pm
@@ -1,28 +1,10 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::ResetIncorrectAttempts;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to reset number of incorrect attempts.
-use Mojo::JSON qw(encode_json);
-
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x);
use WeBWorK::Utils::DateTime qw(between);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
sub new ($class) {
return bless {
@@ -32,85 +14,56 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- # Construct a dropdown with open sets and another with problems.
- # Javascript ensures the appropriate problems are shown for the selected set.
-
- my (@openSets, @initialProblemIDs);
-
- for my $i (0 .. $#$sets) {
- if (between($sets->[$i]->open_date, $sets->[$i]->due_date)
- && $sets->[$i]->assignment_type eq 'default'
- && @{ $setProblemIds->{ $sets->[$i]->set_id } })
- {
- push(
- @openSets,
- [
- format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id,
- data => { problem_ids => encode_json($setProblemIds->{ $sets->[$i]->set_id }) }
- ]
- );
- @initialProblemIDs = @{ $setProblemIds->{ $sets->[$i]->set_id } } unless @initialProblemIDs;
- }
- }
+sub can_use ($self, $set, $records) {
+ return 0
+ unless $set->assignment_type eq 'default'
+ && between($set->open_date, $set->due_date);
- return unless @openSets;
-
- return $c->c(
- $c->tag(
- 'p',
- $c->maketext(
- 'Please choose the set name and problem number of the question which '
- . 'should have its incorrect attempt count reset.'
- )
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'ria_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr', data => { problems => 'ria_problem_id' } }
- ),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'ria_problem_id',
- label_text => $c->maketext('Problem Number'),
- values => \@initialProblemIDs,
- menu_container_attr => { class => 'col-3' }
- )
- )->join('');
+ $self->{usableProblems} = [ grep { $_->max_attempts > 0 && $_->num_incorrect > 0 && $_->status < 1 } @$records ];
+ return @{ $self->{usableProblems} } ? 1 : 0;
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('ria_set_id');
- return 'You need to input a Set Name' unless defined $setID;
+sub print_form ($self, $set, $records, $c) {
+ return WeBWorK::AchievementItems::form_popup_menu_row(
+ $c,
+ id => 'reset_attempts_problem_id',
+ label_text => $c->maketext('Problem number to reset incorrect attempts'),
+ first_item => $c->maketext('Choose problem to reset incorrect attempts.'),
+ values => [
+ map { [
+ $c->maketext('Problem [_1] ([_2] of [_3] used)',
+ $_->problem_id, $_->num_incorrect, $_->max_attempts) => $_->problem_id
+ ] } @{ $self->{usableProblems} }
+ ],
+ );
+}
- my $problemID = $c->param('ria_problem_id');
- return 'You need to input a Problem Number' unless $problemID;
+# use_item is called after print_form returns a non-empty form.
+# So we can assume that $set and $records have already been validated.
+sub use_item ($self, $set, $records, $c) {
+ my $problemID = $c->param('reset_attempts_problem_id');
+ unless ($problemID) {
+ $c->addbadmessage($c->maketext('Select problem to reset with the [_1].', $self->name));
+ return '';
+ }
- my $problem = $db->getUserProblem($userName, $setID, $problemID);
- return 'There was an error accessing that problem.' unless $problem;
+ my $problem;
+ for (@$records) {
+ if ($_->problem_id == $problemID) {
+ $problem = $_;
+ last;
+ }
+ }
+ return '' unless $problem;
# Set the number of incorrect attempts to zero.
+ my $db = $c->db;
+ my $userProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id);
$problem->num_incorrect(0);
- $db->putUserProblem($problem);
-
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
+ $userProblem->num_incorrect(0);
+ $db->putUserProblem($userProblem);
- return;
+ return $c->maketext('Reset the number of attempts on problem [_1].', $problemID);
}
1;
diff --git a/lib/WeBWorK/AchievementItems/ResurrectGW.pm b/lib/WeBWorK/AchievementItems/ResurrectGW.pm
index beb9563f36..00b99ca4ce 100644
--- a/lib/WeBWorK/AchievementItems/ResurrectGW.pm
+++ b/lib/WeBWorK/AchievementItems/ResurrectGW.pm
@@ -1,26 +1,12 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::ResurrectGW;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to extend the due date on a gateway
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x);
use WeBWorK::Utils::DateTime qw(after);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
+
+use constant ONE_DAY => 86400;
sub new ($class) {
return bless {
@@ -33,63 +19,43 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my $db = $c->db;
-
- my @closed_gateway_sets;
-
- # Find the template sets of gateway quizzes.
- for my $set (@$sets) {
- push(@closed_gateway_sets, [ format_set_name_display($set->set_id) => $set->set_id ])
- if $set->assignment_type =~ /gateway/
- && $set->set_id !~ /,v\d+$/
- && (after($set->due_date)
- || ($set->reduced_scoring_date && after($set->reduced_scoring_date)));
- }
-
- return unless @closed_gateway_sets;
+sub can_use($self, $set, $records) {
+ return $set->assignment_type =~ /gateway/
+ && (after($set->due_date) || ($set->reduced_scoring_date && after($set->reduced_scoring_date)));
+ # TODO: Check if a new version can be created, and only allow using this reward in that case.
+}
- return $c->c(
- $c->tag('p', $c->maketext('Resurrect which test?')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'resgw_gw_id',
- label_text => $c->maketext('Test Name'),
- values => \@closed_gateway_sets,
- menu_attr => { dir => 'ltr' }
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Reopen this test for the next 24 hours. This item does not allow you to take any additional '
+ . 'versions of the test.'
)
- )->join('');
+ );
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('resgw_gw_id');
- return 'You need to input a Test Name' unless defined $setID;
-
- my $set = $db->getUserSet($userName, $setID);
- return q{Couldn't find that set!} unless $set;
+sub use_item ($self, $set, $records, $c) {
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
# Add time to the reduced scoring date, due date, and answer date.
- $set->reduced_scoring_date(time + 86400) if defined($set->reduced_scoring_date()) && $set->reduced_scoring_date();
- $set->due_date(time + 86400);
- $set->answer_date(time + 86400);
- $db->putUserSet($set);
-
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
+ if ($set->reduced_scoring_date) {
+ $set->reduced_scoring_date(time + ONE_DAY);
+ $userSet->reduced_scoring_date($set->reduced_scoring_date);
+ }
+ $set->due_date(time + ONE_DAY);
+ $userSet->due_date($set->due_date);
+ if ($set->due_date > $set->answer_date) {
+ $set->answer_date(time + ONE_DAY);
+ $userSet->answer_date($set->answer_date);
+ }
+ $db->putUserSet($userSet);
- return;
+ return $c->maketext(
+ 'This assignment has been reopened and will now close on [_1].',
+ $c->formatDateTime($set->due_date, $c->ce->{studentDateDisplayFormat})
+ );
}
1;
diff --git a/lib/WeBWorK/AchievementItems/ResurrectHW.pm b/lib/WeBWorK/AchievementItems/ResurrectHW.pm
index 6cc97529fb..da8364822f 100644
--- a/lib/WeBWorK/AchievementItems/ResurrectHW.pm
+++ b/lib/WeBWorK/AchievementItems/ResurrectHW.pm
@@ -1,26 +1,12 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::ResurrectHW;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to resurrect a homework for 24 hours
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
+use WeBWorK::Utils qw(x);
use WeBWorK::Utils::DateTime qw(after);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
+
+use constant ONE_DAY => 86400;
sub new ($class) {
return bless {
@@ -30,70 +16,48 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- # List all of the sets that are closed or past their reduced scoring date.
-
- my @closedSets;
-
- for my $i (0 .. $#$sets) {
- push(@closedSets, [ format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id ])
- if $sets->[$i]->assignment_type eq 'default'
- && (after($sets->[$i]->due_date)
- || ($sets->[$i]->reduced_scoring_date && after($sets->[$i]->reduced_scoring_date)));
- }
-
- return unless @closedSets;
-
- return $c->c(
- $c->tag('p', $c->maketext('Choose the set which you would like to resurrect.')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'res_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@closedSets,
- menu_attr => { dir => 'ltr' }
- )
- )->join('');
+sub can_use($self, $set, $records) {
+ return $set->assignment_type eq 'default'
+ && (after($set->due_date) || ($set->reduced_scoring_date && after($set->reduced_scoring_date)));
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('res_set_id');
- return 'You need to input a Set Name' unless defined $setID;
-
- my $set = $db->getUserSet($userName, $setID);
- return q{Couldn't find that set!} unless $set;
-
- # Set a new reduced scoring date, close date, and answer date for the student.
- $set->reduced_scoring_date(time + 86400);
- $set->due_date(time + 86400);
- $set->answer_date(time + 86400);
- $db->putUserSet($set);
-
- my @probIDs = $db->listUserProblems($userName, $setID);
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag('p',
+ $c->maketext('Reopen this homework assignment for the next 24 hours. All problems will be rerandomized.'));
+}
- # Change the seed for all of the problems in the set.
- for my $probID (@probIDs) {
- my $problem = $db->getUserProblem($userName, $setID, $probID);
- $problem->problem_seed($problem->problem_seed + 100);
- $db->putUserProblem($problem);
+sub use_item ($self, $set, $records, $c) {
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
+
+ # Change the seed for all of the problems since the set is currently closed.
+ my %userProblems =
+ map { $_->problem_id => $_ } $db->getUserProblemsWhere({ user_id => $set->user_id, set_id => $set->set_id });
+ for my $problem (@$records) {
+ my $userProblem = $userProblems{ $problem->problem_id };
+ $userProblem->problem_seed($userProblem->problem_seed % 2**31 + 1);
+ $problem->problem_seed($userProblem->problem_seed);
+ $db->putUserProblem($userProblem);
}
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
+ # Add time to the reduced scoring date if it was defined in the first place
+ if ($set->reduced_scoring_date) {
+ $set->reduced_scoring_date(time + ONE_DAY);
+ $userSet->reduced_scoring_date($set->reduced_scoring_date);
+ }
+ # Add time to the close date
+ $set->due_date(time + ONE_DAY);
+ $userSet->due_date($set->due_date);
+ # This may require also extending the answer date.
+ if ($set->due_date > $set->answer_date) {
+ $set->answer_date($set->due_date);
+ $userSet->answer_date($set->answer_date);
+ }
+ $db->putUserSet($userSet);
- return;
+ return $c->maketext(
+ 'This assignment has been reopened and will now close on [_1]. Problems have been rerandomized.',
+ $c->formatDateTime($set->due_date, $c->ce->{studentDateDisplayFormat}));
}
1;
diff --git a/lib/WeBWorK/AchievementItems/SuperExtendDueDate.pm b/lib/WeBWorK/AchievementItems/SuperExtendDueDate.pm
index 2c2216caa7..7932e6de20 100644
--- a/lib/WeBWorK/AchievementItems/SuperExtendDueDate.pm
+++ b/lib/WeBWorK/AchievementItems/SuperExtendDueDate.pm
@@ -1,87 +1,76 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::SuperExtendDueDate;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
# Item to extend a close date by 48 hours.
-use WeBWorK::Utils qw(x nfreeze_base64 thaw_base64);
-use WeBWorK::Utils::DateTime qw(between);
-use WeBWorK::Utils::Sets qw(format_set_name_display);
+use WeBWorK::Utils qw(x);
+use WeBWorK::Utils::DateTime qw(after between);
+
+use constant TWO_DAYS => 172800;
sub new ($class) {
return bless {
id => 'SuperExtendDueDate',
name => x('Robe of Longevity'),
- description => x('Adds 48 hours to the close date of a homework.')
+ description => x(
+ 'Adds 48 hours to the close date of a homework. '
+ . 'This will randomize problem details if used after the original close date.'
+ )
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- my @openSets;
-
- for my $i (0 .. $#$sets) {
- push(@openSets, [ format_set_name_display($sets->[$i]->set_id) => $sets->[$i]->set_id ])
- if (between($sets->[$i]->open_date, $sets->[$i]->due_date) && $sets->[$i]->assignment_type eq 'default');
- }
-
- return unless @openSets;
+sub can_use ($self, $set, $records) {
+ return $set->assignment_type eq 'default' && between($set->open_date, $set->due_date + TWO_DAYS);
+}
- return $c->c(
- $c->tag('p', $c->maketext('Choose the set whose close date you would like to extend.')),
- WeBWorK::AchievementItems::form_popup_menu_row(
- $c,
- id => 'super_ext_set_id',
- label_text => $c->maketext('Set Name'),
- values => \@openSets,
- menu_attr => { dir => 'ltr' }
+sub print_form ($self, $set, $records, $c) {
+ my $randomization_statement = after($set->due_date) ? $c->maketext('All problems will be rerandomized.') : '';
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Extend the close date of this assignment to [_1] (an additional 48 hours). [_2]',
+ $c->formatDateTime($set->due_date + TWO_DAYS, $c->ce->{studentDateDisplayFormat}),
+ $randomization_statement
)
- )->join('');
+ );
}
-sub use_item ($self, $userName, $c) {
- my $db = $c->db;
- my $ce = $c->ce;
-
- # Validate data
-
- my $globalUserAchievement = $db->getGlobalUserAchievement($userName);
- return 'No achievement data?!?!?!' unless $globalUserAchievement->frozen_hash;
-
- my $globalData = thaw_base64($globalUserAchievement->frozen_hash);
- return "You are $self->{id} trying to use an item you don't have" unless $globalData->{ $self->{id} };
-
- my $setID = $c->param('super_ext_set_id');
- return 'You need to input a Set Name' unless defined $setID;
-
- my $set = $db->getMergedSet($userName, $setID);
- my $userSet = $db->getUserSet($userName, $setID);
- return q{Couldn't find that set!} unless $set && $userSet;
+sub use_item ($self, $set, $records, $c) {
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
+
+ # Change the seed for all of the problems if the set is currently closed.
+ if (after($set->due_date)) {
+ my %userProblems =
+ map { $_->problem_id => $_ }
+ $db->getUserProblemsWhere({ user_id => $set->user_id, set_id => $set->set_id });
+ for my $problem (@$records) {
+ my $userProblem = $userProblems{ $problem->problem_id };
+ $userProblem->problem_seed($userProblem->problem_seed % 2**31 + 1);
+ $problem->problem_seed($userProblem->problem_seed);
+ $db->putUserProblem($userProblem);
+ }
+ }
- # Add time to the reduced scoring date, due date, and answer date.
- $userSet->reduced_scoring_date($set->reduced_scoring_date() + 172800) if $set->reduced_scoring_date;
- $userSet->due_date($set->due_date() + 172800);
- $userSet->answer_date($set->answer_date() + 172800);
+ # Add time to the reduced scoring date if it was defined in the first place
+ if ($set->reduced_scoring_date) {
+ $set->reduced_scoring_date($set->reduced_scoring_date + TWO_DAYS);
+ $userSet->reduced_scoring_date($set->reduced_scoring_date);
+ }
+ # Add time to the close date
+ $set->due_date($set->due_date + TWO_DAYS);
+ $userSet->due_date($set->due_date);
+ # This may require also extending the answer date.
+ if ($set->due_date > $set->answer_date) {
+ $set->answer_date($set->due_date);
+ $userSet->answer_date($set->answer_date);
+ }
$db->putUserSet($userSet);
- $globalData->{ $self->{id} }--;
- $globalUserAchievement->frozen_hash(nfreeze_base64($globalData));
- $db->putGlobalUserAchievement($globalUserAchievement);
-
- return;
+ return $c->maketext(
+ 'Close date of this assignment extended by 48 hours to [_1].',
+ $c->formatDateTime($set->due_date, $c->ce->{studentDateDisplayFormat})
+ );
}
1;
diff --git a/lib/WeBWorK/AchievementItems/SuperExtendReducedDate.pm b/lib/WeBWorK/AchievementItems/SuperExtendReducedDate.pm
new file mode 100644
index 0000000000..3f2361b835
--- /dev/null
+++ b/lib/WeBWorK/AchievementItems/SuperExtendReducedDate.pm
@@ -0,0 +1,69 @@
+package WeBWorK::AchievementItems::SuperExtendReducedDate;
+use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
+
+# Item to extend a close date by 48 hours.
+
+use WeBWorK::Utils qw(x);
+use WeBWorK::Utils::DateTime qw(between);
+
+use constant TWO_DAYS => 172800;
+
+sub new ($class) {
+ return bless {
+ id => 'SuperExtendReducedDate',
+ name => x('Scroll of Longevity'),
+ description => x(
+ 'Adds 48 hours to the reduced scoring date of an assignment. You will have to resubmit '
+ . 'any problems that have already been penalized to earn full credit. You cannot '
+ . 'extend the reduced scoring date beyond the due date of an assignment.'
+ )
+ }, $class;
+}
+
+sub can_use ($self, $set, $records) {
+ return 0
+ unless $set->assignment_type eq 'default'
+ && $set->enable_reduced_scoring
+ && $set->reduced_scoring_date
+ && $set->reduced_scoring_date < $set->due_date;
+
+ $self->{new_date} = $set->reduced_scoring_date + TWO_DAYS;
+ $self->{new_date} = $set->due_date if $set->due_date < $self->{new_date};
+ return between($set->open_date, $self->{new_date});
+}
+
+sub print_form ($self, $set, $records, $c) {
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ q{This item won't work unless your instructor enables the reduced scoring feature. }
+ . 'Let your instructor know that you recieved this message.'
+ )
+ ) unless $c->{ce}->{pg}{ansEvalDefaults}{enableReducedScoring};
+
+ return $c->tag(
+ 'p',
+ $c->maketext(
+ 'Extend the reduced scoring date to [_1] (an additional 48 hours).',
+ $c->formatDateTime($self->{new_date}, $c->ce->{studentDateDisplayFormat})
+ )
+ );
+}
+
+sub use_item ($self, $set, $records, $c) {
+ return '' unless $c->{ce}->{pg}{ansEvalDefaults}{enableReducedScoring};
+
+ my $db = $c->db;
+ my $userSet = $db->getUserSet($set->user_id, $set->set_id);
+
+ $set->reduced_scoring_date($self->{new_date});
+ $userSet->reduced_scoring_date($set->reduced_scoring_date);
+ $db->putUserSet($userSet);
+
+ return $c->maketext(
+ 'Reduced scoring date of this assignment extended by 48 hours to [_1].',
+ $c->formatDateTime($self->{new_date}, $c->ce->{studentDateDisplayFormat})
+ );
+}
+
+1;
diff --git a/lib/WeBWorK/AchievementItems/Surprise.pm b/lib/WeBWorK/AchievementItems/Surprise.pm
index 299634df28..b598b75352 100644
--- a/lib/WeBWorK/AchievementItems/Surprise.pm
+++ b/lib/WeBWorK/AchievementItems/Surprise.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::AchievementItems::Surprise;
use Mojo::Base 'WeBWorK::AchievementItems', -signatures;
@@ -28,10 +13,18 @@ sub new ($class) {
}, $class;
}
-sub print_form ($self, $sets, $setProblemIds, $c) {
- # The form opens the file "suprise_message.txt" in the achievements
- # folder and prints the contents of the file.
+# Override to not print number of items that remain.
+sub remaining_title ($self, $c) {
+ return $c->maketext($self->name);
+}
+sub can_use ($self, $set, $records) { return 1; }
+
+sub print_form ($self, $set, $records, $c) {
+ $self->{hideUseButton} = 1;
+
+ # The form opens the file "surprise_message.txt" in the achievements
+ # folder and prints the contents of the file.
open my $MESSAGE, '<', "$c->{ce}{courseDirs}{achievements}/surprise_message.txt"
or return $c->tag('p', $c->maketext(q{I couldn't find the file [ACHIEVEMENT_DIR]/surprise_message.txt!}));
local $/ = undef;
@@ -41,7 +34,7 @@ sub print_form ($self, $sets, $setProblemIds, $c) {
return $c->tag('div', $c->b($message));
}
-sub use_item ($self, $userName, $c) {
+sub use_item ($self, $set, $records, $c) {
# This doesn't do anything.
}
diff --git a/lib/WeBWorK/Authen.pm b/lib/WeBWorK/Authen.pm
index df77a42cfb..626eff938c 100644
--- a/lib/WeBWorK/Authen.pm
+++ b/lib/WeBWorK/Authen.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen;
=head1 NAME
@@ -52,10 +37,10 @@ use warnings;
use Date::Format;
use Scalar::Util qw(weaken);
-use Mojo::Util qw(b64_encode b64_decode);
+use Mojo::Util qw(b64_encode b64_decode);
use WeBWorK::Debug;
-use WeBWorK::Utils qw(x runtime_use);
+use WeBWorK::Utils qw(x runtime_use utf8Crypt);
use WeBWorK::Utils::Logs qw(writeCourseLog);
use WeBWorK::Utils::TOTP;
use WeBWorK::Localize;
@@ -94,29 +79,21 @@ sub class {
my ($ce, $type) = @_;
if (exists $ce->{authen}{$type}) {
- if (ref $ce->{authen}{$type} eq "ARRAY") {
+ if (ref $ce->{authen}{$type} eq 'ARRAY') {
my $authen_type = shift @{ $ce->{authen}{$type} };
- if (ref($authen_type) eq "HASH") {
- if (exists $authen_type->{ $ce->{dbLayoutName} }) {
- return $authen_type->{ $ce->{dbLayoutName} };
- } elsif (exists $authen_type->{"*"}) {
- return $authen_type->{"*"};
- } else {
- die "authentication type '$type' in the course environment has no entry for db layout '",
- $ce->{dbLayoutName}, "' and no default entry (*)";
- }
+ if (ref($authen_type) eq 'HASH') {
+ # Basic backwards compatibility.
+ return $authen_type->{'*'} if exists $authen_type->{'*'};
+ return $authen_type->{sql_single} if exists $authen_type->{sql_single};
+ die 'Unsupported authentication module format in the course environment.';
} else {
return $authen_type;
}
- } elsif (ref $ce->{authen}{$type} eq "HASH") {
- if (exists $ce->{authen}{$type}{ $ce->{dbLayoutName} }) {
- return $ce->{authen}{$type}{ $ce->{dbLayoutName} };
- } elsif (exists $ce->{authen}{$type}{"*"}) {
- return $ce->{authen}{$type}{"*"};
- } else {
- die "authentication type '$type' in the course environment has no entry for db layout '",
- $ce->{dbLayoutName}, "' and no default entry (*)";
- }
+ } elsif (ref $ce->{authen}{$type} eq 'HASH') {
+ # Basic backwards compatibility.
+ return $ce->{authen}{$type}{'*'} if exists $ce->{authen}{$type}{'*'};
+ return $ce->{authen}{$type}{sql_single} if exists $ce->{authen}{$type}{sql_single};
+ die 'Unsupported authentication module format in the course environment.';
} else {
return $ce->{authen}{$type};
}
@@ -130,9 +107,7 @@ sub call_next_authen_method {
my $c = $self->{c};
my $ce = $c->{ce};
- my $user_authen_module =
- WeBWorK::Authen::class($ce, $ce->{courseName} eq $ce->{admin_course_id} ? 'admin_module' : 'user_module');
-
+ my $user_authen_module = class($ce, $ce->{courseName} eq $ce->{admin_course_id} ? 'admin_module' : 'user_module');
if (!defined $user_authen_module || $user_authen_module eq '') {
$self->{error} = $c->maketext(
"No authentication method found for your request. If this recurs, please speak with your instructor.");
@@ -633,7 +608,7 @@ sub checkPassword {
my $Password = $db->getPassword($userID);
if (defined $Password) {
# Check against the password in the database.
- my $possibleCryptPassword = crypt $possibleClearPassword, $Password->password;
+ my $possibleCryptPassword = utf8Crypt($possibleClearPassword, $Password->password);
my $dbPassword = $Password->password;
# This next line explicitly insures that blank or null passwords from the database can never succeed in matching
# an entered password. This also rejects cases when the database has a crypted password which matches a
diff --git a/lib/WeBWorK/Authen/Basic_TheLastOption.pm b/lib/WeBWorK/Authen/Basic_TheLastOption.pm
index 4acaf4f4d7..4d431b4e10 100644
--- a/lib/WeBWorK/Authen/Basic_TheLastOption.pm
+++ b/lib/WeBWorK/Authen/Basic_TheLastOption.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::Basic_TheLastOption;
use base qw/WeBWorK::Authen/;
diff --git a/lib/WeBWorK/Authen/CAS.pm b/lib/WeBWorK/Authen/CAS.pm
index 338fd5f2ee..1c3280dd82 100644
--- a/lib/WeBWorK/Authen/CAS.pm
+++ b/lib/WeBWorK/Authen/CAS.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::CAS;
use base qw/WeBWorK::Authen/;
diff --git a/lib/WeBWorK/Authen/Cosign.pm b/lib/WeBWorK/Authen/Cosign.pm
index 161792f4bb..3bdae8976c 100644
--- a/lib/WeBWorK/Authen/Cosign.pm
+++ b/lib/WeBWorK/Authen/Cosign.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::Cosign;
use base qw/WeBWorK::Authen/;
diff --git a/lib/WeBWorK/Authen/LDAP.pm b/lib/WeBWorK/Authen/LDAP.pm
index af2e5b83ef..b408d24a83 100644
--- a/lib/WeBWorK/Authen/LDAP.pm
+++ b/lib/WeBWorK/Authen/LDAP.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::LDAP;
use base qw/WeBWorK::Authen/;
@@ -20,7 +5,7 @@ use strict;
use warnings;
use WeBWorK::Debug qw(debug);
-use Net::LDAP qw(LDAP_INVALID_CREDENTIALS);
+use Net::LDAP qw(LDAP_INVALID_CREDENTIALS);
sub checkPassword {
my ($self, $userID, $possibleClearPassword) = @_;
diff --git a/lib/WeBWorK/Authen/LTI/GradePassback.pm b/lib/WeBWorK/Authen/LTI/GradePassback.pm
new file mode 100644
index 0000000000..f1f91f4d48
--- /dev/null
+++ b/lib/WeBWorK/Authen/LTI/GradePassback.pm
@@ -0,0 +1,170 @@
+package WeBWorK::Authen::LTI::GradePassback;
+use Mojo::Base 'Exporter', -signatures, -async_await;
+
+=head1 NAME
+
+WeBWorK::Authen::LTI::GradePassback - Grade passback utilities for LTI authentication
+
+=cut
+
+use WeBWorK::Utils::DateTime qw(after before);
+use WeBWorK::Utils::Sets qw(grade_set grade_gateway);
+
+our @EXPORT_OK = qw(massUpdate passbackGradeOnSubmit getSetPassbackScore);
+
+# These must be required and not used, and must be after the exports are defined above.
+# Otherwise this will create a circular dependency with the SubmitGrade modules.
+require WeBWorK::Authen::LTIAdvanced::SubmitGrade;
+require WeBWorK::Authen::LTIAdvantage::SubmitGrade;
+
+# Perform a mass update of all grades. This is all user grades for course grade mode and all user set grades for
+# homework grade mode if $manual_update is false. Otherwise what is updated is determined by a combination of the grade
+# mode and the useriD and setID parameters. Note that the only required parameter is $c which should be a
+# WeBWorK::Controller object with a valid course environment and database.
+sub massUpdate ($c, $manual_update = 0, $userID = undef, $setID = undef) {
+ my $ce = $c->ce;
+ my $db = $c->db;
+
+ # Sanity check.
+ unless (ref($ce)) {
+ warn('course environment is not defined');
+ return;
+ }
+ unless (ref($db)) {
+ warn('database reference is not defined');
+ return;
+ }
+
+ # Only run an automatic update if the time interval has passed.
+ if (!$manual_update) {
+ my $lastUpdate = $db->getSettingValue('LTILastUpdate') || 0;
+ my $updateInterval = $ce->{LTIMassUpdateInterval} // -1;
+ return unless ($updateInterval != -1 && time - $lastUpdate > $updateInterval);
+ $db->setSettingValue('LTILastUpdate', time);
+ }
+
+ # Send warning if debug_lti_grade_passback is set.
+ if ($ce->{debug_lti_grade_passback}) {
+ if ($setID && $userID && $ce->{LTIGradeMode} eq 'homework') {
+ warn "LTI Mass Update: Queueing grade update for user $userID and set $setID.\n";
+ } elsif ($setID && $ce->{LTIGradeMode} eq 'homework') {
+ warn "LTI Mass Update: Queueing grade update for all users assigned to set $setID.\n";
+ } elsif ($userID) {
+ warn "LTI Mass Update: Queueing grade update of all sets assigned to user $userID.\n";
+ } else {
+ warn "LTI Mass Update: Queueing grade update for all sets and users.\n";
+ }
+ }
+
+ $c->minion->enqueue(lti_mass_update => [ $userID, $setID ], { notes => { courseID => $ce->{courseName} } });
+
+ return;
+}
+
+async sub passbackGradeOnSubmit ($c, $userID, $set) {
+ my $ce = $c->ce;
+
+ my $LMSname = $ce->{LTI}{ $ce->{LTIVersion} }{LMS_name};
+
+ if ($ce->{LTIGradeOnSubmit}) {
+ my $LTIGradeResult = 0;
+
+ my $grader =
+ $ce->{LTIVersion} eq 'v1p1'
+ ? WeBWorK::Authen::LTIAdvanced::SubmitGrade->new($c)
+ : WeBWorK::Authen::LTIAdvantage::SubmitGrade->new($c);
+
+ if ($ce->{LTIGradeMode} eq 'course') {
+ $LTIGradeResult = await $grader->submit_course_grade($userID, $set);
+ } elsif ($ce->{LTIGradeMode} eq 'homework') {
+ $LTIGradeResult = await $grader->submit_set_grade($userID, $set->set_id, $set);
+ }
+ if ($LTIGradeResult == 0) {
+ return $c->maketext('Your score was not successfully sent to [_1].', $LMSname);
+ } elsif ($LTIGradeResult > 0) {
+ return $c->maketext('Your score was successfully sent to [_1].', $LMSname);
+ } elsif ($LTIGradeResult < 0) {
+ return $c->maketext('Your score will be sent to [_1] at a later time.', $LMSname);
+ }
+ } elsif ($ce->{LTIMassUpdateInterval} > 0) {
+ if ($ce->{LTIMassUpdateInterval} < 120) {
+ return $c->maketext('Scores are sent to [_1] every [quant,_2,second].',
+ $LMSname, $ce->{LTIMassUpdateInterval});
+ } elsif ($ce->{LTIMassUpdateInterval} < 7200) {
+ return $c->maketext('Scores are sent to [_1] every [quant,_2,minute].',
+ $LMSname, int($ce->{LTIMassUpdateInterval} / 60 + 0.99));
+ } else {
+ return $c->maketext('Scores are sent to [_1] every [quant,_2,hour].',
+ $LMSname, int($ce->{LTIMassUpdateInterval} / 3600 + 0.9999));
+ }
+ }
+}
+
+sub setAttempted ($problems, $setVersions = undef) {
+ return 0 unless ref($problems) eq 'ARRAY';
+
+ # If this is a test with set versions, then it counts as "attempted" if there is more than one set version.
+ return 1 if ref($setVersions) eq 'ARRAY' && @$setVersions > 1;
+
+ for (@$problems) {
+ return 1 if $_->attempted || $_->status > 0;
+ }
+ return 0;
+}
+
+sub earliestGatewayDate ($ce, $userSet, $setVersions) {
+ # If there are no versions, use the template's date.
+ return getLTISendScoresAfterDate($userSet, $ce) unless ref($setVersions) eq 'ARRAY';
+
+ # Otherwise, use the earliest date among versions.
+ my $earliest_date = -1;
+ for (@$setVersions) {
+ my $versionedSetDate = getLTISendScoresAfterDate($_, $ce);
+ $earliest_date = $versionedSetDate if $earliest_date == -1 || $versionedSetDate < $earliest_date;
+ }
+ return $earliest_date;
+}
+
+sub getLTISendScoresAfterDate ($set, $ce) {
+ if ($ce->{LTISendScoresAfterDate} eq 'open_date') {
+ return $set->open_date;
+ } elsif ($ce->{LTISendScoresAfterDate} eq 'reduced_scoring_date') {
+ return ($ce->{pg}{ansEvalDefaults}{enableReducedScoring}
+ && $set->enable_reduced_scoring
+ && $set->reduced_scoring_date) ? $set->reduced_scoring_date : $set->due_date;
+ } elsif ($ce->{LTISendScoresAfterDate} eq 'due_date') {
+ return $set->due_date;
+ } elsif ($ce->{LTISendScoresAfterDate} eq 'answer_date') {
+ return $set->answer_date;
+ }
+}
+
+# Returns a reference to hash with the keys totalRight, total, and score if the
+# set has met the conditions for grade pass back to occur, and undef otherwise.
+sub getSetPassbackScore ($db, $ce, $userID, $userSet, $gradingSubmission = 0) {
+ my ($totalRight, $total, $problemRecords, $setVersions) =
+ $userSet->assignment_type =~ /gateway/
+ ? grade_gateway($db, $userSet->set_id, $userID)
+ : grade_set($db, $userSet, $userID);
+
+ my $return = { totalRight => $totalRight, total => $total, score => $total ? $totalRight / $total : 0 };
+
+ return $return if $gradingSubmission && $ce->{LTISendGradesEarlyThreshold} eq 'attempted';
+
+ my $criticalDate =
+ $ce->{LTISendScoresAfterDate} ne 'never'
+ ? ($userSet->assignment_type =~ /gateway/
+ ? earliestGatewayDate($ce, $userSet, $setVersions)
+ : getLTISendScoresAfterDate($userSet, $ce))
+ : undef;
+
+ return $return
+ if ($criticalDate && after($criticalDate))
+ || ($ce->{LTISendGradesEarlyThreshold} eq 'attempted' && setAttempted($problemRecords, $setVersions))
+ || ($ce->{LTISendGradesEarlyThreshold} ne 'attempted'
+ && $return->{score} >= $ce->{LTISendGradesEarlyThreshold});
+
+ return;
+}
+
+1;
diff --git a/lib/WeBWorK/Authen/LTI/MassUpdate.pm b/lib/WeBWorK/Authen/LTI/MassUpdate.pm
deleted file mode 100644
index 103498a765..0000000000
--- a/lib/WeBWorK/Authen/LTI/MassUpdate.pm
+++ /dev/null
@@ -1,71 +0,0 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
-package WeBWorK::Authen::LTI::MassUpdate;
-use Mojo::Base 'Exporter', -signatures;
-
-=head1 NAME
-
-WeBWorK::Authen::LTI::MassUpdate - Mass update grades to the LMS with LTI authentication
-
-=cut
-
-our @EXPORT_OK = qw(mass_update);
-
-# Perform a mass update of all grades. This is all user grades for course grade mode and all user set grades for
-# homework grade mode if $manual_update is false. Otherwise what is updated is determined by a combination of the grade
-# mode and the useriD and setID parameters. Note that the only required parameter is $c which should be a
-# WeBWorK::Controller object with a valid course environment and database.
-sub mass_update ($c, $manual_update = 0, $userID = undef, $setID = undef) {
- my $ce = $c->ce;
- my $db = $c->db;
-
- # Sanity check.
- unless (ref($ce)) {
- warn('course environment is not defined');
- return;
- }
- unless (ref($db)) {
- warn('database reference is not defined');
- return;
- }
-
- # Only run an automatic update if the time interval has passed.
- if (!$manual_update) {
- my $lastUpdate = $db->getSettingValue('LTILastUpdate') || 0;
- my $updateInterval = $ce->{LTIMassUpdateInterval} // -1;
- return unless ($updateInterval != -1 && time - $lastUpdate > $updateInterval);
- $db->setSettingValue('LTILastUpdate', time);
- }
-
- # Send warning if debug_lti_grade_passback is set.
- if ($ce->{debug_lti_grade_passback}) {
- if ($setID && $userID && $ce->{LTIGradeMode} eq 'homework') {
- warn "LTI Mass Update: Queueing grade update for user $userID and set $setID.\n";
- } elsif ($setID && $ce->{LTIGradeMode} eq 'homework') {
- warn "LTI Mass Update: Queueing grade update for all users assigned to set $setID.\n";
- } elsif ($userID) {
- warn "LTI Mass Update: Queueing grade update of all sets assigned to user $userID.\n";
- } else {
- warn "LTI Mass Update: Queueing grade update for all sets and users.\n";
- }
- }
-
- $c->minion->enqueue(lti_mass_update => [ $userID, $setID ], { notes => { courseID => $ce->{courseName} } });
-
- return;
-}
-
-1;
diff --git a/lib/WeBWorK/Authen/LTIAdvanced.pm b/lib/WeBWorK/Authen/LTIAdvanced.pm
index 8234abbc02..0bd4706b3a 100644
--- a/lib/WeBWorK/Authen/LTIAdvanced.pm
+++ b/lib/WeBWorK/Authen/LTIAdvanced.pm
@@ -1,18 +1,3 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::LTIAdvanced;
use base qw/WeBWorK::Authen/;
@@ -33,36 +18,13 @@ use URI::Escape;
use Net::OAuth;
use WeBWorK::Debug;
-use WeBWorK::Utils::DateTime qw(formatDateTime);
+use WeBWorK::Utils::DateTime qw(formatDateTime);
use WeBWorK::Utils::Instructor qw(assignSetToUser);
use WeBWorK::Localize;
use WeBWorK::Authen::LTIAdvanced::Nonce;
$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
-=head1 CONSTRUCTOR
-
-=over
-
-=item new($c)
-
-Instantiates a new WeBWorK::Authen object for the given WeBWorK::Controller ($c).
-
-=cut
-
-sub new {
- my ($invocant, $c) = @_;
- my $class = ref($invocant) || $invocant;
- my $self = { c => $c, };
- #initialize
- bless $self, $class;
- return $self;
-}
-
-=back
-
-=cut
-
## this is only overridden for debug logging
#sub verify {
# debug("BEGIN LTIAdvanced VERIFY");
@@ -238,7 +200,7 @@ sub get_credentials {
[ 'oauth_signature', 'oauth_signature' ],
[ 'oauth_nonce', 'oauth_nonce' ],
[ 'oauth_timestamp', 'oauth_timestamp' ],
- [ 'section', 'custom_section' ],
+ [ 'section', 'context_label' ],
[ 'recitation', 'custom_recitation' ],
);
diff --git a/lib/WeBWorK/Authen/LTIAdvanced/Nonce.pm b/lib/WeBWorK/Authen/LTIAdvanced/Nonce.pm
index 883f954a43..0a94c57ba3 100644
--- a/lib/WeBWorK/Authen/LTIAdvanced/Nonce.pm
+++ b/lib/WeBWorK/Authen/LTIAdvanced/Nonce.pm
@@ -1,18 +1,3 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::LTIAdvanced::Nonce;
use strict;
diff --git a/lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm b/lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm
index a3ec411f47..26aa71071d 100644
--- a/lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm
+++ b/lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm
@@ -1,18 +1,3 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::LTIAdvanced::SubmitGrade;
=head1 NAME
@@ -29,8 +14,9 @@ use UUID::Tiny ':std';
use Digest::SHA qw(sha1_base64);
use WeBWorK::Debug;
-use WeBWorK::Utils qw(wwRound);
-use WeBWorK::Utils::Sets qw(grade_set grade_gateway grade_all_sets);
+use WeBWorK::Utils qw(wwRound);
+use WeBWorK::Utils::Sets qw(grade_all_sets);
+use WeBWorK::Authen::LTI::GradePassback qw(getSetPassbackScore);
# This package contains utilities for submitting grades to the LMS
sub new ($invocant, $c, $post_processing_mode = 0) {
@@ -115,7 +101,7 @@ sub update_sourcedid ($self, $userID) {
# Computes and submits the course grade for userID to the LMS.
# The course grade is the average of all sets assigned to the user.
-async sub submit_course_grade ($self, $userID) {
+async sub submit_course_grade ($self, $userID, $submittedSet = undef) {
my $c = $self->{c};
my $ce = $c->{ce};
my $db = $c->{db};
@@ -123,16 +109,36 @@ async sub submit_course_grade ($self, $userID) {
my $user = $db->getUser($userID);
return 0 unless $user;
- $self->warning("submitting all grades for user: $userID")
+ $self->warning("Preparing to submit overall course grade to LMS for user $userID.")
if $ce->{debug_lti_grade_passback} || $self->{post_processing_mode};
- $self->warning("lis_source_did is not available for user: $userID")
- if !$user->lis_source_did && ($ce->{debug_lti_grade_passback} || $self->{post_processing_mode});
- return await $self->submit_grade($user->lis_source_did, scalar(grade_all_sets($db, $userID)));
+ unless ($user->lis_source_did) {
+ $self->warning("lis_source_did is not available for this user")
+ if $ce->{debug_lti_grade_passback} || $self->{post_processing_mode};
+ return 0;
+ }
+
+ if ($submittedSet && !getSetPassbackScore($db, $ce, $userID, $submittedSet, 1)) {
+ $self->warning("Set's critical date has not yet passed, and user has not yet met the threshold to send set's "
+ . 'score early. Not submitting grade.');
+ return -1;
+ }
+
+ my ($courseTotalRight, $courseTotal, $includedSets) = grade_all_sets($db, $ce, $userID, \&getSetPassbackScore);
+ if (@$includedSets) {
+ $self->warning(
+ "Submitting overall score for user $userID for sets: " . join(', ', map { $_->set_id } @$includedSets))
+ if $ce->{debug_lti_grade_passback} || $self->{post_processing_mode};
+ my $score = $courseTotal ? $courseTotalRight / $courseTotal : 0;
+ return await $self->submit_grade($user->lis_source_did, $score);
+ } else {
+ $self->warning("No sets for user $userID meet criteria to be included in course grade calculation.");
+ return -1;
+ }
}
# Computes and submits the set grade for $userID and $setID to the LMS. For gateways the best score is used.
-async sub submit_set_grade ($self, $userID, $setID) {
+async sub submit_set_grade ($self, $userID, $setID, $submittedSet = undef) {
my $c = $self->{c};
my $ce = $c->{ce};
my $db = $c->{db};
@@ -140,21 +146,24 @@ async sub submit_set_grade ($self, $userID, $setID) {
my $user = $db->getUser($userID);
return 0 unless $user;
- my $userSet = $db->getMergedSet($userID, $setID);
-
- $self->warning("Submitting grade for user $userID and set $setID.")
+ $self->warning("Preparing to submit grade to LMS for user $userID and set $setID.")
if $ce->{debug_lti_grade_passback} || $self->{post_processing_mode};
- $self->warning('lis_source_did is not available for this set.')
- if !$userSet->lis_source_did && ($ce->{debug_lti_grade_passback} || $self->{post_processing_mode});
-
- return await $self->submit_grade(
- $userSet->lis_source_did,
- scalar(
- $userSet->assignment_type =~ /gateway/
- ? grade_gateway($db, $userSet, $userSet->set_id, $userID)
- : grade_set($db, $userSet, $userID, 0)
- )
- );
+
+ my $userSet = $submittedSet // $db->getMergedSet($userID, $setID);
+ unless ($userSet->lis_source_did) {
+ $self->warning('lis_source_did is not available for this set.')
+ if $ce->{debug_lti_grade_passback} || $self->{post_processing_mode};
+ return 0;
+ }
+
+ my $score = getSetPassbackScore($db, $ce, $userID, $userSet, !$self->{post_processing_mode});
+ unless ($score) {
+ $self->warning("Set's critical date has not yet passed, and user has not yet met the threshold to send set's "
+ . 'score early. Not submitting grade.');
+ return -1;
+ }
+
+ return await $self->submit_grade($userSet->lis_source_did, $score->{score});
}
# Submits a score of $score to the lms with $sourcedid as the identifier.
@@ -165,9 +174,6 @@ async sub submit_grade ($self, $sourcedid, $score) {
$score = wwRound(2, $score);
- # Fail gracefully. Some users, like instructors, may not actually have a sourcedid.
- return 0 if !$sourcedid;
-
my $request_url = $db->getSettingValue('lis_outcome_service_url');
if (!$request_url) {
$self->warning('Cannot send/retrieve grades to/from the LMS, no lis_outcome_service_url');
@@ -278,50 +284,47 @@ EOS
$content =~ /\s*(\w+)\s*<\/imsx_codeMajor>/;
my $message = $1;
if ($message ne 'success') {
- $self->warning(
- 'Unable to retrieve prior grade from LMS. Note that if your server time is not correct, '
- . 'this may fail for reasons which are less than obvious from the error messages. Error: '
- . $message);
- debug('Unable to retrieve prior grade from LMS. Note that if your server time is not correct, '
- . 'this may fail for reasons which are less than obvious from the error messages. Error: '
- . $message);
+ $self->warning('Unable to retrieve prior grade from LMS. Error: ' . $message);
+ debug('Unable to retrieve prior grade from LMS. Error: ' . $message);
return 0;
} else {
- my $oldScore;
+ my $priorScore;
# Possibly no score yet.
if ($content =~ //) {
- $oldScore = '';
+ $priorScore = '';
} else {
$content =~ /\s*(\S+)\s*<\/textString>/;
- $oldScore = $1;
+ $priorScore = $1;
}
- # Do not update the score if no change.
- if ($oldScore eq 'success') {
- # Blackboard seems to return this when there is no prior grade.
- # See: https://webwork.maa.org/moodle/mod/forum/discuss.php?d=5002
- debug("LMS grade will be updated. sourcedid: $sourcedid; Old score: $oldScore; New score: $score")
- if $ce->{debug_lti_grade_passback};
- } elsif ($oldScore ne '' && abs($score - $oldScore) < 0.001) {
+
+ # Blackboard seems to return this when there is no prior grade.
+ # See: https://webwork.maa.org/moodle/mod/forum/discuss.php?d=5002
+ $priorScore = '' if $priorScore eq 'success';
+
+ # Do not update the score if there is no significant change. Note that the cases where the webwork score
+ # is exactly 1 and the LMS score is not exactly 1, and the case where the webwork score is 0 and the LMS
+ # score is not set are considered significant changes.
+ if (abs($score - ($priorScore || 0)) < 0.001
+ && ($score != 1 || $priorScore == 1)
+ && ($score != 0 || $priorScore ne ''))
+ {
# LMS has essentially the same score, no reason to update it
- debug("LMS grade will NOT be updated - grade unchanges. Old score: $oldScore; New score: $score")
+ debug('LMS grade will NOT be updated - grade has not significantly changed. '
+ . "Old score: $priorScore; New score: $score")
if $ce->{debug_lti_grade_passback};
- $self->warning('LMS grade will NOT be updated - grade unchanged. '
- . "Old score: $oldScore; New score: $score")
+ $self->warning('LMS grade will NOT be updated - grade has not significantly changed. '
+ . "Old score: $priorScore; New score: $score")
if $ce->{debug_lti_grade_passback} || $self->{post_processing_mode};
return 1;
} else {
- debug("LMS grade will be updated. sourcedid: $sourcedid; Old score: $oldScore; New score: $score")
+ debug("LMS grade will be updated. sourcedid: $sourcedid; Old score: $priorScore; New score: $score")
if $ce->{debug_lti_grade_passback};
}
}
} else {
- $self->warning('Unable to retrieve prior grade from LMS. Note that if your server time is not correct, '
- . 'this may fail for reasons which are less than obvious from the error messages. Error: '
- . $response->message)
+ $self->warning('Unable to retrieve prior grade from LMS. Error: ' . $response->message)
if $ce->{debug_lti_grade_passback} || $self->{post_processing_mode};
- debug('Unable to retrieve prior grade from LMS. Note that if your server time is not correct, '
- . 'this may fail for reasons which are less than obvious from the error messages. Error: '
- . $response->message);
+ debug('Unable to retrieve prior grade from LMS. Error: ' . $response->message);
debug($response->body);
return 0;
}
diff --git a/lib/WeBWorK/Authen/LTIAdvantage.pm b/lib/WeBWorK/Authen/LTIAdvantage.pm
index 863dcd5481..22f6c0b651 100644
--- a/lib/WeBWorK/Authen/LTIAdvantage.pm
+++ b/lib/WeBWorK/Authen/LTIAdvantage.pm
@@ -1,18 +1,3 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::LTIAdvantage;
use parent qw(WeBWorK::Authen);
@@ -29,26 +14,10 @@ use experimental 'signatures';
use WeBWorK::Debug;
use WeBWorK::Localize;
-use WeBWorK::Utils::DateTime qw(formatDateTime);
+use WeBWorK::Utils::DateTime qw(formatDateTime);
use WeBWorK::Utils::Instructor qw(assignSetToUser);
use WeBWorK::Authen::LTIAdvantage::SubmitGrade;
-=head1 CONSTRUCTOR
-
-=over
-
-=item new($c)
-
-Instantiates a new WeBWorK::Authen object for the given WeBWorK::Controller ($c).
-
-=back
-
-=cut
-
-sub new ($invocant, $c) {
- return bless { c => $c }, ref($invocant) || $invocant;
-}
-
sub request_has_data_for_this_verification_module ($self) {
debug('LTIAdvantage has been called for data verification');
my $c = $self->{c};
@@ -159,17 +128,26 @@ sub get_credentials ($self) {
return $value;
};
- if (my $user_id = $extract_claim->($ce->{LTI}{v1p3}{preferred_source_of_username})) {
- $user_id_source = $ce->{LTI}{v1p3}{preferred_source_of_username};
- $type_of_source = 'preferred_source_of_username';
- $self->{user_id} = $user_id;
- }
+ # First check if there is a user with the current LMS user id saved in the lis_source_did column.
+ if ($claims->{sub} && (my $user = ($c->db->getUsersWhere({ lis_source_did => $claims->{sub} }))[0])) {
+ $user_id_source = 'database';
+ $type_of_source = 'existing database user';
+ $self->{user_id} = $user->user_id;
+ } else {
+ if (my $user_id = $extract_claim->($ce->{LTI}{v1p3}{preferred_source_of_username})) {
+ $user_id_source = $ce->{LTI}{v1p3}{preferred_source_of_username};
+ $type_of_source = "$user_id_source which was preferred_source_of_username";
+ $self->{user_id} = $user_id;
+ }
- # Fallback if necessary
- if (!defined $self->{user_id} && (my $user_id = $extract_claim->($ce->{LTI}{v1p3}{fallback_source_of_username}))) {
- $user_id_source = $ce->{LTI}{v1p3}{fallback_source_of_username};
- $type_of_source = 'fallback_source_of_username';
- $self->{user_id} = $user_id;
+ # Fallback if necessary
+ if (!defined $self->{user_id}
+ && (my $user_id = $extract_claim->($ce->{LTI}{v1p3}{fallback_source_of_username})))
+ {
+ $user_id_source = $ce->{LTI}{v1p3}{fallback_source_of_username};
+ $type_of_source = "$user_id_source which was fallback_source_of_username";
+ $self->{user_id} = $user_id;
+ }
}
if ($self->{user_id}) {
@@ -184,7 +162,7 @@ sub get_credentials ($self) {
[ roles => 'https://purl.imsglobal.org/spec/lti/claim/roles' ],
[ last_name => 'family_name' ],
[ first_name => 'given_name' ],
- [ section => 'https://purl.imsglobal.org/spec/lti/claim/custom#section' ],
+ [ section => 'https://purl.imsglobal.org/spec/lti/claim/lis#course_section_sourcedid' ],
[ recitation => 'https://purl.imsglobal.org/spec/lti/claim/custom#recitation' ],
);
@@ -196,7 +174,7 @@ sub get_credentials ($self) {
# For setting up it is helpful to print out what is believed to be the user id and address is at this point.
if ($ce->{debug_lti_parameters}) {
warn "=========== SUMMARY ============\n";
- warn "User id is |$self->{user_id}| (obtained from $user_id_source which was $type_of_source)\n";
+ warn "User id is |$self->{user_id}| (obtained from $type_of_source)\n";
warn "User email address is |$self->{email}|\n";
warn "strip_domain_from_email is |", $ce->{LTI}{v1p3}{strip_domain_from_email} // 0, "|\n";
warn "Student id is |$self->{student_id}|\n";
@@ -213,7 +191,7 @@ sub get_credentials ($self) {
}
# Save these for later if they are available in the JWT. It is important that the lti_lms_user_id be updated
- # with the 'sub' value from the claim. The value from the state can not entirely be trusted. In addition, this
+ # with the 'sub' value from the claim. The value from the state cannot entirely be trusted. In addition, this
# may not be the same as the original login_hint (it is different for Canvas, but the same for Moodle).
$c->stash->{lti_lms_user_id} = $claims->{sub};
$c->stash->{lti_lms_lineitem} =
@@ -323,8 +301,7 @@ sub authenticate ($self) {
"Account creation blocked by block_lti_create_user setting. Did not create user $self->{user_id}.";
if ($ce->{debug_lti_parameters}) {
warn $c->maketext('Account creation is currently disabled in this course. '
- . 'Please speak to your instructor or system administrator.')
- . "\n";
+ . 'Please speak to your instructor or system administrator.') . "\n";
}
return 0;
} else {
@@ -369,10 +346,15 @@ sub create_user ($self) {
# Determine the roles defined for this user defined in the LTI request and assign a permission level on that basis.
my @LTIroles = @{ $self->{roles} };
- # Restrict to institution and context roles and remove the purl link portion (ignore system roles).
+ # Restrict to context roles and remove the purl link portion. System roles are always ignored, but institution
+ # roles are also included if $LTI{v1p3}{AllowInstitutionRoles} = 1.
@LTIroles =
map {s|^[^#]*#||r}
- grep {m!^http://purl.imsglobal.org/vocab/lis/v2/(membership|institution\/person)#!} @LTIroles;
+ grep {
+ m!^http://purl.imsglobal.org/vocab/lis/v2/membership#!
+ || ($ce->{LTI}{v1p3}{AllowInstitutionRoles}
+ && m!^http://purl.imsglobal.org/vocab/lis/v2/institution/person#!)
+ } @LTIroles;
if ($ce->{debug_lti_parameters}) {
warn "The adjusted LTI roles defined for this user are: \n-- " . join("\n-- ", @LTIroles),
@@ -415,6 +397,7 @@ sub create_user ($self) {
$newUser->recitation($self->{recitation} // '');
$newUser->comment(formatDateTime(time, 0, $ce->{siteDefaults}{timezone}, $ce->{language}));
$newUser->student_id($self->{student_id} // '');
+ $newUser->lis_source_did($c->stash->{lti_lms_user_id}) if $c->stash->{lti_lms_user_id};
# Allow sites to customize the user.
$ce->{LTI}{v1p3}{modify_user}($self, $newUser) if ref($ce->{LTI}{v1p3}{modify_user}) eq 'CODE';
@@ -504,6 +487,7 @@ sub maybe_update_user ($self) {
$tempUser->section($self->{section} // '');
$tempUser->recitation($self->{recitation} // '');
$tempUser->student_id($self->{student_id} // '');
+ $tempUser->lis_source_did($c->stash->{lti_lms_user_id}) if $c->stash->{lti_lms_user_id};
# Allow sites to customize the temp user
$ce->{LTI}{v1p3}{modify_user}($self, $tempUser) if ref($ce->{LTI}{v1p3}{modify_user}) eq 'CODE';
diff --git a/lib/WeBWorK/Authen/LTIAdvantage/SubmitGrade.pm b/lib/WeBWorK/Authen/LTIAdvantage/SubmitGrade.pm
index c7c9418767..6fc66b97f4 100644
--- a/lib/WeBWorK/Authen/LTIAdvantage/SubmitGrade.pm
+++ b/lib/WeBWorK/Authen/LTIAdvantage/SubmitGrade.pm
@@ -1,18 +1,3 @@
-###############################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::LTIAdvantage::SubmitGrade;
=head1 NAME
@@ -34,12 +19,13 @@ use Mojo::IOLoop;
use Crypt::JWT qw(encode_jwt);
use Crypt::PK::RSA;
use Math::Random::Secure qw(irand);
-use Digest::SHA qw(sha256_hex);
+use Digest::SHA qw(sha256_hex);
use Time::HiRes;
use WeBWorK::Debug;
-use WeBWorK::Utils qw(wwRound);
-use WeBWorK::Utils::Sets qw(grade_set grade_gateway grade_all_sets);
+use WeBWorK::Utils qw(wwRound);
+use WeBWorK::Utils::Sets qw(grade_all_sets);
+use WeBWorK::Authen::LTI::GradePassback qw(getSetPassbackScore);
# This package contains utilities for submitting grades to the LMS via LTI 1.3.
sub new ($invocant, $c, $post_processing_mode = 0) {
@@ -193,7 +179,7 @@ async sub get_access_token ($self) {
# Computes and submits the course grade for userID to the LMS.
# The course grade is the sum of all (weighted) problems assigned to the user.
-async sub submit_course_grade ($self, $userID) {
+async sub submit_course_grade ($self, $userID, $submittedSet = undef) {
my $c = $self->{c};
my $ce = $c->{ce};
my $db = $c->{db};
@@ -201,17 +187,38 @@ async sub submit_course_grade ($self, $userID) {
my $user = $db->getUser($userID);
return 0 unless $user;
+ $self->warning("Preparing to submit overall course grade to LMS for user $userID.");
+
my $lineitem = $db->getSettingValue('LTIAdvantageCourseLineitem');
+ unless ($lineitem) {
+ $self->warning('LMS lineitem is not available for the course.');
+ return 0;
+ }
+
+ unless ($user->lis_source_did) {
+ $self->warning('LMS user id is not available for this user.');
+ return 0;
+ }
- $self->warning("Submitting all grades for user $userID");
- $self->warning('LMS user id is not available for this user.') unless $user->lis_source_did;
- $self->warning('LMS lineitem is not available for the course.') unless $lineitem;
+ if ($submittedSet && !getSetPassbackScore($db, $ce, $userID, $submittedSet, 1)) {
+ $self->warning("Set's critical date has not yet passed, and user has not yet met the threshold to send set's "
+ . 'score early. Not submitting grade.');
+ return -1;
+ }
- return await $self->submit_grade($user->lis_source_did, $lineitem, grade_all_sets($db, $userID));
+ my ($courseTotalRight, $courseTotal, $includedSets) = grade_all_sets($db, $ce, $userID, \&getSetPassbackScore);
+ if (@$includedSets) {
+ $self->warning("Submitting overall score for user $userID for sets: "
+ . join(', ', map { $_->set_id } (@$includedSets)));
+ return await $self->submit_grade($user->lis_source_did, $lineitem, $courseTotalRight, $courseTotal);
+ } else {
+ $self->warning("No sets for user $userID meet criteria to be included in course grade calculation.");
+ return -1;
+ }
}
# Computes and submits the set grade for $userID and $setID to the LMS. For gateways the best score is used.
-async sub submit_set_grade ($self, $userID, $setID) {
+async sub submit_set_grade ($self, $userID, $setID, $submittedSet = undef) {
my $c = $self->{c};
my $ce = $c->{ce};
my $db = $c->{db};
@@ -219,16 +226,28 @@ async sub submit_set_grade ($self, $userID, $setID) {
my $user = $db->getUser($userID);
return 0 unless $user;
- my $userSet = $db->getMergedSet($userID, $setID);
+ $self->warning("Preparing to submit grade to LMS for user $userID and set $setID.");
- $self->warning("Submitting grade for user $userID and set $setID.");
- $self->warning('LMS user id is not available for this user.') unless $user->lis_source_did;
- $self->warning('LMS lineitem is not available for this set.') unless $userSet->lis_source_did;
+ unless ($user->lis_source_did) {
+ $self->warning('LMS user id is not available for this user.');
+ return 0;
+ }
+
+ my $userSet = $submittedSet // $db->getMergedSet($userID, $setID);
+ unless ($userSet->lis_source_did) {
+ $self->warning('LMS lineitem is not available for this set.');
+ return 0;
+ }
- return await $self->submit_grade($user->lis_source_did, $userSet->lis_source_did,
- $userSet->assignment_type =~ /gateway/
- ? grade_gateway($db, $userSet, $userSet->set_id, $userID)
- : (grade_set($db, $userSet, $userID, 0))[ 0, 1 ]);
+ my $score = getSetPassbackScore($db, $ce, $userID, $userSet, !$self->{post_processing_mode});
+ unless ($score) {
+ $self->warning("Set's critical date has not yet passed, and user has not yet met the threshold to send set's "
+ . 'score early. Not submitting grade.');
+ return -1;
+ }
+
+ return await $self->submit_grade($user->lis_source_did, $userSet->lis_source_did, $score->{totalRight},
+ $score->{total});
}
# Submits scoreGiven and scoreMaximum to the lms with $sourcedid as the identifier.
@@ -236,7 +255,7 @@ async sub submit_grade ($self, $LMSuserID, $lineitem, $scoreGiven, $scoreMaximum
my $c = $self->{c};
my $ce = $c->{ce};
- return 0 unless $LMSuserID && $lineitem && (my $access_token = await $self->get_access_token);
+ return 0 unless (my $access_token = await $self->get_access_token);
$self->warning('Found data required for submitting grades to LMS.');
@@ -270,14 +289,23 @@ async sub submit_grade ($self, $LMSuserID, $lineitem, $scoreGiven, $scoreMaximum
return 0;
}
- my $priorData = decode_json($response->body);
- my $priorScore = @$priorData
- && $priorData->[0]{resultMaximum} ? $priorData->[0]{resultScore} / $priorData->[0]{resultMaximum} : 0;
+ my $priorData = decode_json($response->body);
+ my $priorScore =
+ (@$priorData && $priorData->[0]{resultMaximum} && defined $priorData->[0]{resultScore})
+ ? $priorData->[0]{resultScore} / $priorData->[0]{resultMaximum}
+ : 0;
+
+ my $score = $scoreMaximum ? $scoreGiven / $scoreMaximum : 0;
- my $score = $scoreGiven / $scoreMaximum;
- if (abs($score - $priorScore) < 0.001) {
- $self->warning(
- "LMS grade will NOT be updated as the grade is unchanged. Old score: $priorScore, New score: $score.");
+ # Do not update the score if there is no significant change. Note that the cases where the webwork score
+ # is exactly 1 and the LMS score is not exactly 1, and the case where the webwork score is 0 and the LMS
+ # score is not set are considered significant changes.
+ if (abs($score - $priorScore) < 0.001
+ && ($score != 1 || $priorScore == 1)
+ && ($score != 0 || (@$priorData && defined $priorData->[0]{resultScore})))
+ {
+ $self->warning('LMS grade will NOT be updated as the grade has not significantly changed. '
+ . "Old score: $priorScore, New score: $score.");
return 1;
}
@@ -323,7 +351,7 @@ async sub submit_grade ($self, $LMSuserID, $lineitem, $scoreGiven, $scoreMaximum
# already exist. If $private is true then the JSON decoded private key is returned, otherwise the JSON decoded public
# key is returned as a keyset. If an error occurs in this process then the returned key will be undefined, and the error
# that was thrown will also be returned. Note that this is not a class method and the only required parameter is $ce
-# which should be a a minimal course environment. The course environment is only needed to determine the site DATA
+# which should be a minimal course environment. The course environment is only needed to determine the site DATA
# directory.
sub get_site_key ($ce, $private = 0) {
my $key;
diff --git a/lib/WeBWorK/Authen/Moodle.pm b/lib/WeBWorK/Authen/Moodle.pm
index 3b04545d1e..58eccc42cd 100644
--- a/lib/WeBWorK/Authen/Moodle.pm
+++ b/lib/WeBWorK/Authen/Moodle.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::Moodle;
use base qw/WeBWorK::Authen/;
diff --git a/lib/WeBWorK/Authen/Proctor.pm b/lib/WeBWorK/Authen/Proctor.pm
index 90e2b318a5..feb4e3a979 100644
--- a/lib/WeBWorK/Authen/Proctor.pm
+++ b/lib/WeBWorK/Authen/Proctor.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::Proctor;
use base 'WeBWorK::Authen';
@@ -25,7 +10,7 @@ WeBWorK::Authen::Proctor - Authenticate gateway test proctors.
use strict;
use warnings;
-use WeBWorK::Utils qw(x);
+use WeBWorK::Utils qw(x);
use WeBWorK::DB::Utils qw(grok_vsetID);
use constant GENERIC_ERROR_MESSAGE => x('Invalid user ID or password.');
@@ -39,7 +24,7 @@ sub verify {
my $c = $self->{c};
# At this point the usual authentication has already occurred and the user has been verified. If the
- # use_grade_auth_proctor option is set to 'No', then proctor authorization is not not needed. So return
+ # use_grade_auth_proctor option is set to 'No', then proctor authorization is not needed. So return
# 1 here to skip proctor authorization and proceed on to the GatewayQuiz module which will grade the test.
if ($c->req->body_params->param('submitAnswers')) {
my ($setName, $versionNum) = grok_vsetID($c->stash('setID'));
@@ -96,10 +81,17 @@ sub verify_normal_user {
# is 'No', then the verify method will have returned 1, and this never happens. For an ongoing login session, only
# a key with versioned set information is accepted, and that version must match the requested set version. The set
# id will not have a version when opening a new version. For that new proctor credentials are required.
- if ($self->{login_type} eq 'proctor_login'
- && $c->stash('setID') =~ /,v\d+$/
+ if (
+ $self->{login_type} eq 'proctor_login'
&& $c->authen->session('proctor_authorization_granted')
- && $c->authen->session('proctor_authorization_granted') eq $c->stash('setID'))
+ && (
+ (
+ $c->stash('setID') =~ /,v\d+$/
+ && $c->authen->session('proctor_authorization_granted') eq $c->stash('setID')
+ )
+ || $c->authen->session('acting_proctor')
+ )
+ )
{
return 1;
} else {
diff --git a/lib/WeBWorK/Authen/Saml2.pm b/lib/WeBWorK/Authen/Saml2.pm
new file mode 100644
index 0000000000..254a07e874
--- /dev/null
+++ b/lib/WeBWorK/Authen/Saml2.pm
@@ -0,0 +1,333 @@
+package WeBWorK::Authen::Saml2;
+use Mojo::Base 'WeBWorK::Authen', -signatures;
+
+use Mojo::File qw(path tempfile);
+use Mojo::JSON qw(encode_json);
+use Mojo::UserAgent;
+use Net::SAML2::IdP;
+use Net::SAML2::SP;
+use URN::OASIS::SAML2 qw(BINDING_HTTP_POST BINDING_HTTP_REDIRECT);
+use Net::SAML2::Binding::POST;
+use Net::SAML2::Protocol::Assertion;
+
+use WeBWorK::Debug qw(debug);
+use WeBWorK::Authen::LTIAdvanced::Nonce;
+
+=head1 NAME
+
+WeBWorK::Authen::Saml2 - Authenticate using a SAML2 identity provider.
+
+=cut
+
+sub request_has_data_for_this_verification_module ($self) {
+ my $c = $self->{c};
+
+ # Skip if the bypass_query param is set.
+ if ($c->ce->{saml2}{bypass_query} && $c->param($c->ce->{saml2}{bypass_query})) {
+ debug('Saml2 authen module bypass detected. Going to next authentication module.');
+ return 0;
+ }
+
+ return 1;
+}
+
+sub verify ($self) {
+ my $result = $self->SUPER::verify;
+ my $c = $self->{c};
+
+ if ($c->current_route eq 'saml2_acs') {
+ # Transfer the saml2_nameid and saml2_session to the webwork session.
+ # These are used to logout of the identity provider if that is configured.
+ $self->session->{saml2_nameid} = $c->stash->{saml2_nameid} if $c->stash->{saml2_nameid};
+ $self->session->{saml2_session} = $c->stash->{saml2_session} if $c->stash->{saml2_session};
+
+ # If two factor verification is needed, defer that until after redirecting to the course route.
+ if ($c->stash->{saml2_redirect} && $self->session->{two_factor_verification_needed}) {
+ $self->session->{two_factor_verification_needed_after_redirect} =
+ delete $self->session->{two_factor_verification_needed};
+ return 1;
+ }
+ }
+
+ return $result;
+}
+
+sub do_verify ($self) {
+ my $c = $self->{c};
+ my $ce = $c->ce;
+
+ $self->{external_auth} = 1 if $ce->two_factor_authentication_enabled && $ce->{saml2}{twoFAOnlyWithBypass};
+
+ if ($c->current_route eq 'saml2_acs') {
+ debug('Verifying Saml2 assertion');
+
+ my $idpCertificateFile = $self->idp(1);
+ unless ($idpCertificateFile) {
+ $c->stash->{authen_error} = $c->maketext(
+ 'An internal server error occured. Please contact the system administrator for assistance.');
+ return 0;
+ }
+
+ # Verify that the response is signed by the identity provider and decode it.
+ my $decodedXml = Net::SAML2::Binding::POST->new(cacert => $idpCertificateFile->to_string)
+ ->handle_response($c->stash->{saml2}{samlResp});
+ my $assertion = Net::SAML2::Protocol::Assertion->new_from_xml(
+ xml => $decodedXml,
+ key_file => $self->spKeyFile->to_string
+ );
+
+ # Get the database key containing the authReqId that was generated before redirecting to the identity provider.
+ my $authReqIdKey = $c->db->getKey($assertion->in_response_to);
+ unless ($authReqIdKey) {
+ $c->stash->{authen_error} = $c->maketext('Invalid user ID or password.');
+ debug('Invalid request id in response. Possible CSFR.');
+ return 0;
+ }
+ eval { $c->db->deleteKey($authReqIdKey->user_id) }; # Delete the key to avoid replay.
+
+ # Verify that the response has the same authReqId which means it's responding to the authentication request
+ # generated by webwork2. This also checks that timestamps are valid.
+ my $valid = $assertion->valid($ce->{saml2}{sp}{entity_id}, $authReqIdKey->user_id);
+ unless ($valid) {
+ $c->stash->{authen_error} = $c->maketext('Invalid user ID or password.');
+ debug('Bad timestamp or issuer');
+ return 0;
+ }
+
+ debug('Got valid response and looking for username.');
+ my $userId = $self->getUserId($ce->{saml2}{sp}{attributes}, $assertion);
+ if ($userId) {
+ debug("Got username $userId");
+
+ $c->authen->{saml2UserId} = $userId;
+ if ($self->SUPER::do_verify) {
+ # The user and key need to be set before systemLink is called. They are only used if
+ # $session_management_via is 'key'.
+ $c->param('user', $userId);
+ $c->param('key', $self->{session_key});
+ $c->stash->{saml2_redirect} = $c->systemLink($c->url_for($c->stash->{saml2}{relayState}{url}));
+
+ # Save these in the stash for now. They will be transferred to the session after it has been created.
+ $c->stash->{saml2_nameid} = $assertion->nameid;
+ $c->stash->{saml2_session} = $assertion->{session};
+
+ return 1;
+ }
+ }
+ $c->stash->{authen_error} = $c->maketext('User not found in course.');
+ debug('Unauthorized - User not found in ' . $c->stash->{courseID});
+ return 0;
+ }
+
+ # If there is an existing session, then control will be passed to the authen base class.
+ if ($ce->{session_management_via} eq 'session_cookie') {
+ my ($cookieUser) = $self->fetchCookie;
+ $self->{isLoggedIn} = 1 if defined $cookieUser;
+ } elsif ($c->param('user')) {
+ my $key = $c->db->getKey($c->param('user'));
+ $self->{isLoggedIn} = 1 if $key;
+ }
+
+ if ($self->{isLoggedIn}) {
+ debug('User signed in or was previously signed in. Saml2 passing control back to the authen base class.');
+
+ # There was a successful saml response or the user was already logged in.
+ # So hand off to the authen base class to verify the user and manage the session.
+ my $result = $self->SUPER::do_verify;
+
+ $self->session->{two_factor_verification_needed} =
+ delete $self->session->{two_factor_verification_needed_after_redirect}
+ if $self->session->{two_factor_verification_needed_after_redirect};
+
+ return $result;
+ }
+
+ # This occurs if the user clicks the logout button when the identity provider session has timed out, but the
+ # webwork2 session is still active. In this case return 0 so that the logged out page is shown anyway.
+ return 0 if $c->current_route eq 'logout';
+
+ # The user doesn't have an existing session, so redirect to the identity provider for login.
+ $self->sendLoginRequest;
+
+ return 0;
+}
+
+sub sp ($self) {
+ my $c = $self->{c};
+ return $c->stash->{sp} if $c->stash->{sp};
+
+ my $ce = $c->ce;
+
+ my $spCertificateFile = path($ce->{saml2}{sp}{certificate_file});
+ $spCertificateFile = $c->app->home->child($spCertificateFile) unless $spCertificateFile->is_abs;
+
+ $c->stash->{sp} = Net::SAML2::SP->new(
+ issuer => $ce->{saml2}{sp}{entity_id},
+ url => $ce->{server_root_url} . $c->url_for('root'),
+ error_url => $ce->{server_root_url} . $c->url_for('saml2_error'),
+ cert => $spCertificateFile->to_string,
+ key => $self->spKeyFile->to_string,
+ org_contact => $ce->{saml2}{sp}{org}{contact},
+ org_name => $ce->{saml2}{sp}{org}{name},
+ org_url => $ce->{saml2}{sp}{org}{url},
+ org_display_name => $ce->{saml2}{sp}{org}{display_name},
+ assertion_consumer_service => [ {
+ Binding => BINDING_HTTP_POST,
+ Location => $ce->{server_root_url} . $c->url_for('saml2_acs'),
+ isDefault => 'true',
+ } ],
+ $ce->{saml2}{sp}{enable_sp_initiated_logout}
+ ? (
+ single_logout_service => [ {
+ Binding => BINDING_HTTP_POST,
+ Location => $ce->{server_root_url} . $c->url_for('saml2_logout')
+ } ]
+ )
+ : ()
+ );
+
+ return $c->stash->{sp};
+}
+
+# The first time this method is executed for a given identity provider, the metadata file is retrieved from the metadata
+# URL. It is then saved in the $ce->{saml2}{active_idp} subdirectory of $ce->{webworkDirs}{DATA}/Saml2IDPs together
+# with the identity provider's signing key which is extracted from the retrieved metadata. On later requests the
+# metadata and certificate are used from the saved files. This prevents the need to retrieve the metadata on every
+# login request.
+sub idp ($self, $ceritificateOnly = 0) {
+ if (!$self->{idp_certificate_file} || !$self->{idp}) {
+ my $ce = $self->{c}->ce;
+
+ my $saml2IDPDir = path("$ce->{webworkDirs}{DATA}/Saml2IDPs")->child($ce->{saml2}{active_idp});
+ $saml2IDPDir->make_path;
+
+ my $metadataXMLFile = $saml2IDPDir->child('metadata.xml');
+ my $certificateFile = $saml2IDPDir->child('cacert.crt');
+
+ if (-r $metadataXMLFile && -r $certificateFile) {
+ $self->{idp} =
+ Net::SAML2::IdP->new_from_xml(xml => $metadataXMLFile->slurp, cacert => $certificateFile->to_string);
+ $self->{idp_certificate_file} = $certificateFile;
+ } else {
+ my $response = Mojo::UserAgent->new->get($ce->{saml2}{idps}{ $ce->{saml2}{active_idp} })->result;
+ if ($response->is_success) {
+ my $metadataXML = $response->body;
+ $metadataXMLFile->spew($metadataXML);
+ $self->{idp} = Net::SAML2::IdP->new_from_xml(xml => $metadataXML);
+ $certificateFile->spew($self->{idp}->cert('signing')->[0]);
+ $self->{idp_certificate_file} = $certificateFile;
+ } else {
+ debug("Unable to retrieve metadata from identity provider $ce->{saml2}{active_idp} with "
+ . "metadata URL $ce->{samle}{idps}{$ce->{saml2}{active_idp}}");
+ }
+ }
+ }
+
+ return $self->{idp_certificate_file} if $ceritificateOnly;
+ return $self->{idp};
+}
+
+sub spKeyFile ($self) {
+ my $c = $self->{c};
+ return $self->{spKeyFile} if $self->{spKeyFile};
+ $self->{spKeyFile} = path($c->ce->{saml2}{sp}{private_key_file});
+ $self->{spKeyFile} = $c->app->home->child($self->{spKeyFile}) unless $self->{spKeyFile}->is_abs;
+ return $self->{spKeyFile};
+}
+
+sub sendLoginRequest ($self) {
+ my $c = $self->{c};
+ my $ce = $c->ce;
+
+ my $idp = $self->idp;
+ unless ($idp) {
+ $c->stash->{authen_error} =
+ $c->maketext('An internal server error occured. Please contact the system administrator for assistance.');
+ return 0;
+ }
+
+ my $authReq = $self->sp->authn_request($idp->sso_url(BINDING_HTTP_REDIRECT));
+
+ # Get rid of stale request ids in the database. This borrows the maybe_purge_nonces method from the
+ # WeBWorK::Authen::LTIAdvanced::Nonce package.
+ WeBWorK::Authen::LTIAdvanced::Nonce->new($c, '', 0)->maybe_purge_nonces;
+
+ # The request id needs to be stored so that it can be verified in the identity provider response.
+ # This uses the "nonce" hack to store the request id in the key table.
+ my $key = $c->db->newKey({ user_id => $authReq->id, timestamp => time, key => 'nonce' });
+ eval { $c->db->deleteKey($authReq->id) };
+ eval { $c->db->addKey($key) };
+
+ # The second argument of the sign method contains info that the identity provider relays back.
+ # This information is used to send the user to the right place after login.
+ debug('Redirecting user to the identity provider');
+ $self->{redirect} = $self->sp->sso_redirect_binding($idp, 'SAMLRequest')
+ ->sign($authReq->as_xml, encode_json({ course => $ce->{courseName}, url => $c->req->url->to_string }));
+ return;
+}
+
+sub logout_user ($self) {
+ my $ce = $self->{c}->ce;
+ if ($ce->{saml2}{sp}{enable_sp_initiated_logout}
+ && defined $self->session->{saml2_nameid}
+ && defined $self->session->{saml2_session})
+ {
+ my $idp = $self->idp;
+ return unless $idp;
+
+ my $logoutReq = $self->sp->logout_request(
+ $idp->slo_url(BINDING_HTTP_REDIRECT), $self->session->{saml2_nameid},
+ $idp->format || undef, $self->session->{saml2_session}
+ );
+
+ debug('Redirecting user to the identity provider for logout');
+ $self->{redirect} = $self->sp->slo_redirect_binding($idp, 'SAMLRequest')
+ ->sign($logoutReq->as_xml, encode_json({ course => $ce->{courseName} }));
+ }
+ return;
+}
+
+sub getUserId ($self, $attributeKeys, $assertion) {
+ my $ce = $self->{c}->ce;
+ my $db = $self->{c}->db;
+
+ if ($attributeKeys) {
+ for my $key (@$attributeKeys) {
+ debug("Trying attribute $key for username");
+ my $possibleUserId = $assertion->attributes->{$key}[0];
+ next unless $possibleUserId;
+ if ($db->getUser($possibleUserId)) {
+ debug("Using attribute value for username: $possibleUserId");
+ return $possibleUserId;
+ }
+ }
+ }
+ debug('No username match in attributes. Trying NameID fallback');
+ if ($db->getUser($assertion->nameid)) {
+ debug('Using NameID for username: ' . $assertion->nameid);
+ return $assertion->nameid;
+ }
+ debug('NameID fallback failed. No username found.');
+ return;
+}
+
+sub get_credentials ($self) {
+ if ($self->{saml2UserId}) {
+ # User has been authenticated with the identity provider.
+ $self->{user_id} = $self->{saml2UserId};
+ $self->{login_type} = 'normal';
+ $self->{credential_source} = 'SAML2';
+ $self->{initial_login} = 1;
+ debug('credential source: "SAML2", user: "', $self->{user_id}, '"');
+ return 1;
+ }
+ return $self->SUPER::get_credentials if $self->{isLoggedIn};
+ return 0;
+}
+
+sub authenticate ($self) {
+ # The identity provider handles authentication, so just return 1.
+ return 1;
+}
+
+1;
diff --git a/lib/WeBWorK/Authen/Shibboleth.pm b/lib/WeBWorK/Authen/Shibboleth.pm
index d0d1d419cb..498220f598 100644
--- a/lib/WeBWorK/Authen/Shibboleth.pm
+++ b/lib/WeBWorK/Authen/Shibboleth.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authen::Shibboleth;
use Mojo::Base 'WeBWorK::Authen', -signatures;
diff --git a/lib/WeBWorK/Authz.pm b/lib/WeBWorK/Authz.pm
index 999c00b8ba..07ef5d5abf 100644
--- a/lib/WeBWorK/Authz.pm
+++ b/lib/WeBWorK/Authz.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::Authz;
=head1 NAME
@@ -61,7 +46,7 @@ use warnings;
use Carp qw/croak/;
use WeBWorK::Utils::DateTime qw(before);
-use WeBWorK::Utils::Sets qw(is_restricted);
+use WeBWorK::Utils::Sets qw(is_restricted);
use WeBWorK::Authen::Proctor;
use Net::IP;
use Scalar::Util qw(weaken);
@@ -84,7 +69,7 @@ sub new {
my ($invocant, $c) = @_;
my $class = ref($invocant) || $invocant;
my $self = { c => $c, };
- #weaken $self->{c};
+ weaken $self->{c};
$c->{permission_retrieval_error} = 0;
bless $self, $class;
@@ -404,7 +389,7 @@ sub checkSet {
}
# Don't allow versioned sets to be viewed from the problem-list page.
if ($node_name eq 'problem_list') {
- return $c->maketext("Requested version ([_1]) of set '[_2]' can not be directly accessed.", $verNum,
+ return $c->maketext("Requested version ([_1]) of set '[_2]' cannot be directly accessed.", $verNum,
$setName);
}
} else {
@@ -500,11 +485,12 @@ sub checkSet {
$ce->{LTI}{ $ce->{LTIVersion} }{LMS_url}
? $c->link_to($ce->{LTI}{ $ce->{LTIVersion} }{LMS_name} => $ce->{LTI}{ $ce->{LTIVersion} }{LMS_url})
: $ce->{LTI}{ $ce->{LTIVersion} }{LMS_name};
- return $c->maketext(
+ return $c->b($c->maketext(
'You must use your Learning Management System ([_1]) to access this set. '
. 'Try logging in to the Learning Management System and visiting the set from there.',
$LMS
- ) unless $set->lis_source_did;
+ ))
+ unless $set->lis_source_did;
}
return 0;
diff --git a/lib/WeBWorK/ConfigObject.pm b/lib/WeBWorK/ConfigObject.pm
index 6f23fcf1aa..d9a2c0007f 100644
--- a/lib/WeBWorK/ConfigObject.pm
+++ b/lib/WeBWorK/ConfigObject.pm
@@ -64,8 +64,9 @@ sub entry_widget ($self, $default, $is_secret = 0) {
);
}
-sub help_title ($self) { return $self->{c}->maketext('Variable Documentation') }
-sub help_name ($self) { return '$' . $self->{var} }
+sub help_title ($self) { return $self->{c}->maketext('Variable Documentation') }
+sub help_name ($self) { return '$' . $self->{var} }
+sub help_link_aria_label ($self) { return $self->{c}->maketext('Variable documentation for [_1]', $self->help_name) }
# This produces the documentation string and modal containing detailed documentation.
# It is the same for all config types.
diff --git a/lib/WeBWorK/ConfigObject/boolean.pm b/lib/WeBWorK/ConfigObject/boolean.pm
index be10bb1dfb..f3636211c1 100644
--- a/lib/WeBWorK/ConfigObject/boolean.pm
+++ b/lib/WeBWorK/ConfigObject/boolean.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::boolean;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigObject/checkboxlist.pm b/lib/WeBWorK/ConfigObject/checkboxlist.pm
index 746adec2be..ec569ee17e 100644
--- a/lib/WeBWorK/ConfigObject/checkboxlist.pm
+++ b/lib/WeBWorK/ConfigObject/checkboxlist.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::checkboxlist;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigObject/list.pm b/lib/WeBWorK/ConfigObject/list.pm
index f2ec5b8112..743f9ec74f 100644
--- a/lib/WeBWorK/ConfigObject/list.pm
+++ b/lib/WeBWorK/ConfigObject/list.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::list;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigObject/lms_context_id.pm b/lib/WeBWorK/ConfigObject/lms_context_id.pm
index 42636efb20..73631a2a0d 100644
--- a/lib/WeBWorK/ConfigObject/lms_context_id.pm
+++ b/lib/WeBWorK/ConfigObject/lms_context_id.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::lms_context_id;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
@@ -106,7 +91,8 @@ sub entry_widget ($self, $default, $is_secret = 0) {
return $self->SUPER::entry_widget($default);
}
-sub help_title ($self) { return $self->{c}->maketext('Setting Documentation') }
-sub help_name ($self) { return $self->{c}->maketext('[_1] setting', $self->{var}) }
+sub help_title ($self) { return $self->{c}->maketext('Setting Documentation') }
+sub help_name ($self) { return $self->{c}->maketext('[_1] setting', $self->{var}) }
+sub help_link_aria_label ($self) { return $self->{c}->maketext('Setting documentation for [_1]', $self->{var}) }
1;
diff --git a/lib/WeBWorK/ConfigObject/number.pm b/lib/WeBWorK/ConfigObject/number.pm
index 969eb4a875..2312c98fb5 100644
--- a/lib/WeBWorK/ConfigObject/number.pm
+++ b/lib/WeBWorK/ConfigObject/number.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::number;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigObject/permission.pm b/lib/WeBWorK/ConfigObject/permission.pm
index 80d51e8c30..344cf918f0 100644
--- a/lib/WeBWorK/ConfigObject/permission.pm
+++ b/lib/WeBWorK/ConfigObject/permission.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::permission;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigObject/permission_checkboxlist.pm b/lib/WeBWorK/ConfigObject/permission_checkboxlist.pm
index 4b51cd360a..f1b8f2a6d5 100644
--- a/lib/WeBWorK/ConfigObject/permission_checkboxlist.pm
+++ b/lib/WeBWorK/ConfigObject/permission_checkboxlist.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::permission_checkboxlist;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
use WeBWorK::Utils 'role_and_above';
diff --git a/lib/WeBWorK/ConfigObject/popuplist.pm b/lib/WeBWorK/ConfigObject/popuplist.pm
index 30995adb59..f7a1de6aa6 100644
--- a/lib/WeBWorK/ConfigObject/popuplist.pm
+++ b/lib/WeBWorK/ConfigObject/popuplist.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::popuplist;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigObject/setting.pm b/lib/WeBWorK/ConfigObject/setting.pm
index d6c654e0d4..45eb79f7f8 100644
--- a/lib/WeBWorK/ConfigObject/setting.pm
+++ b/lib/WeBWorK/ConfigObject/setting.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::setting;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
@@ -35,7 +20,8 @@ sub save_string ($self, $oldval, $use_current = 0) {
return '';
}
-sub help_title ($self) { return $self->{c}->maketext('Setting Documentation') }
-sub help_name ($self) { return $self->{c}->maketext('[_1] setting', $self->{var}) }
+sub help_title ($self) { return $self->{c}->maketext('Setting Documentation') }
+sub help_name ($self) { return $self->{c}->maketext('[_1] setting', $self->{var}) }
+sub help_link_aria_label ($self) { return $self->{c}->maketext('Setting documentation for [_1]', $self->{var}) }
1;
diff --git a/lib/WeBWorK/ConfigObject/text.pm b/lib/WeBWorK/ConfigObject/text.pm
index 1780eb8b84..16ebf8ce87 100644
--- a/lib/WeBWorK/ConfigObject/text.pm
+++ b/lib/WeBWorK/ConfigObject/text.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::text;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigObject/time.pm b/lib/WeBWorK/ConfigObject/time.pm
index 74429bc222..20185d8b33 100644
--- a/lib/WeBWorK/ConfigObject/time.pm
+++ b/lib/WeBWorK/ConfigObject/time.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::time;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigObject/timezone.pm b/lib/WeBWorK/ConfigObject/timezone.pm
index 590bd0a09e..feaf1f8acd 100644
--- a/lib/WeBWorK/ConfigObject/timezone.pm
+++ b/lib/WeBWorK/ConfigObject/timezone.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigObject::timezone;
use Mojo::Base 'WeBWorK::ConfigObject', -signatures;
diff --git a/lib/WeBWorK/ConfigValues.pm b/lib/WeBWorK/ConfigValues.pm
index b5d1229c7b..411be4426c 100644
--- a/lib/WeBWorK/ConfigValues.pm
+++ b/lib/WeBWorK/ConfigValues.pm
@@ -1,18 +1,3 @@
-################################################################################
-# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2024 The WeBWorK Project, https://github.com/openwebwork
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of either: (a) the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version, or (b) the "Artistic License" which comes with this package.
-#
-# This program 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 either the GNU General Public License or the
-# Artistic License for more details.
-################################################################################
-
package WeBWorK::ConfigValues;
use Mojo::Base 'Exporter', -signatures;
@@ -290,15 +275,24 @@ sub getConfigValues ($ce) {
var => 'achievementsEnabled',
doc => x('Enable Course Achievements'),
doc2 => x(
- 'Activiating this will enable Mathchievements for webwork. Mathchievements can be managed '
- . 'by using the Achievements Manager link.'
+ "Achievements are a way to gamify WeBWorK. In parallel to a student's regular scores on "
+ . 'assignments, they earn "achievement points" for (a) answering an exercise correctly, and '
+ . '(b) earning badges. Badges can be for tasks like earning 100% on three assignments, '
+ . 'answering five questions correclty on the first attempt, etc. As students earn achivement '
+ . 'points, they can "level up" as well. An instructor can manage Achievents using the '
+ . 'Achievements Manager tool.'
),
type => 'boolean'
},
{
var => 'achievementPointsPerProblem',
doc => x('Achievement Points Per Problem'),
- doc2 => x('This is the number of achievement points given to each user for completing a problem.'),
+ doc2 => x(
+ 'This is the number of achievement points given to each user for completing a problem. The default '
+ . 'collection of achievements is designed for a course where a student who completes all the '
+ . 'exercises would earn 1000 points, not counting points from badges. It is recommended that '
+ . 'if you use the default collection and your course has N problems, set this value to 1000/N.'
+ ),
type => 'number'
},
{
@@ -314,8 +308,16 @@ sub getConfigValues ($ce) {
var => 'achievementItemsEnabled',
doc => x('Enable Achievement Rewards'),
doc2 => x(
- 'Activating this will enable achievement rewards. This feature allows students to earn rewards by '
- . 'completing achievements that allow them to affect their homework in a limited way.'
+ 'Activating this will enable achievement reward items. This feature allows students to earn reward '
+ . 'items as they level up (if level achievements are being used). The default reward items:'
+ . '- award 50% score to one problem
- reset the number of attempts allowed for one '
+ . 'problem
- extend a close date (and the reduced credit date) by 24 hours on one set'
+ . '
- double the weight of one problem within its set
- replaces one problem in a '
+ . 'set with a copy of a different problem in that set
- award 100% score to one problem'
+ . '
- extend a close date (and the reduced credit date) by 48 hours on one set
- '
+ . 'double the weight of all problems within a set
- reopen a set that has past its close '
+ . 'date for 24 hours, with problems rerandomized
- award 100% score to all problems in '
+ . 'one set.
'
),
type => 'boolean'
},
@@ -358,7 +360,7 @@ sub getConfigValues ($ce) {
. 'to set the default length of the reduced scoring period and the value of work done in '
. 'the reduced scoring period below.