From 414f9894783852c090ad267c69061c15e964c201 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Thu, 10 Feb 2022 18:19:00 -0500 Subject: [PATCH 01/31] :construction: Duplicated shortanswer as base for horizontal Parsons --- runestone/hparsons/README.md | 17 + runestone/hparsons/__init__.py | 1 + runestone/hparsons/css/hparsons.css | 6 + runestone/hparsons/css/shortanswer.css | 6 + runestone/hparsons/js/hparsons.js | 294 + runestone/hparsons/js/regex-element.js | 17013 ++++++++++++++++ runestone/hparsons/js/sa_template.html | 32 + runestone/hparsons/js/shortanswer.js | 295 + runestone/hparsons/js/timed_shortanswer.js | 43 + runestone/hparsons/shortanswer.py | 131 + runestone/hparsons/test/__init__.py | 0 .../test/_sources/Figures/LutherBellPic.jpg | Bin 0 -> 64480 bytes runestone/hparsons/test/_sources/index.rst | 36 + runestone/hparsons/test/conf.py | 229 + runestone/hparsons/test/pavement.py | 50 + runestone/hparsons/test/test_shortanswer.py | 61 + runestone/hparsons/toctree.rst | 11 + 17 files changed, 18225 insertions(+) create mode 100644 runestone/hparsons/README.md create mode 100755 runestone/hparsons/__init__.py create mode 100755 runestone/hparsons/css/hparsons.css create mode 100755 runestone/hparsons/css/shortanswer.css create mode 100644 runestone/hparsons/js/hparsons.js create mode 100644 runestone/hparsons/js/regex-element.js create mode 100644 runestone/hparsons/js/sa_template.html create mode 100644 runestone/hparsons/js/shortanswer.js create mode 100644 runestone/hparsons/js/timed_shortanswer.js create mode 100755 runestone/hparsons/shortanswer.py create mode 100644 runestone/hparsons/test/__init__.py create mode 100644 runestone/hparsons/test/_sources/Figures/LutherBellPic.jpg create mode 100644 runestone/hparsons/test/_sources/index.rst create mode 100644 runestone/hparsons/test/conf.py create mode 100644 runestone/hparsons/test/pavement.py create mode 100644 runestone/hparsons/test/test_shortanswer.py create mode 100644 runestone/hparsons/toctree.rst diff --git a/runestone/hparsons/README.md b/runestone/hparsons/README.md new file mode 100644 index 000000000..adcad2e35 --- /dev/null +++ b/runestone/hparsons/README.md @@ -0,0 +1,17 @@ +

Short Answer

+ +```html +

What is the best thing about the color blue?

+``` + +The p tag represents the entire Short Answer component to be rendered. +(more info about the use) + + +Option spec: + + diff --git a/runestone/hparsons/__init__.py b/runestone/hparsons/__init__.py new file mode 100755 index 000000000..6791e4a05 --- /dev/null +++ b/runestone/hparsons/__init__.py @@ -0,0 +1 @@ +from .shortanswer import * diff --git a/runestone/hparsons/css/hparsons.css b/runestone/hparsons/css/hparsons.css new file mode 100755 index 000000000..c6c6f7036 --- /dev/null +++ b/runestone/hparsons/css/hparsons.css @@ -0,0 +1,6 @@ +div.journal div.latexoutput { + background-color: #eeeeee; + padding: 1em; + margin-bottom: 10px; + border-radius: 5px; +} diff --git a/runestone/hparsons/css/shortanswer.css b/runestone/hparsons/css/shortanswer.css new file mode 100755 index 000000000..c6c6f7036 --- /dev/null +++ b/runestone/hparsons/css/shortanswer.css @@ -0,0 +1,6 @@ +div.journal div.latexoutput { + background-color: #eeeeee; + padding: 1em; + margin-bottom: 10px; + border-radius: 5px; +} diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js new file mode 100644 index 000000000..49fd4ba34 --- /dev/null +++ b/runestone/hparsons/js/hparsons.js @@ -0,0 +1,294 @@ +/*========================================== +========= Master hanswers.js ========= +============================================ +=== This file contains the JS for === +=== the Runestone hparsons component. === +============================================ +=== Created by === +=== Zihan Wu === +=== 2022 === +==========================================*/ + +import RunestoneBase from "../../common/js/runestonebase.js"; +import "./../css/hparsons.css"; + +export var hpList; +// Dictionary that contains all instances of horizontal Parsons problem objects +if (hpList === undefined) hpList = {}; + +export default class ShortAnswer extends RunestoneBase { + constructor(opts) { + super(opts); + if (opts) { + var orig = opts.orig; // entire

element that will be replaced by new HTML + this.useRunestoneServices = + opts.useRunestoneServices || eBookConfig.useRunestoneServices; + this.origElem = orig; + this.divid = orig.id; + this.question = this.origElem.innerHTML; + this.optional = false; + if ($(this.origElem).is("[data-optional]")) { + this.optional = true; + } + if ($(this.origElem).is("[data-mathjax]")) { + this.mathjax = true; + } + this.renderHTML(); + this.caption = "shortanswer"; + this.addCaption("runestone"); + this.checkServer("shortanswer", true); + } + } + + renderHTML() { + this.containerDiv = document.createElement("div"); + this.containerDiv.id = this.divid; + $(this.containerDiv).addClass(this.origElem.getAttribute("class")); + this.newForm = document.createElement("form"); + this.newForm.id = this.divid + "_journal"; + this.newForm.name = this.newForm.id; + this.newForm.action = ""; + this.containerDiv.appendChild(this.newForm); + this.fieldSet = document.createElement("fieldset"); + this.newForm.appendChild(this.fieldSet); + this.legend = document.createElement("legend"); + this.legend.innerHTML = "Short Answer"; + this.fieldSet.appendChild(this.legend); + this.firstLegendDiv = document.createElement("div"); + this.firstLegendDiv.innerHTML = this.question; + $(this.firstLegendDiv).addClass("journal-question"); + this.fieldSet.appendChild(this.firstLegendDiv); + this.jInputDiv = document.createElement("div"); + this.jInputDiv.id = this.divid + "_journal_input"; + this.fieldSet.appendChild(this.jInputDiv); + this.jOptionsDiv = document.createElement("div"); + $(this.jOptionsDiv).addClass("journal-options"); + this.jInputDiv.appendChild(this.jOptionsDiv); + this.jLabel = document.createElement("label"); + $(this.jLabel).addClass("radio-inline"); + this.jOptionsDiv.appendChild(this.jLabel); + this.jTextArea = document.createElement("textarea"); + let self = this; + this.jTextArea.onchange = function () { + self.isAnswered = true; + }; + this.jTextArea.id = this.divid + "_solution"; + $(this.jTextArea).attr("aria-label", "textarea"); + $(this.jTextArea).css("display:inline, width:530px"); + $(this.jTextArea).addClass("form-control"); + this.jTextArea.rows = 4; + this.jTextArea.cols = 50; + this.jLabel.appendChild(this.jTextArea); + this.jTextArea.onchange = function () { + this.feedbackDiv.innerHTML = "Your answer has not been saved yet!"; + $(this.feedbackDiv).removeClass("alert-success"); + $(this.feedbackDiv).addClass("alert alert-danger"); + }.bind(this); + this.fieldSet.appendChild(document.createElement("br")); + if (this.mathjax) { + this.renderedAnswer = document.createElement("div"); + $(this.renderedAnswer).addClass("latexoutput"); + this.fieldSet.appendChild(this.renderedAnswer); + } + this.buttonDiv = document.createElement("div"); + this.fieldSet.appendChild(this.buttonDiv); + this.submitButton = document.createElement("button"); + $(this.submitButton).addClass("btn btn-success"); + this.submitButton.type = "button"; + this.submitButton.textContent = "Save"; + this.submitButton.onclick = function () { + this.checkCurrentAnswer(); + this.logCurrentAnswer(); + this.renderFeedback(); + }.bind(this); + this.buttonDiv.appendChild(this.submitButton); + this.randomSpan = document.createElement("span"); + this.randomSpan.innerHTML = "Instructor's Feedback"; + this.fieldSet.appendChild(this.randomSpan); + this.otherOptionsDiv = document.createElement("div"); + $(this.otherOptionsDiv).css("padding-left:20px"); + $(this.otherOptionsDiv).addClass("journal-options"); + this.fieldSet.appendChild(this.otherOptionsDiv); + // add a feedback div to give user feedback + this.feedbackDiv = document.createElement("div"); + //$(this.feedbackDiv).addClass("bg-info form-control"); + //$(this.feedbackDiv).css("width:530px, background-color:#eee, font-style:italic"); + $(this.feedbackDiv).css("width:530px, font-style:italic"); + this.feedbackDiv.id = this.divid + "_feedback"; + this.feedbackDiv.innerHTML = "You have not answered this question yet."; + $(this.feedbackDiv).addClass("alert alert-danger"); + //this.otherOptionsDiv.appendChild(this.feedbackDiv); + this.fieldSet.appendChild(this.feedbackDiv); + //this.fieldSet.appendChild(document.createElement("br")); + $(this.origElem).replaceWith(this.containerDiv); + // This is a stopgap measure for when MathJax is not loaded at all. There is another + // more difficult case that when MathJax is loaded asynchronously we will get here + // before MathJax is loaded. In that case we will need to implement something + // like `the solution described here `_ + if (typeof MathJax !== "undefined") { + this.queueMathJax(this.containerDiv) + } + } + + renderMath(value) { + if (this.mathjax) { + value = value.replace(/\$\$(.*?)\$\$/g, "\\[ $1 \\]"); + value = value.replace(/\$(.*?)\$/g, "\\( $1 \\)"); + $(this.renderedAnswer).text(value); + this.queueMathJax(this.renderedAnswer) + } + } + + checkCurrentAnswer() { } + + async logCurrentAnswer(sid) { + let value = $(document.getElementById(this.divid + "_solution")).val(); + this.renderMath(value); + this.setLocalStorage({ + answer: value, + timestamp: new Date(), + }); + let data = { + event: "shortanswer", + act: value, + answer: value, + div_id: this.divid, + }; + if (typeof sid !== "undefined") { + data.sid = sid; + } + await this.logBookEvent(data); + } + + renderFeedback() { + this.feedbackDiv.innerHTML = "Your answer has been saved."; + $(this.feedbackDiv).removeClass("alert-danger"); + $(this.feedbackDiv).addClass("alert alert-success"); + } + setLocalStorage(data) { + if (!this.graderactive) { + let key = this.localStorageKey(); + localStorage.setItem(key, JSON.stringify(data)); + } + } + checkLocalStorage() { + // Repopulates the short answer text + // which was stored into local storage. + var answer = ""; + if (this.graderactive) { + return; + } + var len = localStorage.length; + if (len > 0) { + var ex = localStorage.getItem(this.localStorageKey()); + if (ex !== null) { + try { + var storedData = JSON.parse(ex); + answer = storedData.answer; + } catch (err) { + // error while parsing; likely due to bad value stored in storage + console.log(err.message); + localStorage.removeItem(this.localStorageKey()); + return; + } + let solution = $("#" + this.divid + "_solution"); + solution.text(answer); + this.renderMath(answer); + this.feedbackDiv.innerHTML = + "Your current saved answer is shown above."; + $(this.feedbackDiv).removeClass("alert-danger"); + $(this.feedbackDiv).addClass("alert alert-success"); + } + } + } + restoreAnswers(data) { + // Restore answers from storage retrieval done in RunestoneBase + // sometimes data.answer can be null + if (!data.answer) { + data.answer = ""; + } + this.answer = data.answer; + this.jTextArea.value = this.answer; + this.renderMath(this.answer); + + let p = document.createElement("p"); + this.jInputDiv.appendChild(p); + var tsString = ""; + if (data.timestamp) { + tsString = new Date(data.timestamp).toLocaleString(); + } else { + tsString = ""; + } + $(p).text(tsString); + if (data.last_answer) { + this.current_answer = "ontime"; + let toggle_answer_button = document.createElement("button"); + toggle_answer_button.type = "button"; + $(toggle_answer_button).text("Show Late Answer"); + $(toggle_answer_button).addClass("btn btn-warning"); + $(toggle_answer_button).css("margin-left", "5px"); + + $(toggle_answer_button).click( + function () { + var display_timestamp, button_text; + if (this.current_answer === "ontime") { + this.jTextArea.value = data.last_answer; + this.answer = data.last_answer; + display_timestamp = new Date( + data.last_timestamp + ).toLocaleString(); + button_text = "Show on-Time Answer"; + this.current_answer = "late"; + } else { + this.jTextArea.value = data.answer; + this.answer = data.answer; + display_timestamp = tsString; + button_text = "Show Late Answer"; + this.current_answer = "ontime"; + } + this.renderMath(this.answer); + $(p).text(`Submitted: ${display_timestamp}`); + $(toggle_answer_button).text(button_text); + }.bind(this) + ); + + this.buttonDiv.appendChild(toggle_answer_button); + } + let feedbackStr = "Your current saved answer is shown above."; + if (typeof data.score !== "undefined") { + feedbackStr = `Score: ${data.score}`; + } + if (data.comment) { + feedbackStr += ` -- ${data.comment}`; + } + this.feedbackDiv.innerHTML = feedbackStr; + + $(this.feedbackDiv).removeClass("alert-danger"); + $(this.feedbackDiv).addClass("alert alert-success"); + } + + disableInteraction() { + this.jTextArea.disabled = true; + } +} + +/*================================= +== Find the custom HTML tags and == +== execute our code on them == +=================================*/ +$(document).bind("runestone:login-complete", function () { + $("[data-component=shortanswer]").each(function () { + if ($(this).closest("[data-component=timedAssessment]").length == 0) { + // If this element exists within a timed component, don't render it here + try { + saList[this.id] = new ShortAnswer({ + orig: this, + useRunestoneServices: eBookConfig.useRunestoneServices, + }); + } catch (err) { + console.log(`Error rendering ShortAnswer Problem ${this.id} + Details: ${err}`); + } + } + }); +}); diff --git a/runestone/hparsons/js/regex-element.js b/runestone/hparsons/js/regex-element.js new file mode 100644 index 000000000..9be7fc770 --- /dev/null +++ b/runestone/hparsons/js/regex-element.js @@ -0,0 +1,17013 @@ +/**! + * Sortable 1.14.0 + * @author RubaXa + * @author owenm + * @license MIT + */ +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + + if (enumerableOnly) { + symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + } + + keys.push.apply(keys, symbols); + } + + return keys; +} + +function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; +} + +function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; +} + +function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); +} + +function _objectWithoutPropertiesLoose(source, excluded) { + if (source == null) return {}; + var target = {}; + var sourceKeys = Object.keys(source); + var key, i; + + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + target[key] = source[key]; + } + + return target; +} + +function _objectWithoutProperties(source, excluded) { + if (source == null) return {}; + + var target = _objectWithoutPropertiesLoose(source, excluded); + + var key, i; + + if (Object.getOwnPropertySymbols) { + var sourceSymbolKeys = Object.getOwnPropertySymbols(source); + + for (i = 0; i < sourceSymbolKeys.length; i++) { + key = sourceSymbolKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; + target[key] = source[key]; + } + } + + return target; +} + +var version = "1.14.0"; + +function userAgent(pattern) { + if (typeof window !== 'undefined' && window.navigator) { + return !! /*@__PURE__*/navigator.userAgent.match(pattern); + } +} + +var IE11OrLess = userAgent(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i); +var Edge = userAgent(/Edge/i); +var FireFox = userAgent(/firefox/i); +var Safari = userAgent(/safari/i) && !userAgent(/chrome/i) && !userAgent(/android/i); +var IOS = userAgent(/iP(ad|od|hone)/i); +var ChromeForAndroid = userAgent(/chrome/i) && userAgent(/android/i); + +var captureMode = { + capture: false, + passive: false +}; + +function on(el, event, fn) { + el.addEventListener(event, fn, !IE11OrLess && captureMode); +} + +function off(el, event, fn) { + el.removeEventListener(event, fn, !IE11OrLess && captureMode); +} + +function matches( +/**HTMLElement*/ +el, +/**String*/ +selector) { + if (!selector) return; + selector[0] === '>' && (selector = selector.substring(1)); + + if (el) { + try { + if (el.matches) { + return el.matches(selector); + } else if (el.msMatchesSelector) { + return el.msMatchesSelector(selector); + } else if (el.webkitMatchesSelector) { + return el.webkitMatchesSelector(selector); + } + } catch (_) { + return false; + } + } + + return false; +} + +function getParentOrHost(el) { + return el.host && el !== document && el.host.nodeType ? el.host : el.parentNode; +} + +function closest( +/**HTMLElement*/ +el, +/**String*/ +selector, +/**HTMLElement*/ +ctx, includeCTX) { + if (el) { + ctx = ctx || document; + + do { + if (selector != null && (selector[0] === '>' ? el.parentNode === ctx && matches(el, selector) : matches(el, selector)) || includeCTX && el === ctx) { + return el; + } + + if (el === ctx) break; + /* jshint boss:true */ + } while (el = getParentOrHost(el)); + } + + return null; +} + +var R_SPACE = /\s+/g; + +function toggleClass(el, name, state) { + if (el && name) { + if (el.classList) { + el.classList[state ? 'add' : 'remove'](name); + } else { + var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' '); + el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' '); + } + } +} + +function css(el, prop, val) { + var style = el && el.style; + + if (style) { + if (val === void 0) { + if (document.defaultView && document.defaultView.getComputedStyle) { + val = document.defaultView.getComputedStyle(el, ''); + } else if (el.currentStyle) { + val = el.currentStyle; + } + + return prop === void 0 ? val : val[prop]; + } else { + if (!(prop in style) && prop.indexOf('webkit') === -1) { + prop = '-webkit-' + prop; + } + + style[prop] = val + (typeof val === 'string' ? '' : 'px'); + } + } +} + +function matrix(el, selfOnly) { + var appliedTransforms = ''; + + if (typeof el === 'string') { + appliedTransforms = el; + } else { + do { + var transform = css(el, 'transform'); + + if (transform && transform !== 'none') { + appliedTransforms = transform + ' ' + appliedTransforms; + } + /* jshint boss:true */ + + } while (!selfOnly && (el = el.parentNode)); + } + + var matrixFn = window.DOMMatrix || window.WebKitCSSMatrix || window.CSSMatrix || window.MSCSSMatrix; + /*jshint -W056 */ + + return matrixFn && new matrixFn(appliedTransforms); +} + +function find(ctx, tagName, iterator) { + if (ctx) { + var list = ctx.getElementsByTagName(tagName), + i = 0, + n = list.length; + + if (iterator) { + for (; i < n; i++) { + iterator(list[i], i); + } + } + + return list; + } + + return []; +} + +function getWindowScrollingElement() { + var scrollingElement = document.scrollingElement; + + if (scrollingElement) { + return scrollingElement; + } else { + return document.documentElement; + } +} +/** + * Returns the "bounding client rect" of given element + * @param {HTMLElement} el The element whose boundingClientRect is wanted + * @param {[Boolean]} relativeToContainingBlock Whether the rect should be relative to the containing block of (including) the container + * @param {[Boolean]} relativeToNonStaticParent Whether the rect should be relative to the relative parent of (including) the contaienr + * @param {[Boolean]} undoScale Whether the container's scale() should be undone + * @param {[HTMLElement]} container The parent the element will be placed in + * @return {Object} The boundingClientRect of el, with specified adjustments + */ + + +function getRect(el, relativeToContainingBlock, relativeToNonStaticParent, undoScale, container) { + if (!el.getBoundingClientRect && el !== window) return; + var elRect, top, left, bottom, right, height, width; + + if (el !== window && el.parentNode && el !== getWindowScrollingElement()) { + elRect = el.getBoundingClientRect(); + top = elRect.top; + left = elRect.left; + bottom = elRect.bottom; + right = elRect.right; + height = elRect.height; + width = elRect.width; + } else { + top = 0; + left = 0; + bottom = window.innerHeight; + right = window.innerWidth; + height = window.innerHeight; + width = window.innerWidth; + } + + if ((relativeToContainingBlock || relativeToNonStaticParent) && el !== window) { + // Adjust for translate() + container = container || el.parentNode; // solves #1123 (see: https://stackoverflow.com/a/37953806/6088312) + // Not needed on <= IE11 + + if (!IE11OrLess) { + do { + if (container && container.getBoundingClientRect && (css(container, 'transform') !== 'none' || relativeToNonStaticParent && css(container, 'position') !== 'static')) { + var containerRect = container.getBoundingClientRect(); // Set relative to edges of padding box of container + + top -= containerRect.top + parseInt(css(container, 'border-top-width')); + left -= containerRect.left + parseInt(css(container, 'border-left-width')); + bottom = top + elRect.height; + right = left + elRect.width; + break; + } + /* jshint boss:true */ + + } while (container = container.parentNode); + } + } + + if (undoScale && el !== window) { + // Adjust for scale() + var elMatrix = matrix(container || el), + scaleX = elMatrix && elMatrix.a, + scaleY = elMatrix && elMatrix.d; + + if (elMatrix) { + top /= scaleY; + left /= scaleX; + width /= scaleX; + height /= scaleY; + bottom = top + height; + right = left + width; + } + } + + return { + top: top, + left: left, + bottom: bottom, + right: right, + width: width, + height: height + }; +} +/** + * Checks if a side of an element is scrolled past a side of its parents + * @param {HTMLElement} el The element who's side being scrolled out of view is in question + * @param {String} elSide Side of the element in question ('top', 'left', 'right', 'bottom') + * @param {String} parentSide Side of the parent in question ('top', 'left', 'right', 'bottom') + * @return {HTMLElement} The parent scroll element that the el's side is scrolled past, or null if there is no such element + */ + + +function isScrolledPast(el, elSide, parentSide) { + var parent = getParentAutoScrollElement(el, true), + elSideVal = getRect(el)[elSide]; + /* jshint boss:true */ + + while (parent) { + var parentSideVal = getRect(parent)[parentSide], + visible = void 0; + + if (parentSide === 'top' || parentSide === 'left') { + visible = elSideVal >= parentSideVal; + } else { + visible = elSideVal <= parentSideVal; + } + + if (!visible) return parent; + if (parent === getWindowScrollingElement()) break; + parent = getParentAutoScrollElement(parent, false); + } + + return false; +} +/** + * Gets nth child of el, ignoring hidden children, sortable's elements (does not ignore clone if it's visible) + * and non-draggable elements + * @param {HTMLElement} el The parent element + * @param {Number} childNum The index of the child + * @param {Object} options Parent Sortable's options + * @return {HTMLElement} The child at index childNum, or null if not found + */ + + +function getChild(el, childNum, options, includeDragEl) { + var currentChild = 0, + i = 0, + children = el.children; + + while (i < children.length) { + if (children[i].style.display !== 'none' && children[i] !== Sortable.ghost && (includeDragEl || children[i] !== Sortable.dragged) && closest(children[i], options.draggable, el, false)) { + if (currentChild === childNum) { + return children[i]; + } + + currentChild++; + } + + i++; + } + + return null; +} +/** + * Gets the last child in the el, ignoring ghostEl or invisible elements (clones) + * @param {HTMLElement} el Parent element + * @param {selector} selector Any other elements that should be ignored + * @return {HTMLElement} The last child, ignoring ghostEl + */ + + +function lastChild(el, selector) { + var last = el.lastElementChild; + + while (last && (last === Sortable.ghost || css(last, 'display') === 'none' || selector && !matches(last, selector))) { + last = last.previousElementSibling; + } + + return last || null; +} +/** + * Returns the index of an element within its parent for a selected set of + * elements + * @param {HTMLElement} el + * @param {selector} selector + * @return {number} + */ + + +function index(el, selector) { + var index = 0; + + if (!el || !el.parentNode) { + return -1; + } + /* jshint boss:true */ + + + while (el = el.previousElementSibling) { + if (el.nodeName.toUpperCase() !== 'TEMPLATE' && el !== Sortable.clone && (!selector || matches(el, selector))) { + index++; + } + } + + return index; +} +/** + * Returns the scroll offset of the given element, added with all the scroll offsets of parent elements. + * The value is returned in real pixels. + * @param {HTMLElement} el + * @return {Array} Offsets in the format of [left, top] + */ + + +function getRelativeScrollOffset(el) { + var offsetLeft = 0, + offsetTop = 0, + winScroller = getWindowScrollingElement(); + + if (el) { + do { + var elMatrix = matrix(el), + scaleX = elMatrix.a, + scaleY = elMatrix.d; + offsetLeft += el.scrollLeft * scaleX; + offsetTop += el.scrollTop * scaleY; + } while (el !== winScroller && (el = el.parentNode)); + } + + return [offsetLeft, offsetTop]; +} +/** + * Returns the index of the object within the given array + * @param {Array} arr Array that may or may not hold the object + * @param {Object} obj An object that has a key-value pair unique to and identical to a key-value pair in the object you want to find + * @return {Number} The index of the object in the array, or -1 + */ + + +function indexOfObject(arr, obj) { + for (var i in arr) { + if (!arr.hasOwnProperty(i)) continue; + + for (var key in obj) { + if (obj.hasOwnProperty(key) && obj[key] === arr[i][key]) return Number(i); + } + } + + return -1; +} + +function getParentAutoScrollElement(el, includeSelf) { + // skip to window + if (!el || !el.getBoundingClientRect) return getWindowScrollingElement(); + var elem = el; + var gotSelf = false; + + do { + // we don't need to get elem css if it isn't even overflowing in the first place (performance) + if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) { + var elemCSS = css(elem); + + if (elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') || elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll')) { + if (!elem.getBoundingClientRect || elem === document.body) return getWindowScrollingElement(); + if (gotSelf || includeSelf) return elem; + gotSelf = true; + } + } + /* jshint boss:true */ + + } while (elem = elem.parentNode); + + return getWindowScrollingElement(); +} + +function extend(dst, src) { + if (dst && src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dst[key] = src[key]; + } + } + } + + return dst; +} + +function isRectEqual(rect1, rect2) { + return Math.round(rect1.top) === Math.round(rect2.top) && Math.round(rect1.left) === Math.round(rect2.left) && Math.round(rect1.height) === Math.round(rect2.height) && Math.round(rect1.width) === Math.round(rect2.width); +} + +var _throttleTimeout; + +function throttle(callback, ms) { + return function () { + if (!_throttleTimeout) { + var args = arguments, + _this = this; + + if (args.length === 1) { + callback.call(_this, args[0]); + } else { + callback.apply(_this, args); + } + + _throttleTimeout = setTimeout(function () { + _throttleTimeout = void 0; + }, ms); + } + }; +} + +function cancelThrottle() { + clearTimeout(_throttleTimeout); + _throttleTimeout = void 0; +} + +function scrollBy(el, x, y) { + el.scrollLeft += x; + el.scrollTop += y; +} + +function clone(el) { + var Polymer = window.Polymer; + var $ = window.jQuery || window.Zepto; + + if (Polymer && Polymer.dom) { + return Polymer.dom(el).cloneNode(true); + } else if ($) { + return $(el).clone(true)[0]; + } else { + return el.cloneNode(true); + } +} + +var expando = 'Sortable' + new Date().getTime(); + +function AnimationStateManager() { + var animationStates = [], + animationCallbackId; + return { + captureAnimationState: function captureAnimationState() { + animationStates = []; + if (!this.options.animation) return; + var children = [].slice.call(this.el.children); + children.forEach(function (child) { + if (css(child, 'display') === 'none' || child === Sortable.ghost) return; + animationStates.push({ + target: child, + rect: getRect(child) + }); + + var fromRect = _objectSpread2({}, animationStates[animationStates.length - 1].rect); // If animating: compensate for current animation + + + if (child.thisAnimationDuration) { + var childMatrix = matrix(child, true); + + if (childMatrix) { + fromRect.top -= childMatrix.f; + fromRect.left -= childMatrix.e; + } + } + + child.fromRect = fromRect; + }); + }, + addAnimationState: function addAnimationState(state) { + animationStates.push(state); + }, + removeAnimationState: function removeAnimationState(target) { + animationStates.splice(indexOfObject(animationStates, { + target: target + }), 1); + }, + animateAll: function animateAll(callback) { + var _this = this; + + if (!this.options.animation) { + clearTimeout(animationCallbackId); + if (typeof callback === 'function') callback(); + return; + } + + var animating = false, + animationTime = 0; + animationStates.forEach(function (state) { + var time = 0, + target = state.target, + fromRect = target.fromRect, + toRect = getRect(target), + prevFromRect = target.prevFromRect, + prevToRect = target.prevToRect, + animatingRect = state.rect, + targetMatrix = matrix(target, true); + + if (targetMatrix) { + // Compensate for current animation + toRect.top -= targetMatrix.f; + toRect.left -= targetMatrix.e; + } + + target.toRect = toRect; + + if (target.thisAnimationDuration) { + // Could also check if animatingRect is between fromRect and toRect + if (isRectEqual(prevFromRect, toRect) && !isRectEqual(fromRect, toRect) && // Make sure animatingRect is on line between toRect & fromRect + (animatingRect.top - toRect.top) / (animatingRect.left - toRect.left) === (fromRect.top - toRect.top) / (fromRect.left - toRect.left)) { + // If returning to same place as started from animation and on same axis + time = calculateRealTime(animatingRect, prevFromRect, prevToRect, _this.options); + } + } // if fromRect != toRect: animate + + + if (!isRectEqual(toRect, fromRect)) { + target.prevFromRect = fromRect; + target.prevToRect = toRect; + + if (!time) { + time = _this.options.animation; + } + + _this.animate(target, animatingRect, toRect, time); + } + + if (time) { + animating = true; + animationTime = Math.max(animationTime, time); + clearTimeout(target.animationResetTimer); + target.animationResetTimer = setTimeout(function () { + target.animationTime = 0; + target.prevFromRect = null; + target.fromRect = null; + target.prevToRect = null; + target.thisAnimationDuration = null; + }, time); + target.thisAnimationDuration = time; + } + }); + clearTimeout(animationCallbackId); + + if (!animating) { + if (typeof callback === 'function') callback(); + } else { + animationCallbackId = setTimeout(function () { + if (typeof callback === 'function') callback(); + }, animationTime); + } + + animationStates = []; + }, + animate: function animate(target, currentRect, toRect, duration) { + if (duration) { + css(target, 'transition', ''); + css(target, 'transform', ''); + var elMatrix = matrix(this.el), + scaleX = elMatrix && elMatrix.a, + scaleY = elMatrix && elMatrix.d, + translateX = (currentRect.left - toRect.left) / (scaleX || 1), + translateY = (currentRect.top - toRect.top) / (scaleY || 1); + target.animatingX = !!translateX; + target.animatingY = !!translateY; + css(target, 'transform', 'translate3d(' + translateX + 'px,' + translateY + 'px,0)'); + this.forRepaintDummy = repaint(target); // repaint + + css(target, 'transition', 'transform ' + duration + 'ms' + (this.options.easing ? ' ' + this.options.easing : '')); + css(target, 'transform', 'translate3d(0,0,0)'); + typeof target.animated === 'number' && clearTimeout(target.animated); + target.animated = setTimeout(function () { + css(target, 'transition', ''); + css(target, 'transform', ''); + target.animated = false; + target.animatingX = false; + target.animatingY = false; + }, duration); + } + } + }; +} + +function repaint(target) { + return target.offsetWidth; +} + +function calculateRealTime(animatingRect, fromRect, toRect, options) { + return Math.sqrt(Math.pow(fromRect.top - animatingRect.top, 2) + Math.pow(fromRect.left - animatingRect.left, 2)) / Math.sqrt(Math.pow(fromRect.top - toRect.top, 2) + Math.pow(fromRect.left - toRect.left, 2)) * options.animation; +} + +var plugins = []; +var defaults = { + initializeByDefault: true +}; +var PluginManager = { + mount: function mount(plugin) { + // Set default static properties + for (var option in defaults) { + if (defaults.hasOwnProperty(option) && !(option in plugin)) { + plugin[option] = defaults[option]; + } + } + + plugins.forEach(function (p) { + if (p.pluginName === plugin.pluginName) { + throw "Sortable: Cannot mount plugin ".concat(plugin.pluginName, " more than once"); + } + }); + plugins.push(plugin); + }, + pluginEvent: function pluginEvent(eventName, sortable, evt) { + var _this = this; + + this.eventCanceled = false; + + evt.cancel = function () { + _this.eventCanceled = true; + }; + + var eventNameGlobal = eventName + 'Global'; + plugins.forEach(function (plugin) { + if (!sortable[plugin.pluginName]) return; // Fire global events if it exists in this sortable + + if (sortable[plugin.pluginName][eventNameGlobal]) { + sortable[plugin.pluginName][eventNameGlobal](_objectSpread2({ + sortable: sortable + }, evt)); + } // Only fire plugin event if plugin is enabled in this sortable, + // and plugin has event defined + + + if (sortable.options[plugin.pluginName] && sortable[plugin.pluginName][eventName]) { + sortable[plugin.pluginName][eventName](_objectSpread2({ + sortable: sortable + }, evt)); + } + }); + }, + initializePlugins: function initializePlugins(sortable, el, defaults, options) { + plugins.forEach(function (plugin) { + var pluginName = plugin.pluginName; + if (!sortable.options[pluginName] && !plugin.initializeByDefault) return; + var initialized = new plugin(sortable, el, sortable.options); + initialized.sortable = sortable; + initialized.options = sortable.options; + sortable[pluginName] = initialized; // Add default options from plugin + + _extends(defaults, initialized.defaults); + }); + + for (var option in sortable.options) { + if (!sortable.options.hasOwnProperty(option)) continue; + var modified = this.modifyOption(sortable, option, sortable.options[option]); + + if (typeof modified !== 'undefined') { + sortable.options[option] = modified; + } + } + }, + getEventProperties: function getEventProperties(name, sortable) { + var eventProperties = {}; + plugins.forEach(function (plugin) { + if (typeof plugin.eventProperties !== 'function') return; + + _extends(eventProperties, plugin.eventProperties.call(sortable[plugin.pluginName], name)); + }); + return eventProperties; + }, + modifyOption: function modifyOption(sortable, name, value) { + var modifiedValue; + plugins.forEach(function (plugin) { + // Plugin must exist on the Sortable + if (!sortable[plugin.pluginName]) return; // If static option listener exists for this option, call in the context of the Sortable's instance of this plugin + + if (plugin.optionListeners && typeof plugin.optionListeners[name] === 'function') { + modifiedValue = plugin.optionListeners[name].call(sortable[plugin.pluginName], value); + } + }); + return modifiedValue; + } +}; + +function dispatchEvent(_ref) { + var sortable = _ref.sortable, + rootEl = _ref.rootEl, + name = _ref.name, + targetEl = _ref.targetEl, + cloneEl = _ref.cloneEl, + toEl = _ref.toEl, + fromEl = _ref.fromEl, + oldIndex = _ref.oldIndex, + newIndex = _ref.newIndex, + oldDraggableIndex = _ref.oldDraggableIndex, + newDraggableIndex = _ref.newDraggableIndex, + originalEvent = _ref.originalEvent, + putSortable = _ref.putSortable, + extraEventProperties = _ref.extraEventProperties; + sortable = sortable || rootEl && rootEl[expando]; + if (!sortable) return; + var evt, + options = sortable.options, + onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1); // Support for new CustomEvent feature + + if (window.CustomEvent && !IE11OrLess && !Edge) { + evt = new CustomEvent(name, { + bubbles: true, + cancelable: true + }); + } else { + evt = document.createEvent('Event'); + evt.initEvent(name, true, true); + } + + evt.to = toEl || rootEl; + evt.from = fromEl || rootEl; + evt.item = targetEl || rootEl; + evt.clone = cloneEl; + evt.oldIndex = oldIndex; + evt.newIndex = newIndex; + evt.oldDraggableIndex = oldDraggableIndex; + evt.newDraggableIndex = newDraggableIndex; + evt.originalEvent = originalEvent; + evt.pullMode = putSortable ? putSortable.lastPutMode : undefined; + + var allEventProperties = _objectSpread2(_objectSpread2({}, extraEventProperties), PluginManager.getEventProperties(name, sortable)); + + for (var option in allEventProperties) { + evt[option] = allEventProperties[option]; + } + + if (rootEl) { + rootEl.dispatchEvent(evt); + } + + if (options[onName]) { + options[onName].call(sortable, evt); + } +} + +var _excluded = ["evt"]; + +var pluginEvent = function pluginEvent(eventName, sortable) { + var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + originalEvent = _ref.evt, + data = _objectWithoutProperties(_ref, _excluded); + + PluginManager.pluginEvent.bind(Sortable)(eventName, sortable, _objectSpread2({ + dragEl: dragEl, + parentEl: parentEl, + ghostEl: ghostEl, + rootEl: rootEl, + nextEl: nextEl, + lastDownEl: lastDownEl, + cloneEl: cloneEl, + cloneHidden: cloneHidden, + dragStarted: moved, + putSortable: putSortable, + activeSortable: Sortable.active, + originalEvent: originalEvent, + oldIndex: oldIndex, + oldDraggableIndex: oldDraggableIndex, + newIndex: newIndex, + newDraggableIndex: newDraggableIndex, + hideGhostForTarget: _hideGhostForTarget, + unhideGhostForTarget: _unhideGhostForTarget, + cloneNowHidden: function cloneNowHidden() { + cloneHidden = true; + }, + cloneNowShown: function cloneNowShown() { + cloneHidden = false; + }, + dispatchSortableEvent: function dispatchSortableEvent(name) { + _dispatchEvent({ + sortable: sortable, + name: name, + originalEvent: originalEvent + }); + } + }, data)); +}; + +function _dispatchEvent(info) { + dispatchEvent(_objectSpread2({ + putSortable: putSortable, + cloneEl: cloneEl, + targetEl: dragEl, + rootEl: rootEl, + oldIndex: oldIndex, + oldDraggableIndex: oldDraggableIndex, + newIndex: newIndex, + newDraggableIndex: newDraggableIndex + }, info)); +} + +var dragEl, + parentEl, + ghostEl, + rootEl, + nextEl, + lastDownEl, + cloneEl, + cloneHidden, + oldIndex, + newIndex, + oldDraggableIndex, + newDraggableIndex, + activeGroup, + putSortable, + awaitingDragStarted = false, + ignoreNextClick = false, + sortables = [], + tapEvt, + touchEvt, + lastDx, + lastDy, + tapDistanceLeft, + tapDistanceTop, + moved, + lastTarget, + lastDirection, + pastFirstInvertThresh = false, + isCircumstantialInvert = false, + targetMoveDistance, + // For positioning ghost absolutely +ghostRelativeParent, + ghostRelativeParentInitialScroll = [], + // (left, top) +_silent = false, + savedInputChecked = []; +/** @const */ + +var documentExists = typeof document !== 'undefined', + PositionGhostAbsolutely = IOS, + CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float', + // This will not pass for IE9, because IE9 DnD only works on anchors +supportDraggable = documentExists && !ChromeForAndroid && !IOS && 'draggable' in document.createElement('div'), + supportCssPointerEvents = function () { + if (!documentExists) return; // false when <= IE11 + + if (IE11OrLess) { + return false; + } + + var el = document.createElement('x'); + el.style.cssText = 'pointer-events:auto'; + return el.style.pointerEvents === 'auto'; +}(), + _detectDirection = function _detectDirection(el, options) { + var elCSS = css(el), + elWidth = parseInt(elCSS.width) - parseInt(elCSS.paddingLeft) - parseInt(elCSS.paddingRight) - parseInt(elCSS.borderLeftWidth) - parseInt(elCSS.borderRightWidth), + child1 = getChild(el, 0, options), + child2 = getChild(el, 1, options), + firstChildCSS = child1 && css(child1), + secondChildCSS = child2 && css(child2), + firstChildWidth = firstChildCSS && parseInt(firstChildCSS.marginLeft) + parseInt(firstChildCSS.marginRight) + getRect(child1).width, + secondChildWidth = secondChildCSS && parseInt(secondChildCSS.marginLeft) + parseInt(secondChildCSS.marginRight) + getRect(child2).width; + + if (elCSS.display === 'flex') { + return elCSS.flexDirection === 'column' || elCSS.flexDirection === 'column-reverse' ? 'vertical' : 'horizontal'; + } + + if (elCSS.display === 'grid') { + return elCSS.gridTemplateColumns.split(' ').length <= 1 ? 'vertical' : 'horizontal'; + } + + if (child1 && firstChildCSS["float"] && firstChildCSS["float"] !== 'none') { + var touchingSideChild2 = firstChildCSS["float"] === 'left' ? 'left' : 'right'; + return child2 && (secondChildCSS.clear === 'both' || secondChildCSS.clear === touchingSideChild2) ? 'vertical' : 'horizontal'; + } + + return child1 && (firstChildCSS.display === 'block' || firstChildCSS.display === 'flex' || firstChildCSS.display === 'table' || firstChildCSS.display === 'grid' || firstChildWidth >= elWidth && elCSS[CSSFloatProperty] === 'none' || child2 && elCSS[CSSFloatProperty] === 'none' && firstChildWidth + secondChildWidth > elWidth) ? 'vertical' : 'horizontal'; +}, + _dragElInRowColumn = function _dragElInRowColumn(dragRect, targetRect, vertical) { + var dragElS1Opp = vertical ? dragRect.left : dragRect.top, + dragElS2Opp = vertical ? dragRect.right : dragRect.bottom, + dragElOppLength = vertical ? dragRect.width : dragRect.height, + targetS1Opp = vertical ? targetRect.left : targetRect.top, + targetS2Opp = vertical ? targetRect.right : targetRect.bottom, + targetOppLength = vertical ? targetRect.width : targetRect.height; + return dragElS1Opp === targetS1Opp || dragElS2Opp === targetS2Opp || dragElS1Opp + dragElOppLength / 2 === targetS1Opp + targetOppLength / 2; +}, + +/** + * Detects first nearest empty sortable to X and Y position using emptyInsertThreshold. + * @param {Number} x X position + * @param {Number} y Y position + * @return {HTMLElement} Element of the first found nearest Sortable + */ +_detectNearestEmptySortable = function _detectNearestEmptySortable(x, y) { + var ret; + sortables.some(function (sortable) { + var threshold = sortable[expando].options.emptyInsertThreshold; + if (!threshold || lastChild(sortable)) return; + var rect = getRect(sortable), + insideHorizontally = x >= rect.left - threshold && x <= rect.right + threshold, + insideVertically = y >= rect.top - threshold && y <= rect.bottom + threshold; + + if (insideHorizontally && insideVertically) { + return ret = sortable; + } + }); + return ret; +}, + _prepareGroup = function _prepareGroup(options) { + function toFn(value, pull) { + return function (to, from, dragEl, evt) { + var sameGroup = to.options.group.name && from.options.group.name && to.options.group.name === from.options.group.name; + + if (value == null && (pull || sameGroup)) { + // Default pull value + // Default pull and put value if same group + return true; + } else if (value == null || value === false) { + return false; + } else if (pull && value === 'clone') { + return value; + } else if (typeof value === 'function') { + return toFn(value(to, from, dragEl, evt), pull)(to, from, dragEl, evt); + } else { + var otherGroup = (pull ? to : from).options.group.name; + return value === true || typeof value === 'string' && value === otherGroup || value.join && value.indexOf(otherGroup) > -1; + } + }; + } + + var group = {}; + var originalGroup = options.group; + + if (!originalGroup || _typeof(originalGroup) != 'object') { + originalGroup = { + name: originalGroup + }; + } + + group.name = originalGroup.name; + group.checkPull = toFn(originalGroup.pull, true); + group.checkPut = toFn(originalGroup.put); + group.revertClone = originalGroup.revertClone; + options.group = group; +}, + _hideGhostForTarget = function _hideGhostForTarget() { + if (!supportCssPointerEvents && ghostEl) { + css(ghostEl, 'display', 'none'); + } +}, + _unhideGhostForTarget = function _unhideGhostForTarget() { + if (!supportCssPointerEvents && ghostEl) { + css(ghostEl, 'display', ''); + } +}; // #1184 fix - Prevent click event on fallback if dragged but item not changed position + + +if (documentExists) { + document.addEventListener('click', function (evt) { + if (ignoreNextClick) { + evt.preventDefault(); + evt.stopPropagation && evt.stopPropagation(); + evt.stopImmediatePropagation && evt.stopImmediatePropagation(); + ignoreNextClick = false; + return false; + } + }, true); +} + +var nearestEmptyInsertDetectEvent = function nearestEmptyInsertDetectEvent(evt) { + if (dragEl) { + evt = evt.touches ? evt.touches[0] : evt; + + var nearest = _detectNearestEmptySortable(evt.clientX, evt.clientY); + + if (nearest) { + // Create imitation event + var event = {}; + + for (var i in evt) { + if (evt.hasOwnProperty(i)) { + event[i] = evt[i]; + } + } + + event.target = event.rootEl = nearest; + event.preventDefault = void 0; + event.stopPropagation = void 0; + + nearest[expando]._onDragOver(event); + } + } +}; + +var _checkOutsideTargetEl = function _checkOutsideTargetEl(evt) { + if (dragEl) { + dragEl.parentNode[expando]._isOutsideThisEl(evt.target); + } +}; +/** + * @class Sortable + * @param {HTMLElement} el + * @param {Object} [options] + */ + + +function Sortable(el, options) { + if (!(el && el.nodeType && el.nodeType === 1)) { + throw "Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(el)); + } + + this.el = el; // root element + + this.options = options = _extends({}, options); // Export instance + + el[expando] = this; + var defaults = { + group: null, + sort: true, + disabled: false, + store: null, + handle: null, + draggable: /^[uo]l$/i.test(el.nodeName) ? '>li' : '>*', + swapThreshold: 1, + // percentage; 0 <= x <= 1 + invertSwap: false, + // invert always + invertedSwapThreshold: null, + // will be set to same as swapThreshold if default + removeCloneOnHide: true, + direction: function direction() { + return _detectDirection(el, this.options); + }, + ghostClass: 'sortable-ghost', + chosenClass: 'sortable-chosen', + dragClass: 'sortable-drag', + ignore: 'a, img', + filter: null, + preventOnFilter: true, + animation: 0, + easing: null, + setData: function setData(dataTransfer, dragEl) { + dataTransfer.setData('Text', dragEl.textContent); + }, + dropBubble: false, + dragoverBubble: false, + dataIdAttr: 'data-id', + delay: 0, + delayOnTouchOnly: false, + touchStartThreshold: (Number.parseInt ? Number : window).parseInt(window.devicePixelRatio, 10) || 1, + forceFallback: false, + fallbackClass: 'sortable-fallback', + fallbackOnBody: false, + fallbackTolerance: 0, + fallbackOffset: { + x: 0, + y: 0 + }, + supportPointer: Sortable.supportPointer !== false && 'PointerEvent' in window && !Safari, + emptyInsertThreshold: 5 + }; + PluginManager.initializePlugins(this, el, defaults); // Set default options + + for (var name in defaults) { + !(name in options) && (options[name] = defaults[name]); + } + + _prepareGroup(options); // Bind all private methods + + + for (var fn in this) { + if (fn.charAt(0) === '_' && typeof this[fn] === 'function') { + this[fn] = this[fn].bind(this); + } + } // Setup drag mode + + + this.nativeDraggable = options.forceFallback ? false : supportDraggable; + + if (this.nativeDraggable) { + // Touch start threshold cannot be greater than the native dragstart threshold + this.options.touchStartThreshold = 1; + } // Bind events + + + if (options.supportPointer) { + on(el, 'pointerdown', this._onTapStart); + } else { + on(el, 'mousedown', this._onTapStart); + on(el, 'touchstart', this._onTapStart); + } + + if (this.nativeDraggable) { + on(el, 'dragover', this); + on(el, 'dragenter', this); + } + + sortables.push(this.el); // Restore sorting + + options.store && options.store.get && this.sort(options.store.get(this) || []); // Add animation state manager + + _extends(this, AnimationStateManager()); +} + +Sortable.prototype = +/** @lends Sortable.prototype */ +{ + constructor: Sortable, + _isOutsideThisEl: function _isOutsideThisEl(target) { + if (!this.el.contains(target) && target !== this.el) { + lastTarget = null; + } + }, + _getDirection: function _getDirection(evt, target) { + return typeof this.options.direction === 'function' ? this.options.direction.call(this, evt, target, dragEl) : this.options.direction; + }, + _onTapStart: function _onTapStart( + /** Event|TouchEvent */ + evt) { + if (!evt.cancelable) return; + + var _this = this, + el = this.el, + options = this.options, + preventOnFilter = options.preventOnFilter, + type = evt.type, + touch = evt.touches && evt.touches[0] || evt.pointerType && evt.pointerType === 'touch' && evt, + target = (touch || evt).target, + originalTarget = evt.target.shadowRoot && (evt.path && evt.path[0] || evt.composedPath && evt.composedPath()[0]) || target, + filter = options.filter; + + _saveInputCheckedState(el); // Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group. + + + if (dragEl) { + return; + } + + if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) { + return; // only left button and enabled + } // cancel dnd if original target is content editable + + + if (originalTarget.isContentEditable) { + return; + } // Safari ignores further event handling after mousedown + + + if (!this.nativeDraggable && Safari && target && target.tagName.toUpperCase() === 'SELECT') { + return; + } + + target = closest(target, options.draggable, el, false); + + if (target && target.animated) { + return; + } + + if (lastDownEl === target) { + // Ignoring duplicate `down` + return; + } // Get the index of the dragged element within its parent + + + oldIndex = index(target); + oldDraggableIndex = index(target, options.draggable); // Check filter + + if (typeof filter === 'function') { + if (filter.call(this, evt, target, this)) { + _dispatchEvent({ + sortable: _this, + rootEl: originalTarget, + name: 'filter', + targetEl: target, + toEl: el, + fromEl: el + }); + + pluginEvent('filter', _this, { + evt: evt + }); + preventOnFilter && evt.cancelable && evt.preventDefault(); + return; // cancel dnd + } + } else if (filter) { + filter = filter.split(',').some(function (criteria) { + criteria = closest(originalTarget, criteria.trim(), el, false); + + if (criteria) { + _dispatchEvent({ + sortable: _this, + rootEl: criteria, + name: 'filter', + targetEl: target, + fromEl: el, + toEl: el + }); + + pluginEvent('filter', _this, { + evt: evt + }); + return true; + } + }); + + if (filter) { + preventOnFilter && evt.cancelable && evt.preventDefault(); + return; // cancel dnd + } + } + + if (options.handle && !closest(originalTarget, options.handle, el, false)) { + return; + } // Prepare `dragstart` + + + this._prepareDragStart(evt, touch, target); + }, + _prepareDragStart: function _prepareDragStart( + /** Event */ + evt, + /** Touch */ + touch, + /** HTMLElement */ + target) { + var _this = this, + el = _this.el, + options = _this.options, + ownerDocument = el.ownerDocument, + dragStartFn; + + if (target && !dragEl && target.parentNode === el) { + var dragRect = getRect(target); + rootEl = el; + dragEl = target; + parentEl = dragEl.parentNode; + nextEl = dragEl.nextSibling; + lastDownEl = target; + activeGroup = options.group; + Sortable.dragged = dragEl; + tapEvt = { + target: dragEl, + clientX: (touch || evt).clientX, + clientY: (touch || evt).clientY + }; + tapDistanceLeft = tapEvt.clientX - dragRect.left; + tapDistanceTop = tapEvt.clientY - dragRect.top; + this._lastX = (touch || evt).clientX; + this._lastY = (touch || evt).clientY; + dragEl.style['will-change'] = 'all'; + + dragStartFn = function dragStartFn() { + pluginEvent('delayEnded', _this, { + evt: evt + }); + + if (Sortable.eventCanceled) { + _this._onDrop(); + + return; + } // Delayed drag has been triggered + // we can re-enable the events: touchmove/mousemove + + + _this._disableDelayedDragEvents(); + + if (!FireFox && _this.nativeDraggable) { + dragEl.draggable = true; + } // Bind the events: dragstart/dragend + + + _this._triggerDragStart(evt, touch); // Drag start event + + + _dispatchEvent({ + sortable: _this, + name: 'choose', + originalEvent: evt + }); // Chosen item + + + toggleClass(dragEl, options.chosenClass, true); + }; // Disable "draggable" + + + options.ignore.split(',').forEach(function (criteria) { + find(dragEl, criteria.trim(), _disableDraggable); + }); + on(ownerDocument, 'dragover', nearestEmptyInsertDetectEvent); + on(ownerDocument, 'mousemove', nearestEmptyInsertDetectEvent); + on(ownerDocument, 'touchmove', nearestEmptyInsertDetectEvent); + on(ownerDocument, 'mouseup', _this._onDrop); + on(ownerDocument, 'touchend', _this._onDrop); + on(ownerDocument, 'touchcancel', _this._onDrop); // Make dragEl draggable (must be before delay for FireFox) + + if (FireFox && this.nativeDraggable) { + this.options.touchStartThreshold = 4; + dragEl.draggable = true; + } + + pluginEvent('delayStart', this, { + evt: evt + }); // Delay is impossible for native DnD in Edge or IE + + if (options.delay && (!options.delayOnTouchOnly || touch) && (!this.nativeDraggable || !(Edge || IE11OrLess))) { + if (Sortable.eventCanceled) { + this._onDrop(); + + return; + } // If the user moves the pointer or let go the click or touch + // before the delay has been reached: + // disable the delayed drag + + + on(ownerDocument, 'mouseup', _this._disableDelayedDrag); + on(ownerDocument, 'touchend', _this._disableDelayedDrag); + on(ownerDocument, 'touchcancel', _this._disableDelayedDrag); + on(ownerDocument, 'mousemove', _this._delayedDragTouchMoveHandler); + on(ownerDocument, 'touchmove', _this._delayedDragTouchMoveHandler); + options.supportPointer && on(ownerDocument, 'pointermove', _this._delayedDragTouchMoveHandler); + _this._dragStartTimer = setTimeout(dragStartFn, options.delay); + } else { + dragStartFn(); + } + } + }, + _delayedDragTouchMoveHandler: function _delayedDragTouchMoveHandler( + /** TouchEvent|PointerEvent **/ + e) { + var touch = e.touches ? e.touches[0] : e; + + if (Math.max(Math.abs(touch.clientX - this._lastX), Math.abs(touch.clientY - this._lastY)) >= Math.floor(this.options.touchStartThreshold / (this.nativeDraggable && window.devicePixelRatio || 1))) { + this._disableDelayedDrag(); + } + }, + _disableDelayedDrag: function _disableDelayedDrag() { + dragEl && _disableDraggable(dragEl); + clearTimeout(this._dragStartTimer); + + this._disableDelayedDragEvents(); + }, + _disableDelayedDragEvents: function _disableDelayedDragEvents() { + var ownerDocument = this.el.ownerDocument; + off(ownerDocument, 'mouseup', this._disableDelayedDrag); + off(ownerDocument, 'touchend', this._disableDelayedDrag); + off(ownerDocument, 'touchcancel', this._disableDelayedDrag); + off(ownerDocument, 'mousemove', this._delayedDragTouchMoveHandler); + off(ownerDocument, 'touchmove', this._delayedDragTouchMoveHandler); + off(ownerDocument, 'pointermove', this._delayedDragTouchMoveHandler); + }, + _triggerDragStart: function _triggerDragStart( + /** Event */ + evt, + /** Touch */ + touch) { + touch = touch || evt.pointerType == 'touch' && evt; + + if (!this.nativeDraggable || touch) { + if (this.options.supportPointer) { + on(document, 'pointermove', this._onTouchMove); + } else if (touch) { + on(document, 'touchmove', this._onTouchMove); + } else { + on(document, 'mousemove', this._onTouchMove); + } + } else { + on(dragEl, 'dragend', this); + on(rootEl, 'dragstart', this._onDragStart); + } + + try { + if (document.selection) { + // Timeout neccessary for IE9 + _nextTick(function () { + document.selection.empty(); + }); + } else { + window.getSelection().removeAllRanges(); + } + } catch (err) {} + }, + _dragStarted: function _dragStarted(fallback, evt) { + + awaitingDragStarted = false; + + if (rootEl && dragEl) { + pluginEvent('dragStarted', this, { + evt: evt + }); + + if (this.nativeDraggable) { + on(document, 'dragover', _checkOutsideTargetEl); + } + + var options = this.options; // Apply effect + + !fallback && toggleClass(dragEl, options.dragClass, false); + toggleClass(dragEl, options.ghostClass, true); + Sortable.active = this; + fallback && this._appendGhost(); // Drag start event + + _dispatchEvent({ + sortable: this, + name: 'start', + originalEvent: evt + }); + } else { + this._nulling(); + } + }, + _emulateDragOver: function _emulateDragOver() { + if (touchEvt) { + this._lastX = touchEvt.clientX; + this._lastY = touchEvt.clientY; + + _hideGhostForTarget(); + + var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY); + var parent = target; + + while (target && target.shadowRoot) { + target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY); + if (target === parent) break; + parent = target; + } + + dragEl.parentNode[expando]._isOutsideThisEl(target); + + if (parent) { + do { + if (parent[expando]) { + var inserted = void 0; + inserted = parent[expando]._onDragOver({ + clientX: touchEvt.clientX, + clientY: touchEvt.clientY, + target: target, + rootEl: parent + }); + + if (inserted && !this.options.dragoverBubble) { + break; + } + } + + target = parent; // store last element + } + /* jshint boss:true */ + while (parent = parent.parentNode); + } + + _unhideGhostForTarget(); + } + }, + _onTouchMove: function _onTouchMove( + /**TouchEvent*/ + evt) { + if (tapEvt) { + var options = this.options, + fallbackTolerance = options.fallbackTolerance, + fallbackOffset = options.fallbackOffset, + touch = evt.touches ? evt.touches[0] : evt, + ghostMatrix = ghostEl && matrix(ghostEl, true), + scaleX = ghostEl && ghostMatrix && ghostMatrix.a, + scaleY = ghostEl && ghostMatrix && ghostMatrix.d, + relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && getRelativeScrollOffset(ghostRelativeParent), + dx = (touch.clientX - tapEvt.clientX + fallbackOffset.x) / (scaleX || 1) + (relativeScrollOffset ? relativeScrollOffset[0] - ghostRelativeParentInitialScroll[0] : 0) / (scaleX || 1), + dy = (touch.clientY - tapEvt.clientY + fallbackOffset.y) / (scaleY || 1) + (relativeScrollOffset ? relativeScrollOffset[1] - ghostRelativeParentInitialScroll[1] : 0) / (scaleY || 1); // only set the status to dragging, when we are actually dragging + + if (!Sortable.active && !awaitingDragStarted) { + if (fallbackTolerance && Math.max(Math.abs(touch.clientX - this._lastX), Math.abs(touch.clientY - this._lastY)) < fallbackTolerance) { + return; + } + + this._onDragStart(evt, true); + } + + if (ghostEl) { + if (ghostMatrix) { + ghostMatrix.e += dx - (lastDx || 0); + ghostMatrix.f += dy - (lastDy || 0); + } else { + ghostMatrix = { + a: 1, + b: 0, + c: 0, + d: 1, + e: dx, + f: dy + }; + } + + var cssMatrix = "matrix(".concat(ghostMatrix.a, ",").concat(ghostMatrix.b, ",").concat(ghostMatrix.c, ",").concat(ghostMatrix.d, ",").concat(ghostMatrix.e, ",").concat(ghostMatrix.f, ")"); + css(ghostEl, 'webkitTransform', cssMatrix); + css(ghostEl, 'mozTransform', cssMatrix); + css(ghostEl, 'msTransform', cssMatrix); + css(ghostEl, 'transform', cssMatrix); + lastDx = dx; + lastDy = dy; + touchEvt = touch; + } + + evt.cancelable && evt.preventDefault(); + } + }, + _appendGhost: function _appendGhost() { + // Bug if using scale(): https://stackoverflow.com/questions/2637058 + // Not being adjusted for + if (!ghostEl) { + var container = this.options.fallbackOnBody ? document.body : rootEl, + rect = getRect(dragEl, true, PositionGhostAbsolutely, true, container), + options = this.options; // Position absolutely + + if (PositionGhostAbsolutely) { + // Get relatively positioned parent + ghostRelativeParent = container; + + while (css(ghostRelativeParent, 'position') === 'static' && css(ghostRelativeParent, 'transform') === 'none' && ghostRelativeParent !== document) { + ghostRelativeParent = ghostRelativeParent.parentNode; + } + + if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) { + if (ghostRelativeParent === document) ghostRelativeParent = getWindowScrollingElement(); + rect.top += ghostRelativeParent.scrollTop; + rect.left += ghostRelativeParent.scrollLeft; + } else { + ghostRelativeParent = getWindowScrollingElement(); + } + + ghostRelativeParentInitialScroll = getRelativeScrollOffset(ghostRelativeParent); + } + + ghostEl = dragEl.cloneNode(true); + toggleClass(ghostEl, options.ghostClass, false); + toggleClass(ghostEl, options.fallbackClass, true); + toggleClass(ghostEl, options.dragClass, true); + css(ghostEl, 'transition', ''); + css(ghostEl, 'transform', ''); + css(ghostEl, 'box-sizing', 'border-box'); + css(ghostEl, 'margin', 0); + css(ghostEl, 'top', rect.top); + css(ghostEl, 'left', rect.left); + css(ghostEl, 'width', rect.width); + css(ghostEl, 'height', rect.height); + css(ghostEl, 'opacity', '0.8'); + css(ghostEl, 'position', PositionGhostAbsolutely ? 'absolute' : 'fixed'); + css(ghostEl, 'zIndex', '100000'); + css(ghostEl, 'pointerEvents', 'none'); + Sortable.ghost = ghostEl; + container.appendChild(ghostEl); // Set transform-origin + + css(ghostEl, 'transform-origin', tapDistanceLeft / parseInt(ghostEl.style.width) * 100 + '% ' + tapDistanceTop / parseInt(ghostEl.style.height) * 100 + '%'); + } + }, + _onDragStart: function _onDragStart( + /**Event*/ + evt, + /**boolean*/ + fallback) { + var _this = this; + + var dataTransfer = evt.dataTransfer; + var options = _this.options; + pluginEvent('dragStart', this, { + evt: evt + }); + + if (Sortable.eventCanceled) { + this._onDrop(); + + return; + } + + pluginEvent('setupClone', this); + + if (!Sortable.eventCanceled) { + cloneEl = clone(dragEl); + cloneEl.draggable = false; + cloneEl.style['will-change'] = ''; + + this._hideClone(); + + toggleClass(cloneEl, this.options.chosenClass, false); + Sortable.clone = cloneEl; + } // #1143: IFrame support workaround + + + _this.cloneId = _nextTick(function () { + pluginEvent('clone', _this); + if (Sortable.eventCanceled) return; + + if (!_this.options.removeCloneOnHide) { + rootEl.insertBefore(cloneEl, dragEl); + } + + _this._hideClone(); + + _dispatchEvent({ + sortable: _this, + name: 'clone' + }); + }); + !fallback && toggleClass(dragEl, options.dragClass, true); // Set proper drop events + + if (fallback) { + ignoreNextClick = true; + _this._loopId = setInterval(_this._emulateDragOver, 50); + } else { + // Undo what was set in _prepareDragStart before drag started + off(document, 'mouseup', _this._onDrop); + off(document, 'touchend', _this._onDrop); + off(document, 'touchcancel', _this._onDrop); + + if (dataTransfer) { + dataTransfer.effectAllowed = 'move'; + options.setData && options.setData.call(_this, dataTransfer, dragEl); + } + + on(document, 'drop', _this); // #1276 fix: + + css(dragEl, 'transform', 'translateZ(0)'); + } + + awaitingDragStarted = true; + _this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback, evt)); + on(document, 'selectstart', _this); + moved = true; + + if (Safari) { + css(document.body, 'user-select', 'none'); + } + }, + // Returns true - if no further action is needed (either inserted or another condition) + _onDragOver: function _onDragOver( + /**Event*/ + evt) { + var el = this.el, + target = evt.target, + dragRect, + targetRect, + revert, + options = this.options, + group = options.group, + activeSortable = Sortable.active, + isOwner = activeGroup === group, + canSort = options.sort, + fromSortable = putSortable || activeSortable, + vertical, + _this = this, + completedFired = false; + + if (_silent) return; + + function dragOverEvent(name, extra) { + pluginEvent(name, _this, _objectSpread2({ + evt: evt, + isOwner: isOwner, + axis: vertical ? 'vertical' : 'horizontal', + revert: revert, + dragRect: dragRect, + targetRect: targetRect, + canSort: canSort, + fromSortable: fromSortable, + target: target, + completed: completed, + onMove: function onMove(target, after) { + return _onMove(rootEl, el, dragEl, dragRect, target, getRect(target), evt, after); + }, + changed: changed + }, extra)); + } // Capture animation state + + + function capture() { + dragOverEvent('dragOverAnimationCapture'); + + _this.captureAnimationState(); + + if (_this !== fromSortable) { + fromSortable.captureAnimationState(); + } + } // Return invocation when dragEl is inserted (or completed) + + + function completed(insertion) { + dragOverEvent('dragOverCompleted', { + insertion: insertion + }); + + if (insertion) { + // Clones must be hidden before folding animation to capture dragRectAbsolute properly + if (isOwner) { + activeSortable._hideClone(); + } else { + activeSortable._showClone(_this); + } + + if (_this !== fromSortable) { + // Set ghost class to new sortable's ghost class + toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : activeSortable.options.ghostClass, false); + toggleClass(dragEl, options.ghostClass, true); + } + + if (putSortable !== _this && _this !== Sortable.active) { + putSortable = _this; + } else if (_this === Sortable.active && putSortable) { + putSortable = null; + } // Animation + + + if (fromSortable === _this) { + _this._ignoreWhileAnimating = target; + } + + _this.animateAll(function () { + dragOverEvent('dragOverAnimationComplete'); + _this._ignoreWhileAnimating = null; + }); + + if (_this !== fromSortable) { + fromSortable.animateAll(); + fromSortable._ignoreWhileAnimating = null; + } + } // Null lastTarget if it is not inside a previously swapped element + + + if (target === dragEl && !dragEl.animated || target === el && !target.animated) { + lastTarget = null; + } // no bubbling and not fallback + + + if (!options.dragoverBubble && !evt.rootEl && target !== document) { + dragEl.parentNode[expando]._isOutsideThisEl(evt.target); // Do not detect for empty insert if already inserted + + + !insertion && nearestEmptyInsertDetectEvent(evt); + } + + !options.dragoverBubble && evt.stopPropagation && evt.stopPropagation(); + return completedFired = true; + } // Call when dragEl has been inserted + + + function changed() { + newIndex = index(dragEl); + newDraggableIndex = index(dragEl, options.draggable); + + _dispatchEvent({ + sortable: _this, + name: 'change', + toEl: el, + newIndex: newIndex, + newDraggableIndex: newDraggableIndex, + originalEvent: evt + }); + } + + if (evt.preventDefault !== void 0) { + evt.cancelable && evt.preventDefault(); + } + + target = closest(target, options.draggable, el, true); + dragOverEvent('dragOver'); + if (Sortable.eventCanceled) return completedFired; + + if (dragEl.contains(evt.target) || target.animated && target.animatingX && target.animatingY || _this._ignoreWhileAnimating === target) { + return completed(false); + } + + ignoreNextClick = false; + + if (activeSortable && !options.disabled && (isOwner ? canSort || (revert = parentEl !== rootEl) // Reverting item into the original list + : putSortable === this || (this.lastPutMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) && group.checkPut(this, activeSortable, dragEl, evt))) { + vertical = this._getDirection(evt, target) === 'vertical'; + dragRect = getRect(dragEl); + dragOverEvent('dragOverValid'); + if (Sortable.eventCanceled) return completedFired; + + if (revert) { + parentEl = rootEl; // actualization + + capture(); + + this._hideClone(); + + dragOverEvent('revert'); + + if (!Sortable.eventCanceled) { + if (nextEl) { + rootEl.insertBefore(dragEl, nextEl); + } else { + rootEl.appendChild(dragEl); + } + } + + return completed(true); + } + + var elLastChild = lastChild(el, options.draggable); + + if (!elLastChild || _ghostIsLast(evt, vertical, this) && !elLastChild.animated) { + // Insert to end of list + // If already at end of list: Do not insert + if (elLastChild === dragEl) { + return completed(false); + } // if there is a last element, it is the target + + + if (elLastChild && el === evt.target) { + target = elLastChild; + } + + if (target) { + targetRect = getRect(target); + } + + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) { + capture(); + el.appendChild(dragEl); + parentEl = el; // actualization + + changed(); + return completed(true); + } + } else if (elLastChild && _ghostIsFirst(evt, vertical, this)) { + // Insert to start of list + var firstChild = getChild(el, 0, options, true); + + if (firstChild === dragEl) { + return completed(false); + } + + target = firstChild; + targetRect = getRect(target); + + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, false) !== false) { + capture(); + el.insertBefore(dragEl, firstChild); + parentEl = el; // actualization + + changed(); + return completed(true); + } + } else if (target.parentNode === el) { + targetRect = getRect(target); + var direction = 0, + targetBeforeFirstSwap, + differentLevel = dragEl.parentNode !== el, + differentRowCol = !_dragElInRowColumn(dragEl.animated && dragEl.toRect || dragRect, target.animated && target.toRect || targetRect, vertical), + side1 = vertical ? 'top' : 'left', + scrolledPastTop = isScrolledPast(target, 'top', 'top') || isScrolledPast(dragEl, 'top', 'top'), + scrollBefore = scrolledPastTop ? scrolledPastTop.scrollTop : void 0; + + if (lastTarget !== target) { + targetBeforeFirstSwap = targetRect[side1]; + pastFirstInvertThresh = false; + isCircumstantialInvert = !differentRowCol && options.invertSwap || differentLevel; + } + + direction = _getSwapDirection(evt, target, targetRect, vertical, differentRowCol ? 1 : options.swapThreshold, options.invertedSwapThreshold == null ? options.swapThreshold : options.invertedSwapThreshold, isCircumstantialInvert, lastTarget === target); + var sibling; + + if (direction !== 0) { + // Check if target is beside dragEl in respective direction (ignoring hidden elements) + var dragIndex = index(dragEl); + + do { + dragIndex -= direction; + sibling = parentEl.children[dragIndex]; + } while (sibling && (css(sibling, 'display') === 'none' || sibling === ghostEl)); + } // If dragEl is already beside target: Do not insert + + + if (direction === 0 || sibling === target) { + return completed(false); + } + + lastTarget = target; + lastDirection = direction; + var nextSibling = target.nextElementSibling, + after = false; + after = direction === 1; + + var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after); + + if (moveVector !== false) { + if (moveVector === 1 || moveVector === -1) { + after = moveVector === 1; + } + + _silent = true; + setTimeout(_unsilent, 30); + capture(); + + if (after && !nextSibling) { + el.appendChild(dragEl); + } else { + target.parentNode.insertBefore(dragEl, after ? nextSibling : target); + } // Undo chrome's scroll adjustment (has no effect on other browsers) + + + if (scrolledPastTop) { + scrollBy(scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop); + } + + parentEl = dragEl.parentNode; // actualization + // must be done before animation + + if (targetBeforeFirstSwap !== undefined && !isCircumstantialInvert) { + targetMoveDistance = Math.abs(targetBeforeFirstSwap - getRect(target)[side1]); + } + + changed(); + return completed(true); + } + } + + if (el.contains(dragEl)) { + return completed(false); + } + } + + return false; + }, + _ignoreWhileAnimating: null, + _offMoveEvents: function _offMoveEvents() { + off(document, 'mousemove', this._onTouchMove); + off(document, 'touchmove', this._onTouchMove); + off(document, 'pointermove', this._onTouchMove); + off(document, 'dragover', nearestEmptyInsertDetectEvent); + off(document, 'mousemove', nearestEmptyInsertDetectEvent); + off(document, 'touchmove', nearestEmptyInsertDetectEvent); + }, + _offUpEvents: function _offUpEvents() { + var ownerDocument = this.el.ownerDocument; + off(ownerDocument, 'mouseup', this._onDrop); + off(ownerDocument, 'touchend', this._onDrop); + off(ownerDocument, 'pointerup', this._onDrop); + off(ownerDocument, 'touchcancel', this._onDrop); + off(document, 'selectstart', this); + }, + _onDrop: function _onDrop( + /**Event*/ + evt) { + var el = this.el, + options = this.options; // Get the index of the dragged element within its parent + + newIndex = index(dragEl); + newDraggableIndex = index(dragEl, options.draggable); + pluginEvent('drop', this, { + evt: evt + }); + parentEl = dragEl && dragEl.parentNode; // Get again after plugin event + + newIndex = index(dragEl); + newDraggableIndex = index(dragEl, options.draggable); + + if (Sortable.eventCanceled) { + this._nulling(); + + return; + } + + awaitingDragStarted = false; + isCircumstantialInvert = false; + pastFirstInvertThresh = false; + clearInterval(this._loopId); + clearTimeout(this._dragStartTimer); + + _cancelNextTick(this.cloneId); + + _cancelNextTick(this._dragStartId); // Unbind events + + + if (this.nativeDraggable) { + off(document, 'drop', this); + off(el, 'dragstart', this._onDragStart); + } + + this._offMoveEvents(); + + this._offUpEvents(); + + if (Safari) { + css(document.body, 'user-select', ''); + } + + css(dragEl, 'transform', ''); + + if (evt) { + if (moved) { + evt.cancelable && evt.preventDefault(); + !options.dropBubble && evt.stopPropagation(); + } + + ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl); + + if (rootEl === parentEl || putSortable && putSortable.lastPutMode !== 'clone') { + // Remove clone(s) + cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl); + } + + if (dragEl) { + if (this.nativeDraggable) { + off(dragEl, 'dragend', this); + } + + _disableDraggable(dragEl); + + dragEl.style['will-change'] = ''; // Remove classes + // ghostClass is added in dragStarted + + if (moved && !awaitingDragStarted) { + toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : this.options.ghostClass, false); + } + + toggleClass(dragEl, this.options.chosenClass, false); // Drag stop event + + _dispatchEvent({ + sortable: this, + name: 'unchoose', + toEl: parentEl, + newIndex: null, + newDraggableIndex: null, + originalEvent: evt + }); + + if (rootEl !== parentEl) { + if (newIndex >= 0) { + // Add event + _dispatchEvent({ + rootEl: parentEl, + name: 'add', + toEl: parentEl, + fromEl: rootEl, + originalEvent: evt + }); // Remove event + + + _dispatchEvent({ + sortable: this, + name: 'remove', + toEl: parentEl, + originalEvent: evt + }); // drag from one list and drop into another + + + _dispatchEvent({ + rootEl: parentEl, + name: 'sort', + toEl: parentEl, + fromEl: rootEl, + originalEvent: evt + }); + + _dispatchEvent({ + sortable: this, + name: 'sort', + toEl: parentEl, + originalEvent: evt + }); + } + + putSortable && putSortable.save(); + } else { + if (newIndex !== oldIndex) { + if (newIndex >= 0) { + // drag & drop within the same list + _dispatchEvent({ + sortable: this, + name: 'update', + toEl: parentEl, + originalEvent: evt + }); + + _dispatchEvent({ + sortable: this, + name: 'sort', + toEl: parentEl, + originalEvent: evt + }); + } + } + } + + if (Sortable.active) { + /* jshint eqnull:true */ + if (newIndex == null || newIndex === -1) { + newIndex = oldIndex; + newDraggableIndex = oldDraggableIndex; + } + + _dispatchEvent({ + sortable: this, + name: 'end', + toEl: parentEl, + originalEvent: evt + }); // Save sorting + + + this.save(); + } + } + } + + this._nulling(); + }, + _nulling: function _nulling() { + pluginEvent('nulling', this); + rootEl = dragEl = parentEl = ghostEl = nextEl = cloneEl = lastDownEl = cloneHidden = tapEvt = touchEvt = moved = newIndex = newDraggableIndex = oldIndex = oldDraggableIndex = lastTarget = lastDirection = putSortable = activeGroup = Sortable.dragged = Sortable.ghost = Sortable.clone = Sortable.active = null; + savedInputChecked.forEach(function (el) { + el.checked = true; + }); + savedInputChecked.length = lastDx = lastDy = 0; + }, + handleEvent: function handleEvent( + /**Event*/ + evt) { + switch (evt.type) { + case 'drop': + case 'dragend': + this._onDrop(evt); + + break; + + case 'dragenter': + case 'dragover': + if (dragEl) { + this._onDragOver(evt); + + _globalDragOver(evt); + } + + break; + + case 'selectstart': + evt.preventDefault(); + break; + } + }, + + /** + * Serializes the item into an array of string. + * @returns {String[]} + */ + toArray: function toArray() { + var order = [], + el, + children = this.el.children, + i = 0, + n = children.length, + options = this.options; + + for (; i < n; i++) { + el = children[i]; + + if (closest(el, options.draggable, this.el, false)) { + order.push(el.getAttribute(options.dataIdAttr) || _generateId(el)); + } + } + + return order; + }, + + /** + * Sorts the elements according to the array. + * @param {String[]} order order of the items + */ + sort: function sort(order, useAnimation) { + var items = {}, + rootEl = this.el; + this.toArray().forEach(function (id, i) { + var el = rootEl.children[i]; + + if (closest(el, this.options.draggable, rootEl, false)) { + items[id] = el; + } + }, this); + useAnimation && this.captureAnimationState(); + order.forEach(function (id) { + if (items[id]) { + rootEl.removeChild(items[id]); + rootEl.appendChild(items[id]); + } + }); + useAnimation && this.animateAll(); + }, + + /** + * Save the current sorting + */ + save: function save() { + var store = this.options.store; + store && store.set && store.set(this); + }, + + /** + * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. + * @param {HTMLElement} el + * @param {String} [selector] default: `options.draggable` + * @returns {HTMLElement|null} + */ + closest: function closest$1(el, selector) { + return closest(el, selector || this.options.draggable, this.el, false); + }, + + /** + * Set/get option + * @param {string} name + * @param {*} [value] + * @returns {*} + */ + option: function option(name, value) { + var options = this.options; + + if (value === void 0) { + return options[name]; + } else { + var modifiedValue = PluginManager.modifyOption(this, name, value); + + if (typeof modifiedValue !== 'undefined') { + options[name] = modifiedValue; + } else { + options[name] = value; + } + + if (name === 'group') { + _prepareGroup(options); + } + } + }, + + /** + * Destroy + */ + destroy: function destroy() { + pluginEvent('destroy', this); + var el = this.el; + el[expando] = null; + off(el, 'mousedown', this._onTapStart); + off(el, 'touchstart', this._onTapStart); + off(el, 'pointerdown', this._onTapStart); + + if (this.nativeDraggable) { + off(el, 'dragover', this); + off(el, 'dragenter', this); + } // Remove draggable attributes + + + Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { + el.removeAttribute('draggable'); + }); + + this._onDrop(); + + this._disableDelayedDragEvents(); + + sortables.splice(sortables.indexOf(this.el), 1); + this.el = el = null; + }, + _hideClone: function _hideClone() { + if (!cloneHidden) { + pluginEvent('hideClone', this); + if (Sortable.eventCanceled) return; + css(cloneEl, 'display', 'none'); + + if (this.options.removeCloneOnHide && cloneEl.parentNode) { + cloneEl.parentNode.removeChild(cloneEl); + } + + cloneHidden = true; + } + }, + _showClone: function _showClone(putSortable) { + if (putSortable.lastPutMode !== 'clone') { + this._hideClone(); + + return; + } + + if (cloneHidden) { + pluginEvent('showClone', this); + if (Sortable.eventCanceled) return; // show clone at dragEl or original position + + if (dragEl.parentNode == rootEl && !this.options.group.revertClone) { + rootEl.insertBefore(cloneEl, dragEl); + } else if (nextEl) { + rootEl.insertBefore(cloneEl, nextEl); + } else { + rootEl.appendChild(cloneEl); + } + + if (this.options.group.revertClone) { + this.animate(dragEl, cloneEl); + } + + css(cloneEl, 'display', ''); + cloneHidden = false; + } + } +}; + +function _globalDragOver( +/**Event*/ +evt) { + if (evt.dataTransfer) { + evt.dataTransfer.dropEffect = 'move'; + } + + evt.cancelable && evt.preventDefault(); +} + +function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvent, willInsertAfter) { + var evt, + sortable = fromEl[expando], + onMoveFn = sortable.options.onMove, + retVal; // Support for new CustomEvent feature + + if (window.CustomEvent && !IE11OrLess && !Edge) { + evt = new CustomEvent('move', { + bubbles: true, + cancelable: true + }); + } else { + evt = document.createEvent('Event'); + evt.initEvent('move', true, true); + } + + evt.to = toEl; + evt.from = fromEl; + evt.dragged = dragEl; + evt.draggedRect = dragRect; + evt.related = targetEl || toEl; + evt.relatedRect = targetRect || getRect(toEl); + evt.willInsertAfter = willInsertAfter; + evt.originalEvent = originalEvent; + fromEl.dispatchEvent(evt); + + if (onMoveFn) { + retVal = onMoveFn.call(sortable, evt, originalEvent); + } + + return retVal; +} + +function _disableDraggable(el) { + el.draggable = false; +} + +function _unsilent() { + _silent = false; +} + +function _ghostIsFirst(evt, vertical, sortable) { + var rect = getRect(getChild(sortable.el, 0, sortable.options, true)); + var spacer = 10; + return vertical ? evt.clientX < rect.left - spacer || evt.clientY < rect.top && evt.clientX < rect.right : evt.clientY < rect.top - spacer || evt.clientY < rect.bottom && evt.clientX < rect.left; +} + +function _ghostIsLast(evt, vertical, sortable) { + var rect = getRect(lastChild(sortable.el, sortable.options.draggable)); + var spacer = 10; + return vertical ? evt.clientX > rect.right + spacer || evt.clientX <= rect.right && evt.clientY > rect.bottom && evt.clientX >= rect.left : evt.clientX > rect.right && evt.clientY > rect.top || evt.clientX <= rect.right && evt.clientY > rect.bottom + spacer; +} + +function _getSwapDirection(evt, target, targetRect, vertical, swapThreshold, invertedSwapThreshold, invertSwap, isLastTarget) { + var mouseOnAxis = vertical ? evt.clientY : evt.clientX, + targetLength = vertical ? targetRect.height : targetRect.width, + targetS1 = vertical ? targetRect.top : targetRect.left, + targetS2 = vertical ? targetRect.bottom : targetRect.right, + invert = false; + + if (!invertSwap) { + // Never invert or create dragEl shadow when target movemenet causes mouse to move past the end of regular swapThreshold + if (isLastTarget && targetMoveDistance < targetLength * swapThreshold) { + // multiplied only by swapThreshold because mouse will already be inside target by (1 - threshold) * targetLength / 2 + // check if past first invert threshold on side opposite of lastDirection + if (!pastFirstInvertThresh && (lastDirection === 1 ? mouseOnAxis > targetS1 + targetLength * invertedSwapThreshold / 2 : mouseOnAxis < targetS2 - targetLength * invertedSwapThreshold / 2)) { + // past first invert threshold, do not restrict inverted threshold to dragEl shadow + pastFirstInvertThresh = true; + } + + if (!pastFirstInvertThresh) { + // dragEl shadow (target move distance shadow) + if (lastDirection === 1 ? mouseOnAxis < targetS1 + targetMoveDistance // over dragEl shadow + : mouseOnAxis > targetS2 - targetMoveDistance) { + return -lastDirection; + } + } else { + invert = true; + } + } else { + // Regular + if (mouseOnAxis > targetS1 + targetLength * (1 - swapThreshold) / 2 && mouseOnAxis < targetS2 - targetLength * (1 - swapThreshold) / 2) { + return _getInsertDirection(target); + } + } + } + + invert = invert || invertSwap; + + if (invert) { + // Invert of regular + if (mouseOnAxis < targetS1 + targetLength * invertedSwapThreshold / 2 || mouseOnAxis > targetS2 - targetLength * invertedSwapThreshold / 2) { + return mouseOnAxis > targetS1 + targetLength / 2 ? 1 : -1; + } + } + + return 0; +} +/** + * Gets the direction dragEl must be swapped relative to target in order to make it + * seem that dragEl has been "inserted" into that element's position + * @param {HTMLElement} target The target whose position dragEl is being inserted at + * @return {Number} Direction dragEl must be swapped + */ + + +function _getInsertDirection(target) { + if (index(dragEl) < index(target)) { + return 1; + } else { + return -1; + } +} +/** + * Generate id + * @param {HTMLElement} el + * @returns {String} + * @private + */ + + +function _generateId(el) { + var str = el.tagName + el.className + el.src + el.href + el.textContent, + i = str.length, + sum = 0; + + while (i--) { + sum += str.charCodeAt(i); + } + + return sum.toString(36); +} + +function _saveInputCheckedState(root) { + savedInputChecked.length = 0; + var inputs = root.getElementsByTagName('input'); + var idx = inputs.length; + + while (idx--) { + var el = inputs[idx]; + el.checked && savedInputChecked.push(el); + } +} + +function _nextTick(fn) { + return setTimeout(fn, 0); +} + +function _cancelNextTick(id) { + return clearTimeout(id); +} // Fixed #973: + + +if (documentExists) { + on(document, 'touchmove', function (evt) { + if ((Sortable.active || awaitingDragStarted) && evt.cancelable) { + evt.preventDefault(); + } + }); +} // Export utils + + +Sortable.utils = { + on: on, + off: off, + css: css, + find: find, + is: function is(el, selector) { + return !!closest(el, selector, el, false); + }, + extend: extend, + throttle: throttle, + closest: closest, + toggleClass: toggleClass, + clone: clone, + index: index, + nextTick: _nextTick, + cancelNextTick: _cancelNextTick, + detectDirection: _detectDirection, + getChild: getChild +}; +/** + * Get the Sortable instance of an element + * @param {HTMLElement} element The element + * @return {Sortable|undefined} The instance of Sortable + */ + +Sortable.get = function (element) { + return element[expando]; +}; +/** + * Mount a plugin to Sortable + * @param {...SortablePlugin|SortablePlugin[]} plugins Plugins being mounted + */ + + +Sortable.mount = function () { + for (var _len = arguments.length, plugins = new Array(_len), _key = 0; _key < _len; _key++) { + plugins[_key] = arguments[_key]; + } + + if (plugins[0].constructor === Array) plugins = plugins[0]; + plugins.forEach(function (plugin) { + if (!plugin.prototype || !plugin.prototype.constructor) { + throw "Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(plugin)); + } + + if (plugin.utils) Sortable.utils = _objectSpread2(_objectSpread2({}, Sortable.utils), plugin.utils); + PluginManager.mount(plugin); + }); +}; +/** + * Create sortable instance + * @param {HTMLElement} el + * @param {Object} [options] + */ + + +Sortable.create = function (el, options) { + return new Sortable(el, options); +}; // Export + + +Sortable.version = version; + +var autoScrolls = [], + scrollEl, + scrollRootEl, + scrolling = false, + lastAutoScrollX, + lastAutoScrollY, + touchEvt$1, + pointerElemChangedInterval; + +function AutoScrollPlugin() { + function AutoScroll() { + this.defaults = { + scroll: true, + forceAutoScrollFallback: false, + scrollSensitivity: 30, + scrollSpeed: 10, + bubbleScroll: true + }; // Bind all private methods + + for (var fn in this) { + if (fn.charAt(0) === '_' && typeof this[fn] === 'function') { + this[fn] = this[fn].bind(this); + } + } + } + + AutoScroll.prototype = { + dragStarted: function dragStarted(_ref) { + var originalEvent = _ref.originalEvent; + + if (this.sortable.nativeDraggable) { + on(document, 'dragover', this._handleAutoScroll); + } else { + if (this.options.supportPointer) { + on(document, 'pointermove', this._handleFallbackAutoScroll); + } else if (originalEvent.touches) { + on(document, 'touchmove', this._handleFallbackAutoScroll); + } else { + on(document, 'mousemove', this._handleFallbackAutoScroll); + } + } + }, + dragOverCompleted: function dragOverCompleted(_ref2) { + var originalEvent = _ref2.originalEvent; + + // For when bubbling is canceled and using fallback (fallback 'touchmove' always reached) + if (!this.options.dragOverBubble && !originalEvent.rootEl) { + this._handleAutoScroll(originalEvent); + } + }, + drop: function drop() { + if (this.sortable.nativeDraggable) { + off(document, 'dragover', this._handleAutoScroll); + } else { + off(document, 'pointermove', this._handleFallbackAutoScroll); + off(document, 'touchmove', this._handleFallbackAutoScroll); + off(document, 'mousemove', this._handleFallbackAutoScroll); + } + + clearPointerElemChangedInterval(); + clearAutoScrolls(); + cancelThrottle(); + }, + nulling: function nulling() { + touchEvt$1 = scrollRootEl = scrollEl = scrolling = pointerElemChangedInterval = lastAutoScrollX = lastAutoScrollY = null; + autoScrolls.length = 0; + }, + _handleFallbackAutoScroll: function _handleFallbackAutoScroll(evt) { + this._handleAutoScroll(evt, true); + }, + _handleAutoScroll: function _handleAutoScroll(evt, fallback) { + var _this = this; + + var x = (evt.touches ? evt.touches[0] : evt).clientX, + y = (evt.touches ? evt.touches[0] : evt).clientY, + elem = document.elementFromPoint(x, y); + touchEvt$1 = evt; // IE does not seem to have native autoscroll, + // Edge's autoscroll seems too conditional, + // MACOS Safari does not have autoscroll, + // Firefox and Chrome are good + + if (fallback || this.options.forceAutoScrollFallback || Edge || IE11OrLess || Safari) { + autoScroll(evt, this.options, elem, fallback); // Listener for pointer element change + + var ogElemScroller = getParentAutoScrollElement(elem, true); + + if (scrolling && (!pointerElemChangedInterval || x !== lastAutoScrollX || y !== lastAutoScrollY)) { + pointerElemChangedInterval && clearPointerElemChangedInterval(); // Detect for pointer elem change, emulating native DnD behaviour + + pointerElemChangedInterval = setInterval(function () { + var newElem = getParentAutoScrollElement(document.elementFromPoint(x, y), true); + + if (newElem !== ogElemScroller) { + ogElemScroller = newElem; + clearAutoScrolls(); + } + + autoScroll(evt, _this.options, newElem, fallback); + }, 10); + lastAutoScrollX = x; + lastAutoScrollY = y; + } + } else { + // if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll + if (!this.options.bubbleScroll || getParentAutoScrollElement(elem, true) === getWindowScrollingElement()) { + clearAutoScrolls(); + return; + } + + autoScroll(evt, this.options, getParentAutoScrollElement(elem, false), false); + } + } + }; + return _extends(AutoScroll, { + pluginName: 'scroll', + initializeByDefault: true + }); +} + +function clearAutoScrolls() { + autoScrolls.forEach(function (autoScroll) { + clearInterval(autoScroll.pid); + }); + autoScrolls = []; +} + +function clearPointerElemChangedInterval() { + clearInterval(pointerElemChangedInterval); +} + +var autoScroll = throttle(function (evt, options, rootEl, isFallback) { + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 + if (!options.scroll) return; + var x = (evt.touches ? evt.touches[0] : evt).clientX, + y = (evt.touches ? evt.touches[0] : evt).clientY, + sens = options.scrollSensitivity, + speed = options.scrollSpeed, + winScroller = getWindowScrollingElement(); + var scrollThisInstance = false, + scrollCustomFn; // New scroll root, set scrollEl + + if (scrollRootEl !== rootEl) { + scrollRootEl = rootEl; + clearAutoScrolls(); + scrollEl = options.scroll; + scrollCustomFn = options.scrollFn; + + if (scrollEl === true) { + scrollEl = getParentAutoScrollElement(rootEl, true); + } + } + + var layersOut = 0; + var currentParent = scrollEl; + + do { + var el = currentParent, + rect = getRect(el), + top = rect.top, + bottom = rect.bottom, + left = rect.left, + right = rect.right, + width = rect.width, + height = rect.height, + canScrollX = void 0, + canScrollY = void 0, + scrollWidth = el.scrollWidth, + scrollHeight = el.scrollHeight, + elCSS = css(el), + scrollPosX = el.scrollLeft, + scrollPosY = el.scrollTop; + + if (el === winScroller) { + canScrollX = width < scrollWidth && (elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll' || elCSS.overflowX === 'visible'); + canScrollY = height < scrollHeight && (elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll' || elCSS.overflowY === 'visible'); + } else { + canScrollX = width < scrollWidth && (elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll'); + canScrollY = height < scrollHeight && (elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll'); + } + + var vx = canScrollX && (Math.abs(right - x) <= sens && scrollPosX + width < scrollWidth) - (Math.abs(left - x) <= sens && !!scrollPosX); + var vy = canScrollY && (Math.abs(bottom - y) <= sens && scrollPosY + height < scrollHeight) - (Math.abs(top - y) <= sens && !!scrollPosY); + + if (!autoScrolls[layersOut]) { + for (var i = 0; i <= layersOut; i++) { + if (!autoScrolls[i]) { + autoScrolls[i] = {}; + } + } + } + + if (autoScrolls[layersOut].vx != vx || autoScrolls[layersOut].vy != vy || autoScrolls[layersOut].el !== el) { + autoScrolls[layersOut].el = el; + autoScrolls[layersOut].vx = vx; + autoScrolls[layersOut].vy = vy; + clearInterval(autoScrolls[layersOut].pid); + + if (vx != 0 || vy != 0) { + scrollThisInstance = true; + /* jshint loopfunc:true */ + + autoScrolls[layersOut].pid = setInterval(function () { + // emulate drag over during autoscroll (fallback), emulating native DnD behaviour + if (isFallback && this.layer === 0) { + Sortable.active._onTouchMove(touchEvt$1); // To move ghost if it is positioned absolutely + + } + + var scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0; + var scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0; + + if (typeof scrollCustomFn === 'function') { + if (scrollCustomFn.call(Sortable.dragged.parentNode[expando], scrollOffsetX, scrollOffsetY, evt, touchEvt$1, autoScrolls[this.layer].el) !== 'continue') { + return; + } + } + + scrollBy(autoScrolls[this.layer].el, scrollOffsetX, scrollOffsetY); + }.bind({ + layer: layersOut + }), 24); + } + } + + layersOut++; + } while (options.bubbleScroll && currentParent !== winScroller && (currentParent = getParentAutoScrollElement(currentParent, false))); + + scrolling = scrollThisInstance; // in case another function catches scrolling as false in between when it is not +}, 30); + +var drop = function drop(_ref) { + var originalEvent = _ref.originalEvent, + putSortable = _ref.putSortable, + dragEl = _ref.dragEl, + activeSortable = _ref.activeSortable, + dispatchSortableEvent = _ref.dispatchSortableEvent, + hideGhostForTarget = _ref.hideGhostForTarget, + unhideGhostForTarget = _ref.unhideGhostForTarget; + if (!originalEvent) return; + var toSortable = putSortable || activeSortable; + hideGhostForTarget(); + var touch = originalEvent.changedTouches && originalEvent.changedTouches.length ? originalEvent.changedTouches[0] : originalEvent; + var target = document.elementFromPoint(touch.clientX, touch.clientY); + unhideGhostForTarget(); + + if (toSortable && !toSortable.el.contains(target)) { + dispatchSortableEvent('spill'); + this.onSpill({ + dragEl: dragEl, + putSortable: putSortable + }); + } +}; + +function Revert() {} + +Revert.prototype = { + startIndex: null, + dragStart: function dragStart(_ref2) { + var oldDraggableIndex = _ref2.oldDraggableIndex; + this.startIndex = oldDraggableIndex; + }, + onSpill: function onSpill(_ref3) { + var dragEl = _ref3.dragEl, + putSortable = _ref3.putSortable; + this.sortable.captureAnimationState(); + + if (putSortable) { + putSortable.captureAnimationState(); + } + + var nextSibling = getChild(this.sortable.el, this.startIndex, this.options); + + if (nextSibling) { + this.sortable.el.insertBefore(dragEl, nextSibling); + } else { + this.sortable.el.appendChild(dragEl); + } + + this.sortable.animateAll(); + + if (putSortable) { + putSortable.animateAll(); + } + }, + drop: drop +}; + +_extends(Revert, { + pluginName: 'revertOnSpill' +}); + +function Remove() {} + +Remove.prototype = { + onSpill: function onSpill(_ref4) { + var dragEl = _ref4.dragEl, + putSortable = _ref4.putSortable; + var parentSortable = putSortable || this.sortable; + parentSortable.captureAnimationState(); + dragEl.parentNode && dragEl.parentNode.removeChild(dragEl); + parentSortable.animateAll(); + }, + drop: drop +}; + +_extends(Remove, { + pluginName: 'removeOnSpill' +}); + +Sortable.mount(new AutoScrollPlugin()); +Sortable.mount(Remove, Revert); + +var RegexEvent; +(function (RegexEvent) { + (function (ParsonsInputAction) { + ParsonsInputAction["ADD"] = "add"; + ParsonsInputAction["MOVE"] = "move"; + ParsonsInputAction["REMOVE"] = "remove"; + })(RegexEvent.ParsonsInputAction || (RegexEvent.ParsonsInputAction = {})); + (function (RegexCompilationStatus) { + RegexCompilationStatus["ERROR"] = "error"; + RegexCompilationStatus["SUCCESS"] = "success"; + })(RegexEvent.RegexCompilationStatus || (RegexEvent.RegexCompilationStatus = {})); + (function (MatchTriggerType) { + MatchTriggerType["MANUAL"] = "manual"; + MatchTriggerType["AUTO"] = "auto"; + })(RegexEvent.MatchTriggerType || (RegexEvent.MatchTriggerType = {})); + (function (PageStatus) { + PageStatus["FOCUS"] = "focus"; + PageStatus["VISIBILITY"] = "visibility"; + })(RegexEvent.PageStatus || (RegexEvent.PageStatus = {})); +})(RegexEvent || (RegexEvent = {})); + +class ParsonsInput { + // The input element + el; + // TODO(refactor): make expandable blocks more easy to use + expandableBlocks; + expandableBlockTooltips; + _dropArea; + _dragArea; + _dropSortable; + _dragSortable; + parentElement; + _prevPosition; + constructor() { + this.el = document.createElement('div'); + this.el.id = 'parsons-input'; + this.el.append('Drag or click to select from the symbols below to form your regex'); + this._dragArea = document.createElement('div'); + this.el.appendChild(this._dragArea); + this._dragArea.classList.add('drag-area'); + this._dragArea.style.height = '20px'; + this._dragArea.style.backgroundColor = '#fffcc4'; + this.el.append('Regex:'); + this._dropArea = document.createElement('div'); + this.el.appendChild(this._dropArea); + this._dropArea.classList.add('drop-area'); + this._dropArea.style.height = '20px'; + // this._dropArea.style.backgroundColor = '#bcebd7'; + this._prevPosition = -1; + this.expandableBlocks = []; + this.expandableBlockTooltips = null; + this._dragSortable = new Sortable(this._dragArea, { + group: { + name: 'shared', + pull: 'clone', + put: false + }, + sort: false, + direction: 'horizontal', + animation: 150 + }); + this._dropSortable = new Sortable(this._dropArea, { + group: 'shared', + direction: 'horizontal', + animation: 150 + }); + this._initSortable(); + this.parentElement = null; + } + getText = () => { + let ret = ''; + if (this._dropArea.hasChildNodes()) { + let el = this._dropArea.firstChild; + // if (el.innerHTML == ' ') { + // console.log('here') + // ret += ' '; + // } else { + // console.log(el.innerText) + // ret += el.innerText; + // } + // dealing with   + ret += el.innerText.replace(/\xA0/g, ' '); + while (el.nextSibling) { + el = el.nextSibling; + ret += el.innerText.replace(/\xA0/g, ' '); + } + return ret; + } + else { + return ret; + } + }; + setSourceBlocks = (data, tooltips) => { + // reset + // this._dragSortable.destroy(); + // this._dropSortable.destroy(); + // clearing previous settings + this._dragArea.innerHTML = ''; + this._dropArea.innerHTML = ''; + // adding expandable blocks + for (let i = 0; i < this.expandableBlocks.length; ++i) { + const newBlock = document.createElement('div'); + this._dragArea.appendChild(newBlock); + newBlock.innerText = this.expandableBlocks[i]; + newBlock.style.display = 'inline-block'; + newBlock.classList.add('parsons-block'); + newBlock.classList.add('expandable-block'); + if (this.expandableBlockTooltips && this.expandableBlockTooltips.length > i) { + const tooltip = document.createElement('span'); + newBlock.appendChild(tooltip); + tooltip.innerText = this.expandableBlockTooltips[i]; + tooltip.classList.add('tooltip'); + newBlock.onmouseover = () => { + if (newBlock.parentNode.classList.contains('drag-area')) { + const parsonsTooltipEvent = { + 'event-type': 'parsons-tooltip', + block: this.expandableBlocks[i], + tooltip: tooltip.innerText, + start: true + }; + this.parentElement?.logEvent(parsonsTooltipEvent); + } + }; + newBlock.onmouseout = () => { + if (newBlock.parentNode.classList.contains('drag-area')) { + const parsonsTooltipEvent = { + 'event-type': 'parsons-tooltip', + block: this.expandableBlocks[i], + tooltip: tooltip.innerText, + start: false + }; + this.parentElement?.logEvent(parsonsTooltipEvent); + } + }; + } + newBlock.onclick = () => { + // console.log('expandable block onclick'); + if (newBlock.parentNode.classList.contains('drag-area')) { + const text = this.expandableBlocks[i]; + let firstBlock = null; + for (let i = 0; i < text.length; ++i) { + const newBlock = document.createElement('div'); + this._dropArea.appendChild(newBlock); + newBlock.innerText = text.charAt(i); + newBlock.style.display = 'inline-block'; + newBlock.classList.add('parsons-block'); + newBlock.onclick = () => { + // clicking the new block generated by clicking an extendable block to remove that block + // console.log('expandable new block onclick') + const endPosition = this._getBlockPosition(newBlock); + newBlock.parentNode?.removeChild(newBlock); + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.REMOVE, + position: [endPosition, -1], + answer: this._getTextArray() + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + }; + if (firstBlock == null) { + firstBlock = newBlock; + } + } + if (this.parentElement && firstBlock) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.ADD, + position: [-1, this._getBlockPosition(firstBlock)], + answer: this._getTextArray(), + 'add-block-cnt': text.length, + 'is-expandable': true, + 'add-by-click': true + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + } + }; + } + // adding normal blocks + for (let i = 0; i < data.length; ++i) { + const newBlock = document.createElement('div'); + this._dragArea.appendChild(newBlock); + if (data[i] === ' ') { + // console.log('here'); + newBlock.innerHTML = ' '; + } + else { + // console.log(data[i]); + newBlock.innerText = data[i]; + } + newBlock.style.display = 'inline-block'; + newBlock.classList.add('parsons-block'); + if (tooltips && tooltips.length > i) { + const tooltip = document.createElement('span'); + newBlock.appendChild(tooltip); + tooltip.innerText = tooltips[i]; + tooltip.classList.add('tooltip'); + newBlock.onmouseover = () => { + if (newBlock.parentNode.classList.contains('drag-area')) { + const parsonsTooltipEvent = { + 'event-type': 'parsons-tooltip', + block: data[i], + tooltip: tooltips[i], + start: true + }; + this.parentElement?.logEvent(parsonsTooltipEvent); + } + }; + newBlock.onmouseout = () => { + if (newBlock.parentNode.classList.contains('drag-area')) { + const parsonsTooltipEvent = { + 'event-type': 'parsons-tooltip', + block: data[i], + tooltip: tooltips[i], + start: false + }; + this.parentElement?.logEvent(parsonsTooltipEvent); + } + }; + } + newBlock.onclick = () => { + // console.log('normal block onclick'); + if (newBlock.parentNode.classList.contains('drag-area')) { + let newBlockCopy = newBlock.cloneNode(true); + this._dropArea.appendChild(newBlockCopy); + newBlockCopy.onclick = () => { + // console.log('normal new block onclick') + // clicking on the new block generated by a normal block to remove the new block + const endPosition = this._getBlockPosition(newBlockCopy); + newBlockCopy.parentNode?.removeChild(newBlockCopy); + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.REMOVE, + position: [endPosition, -1], + answer: this._getTextArray() + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + }; + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.ADD, + position: [-1, this._getBlockPosition(newBlockCopy)], + answer: this._getTextArray(), + 'add-block-cnt': 1, + 'is-expandable': false, + 'add-by-click': true + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + } + if (newBlock.parentNode.classList.contains('drop-area')) { + // clicking on the normal block dragged down to remove the normal block + const endPosition = this._getBlockPosition(newBlock); + newBlock.parentNode?.removeChild(newBlock); + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.REMOVE, + position: [endPosition, -1], + answer: this._getTextArray(), + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + } + }; + } + this._initSortable(); + }; + _initSortable = () => { + this._dragSortable.destroy(); + this._dropSortable.destroy(); + this._dragSortable = new Sortable(this._dragArea, { + group: { + name: 'shared', + pull: 'clone', + put: false + }, + sort: false, + direction: 'horizontal', + animation: 150, + draggable: '.parsons-block', + onClone: (event) => { + const newBlock = event.clone; + if (event.item.classList.contains('expandable-block')) { + newBlock.onclick = () => { + // console.log('expandable block onclick'); + if (newBlock.parentNode.classList.contains('drag-area')) { + const text = (newBlock.firstChild?.textContent) || ''; + let firstBlock = null; + for (let i = 0; i < text.length; ++i) { + const newBlock = document.createElement('div'); + this._dropArea.appendChild(newBlock); + newBlock.innerText = text.charAt(i); + newBlock.style.display = 'inline-block'; + newBlock.classList.add('parsons-block'); + newBlock.onclick = () => { + // console.log('expandable new block onclick') + // clicking on a new block generated by an expandable block to remove the new block + const endPosition = this._getBlockPosition(newBlock); + newBlock.parentNode?.removeChild(newBlock); + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.REMOVE, + position: [endPosition, -1], + answer: this._getTextArray(), + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + }; + if (firstBlock == null) { + firstBlock = newBlock; + } + } + if (this.parentElement && firstBlock) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.ADD, + position: [-1, this._getBlockPosition(firstBlock)], + answer: this._getTextArray(), + 'add-block-cnt': text.length, + 'is-expandable': true, + 'add-by-click': true + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + } + }; + } + else { + newBlock.onclick = () => { + // console.log('normal block onclick'); + if (newBlock.parentNode.classList.contains('drag-area')) { + let newBlockCopy = newBlock.cloneNode(true); + this._dropArea.appendChild(newBlockCopy); + newBlockCopy.onclick = () => { + // console.log('normal new block onclick') + // clicking on a new block added by clicking to remove the new block + const endPosition = this._getBlockPosition(newBlockCopy); + newBlockCopy.parentNode?.removeChild(newBlockCopy); + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.REMOVE, + position: [endPosition, -1], + answer: this._getTextArray(), + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + }; + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.ADD, + position: [-1, this._getBlockPosition(newBlockCopy)], + answer: this._getTextArray(), + 'add-block-cnt': 1, + 'is-expandable': false, + 'add-by-click': true + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + } + if (newBlock.parentNode.classList.contains('drop-area')) { + // console.log('359') + // (not sure) clicking on a normal block added by dragging to remove the new block + const endPosition = this._getBlockPosition(newBlock); + newBlock.parentNode?.removeChild(newBlock); + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.REMOVE, + position: [endPosition, -1], + answer: this._getTextArray(), + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + } + }; + } + } + }); + this._dropSortable = new Sortable(this._dropArea, { + group: 'shared', + direction: 'horizontal', + animation: 150, + draggable: '.parsons-block', + onAdd: (event) => { + // getting the position + const isExpandable = event.item.classList.contains('expandable-block'); + // console.log(isExpandable); + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.ADD, + position: [-1, this._getBlockPosition(event.item)], + answer: this._getTextArray(), + 'add-block-cnt': isExpandable ? event.item.innerText.length : 1, + 'is-expandable': isExpandable, + 'add-by-click': false + }; + } + if (isExpandable) { + const parentNode = event.item.parentNode; + const text = event.item.innerText; + this._getBlockPosition(event.item); + const nextSibling = event.item.nextSibling; + parentNode?.removeChild(event.item); + for (let i = 0; i < text.length; ++i) { + const newBlock = document.createElement('div'); + if (!nextSibling) { + this._dropArea.appendChild(newBlock); + } + else { + parentNode?.insertBefore(newBlock, nextSibling); + } + newBlock.innerText = text.charAt(i); + newBlock.style.display = 'inline-block'; + newBlock.classList.add('parsons-block'); + newBlock.onclick = () => { + // console.log('expandable new block onclick') + // clicking on a block added by clicking an expandable block to remove the new block + const endPosition = this._getBlockPosition(newBlock); + newBlock.parentNode?.removeChild(newBlock); + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: RegexEvent.ParsonsInputAction.REMOVE, + position: [endPosition, -1], + answer: this._getTextArray(), + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + }; + } + } + if (this.parentElement) { + this.parentElement.temporaryInputEvent.answer = this._getTextArray(); + } + this.el.dispatchEvent(new Event('regexChanged')); + }, + onStart: (event) => { + this._prevPosition = this._getBlockPosition(event.item); + }, + onEnd: (event) => { + // TODO: (bug) This is a workaround that only works in the demo. + // compare clientY with the position of item. + // console.log(event.item.onclick); + let endposition = 0; + let action = RegexEvent.ParsonsInputAction.MOVE; + const upperbound = this._dropArea.getBoundingClientRect().top; + const lowerbound = this._dropArea.getBoundingClientRect().bottom; + if (event.originalEvent.clientY > lowerbound || event.originalEvent.clientY < upperbound) { + const item = event.item; + if (item.parentNode) { + item.parentNode.removeChild(item); + } + endposition = -1; + action = RegexEvent.ParsonsInputAction.REMOVE; + } + else { + endposition = this._getBlockPosition(event.item); + } + if (this.parentElement) { + this.parentElement.temporaryInputEvent = { + 'event-type': 'parsons', + action: action, + position: [this._prevPosition, endposition], + answer: this._getTextArray(), + }; + } + this.el.dispatchEvent(new Event('regexChanged')); + }, + }); + }; + updateTestStatus = (result) => { + if (this._dropArea.classList.contains(result)) { + return; + } + if (this._dropArea.classList.contains('Pass')) { + this._dropArea.classList.remove('Pass'); + } + else if (this._dropArea.classList.contains('Fail')) { + this._dropArea.classList.remove('Fail'); + } + else if (this._dropArea.classList.contains('Error')) { + this._dropArea.classList.remove('Error'); + } + this._dropArea.classList.add(result); + }; + setExpandableBlocks = (expandableBlocks) => { + this.expandableBlocks = expandableBlocks; + }; + _getBlockPosition = (block) => { + let position = 0; + const parent = block.parentNode; + if (parent) { + for (position = 0; position < parent.childNodes.length; ++position) { + if (parent.childNodes[position] === block) { + break; + } + } + } + return position; + }; + _getTextArray = () => { + let answer = []; + if (this._dropArea.hasChildNodes()) { + let el = this._dropArea.firstChild; + answer.push(el.innerText); + while (el.nextSibling) { + el = el.nextSibling; + answer.push(el.innerText); + } + } + return answer; + }; + highlightError = (position) => { + // console.log(position); + let count = 0; + if (this._dropArea.hasChildNodes()) { + let el = this._dropArea.firstChild; + count += el.innerText.length; + if (count >= position) { + // the current block contains the symbol with error + el.style.backgroundColor = '#ff99b3'; + } + else { + while (el.nextSibling) { + el = el.nextSibling; + count += el.innerText.length; + if (count >= position) { + // the current block contains the symbol with error + el.style.backgroundColor = '#ff99b3'; + break; + } + } + } + } + }; + removeFormat = () => { + if (this._dropArea.hasChildNodes()) { + let el = this._dropArea.firstChild; + el.style.removeProperty('background-color'); + while (el.nextSibling) { + el = el.nextSibling; + el.style.removeProperty('background-color'); + } + } + }; +} + +var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + +function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} + +function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; +} + +/*! + * Quill Editor v1.3.7 + * https://quilljs.com/ + * Copyright (c) 2014, Jason Chen + * Copyright (c) 2013, salesforce.com + */ + +var quill = createCommonjsModule(function (module, exports) { +(function webpackUniversalModuleDefinition(root, factory) { + module.exports = factory(); +})(typeof self !== 'undefined' ? self : commonjsGlobal, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 109); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var container_1 = __webpack_require__(17); +var format_1 = __webpack_require__(18); +var leaf_1 = __webpack_require__(19); +var scroll_1 = __webpack_require__(45); +var inline_1 = __webpack_require__(46); +var block_1 = __webpack_require__(47); +var embed_1 = __webpack_require__(48); +var text_1 = __webpack_require__(49); +var attributor_1 = __webpack_require__(12); +var class_1 = __webpack_require__(32); +var style_1 = __webpack_require__(33); +var store_1 = __webpack_require__(31); +var Registry = __webpack_require__(1); +var Parchment = { + Scope: Registry.Scope, + create: Registry.create, + find: Registry.find, + query: Registry.query, + register: Registry.register, + Container: container_1.default, + Format: format_1.default, + Leaf: leaf_1.default, + Embed: embed_1.default, + Scroll: scroll_1.default, + Block: block_1.default, + Inline: inline_1.default, + Text: text_1.default, + Attributor: { + Attribute: attributor_1.default, + Class: class_1.default, + Style: style_1.default, + Store: store_1.default, + }, +}; +exports.default = Parchment; + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ParchmentError = /** @class */ (function (_super) { + __extends(ParchmentError, _super); + function ParchmentError(message) { + var _this = this; + message = '[Parchment] ' + message; + _this = _super.call(this, message) || this; + _this.message = message; + _this.name = _this.constructor.name; + return _this; + } + return ParchmentError; +}(Error)); +exports.ParchmentError = ParchmentError; +var attributes = {}; +var classes = {}; +var tags = {}; +var types = {}; +exports.DATA_KEY = '__blot'; +var Scope; +(function (Scope) { + Scope[Scope["TYPE"] = 3] = "TYPE"; + Scope[Scope["LEVEL"] = 12] = "LEVEL"; + Scope[Scope["ATTRIBUTE"] = 13] = "ATTRIBUTE"; + Scope[Scope["BLOT"] = 14] = "BLOT"; + Scope[Scope["INLINE"] = 7] = "INLINE"; + Scope[Scope["BLOCK"] = 11] = "BLOCK"; + Scope[Scope["BLOCK_BLOT"] = 10] = "BLOCK_BLOT"; + Scope[Scope["INLINE_BLOT"] = 6] = "INLINE_BLOT"; + Scope[Scope["BLOCK_ATTRIBUTE"] = 9] = "BLOCK_ATTRIBUTE"; + Scope[Scope["INLINE_ATTRIBUTE"] = 5] = "INLINE_ATTRIBUTE"; + Scope[Scope["ANY"] = 15] = "ANY"; +})(Scope = exports.Scope || (exports.Scope = {})); +function create(input, value) { + var match = query(input); + if (match == null) { + throw new ParchmentError("Unable to create " + input + " blot"); + } + var BlotClass = match; + var node = + // @ts-ignore + input instanceof Node || input['nodeType'] === Node.TEXT_NODE ? input : BlotClass.create(value); + return new BlotClass(node, value); +} +exports.create = create; +function find(node, bubble) { + if (bubble === void 0) { bubble = false; } + if (node == null) + return null; + // @ts-ignore + if (node[exports.DATA_KEY] != null) + return node[exports.DATA_KEY].blot; + if (bubble) + return find(node.parentNode, bubble); + return null; +} +exports.find = find; +function query(query, scope) { + if (scope === void 0) { scope = Scope.ANY; } + var match; + if (typeof query === 'string') { + match = types[query] || attributes[query]; + // @ts-ignore + } + else if (query instanceof Text || query['nodeType'] === Node.TEXT_NODE) { + match = types['text']; + } + else if (typeof query === 'number') { + if (query & Scope.LEVEL & Scope.BLOCK) { + match = types['block']; + } + else if (query & Scope.LEVEL & Scope.INLINE) { + match = types['inline']; + } + } + else if (query instanceof HTMLElement) { + var names = (query.getAttribute('class') || '').split(/\s+/); + for (var i in names) { + match = classes[names[i]]; + if (match) + break; + } + match = match || tags[query.tagName]; + } + if (match == null) + return null; + // @ts-ignore + if (scope & Scope.LEVEL & match.scope && scope & Scope.TYPE & match.scope) + return match; + return null; +} +exports.query = query; +function register() { + var Definitions = []; + for (var _i = 0; _i < arguments.length; _i++) { + Definitions[_i] = arguments[_i]; + } + if (Definitions.length > 1) { + return Definitions.map(function (d) { + return register(d); + }); + } + var Definition = Definitions[0]; + if (typeof Definition.blotName !== 'string' && typeof Definition.attrName !== 'string') { + throw new ParchmentError('Invalid definition'); + } + else if (Definition.blotName === 'abstract') { + throw new ParchmentError('Cannot register abstract class'); + } + types[Definition.blotName || Definition.attrName] = Definition; + if (typeof Definition.keyName === 'string') { + attributes[Definition.keyName] = Definition; + } + else { + if (Definition.className != null) { + classes[Definition.className] = Definition; + } + if (Definition.tagName != null) { + if (Array.isArray(Definition.tagName)) { + Definition.tagName = Definition.tagName.map(function (tagName) { + return tagName.toUpperCase(); + }); + } + else { + Definition.tagName = Definition.tagName.toUpperCase(); + } + var tagNames = Array.isArray(Definition.tagName) ? Definition.tagName : [Definition.tagName]; + tagNames.forEach(function (tag) { + if (tags[tag] == null || Definition.className == null) { + tags[tag] = Definition; + } + }); + } + } + return Definition; +} +exports.register = register; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +var diff = __webpack_require__(51); +var equal = __webpack_require__(11); +var extend = __webpack_require__(3); +var op = __webpack_require__(20); + + +var NULL_CHARACTER = String.fromCharCode(0); // Placeholder char for embed in diff() + + +var Delta = function (ops) { + // Assume we are given a well formed ops + if (Array.isArray(ops)) { + this.ops = ops; + } else if (ops != null && Array.isArray(ops.ops)) { + this.ops = ops.ops; + } else { + this.ops = []; + } +}; + + +Delta.prototype.insert = function (text, attributes) { + var newOp = {}; + if (text.length === 0) return this; + newOp.insert = text; + if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) { + newOp.attributes = attributes; + } + return this.push(newOp); +}; + +Delta.prototype['delete'] = function (length) { + if (length <= 0) return this; + return this.push({ 'delete': length }); +}; + +Delta.prototype.retain = function (length, attributes) { + if (length <= 0) return this; + var newOp = { retain: length }; + if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) { + newOp.attributes = attributes; + } + return this.push(newOp); +}; + +Delta.prototype.push = function (newOp) { + var index = this.ops.length; + var lastOp = this.ops[index - 1]; + newOp = extend(true, {}, newOp); + if (typeof lastOp === 'object') { + if (typeof newOp['delete'] === 'number' && typeof lastOp['delete'] === 'number') { + this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] }; + return this; + } + // Since it does not matter if we insert before or after deleting at the same index, + // always prefer to insert first + if (typeof lastOp['delete'] === 'number' && newOp.insert != null) { + index -= 1; + lastOp = this.ops[index - 1]; + if (typeof lastOp !== 'object') { + this.ops.unshift(newOp); + return this; + } + } + if (equal(newOp.attributes, lastOp.attributes)) { + if (typeof newOp.insert === 'string' && typeof lastOp.insert === 'string') { + this.ops[index - 1] = { insert: lastOp.insert + newOp.insert }; + if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes; + return this; + } else if (typeof newOp.retain === 'number' && typeof lastOp.retain === 'number') { + this.ops[index - 1] = { retain: lastOp.retain + newOp.retain }; + if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes; + return this; + } + } + } + if (index === this.ops.length) { + this.ops.push(newOp); + } else { + this.ops.splice(index, 0, newOp); + } + return this; +}; + +Delta.prototype.chop = function () { + var lastOp = this.ops[this.ops.length - 1]; + if (lastOp && lastOp.retain && !lastOp.attributes) { + this.ops.pop(); + } + return this; +}; + +Delta.prototype.filter = function (predicate) { + return this.ops.filter(predicate); +}; + +Delta.prototype.forEach = function (predicate) { + this.ops.forEach(predicate); +}; + +Delta.prototype.map = function (predicate) { + return this.ops.map(predicate); +}; + +Delta.prototype.partition = function (predicate) { + var passed = [], failed = []; + this.forEach(function(op) { + var target = predicate(op) ? passed : failed; + target.push(op); + }); + return [passed, failed]; +}; + +Delta.prototype.reduce = function (predicate, initial) { + return this.ops.reduce(predicate, initial); +}; + +Delta.prototype.changeLength = function () { + return this.reduce(function (length, elem) { + if (elem.insert) { + return length + op.length(elem); + } else if (elem.delete) { + return length - elem.delete; + } + return length; + }, 0); +}; + +Delta.prototype.length = function () { + return this.reduce(function (length, elem) { + return length + op.length(elem); + }, 0); +}; + +Delta.prototype.slice = function (start, end) { + start = start || 0; + if (typeof end !== 'number') end = Infinity; + var ops = []; + var iter = op.iterator(this.ops); + var index = 0; + while (index < end && iter.hasNext()) { + var nextOp; + if (index < start) { + nextOp = iter.next(start - index); + } else { + nextOp = iter.next(end - index); + ops.push(nextOp); + } + index += op.length(nextOp); + } + return new Delta(ops); +}; + + +Delta.prototype.compose = function (other) { + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + var ops = []; + var firstOther = otherIter.peek(); + if (firstOther != null && typeof firstOther.retain === 'number' && firstOther.attributes == null) { + var firstLeft = firstOther.retain; + while (thisIter.peekType() === 'insert' && thisIter.peekLength() <= firstLeft) { + firstLeft -= thisIter.peekLength(); + ops.push(thisIter.next()); + } + if (firstOther.retain - firstLeft > 0) { + otherIter.next(firstOther.retain - firstLeft); + } + } + var delta = new Delta(ops); + while (thisIter.hasNext() || otherIter.hasNext()) { + if (otherIter.peekType() === 'insert') { + delta.push(otherIter.next()); + } else if (thisIter.peekType() === 'delete') { + delta.push(thisIter.next()); + } else { + var length = Math.min(thisIter.peekLength(), otherIter.peekLength()); + var thisOp = thisIter.next(length); + var otherOp = otherIter.next(length); + if (typeof otherOp.retain === 'number') { + var newOp = {}; + if (typeof thisOp.retain === 'number') { + newOp.retain = length; + } else { + newOp.insert = thisOp.insert; + } + // Preserve null when composing with a retain, otherwise remove it for inserts + var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, typeof thisOp.retain === 'number'); + if (attributes) newOp.attributes = attributes; + delta.push(newOp); + + // Optimization if rest of other is just retain + if (!otherIter.hasNext() && equal(delta.ops[delta.ops.length - 1], newOp)) { + var rest = new Delta(thisIter.rest()); + return delta.concat(rest).chop(); + } + + // Other op should be delete, we could be an insert or retain + // Insert + delete cancels out + } else if (typeof otherOp['delete'] === 'number' && typeof thisOp.retain === 'number') { + delta.push(otherOp); + } + } + } + return delta.chop(); +}; + +Delta.prototype.concat = function (other) { + var delta = new Delta(this.ops.slice()); + if (other.ops.length > 0) { + delta.push(other.ops[0]); + delta.ops = delta.ops.concat(other.ops.slice(1)); + } + return delta; +}; + +Delta.prototype.diff = function (other, index) { + if (this.ops === other.ops) { + return new Delta(); + } + var strings = [this, other].map(function (delta) { + return delta.map(function (op) { + if (op.insert != null) { + return typeof op.insert === 'string' ? op.insert : NULL_CHARACTER; + } + var prep = (delta === other) ? 'on' : 'with'; + throw new Error('diff() called ' + prep + ' non-document'); + }).join(''); + }); + var delta = new Delta(); + var diffResult = diff(strings[0], strings[1], index); + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + diffResult.forEach(function (component) { + var length = component[1].length; + while (length > 0) { + var opLength = 0; + switch (component[0]) { + case diff.INSERT: + opLength = Math.min(otherIter.peekLength(), length); + delta.push(otherIter.next(opLength)); + break; + case diff.DELETE: + opLength = Math.min(length, thisIter.peekLength()); + thisIter.next(opLength); + delta['delete'](opLength); + break; + case diff.EQUAL: + opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length); + var thisOp = thisIter.next(opLength); + var otherOp = otherIter.next(opLength); + if (equal(thisOp.insert, otherOp.insert)) { + delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes)); + } else { + delta.push(otherOp)['delete'](opLength); + } + break; + } + length -= opLength; + } + }); + return delta.chop(); +}; + +Delta.prototype.eachLine = function (predicate, newline) { + newline = newline || '\n'; + var iter = op.iterator(this.ops); + var line = new Delta(); + var i = 0; + while (iter.hasNext()) { + if (iter.peekType() !== 'insert') return; + var thisOp = iter.peek(); + var start = op.length(thisOp) - iter.peekLength(); + var index = typeof thisOp.insert === 'string' ? + thisOp.insert.indexOf(newline, start) - start : -1; + if (index < 0) { + line.push(iter.next()); + } else if (index > 0) { + line.push(iter.next(index)); + } else { + if (predicate(line, iter.next(1).attributes || {}, i) === false) { + return; + } + i += 1; + line = new Delta(); + } + } + if (line.length() > 0) { + predicate(line, {}, i); + } +}; + +Delta.prototype.transform = function (other, priority) { + priority = !!priority; + if (typeof other === 'number') { + return this.transformPosition(other, priority); + } + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + var delta = new Delta(); + while (thisIter.hasNext() || otherIter.hasNext()) { + if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) { + delta.retain(op.length(thisIter.next())); + } else if (otherIter.peekType() === 'insert') { + delta.push(otherIter.next()); + } else { + var length = Math.min(thisIter.peekLength(), otherIter.peekLength()); + var thisOp = thisIter.next(length); + var otherOp = otherIter.next(length); + if (thisOp['delete']) { + // Our delete either makes their delete redundant or removes their retain + continue; + } else if (otherOp['delete']) { + delta.push(otherOp); + } else { + // We retain either their retain or insert + delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority)); + } + } + } + return delta.chop(); +}; + +Delta.prototype.transformPosition = function (index, priority) { + priority = !!priority; + var thisIter = op.iterator(this.ops); + var offset = 0; + while (thisIter.hasNext() && offset <= index) { + var length = thisIter.peekLength(); + var nextType = thisIter.peekType(); + thisIter.next(); + if (nextType === 'delete') { + index -= Math.min(length, index - offset); + continue; + } else if (nextType === 'insert' && (offset < index || !priority)) { + index += length; + } + offset += length; + } + return index; +}; + + +module.exports = Delta; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +var hasOwn = Object.prototype.hasOwnProperty; +var toStr = Object.prototype.toString; +var defineProperty = Object.defineProperty; +var gOPD = Object.getOwnPropertyDescriptor; + +var isArray = function isArray(arr) { + if (typeof Array.isArray === 'function') { + return Array.isArray(arr); + } + + return toStr.call(arr) === '[object Array]'; +}; + +var isPlainObject = function isPlainObject(obj) { + if (!obj || toStr.call(obj) !== '[object Object]') { + return false; + } + + var hasOwnConstructor = hasOwn.call(obj, 'constructor'); + var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); + // Not own constructor property must be Object + if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + var key; + for (key in obj) { /**/ } + + return typeof key === 'undefined' || hasOwn.call(obj, key); +}; + +// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target +var setProperty = function setProperty(target, options) { + if (defineProperty && options.name === '__proto__') { + defineProperty(target, options.name, { + enumerable: true, + configurable: true, + value: options.newValue, + writable: true + }); + } else { + target[options.name] = options.newValue; + } +}; + +// Return undefined instead of __proto__ if '__proto__' is not an own property +var getProperty = function getProperty(obj, name) { + if (name === '__proto__') { + if (!hasOwn.call(obj, name)) { + return void 0; + } else if (gOPD) { + // In early versions of node, obj['__proto__'] is buggy when obj has + // __proto__ as an own property. Object.getOwnPropertyDescriptor() works. + return gOPD(obj, name).value; + } + } + + return obj[name]; +}; + +module.exports = function extend() { + var options, name, src, copy, copyIsArray, clone; + var target = arguments[0]; + var i = 1; + var length = arguments.length; + var deep = false; + + // Handle a deep copy situation + if (typeof target === 'boolean') { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + if (target == null || (typeof target !== 'object' && typeof target !== 'function')) { + target = {}; + } + + for (; i < length; ++i) { + options = arguments[i]; + // Only deal with non-null/undefined values + if (options != null) { + // Extend the base object + for (name in options) { + src = getProperty(target, name); + copy = getProperty(options, name); + + // Prevent never-ending loop + if (target !== copy) { + // Recurse if we're merging plain objects or arrays + if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { + if (copyIsArray) { + copyIsArray = false; + clone = src && isArray(src) ? src : []; + } else { + clone = src && isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + setProperty(target, { name: name, newValue: extend(deep, clone, copy) }); + + // Don't bring in undefined values + } else if (typeof copy !== 'undefined') { + setProperty(target, { name: name, newValue: copy }); + } + } + } + } + } + + // Return the modified object + return target; +}; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.BlockEmbed = exports.bubbleFormats = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var NEWLINE_LENGTH = 1; + +var BlockEmbed = function (_Parchment$Embed) { + _inherits(BlockEmbed, _Parchment$Embed); + + function BlockEmbed() { + _classCallCheck(this, BlockEmbed); + + return _possibleConstructorReturn(this, (BlockEmbed.__proto__ || Object.getPrototypeOf(BlockEmbed)).apply(this, arguments)); + } + + _createClass(BlockEmbed, [{ + key: 'attach', + value: function attach() { + _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'attach', this).call(this); + this.attributes = new _parchment2.default.Attributor.Store(this.domNode); + } + }, { + key: 'delta', + value: function delta() { + return new _quillDelta2.default().insert(this.value(), (0, _extend2.default)(this.formats(), this.attributes.values())); + } + }, { + key: 'format', + value: function format(name, value) { + var attribute = _parchment2.default.query(name, _parchment2.default.Scope.BLOCK_ATTRIBUTE); + if (attribute != null) { + this.attributes.attribute(attribute, value); + } + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + this.format(name, value); + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (typeof value === 'string' && value.endsWith('\n')) { + var block = _parchment2.default.create(Block.blotName); + this.parent.insertBefore(block, index === 0 ? this : this.next); + block.insertAt(0, value.slice(0, -1)); + } else { + _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'insertAt', this).call(this, index, value, def); + } + } + }]); + + return BlockEmbed; +}(_parchment2.default.Embed); + +BlockEmbed.scope = _parchment2.default.Scope.BLOCK_BLOT; +// It is important for cursor behavior BlockEmbeds use tags that are block level elements + + +var Block = function (_Parchment$Block) { + _inherits(Block, _Parchment$Block); + + function Block(domNode) { + _classCallCheck(this, Block); + + var _this2 = _possibleConstructorReturn(this, (Block.__proto__ || Object.getPrototypeOf(Block)).call(this, domNode)); + + _this2.cache = {}; + return _this2; + } + + _createClass(Block, [{ + key: 'delta', + value: function delta() { + if (this.cache.delta == null) { + this.cache.delta = this.descendants(_parchment2.default.Leaf).reduce(function (delta, leaf) { + if (leaf.length() === 0) { + return delta; + } else { + return delta.insert(leaf.value(), bubbleFormats(leaf)); + } + }, new _quillDelta2.default()).insert('\n', bubbleFormats(this)); + } + return this.cache.delta; + } + }, { + key: 'deleteAt', + value: function deleteAt(index, length) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'deleteAt', this).call(this, index, length); + this.cache = {}; + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (length <= 0) return; + if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) { + if (index + length === this.length()) { + this.format(name, value); + } + } else { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'formatAt', this).call(this, index, Math.min(length, this.length() - index - 1), name, value); + } + this.cache = {}; + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null) return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, index, value, def); + if (value.length === 0) return; + var lines = value.split('\n'); + var text = lines.shift(); + if (text.length > 0) { + if (index < this.length() - 1 || this.children.tail == null) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, Math.min(index, this.length() - 1), text); + } else { + this.children.tail.insertAt(this.children.tail.length(), text); + } + this.cache = {}; + } + var block = this; + lines.reduce(function (index, line) { + block = block.split(index, true); + block.insertAt(0, line); + return line.length; + }, index + text.length); + } + }, { + key: 'insertBefore', + value: function insertBefore(blot, ref) { + var head = this.children.head; + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertBefore', this).call(this, blot, ref); + if (head instanceof _break2.default) { + head.remove(); + } + this.cache = {}; + } + }, { + key: 'length', + value: function length() { + if (this.cache.length == null) { + this.cache.length = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'length', this).call(this) + NEWLINE_LENGTH; + } + return this.cache.length; + } + }, { + key: 'moveChildren', + value: function moveChildren(target, ref) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'moveChildren', this).call(this, target, ref); + this.cache = {}; + } + }, { + key: 'optimize', + value: function optimize(context) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'optimize', this).call(this, context); + this.cache = {}; + } + }, { + key: 'path', + value: function path(index) { + return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'path', this).call(this, index, true); + } + }, { + key: 'removeChild', + value: function removeChild(child) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'removeChild', this).call(this, child); + this.cache = {}; + } + }, { + key: 'split', + value: function split(index) { + var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) { + var clone = this.clone(); + if (index === 0) { + this.parent.insertBefore(clone, this); + return this; + } else { + this.parent.insertBefore(clone, this.next); + return clone; + } + } else { + var next = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'split', this).call(this, index, force); + this.cache = {}; + return next; + } + } + }]); + + return Block; +}(_parchment2.default.Block); + +Block.blotName = 'block'; +Block.tagName = 'P'; +Block.defaultChild = 'break'; +Block.allowedChildren = [_inline2.default, _parchment2.default.Embed, _text2.default]; + +function bubbleFormats(blot) { + var formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + if (blot == null) return formats; + if (typeof blot.formats === 'function') { + formats = (0, _extend2.default)(formats, blot.formats()); + } + if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) { + return formats; + } + return bubbleFormats(blot.parent, formats); +} + +exports.bubbleFormats = bubbleFormats; +exports.BlockEmbed = BlockEmbed; +exports.default = Block; + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.overload = exports.expandConfig = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +__webpack_require__(50); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _editor = __webpack_require__(14); + +var _editor2 = _interopRequireDefault(_editor); + +var _emitter3 = __webpack_require__(8); + +var _emitter4 = _interopRequireDefault(_emitter3); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _selection = __webpack_require__(15); + +var _selection2 = _interopRequireDefault(_selection); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _theme = __webpack_require__(34); + +var _theme2 = _interopRequireDefault(_theme); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var debug = (0, _logger2.default)('quill'); + +var Quill = function () { + _createClass(Quill, null, [{ + key: 'debug', + value: function debug(limit) { + if (limit === true) { + limit = 'log'; + } + _logger2.default.level(limit); + } + }, { + key: 'find', + value: function find(node) { + return node.__quill || _parchment2.default.find(node); + } + }, { + key: 'import', + value: function _import(name) { + if (this.imports[name] == null) { + debug.error('Cannot import ' + name + '. Are you sure it was registered?'); + } + return this.imports[name]; + } + }, { + key: 'register', + value: function register(path, target) { + var _this = this; + + var overwrite = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + if (typeof path !== 'string') { + var name = path.attrName || path.blotName; + if (typeof name === 'string') { + // register(Blot | Attributor, overwrite) + this.register('formats/' + name, path, target); + } else { + Object.keys(path).forEach(function (key) { + _this.register(key, path[key], target); + }); + } + } else { + if (this.imports[path] != null && !overwrite) { + debug.warn('Overwriting ' + path + ' with', target); + } + this.imports[path] = target; + if ((path.startsWith('blots/') || path.startsWith('formats/')) && target.blotName !== 'abstract') { + _parchment2.default.register(target); + } else if (path.startsWith('modules') && typeof target.register === 'function') { + target.register(); + } + } + } + }]); + + function Quill(container) { + var _this2 = this; + + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, Quill); + + this.options = expandConfig(container, options); + this.container = this.options.container; + if (this.container == null) { + return debug.error('Invalid Quill container', container); + } + if (this.options.debug) { + Quill.debug(this.options.debug); + } + var html = this.container.innerHTML.trim(); + this.container.classList.add('ql-container'); + this.container.innerHTML = ''; + this.container.__quill = this; + this.root = this.addContainer('ql-editor'); + this.root.classList.add('ql-blank'); + this.root.setAttribute('data-gramm', false); + this.scrollingContainer = this.options.scrollingContainer || this.root; + this.emitter = new _emitter4.default(); + this.scroll = _parchment2.default.create(this.root, { + emitter: this.emitter, + whitelist: this.options.formats + }); + this.editor = new _editor2.default(this.scroll); + this.selection = new _selection2.default(this.scroll, this.emitter); + this.theme = new this.options.theme(this, this.options); + this.keyboard = this.theme.addModule('keyboard'); + this.clipboard = this.theme.addModule('clipboard'); + this.history = this.theme.addModule('history'); + this.theme.init(); + this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type) { + if (type === _emitter4.default.events.TEXT_CHANGE) { + _this2.root.classList.toggle('ql-blank', _this2.editor.isBlank()); + } + }); + this.emitter.on(_emitter4.default.events.SCROLL_UPDATE, function (source, mutations) { + var range = _this2.selection.lastRange; + var index = range && range.length === 0 ? range.index : undefined; + modify.call(_this2, function () { + return _this2.editor.update(null, mutations, index); + }, source); + }); + var contents = this.clipboard.convert('

' + html + '


'); + this.setContents(contents); + this.history.clear(); + if (this.options.placeholder) { + this.root.setAttribute('data-placeholder', this.options.placeholder); + } + if (this.options.readOnly) { + this.disable(); + } + } + + _createClass(Quill, [{ + key: 'addContainer', + value: function addContainer(container) { + var refNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + if (typeof container === 'string') { + var className = container; + container = document.createElement('div'); + container.classList.add(className); + } + this.container.insertBefore(container, refNode); + return container; + } + }, { + key: 'blur', + value: function blur() { + this.selection.setRange(null); + } + }, { + key: 'deleteText', + value: function deleteText(index, length, source) { + var _this3 = this; + + var _overload = overload(index, length, source); + + var _overload2 = _slicedToArray(_overload, 4); + + index = _overload2[0]; + length = _overload2[1]; + source = _overload2[3]; + + return modify.call(this, function () { + return _this3.editor.deleteText(index, length); + }, source, index, -1 * length); + } + }, { + key: 'disable', + value: function disable() { + this.enable(false); + } + }, { + key: 'enable', + value: function enable() { + var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + + this.scroll.enable(enabled); + this.container.classList.toggle('ql-disabled', !enabled); + } + }, { + key: 'focus', + value: function focus() { + var scrollTop = this.scrollingContainer.scrollTop; + this.selection.focus(); + this.scrollingContainer.scrollTop = scrollTop; + this.scrollIntoView(); + } + }, { + key: 'format', + value: function format(name, value) { + var _this4 = this; + + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API; + + return modify.call(this, function () { + var range = _this4.getSelection(true); + var change = new _quillDelta2.default(); + if (range == null) { + return change; + } else if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) { + change = _this4.editor.formatLine(range.index, range.length, _defineProperty({}, name, value)); + } else if (range.length === 0) { + _this4.selection.format(name, value); + return change; + } else { + change = _this4.editor.formatText(range.index, range.length, _defineProperty({}, name, value)); + } + _this4.setSelection(range, _emitter4.default.sources.SILENT); + return change; + }, source); + } + }, { + key: 'formatLine', + value: function formatLine(index, length, name, value, source) { + var _this5 = this; + + var formats = void 0; + + var _overload3 = overload(index, length, name, value, source); + + var _overload4 = _slicedToArray(_overload3, 4); + + index = _overload4[0]; + length = _overload4[1]; + formats = _overload4[2]; + source = _overload4[3]; + + return modify.call(this, function () { + return _this5.editor.formatLine(index, length, formats); + }, source, index, 0); + } + }, { + key: 'formatText', + value: function formatText(index, length, name, value, source) { + var _this6 = this; + + var formats = void 0; + + var _overload5 = overload(index, length, name, value, source); + + var _overload6 = _slicedToArray(_overload5, 4); + + index = _overload6[0]; + length = _overload6[1]; + formats = _overload6[2]; + source = _overload6[3]; + + return modify.call(this, function () { + return _this6.editor.formatText(index, length, formats); + }, source, index, 0); + } + }, { + key: 'getBounds', + value: function getBounds(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var bounds = void 0; + if (typeof index === 'number') { + bounds = this.selection.getBounds(index, length); + } else { + bounds = this.selection.getBounds(index.index, index.length); + } + var containerBounds = this.container.getBoundingClientRect(); + return { + bottom: bounds.bottom - containerBounds.top, + height: bounds.height, + left: bounds.left - containerBounds.left, + right: bounds.right - containerBounds.left, + top: bounds.top - containerBounds.top, + width: bounds.width + }; + } + }, { + key: 'getContents', + value: function getContents() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index; + + var _overload7 = overload(index, length); + + var _overload8 = _slicedToArray(_overload7, 2); + + index = _overload8[0]; + length = _overload8[1]; + + return this.editor.getContents(index, length); + } + }, { + key: 'getFormat', + value: function getFormat() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getSelection(true); + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + if (typeof index === 'number') { + return this.editor.getFormat(index, length); + } else { + return this.editor.getFormat(index.index, index.length); + } + } + }, { + key: 'getIndex', + value: function getIndex(blot) { + return blot.offset(this.scroll); + } + }, { + key: 'getLength', + value: function getLength() { + return this.scroll.length(); + } + }, { + key: 'getLeaf', + value: function getLeaf(index) { + return this.scroll.leaf(index); + } + }, { + key: 'getLine', + value: function getLine(index) { + return this.scroll.line(index); + } + }, { + key: 'getLines', + value: function getLines() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE; + + if (typeof index !== 'number') { + return this.scroll.lines(index.index, index.length); + } else { + return this.scroll.lines(index, length); + } + } + }, { + key: 'getModule', + value: function getModule(name) { + return this.theme.modules[name]; + } + }, { + key: 'getSelection', + value: function getSelection() { + var focus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + if (focus) this.focus(); + this.update(); // Make sure we access getRange with editor in consistent state + return this.selection.getRange()[0]; + } + }, { + key: 'getText', + value: function getText() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index; + + var _overload9 = overload(index, length); + + var _overload10 = _slicedToArray(_overload9, 2); + + index = _overload10[0]; + length = _overload10[1]; + + return this.editor.getText(index, length); + } + }, { + key: 'hasFocus', + value: function hasFocus() { + return this.selection.hasFocus(); + } + }, { + key: 'insertEmbed', + value: function insertEmbed(index, embed, value) { + var _this7 = this; + + var source = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Quill.sources.API; + + return modify.call(this, function () { + return _this7.editor.insertEmbed(index, embed, value); + }, source, index); + } + }, { + key: 'insertText', + value: function insertText(index, text, name, value, source) { + var _this8 = this; + + var formats = void 0; + + var _overload11 = overload(index, 0, name, value, source); + + var _overload12 = _slicedToArray(_overload11, 4); + + index = _overload12[0]; + formats = _overload12[2]; + source = _overload12[3]; + + return modify.call(this, function () { + return _this8.editor.insertText(index, text, formats); + }, source, index, text.length); + } + }, { + key: 'isEnabled', + value: function isEnabled() { + return !this.container.classList.contains('ql-disabled'); + } + }, { + key: 'off', + value: function off() { + return this.emitter.off.apply(this.emitter, arguments); + } + }, { + key: 'on', + value: function on() { + return this.emitter.on.apply(this.emitter, arguments); + } + }, { + key: 'once', + value: function once() { + return this.emitter.once.apply(this.emitter, arguments); + } + }, { + key: 'pasteHTML', + value: function pasteHTML(index, html, source) { + this.clipboard.dangerouslyPasteHTML(index, html, source); + } + }, { + key: 'removeFormat', + value: function removeFormat(index, length, source) { + var _this9 = this; + + var _overload13 = overload(index, length, source); + + var _overload14 = _slicedToArray(_overload13, 4); + + index = _overload14[0]; + length = _overload14[1]; + source = _overload14[3]; + + return modify.call(this, function () { + return _this9.editor.removeFormat(index, length); + }, source, index); + } + }, { + key: 'scrollIntoView', + value: function scrollIntoView() { + this.selection.scrollIntoView(this.scrollingContainer); + } + }, { + key: 'setContents', + value: function setContents(delta) { + var _this10 = this; + + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + return modify.call(this, function () { + delta = new _quillDelta2.default(delta); + var length = _this10.getLength(); + var deleted = _this10.editor.deleteText(0, length); + var applied = _this10.editor.applyDelta(delta); + var lastOp = applied.ops[applied.ops.length - 1]; + if (lastOp != null && typeof lastOp.insert === 'string' && lastOp.insert[lastOp.insert.length - 1] === '\n') { + _this10.editor.deleteText(_this10.getLength() - 1, 1); + applied.delete(1); + } + var ret = deleted.compose(applied); + return ret; + }, source); + } + }, { + key: 'setSelection', + value: function setSelection(index, length, source) { + if (index == null) { + this.selection.setRange(null, length || Quill.sources.API); + } else { + var _overload15 = overload(index, length, source); + + var _overload16 = _slicedToArray(_overload15, 4); + + index = _overload16[0]; + length = _overload16[1]; + source = _overload16[3]; + + this.selection.setRange(new _selection.Range(index, length), source); + if (source !== _emitter4.default.sources.SILENT) { + this.selection.scrollIntoView(this.scrollingContainer); + } + } + } + }, { + key: 'setText', + value: function setText(text) { + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + var delta = new _quillDelta2.default().insert(text); + return this.setContents(delta, source); + } + }, { + key: 'update', + value: function update() { + var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER; + + var change = this.scroll.update(source); // Will update selection before selection.update() does if text changes + this.selection.update(source); + return change; + } + }, { + key: 'updateContents', + value: function updateContents(delta) { + var _this11 = this; + + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + return modify.call(this, function () { + delta = new _quillDelta2.default(delta); + return _this11.editor.applyDelta(delta, source); + }, source, true); + } + }]); + + return Quill; +}(); + +Quill.DEFAULTS = { + bounds: null, + formats: null, + modules: {}, + placeholder: '', + readOnly: false, + scrollingContainer: null, + strict: true, + theme: 'default' +}; +Quill.events = _emitter4.default.events; +Quill.sources = _emitter4.default.sources; +// eslint-disable-next-line no-undef +Quill.version = "1.3.7"; + +Quill.imports = { + 'delta': _quillDelta2.default, + 'parchment': _parchment2.default, + 'core/module': _module2.default, + 'core/theme': _theme2.default +}; + +function expandConfig(container, userConfig) { + userConfig = (0, _extend2.default)(true, { + container: container, + modules: { + clipboard: true, + keyboard: true, + history: true + } + }, userConfig); + if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) { + userConfig.theme = _theme2.default; + } else { + userConfig.theme = Quill.import('themes/' + userConfig.theme); + if (userConfig.theme == null) { + throw new Error('Invalid theme ' + userConfig.theme + '. Did you register it?'); + } + } + var themeConfig = (0, _extend2.default)(true, {}, userConfig.theme.DEFAULTS); + [themeConfig, userConfig].forEach(function (config) { + config.modules = config.modules || {}; + Object.keys(config.modules).forEach(function (module) { + if (config.modules[module] === true) { + config.modules[module] = {}; + } + }); + }); + var moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules)); + var moduleConfig = moduleNames.reduce(function (config, name) { + var moduleClass = Quill.import('modules/' + name); + if (moduleClass == null) { + debug.error('Cannot load ' + name + ' module. Are you sure you registered it?'); + } else { + config[name] = moduleClass.DEFAULTS || {}; + } + return config; + }, {}); + // Special case toolbar shorthand + if (userConfig.modules != null && userConfig.modules.toolbar && userConfig.modules.toolbar.constructor !== Object) { + userConfig.modules.toolbar = { + container: userConfig.modules.toolbar + }; + } + userConfig = (0, _extend2.default)(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig); + ['bounds', 'container', 'scrollingContainer'].forEach(function (key) { + if (typeof userConfig[key] === 'string') { + userConfig[key] = document.querySelector(userConfig[key]); + } + }); + userConfig.modules = Object.keys(userConfig.modules).reduce(function (config, name) { + if (userConfig.modules[name]) { + config[name] = userConfig.modules[name]; + } + return config; + }, {}); + return userConfig; +} + +// Handle selection preservation and TEXT_CHANGE emission +// common to modification APIs +function modify(modifier, source, index, shift) { + if (this.options.strict && !this.isEnabled() && source === _emitter4.default.sources.USER) { + return new _quillDelta2.default(); + } + var range = index == null ? null : this.getSelection(); + var oldDelta = this.editor.delta; + var change = modifier(); + if (range != null) { + if (index === true) index = range.index; + if (shift == null) { + range = shiftRange(range, change, source); + } else if (shift !== 0) { + range = shiftRange(range, index, shift, source); + } + this.setSelection(range, _emitter4.default.sources.SILENT); + } + if (change.length() > 0) { + var _emitter; + + var args = [_emitter4.default.events.TEXT_CHANGE, change, oldDelta, source]; + (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args)); + if (source !== _emitter4.default.sources.SILENT) { + var _emitter2; + + (_emitter2 = this.emitter).emit.apply(_emitter2, args); + } + } + return change; +} + +function overload(index, length, name, value, source) { + var formats = {}; + if (typeof index.index === 'number' && typeof index.length === 'number') { + // Allow for throwaway end (used by insertText/insertEmbed) + if (typeof length !== 'number') { + source = value, value = name, name = length, length = index.length, index = index.index; + } else { + length = index.length, index = index.index; + } + } else if (typeof length !== 'number') { + source = value, value = name, name = length, length = 0; + } + // Handle format being object, two format name/value strings or excluded + if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') { + formats = name; + source = value; + } else if (typeof name === 'string') { + if (value != null) { + formats[name] = value; + } else { + source = name; + } + } + // Handle optional source + source = source || _emitter4.default.sources.API; + return [index, length, formats, source]; +} + +function shiftRange(range, index, length, source) { + if (range == null) return null; + var start = void 0, + end = void 0; + if (index instanceof _quillDelta2.default) { + var _map = [range.index, range.index + range.length].map(function (pos) { + return index.transformPosition(pos, source !== _emitter4.default.sources.USER); + }); + + var _map2 = _slicedToArray(_map, 2); + + start = _map2[0]; + end = _map2[1]; + } else { + var _map3 = [range.index, range.index + range.length].map(function (pos) { + if (pos < index || pos === index && source === _emitter4.default.sources.USER) return pos; + if (length >= 0) { + return pos + length; + } else { + return Math.max(index, pos + length); + } + }); + + var _map4 = _slicedToArray(_map3, 2); + + start = _map4[0]; + end = _map4[1]; + } + return new _selection.Range(start, end - start); +} + +exports.expandConfig = expandConfig; +exports.overload = overload; +exports.default = Quill; + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Inline = function (_Parchment$Inline) { + _inherits(Inline, _Parchment$Inline); + + function Inline() { + _classCallCheck(this, Inline); + + return _possibleConstructorReturn(this, (Inline.__proto__ || Object.getPrototypeOf(Inline)).apply(this, arguments)); + } + + _createClass(Inline, [{ + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (Inline.compare(this.statics.blotName, name) < 0 && _parchment2.default.query(name, _parchment2.default.Scope.BLOT)) { + var blot = this.isolate(index, length); + if (value) { + blot.wrap(name, value); + } + } else { + _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'formatAt', this).call(this, index, length, name, value); + } + } + }, { + key: 'optimize', + value: function optimize(context) { + _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'optimize', this).call(this, context); + if (this.parent instanceof Inline && Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) { + var parent = this.parent.isolate(this.offset(), this.length()); + this.moveChildren(parent); + parent.wrap(this); + } + } + }], [{ + key: 'compare', + value: function compare(self, other) { + var selfIndex = Inline.order.indexOf(self); + var otherIndex = Inline.order.indexOf(other); + if (selfIndex >= 0 || otherIndex >= 0) { + return selfIndex - otherIndex; + } else if (self === other) { + return 0; + } else if (self < other) { + return -1; + } else { + return 1; + } + } + }]); + + return Inline; +}(_parchment2.default.Inline); + +Inline.allowedChildren = [Inline, _parchment2.default.Embed, _text2.default]; +// Lower index means deeper in the DOM tree, since not found (-1) is for embeds +Inline.order = ['cursor', 'inline', // Must be lower +'underline', 'strike', 'italic', 'bold', 'script', 'link', 'code' // Must be higher +]; + +exports.default = Inline; + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var TextBlot = function (_Parchment$Text) { + _inherits(TextBlot, _Parchment$Text); + + function TextBlot() { + _classCallCheck(this, TextBlot); + + return _possibleConstructorReturn(this, (TextBlot.__proto__ || Object.getPrototypeOf(TextBlot)).apply(this, arguments)); + } + + return TextBlot; +}(_parchment2.default.Text); + +exports.default = TextBlot; + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _eventemitter = __webpack_require__(54); + +var _eventemitter2 = _interopRequireDefault(_eventemitter); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:events'); + +var EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click']; + +EVENTS.forEach(function (eventName) { + document.addEventListener(eventName, function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + [].slice.call(document.querySelectorAll('.ql-container')).forEach(function (node) { + // TODO use WeakMap + if (node.__quill && node.__quill.emitter) { + var _node$__quill$emitter; + + (_node$__quill$emitter = node.__quill.emitter).handleDOM.apply(_node$__quill$emitter, args); + } + }); + }); +}); + +var Emitter = function (_EventEmitter) { + _inherits(Emitter, _EventEmitter); + + function Emitter() { + _classCallCheck(this, Emitter); + + var _this = _possibleConstructorReturn(this, (Emitter.__proto__ || Object.getPrototypeOf(Emitter)).call(this)); + + _this.listeners = {}; + _this.on('error', debug.error); + return _this; + } + + _createClass(Emitter, [{ + key: 'emit', + value: function emit() { + debug.log.apply(debug, arguments); + _get(Emitter.prototype.__proto__ || Object.getPrototypeOf(Emitter.prototype), 'emit', this).apply(this, arguments); + } + }, { + key: 'handleDOM', + value: function handleDOM(event) { + for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + (this.listeners[event.type] || []).forEach(function (_ref) { + var node = _ref.node, + handler = _ref.handler; + + if (event.target === node || node.contains(event.target)) { + handler.apply(undefined, [event].concat(args)); + } + }); + } + }, { + key: 'listenDOM', + value: function listenDOM(eventName, node, handler) { + if (!this.listeners[eventName]) { + this.listeners[eventName] = []; + } + this.listeners[eventName].push({ node: node, handler: handler }); + } + }]); + + return Emitter; +}(_eventemitter2.default); + +Emitter.events = { + EDITOR_CHANGE: 'editor-change', + SCROLL_BEFORE_UPDATE: 'scroll-before-update', + SCROLL_OPTIMIZE: 'scroll-optimize', + SCROLL_UPDATE: 'scroll-update', + SELECTION_CHANGE: 'selection-change', + TEXT_CHANGE: 'text-change' +}; +Emitter.sources = { + API: 'api', + SILENT: 'silent', + USER: 'user' +}; + +exports.default = Emitter; + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Module = function Module(quill) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, Module); + + this.quill = quill; + this.options = options; +}; + +Module.DEFAULTS = {}; + +exports.default = Module; + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var levels = ['error', 'warn', 'log', 'info']; +var level = 'warn'; + +function debug(method) { + if (levels.indexOf(method) <= levels.indexOf(level)) { + var _console; + + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + (_console = console)[method].apply(_console, args); // eslint-disable-line no-console + } +} + +function namespace(ns) { + return levels.reduce(function (logger, method) { + logger[method] = debug.bind(console, method, ns); + return logger; + }, {}); +} + +debug.level = namespace.level = function (newLevel) { + level = newLevel; +}; + +exports.default = namespace; + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + +var pSlice = Array.prototype.slice; +var objectKeys = __webpack_require__(52); +var isArguments = __webpack_require__(53); + +var deepEqual = module.exports = function (actual, expected, opts) { + if (!opts) opts = {}; + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') { + return opts.strict ? actual === expected : actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected, opts); + } +}; + +function isUndefinedOrNull(value) { + return value === null || value === undefined; +} + +function isBuffer (x) { + if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false; + if (typeof x.copy !== 'function' || typeof x.slice !== 'function') { + return false; + } + if (x.length > 0 && typeof x[0] !== 'number') return false; + return true; +} + +function objEquiv(a, b, opts) { + var i, key; + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return deepEqual(a, b, opts); + } + if (isBuffer(a)) { + if (!isBuffer(b)) { + return false; + } + if (a.length !== b.length) return false; + for (i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; + } + try { + var ka = objectKeys(a), + kb = objectKeys(b); + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!deepEqual(a[key], b[key], opts)) return false; + } + return typeof a === typeof b; +} + + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var Registry = __webpack_require__(1); +var Attributor = /** @class */ (function () { + function Attributor(attrName, keyName, options) { + if (options === void 0) { options = {}; } + this.attrName = attrName; + this.keyName = keyName; + var attributeBit = Registry.Scope.TYPE & Registry.Scope.ATTRIBUTE; + if (options.scope != null) { + // Ignore type bits, force attribute bit + this.scope = (options.scope & Registry.Scope.LEVEL) | attributeBit; + } + else { + this.scope = Registry.Scope.ATTRIBUTE; + } + if (options.whitelist != null) + this.whitelist = options.whitelist; + } + Attributor.keys = function (node) { + return [].map.call(node.attributes, function (item) { + return item.name; + }); + }; + Attributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + node.setAttribute(this.keyName, value); + return true; + }; + Attributor.prototype.canAdd = function (node, value) { + var match = Registry.query(node, Registry.Scope.BLOT & (this.scope | Registry.Scope.TYPE)); + if (match == null) + return false; + if (this.whitelist == null) + return true; + if (typeof value === 'string') { + return this.whitelist.indexOf(value.replace(/["']/g, '')) > -1; + } + else { + return this.whitelist.indexOf(value) > -1; + } + }; + Attributor.prototype.remove = function (node) { + node.removeAttribute(this.keyName); + }; + Attributor.prototype.value = function (node) { + var value = node.getAttribute(this.keyName); + if (this.canAdd(node, value) && value) { + return value; + } + return ''; + }; + return Attributor; +}()); +exports.default = Attributor; + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.Code = undefined; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Code = function (_Inline) { + _inherits(Code, _Inline); + + function Code() { + _classCallCheck(this, Code); + + return _possibleConstructorReturn(this, (Code.__proto__ || Object.getPrototypeOf(Code)).apply(this, arguments)); + } + + return Code; +}(_inline2.default); + +Code.blotName = 'code'; +Code.tagName = 'CODE'; + +var CodeBlock = function (_Block) { + _inherits(CodeBlock, _Block); + + function CodeBlock() { + _classCallCheck(this, CodeBlock); + + return _possibleConstructorReturn(this, (CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock)).apply(this, arguments)); + } + + _createClass(CodeBlock, [{ + key: 'delta', + value: function delta() { + var _this3 = this; + + var text = this.domNode.textContent; + if (text.endsWith('\n')) { + // Should always be true + text = text.slice(0, -1); + } + return text.split('\n').reduce(function (delta, frag) { + return delta.insert(frag).insert('\n', _this3.formats()); + }, new _quillDelta2.default()); + } + }, { + key: 'format', + value: function format(name, value) { + if (name === this.statics.blotName && value) return; + + var _descendant = this.descendant(_text2.default, this.length() - 1), + _descendant2 = _slicedToArray(_descendant, 1), + text = _descendant2[0]; + + if (text != null) { + text.deleteAt(text.length() - 1, 1); + } + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'format', this).call(this, name, value); + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (length === 0) return; + if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK) == null || name === this.statics.blotName && value === this.statics.formats(this.domNode)) { + return; + } + var nextNewline = this.newlineIndex(index); + if (nextNewline < 0 || nextNewline >= index + length) return; + var prevNewline = this.newlineIndex(index, true) + 1; + var isolateLength = nextNewline - prevNewline + 1; + var blot = this.isolate(prevNewline, isolateLength); + var next = blot.next; + blot.format(name, value); + if (next instanceof CodeBlock) { + next.formatAt(0, index - prevNewline + length - isolateLength, name, value); + } + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null) return; + + var _descendant3 = this.descendant(_text2.default, index), + _descendant4 = _slicedToArray(_descendant3, 2), + text = _descendant4[0], + offset = _descendant4[1]; + + text.insertAt(offset, value); + } + }, { + key: 'length', + value: function length() { + var length = this.domNode.textContent.length; + if (!this.domNode.textContent.endsWith('\n')) { + return length + 1; + } + return length; + } + }, { + key: 'newlineIndex', + value: function newlineIndex(searchIndex) { + var reverse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (!reverse) { + var offset = this.domNode.textContent.slice(searchIndex).indexOf('\n'); + return offset > -1 ? searchIndex + offset : -1; + } else { + return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\n'); + } + } + }, { + key: 'optimize', + value: function optimize(context) { + if (!this.domNode.textContent.endsWith('\n')) { + this.appendChild(_parchment2.default.create('text', '\n')); + } + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'optimize', this).call(this, context); + var next = this.next; + if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) { + next.optimize(context); + next.moveChildren(this); + next.remove(); + } + } + }, { + key: 'replace', + value: function replace(target) { + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'replace', this).call(this, target); + [].slice.call(this.domNode.querySelectorAll('*')).forEach(function (node) { + var blot = _parchment2.default.find(node); + if (blot == null) { + node.parentNode.removeChild(node); + } else if (blot instanceof _parchment2.default.Embed) { + blot.remove(); + } else { + blot.unwrap(); + } + }); + } + }], [{ + key: 'create', + value: function create(value) { + var domNode = _get(CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock), 'create', this).call(this, value); + domNode.setAttribute('spellcheck', false); + return domNode; + } + }, { + key: 'formats', + value: function formats() { + return true; + } + }]); + + return CodeBlock; +}(_block2.default); + +CodeBlock.blotName = 'code-block'; +CodeBlock.tagName = 'PRE'; +CodeBlock.TAB = ' '; + +exports.Code = Code; +exports.default = CodeBlock; + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _op = __webpack_require__(20); + +var _op2 = _interopRequireDefault(_op); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _cursor = __webpack_require__(24); + +var _cursor2 = _interopRequireDefault(_cursor); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var ASCII = /^[ -~]*$/; + +var Editor = function () { + function Editor(scroll) { + _classCallCheck(this, Editor); + + this.scroll = scroll; + this.delta = this.getDelta(); + } + + _createClass(Editor, [{ + key: 'applyDelta', + value: function applyDelta(delta) { + var _this = this; + + var consumeNextNewline = false; + this.scroll.update(); + var scrollLength = this.scroll.length(); + this.scroll.batchStart(); + delta = normalizeDelta(delta); + delta.reduce(function (index, op) { + var length = op.retain || op.delete || op.insert.length || 1; + var attributes = op.attributes || {}; + if (op.insert != null) { + if (typeof op.insert === 'string') { + var text = op.insert; + if (text.endsWith('\n') && consumeNextNewline) { + consumeNextNewline = false; + text = text.slice(0, -1); + } + if (index >= scrollLength && !text.endsWith('\n')) { + consumeNextNewline = true; + } + _this.scroll.insertAt(index, text); + + var _scroll$line = _this.scroll.line(index), + _scroll$line2 = _slicedToArray(_scroll$line, 2), + line = _scroll$line2[0], + offset = _scroll$line2[1]; + + var formats = (0, _extend2.default)({}, (0, _block.bubbleFormats)(line)); + if (line instanceof _block2.default) { + var _line$descendant = line.descendant(_parchment2.default.Leaf, offset), + _line$descendant2 = _slicedToArray(_line$descendant, 1), + leaf = _line$descendant2[0]; + + formats = (0, _extend2.default)(formats, (0, _block.bubbleFormats)(leaf)); + } + attributes = _op2.default.attributes.diff(formats, attributes) || {}; + } else if (_typeof(op.insert) === 'object') { + var key = Object.keys(op.insert)[0]; // There should only be one key + if (key == null) return index; + _this.scroll.insertAt(index, key, op.insert[key]); + } + scrollLength += length; + } + Object.keys(attributes).forEach(function (name) { + _this.scroll.formatAt(index, length, name, attributes[name]); + }); + return index + length; + }, 0); + delta.reduce(function (index, op) { + if (typeof op.delete === 'number') { + _this.scroll.deleteAt(index, op.delete); + return index; + } + return index + (op.retain || op.insert.length || 1); + }, 0); + this.scroll.batchEnd(); + return this.update(delta); + } + }, { + key: 'deleteText', + value: function deleteText(index, length) { + this.scroll.deleteAt(index, length); + return this.update(new _quillDelta2.default().retain(index).delete(length)); + } + }, { + key: 'formatLine', + value: function formatLine(index, length) { + var _this2 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + this.scroll.update(); + Object.keys(formats).forEach(function (format) { + if (_this2.scroll.whitelist != null && !_this2.scroll.whitelist[format]) return; + var lines = _this2.scroll.lines(index, Math.max(length, 1)); + var lengthRemaining = length; + lines.forEach(function (line) { + var lineLength = line.length(); + if (!(line instanceof _code2.default)) { + line.format(format, formats[format]); + } else { + var codeIndex = index - line.offset(_this2.scroll); + var codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1; + line.formatAt(codeIndex, codeLength, format, formats[format]); + } + lengthRemaining -= lineLength; + }); + }); + this.scroll.optimize(); + return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats))); + } + }, { + key: 'formatText', + value: function formatText(index, length) { + var _this3 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + Object.keys(formats).forEach(function (format) { + _this3.scroll.formatAt(index, length, format, formats[format]); + }); + return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats))); + } + }, { + key: 'getContents', + value: function getContents(index, length) { + return this.delta.slice(index, index + length); + } + }, { + key: 'getDelta', + value: function getDelta() { + return this.scroll.lines().reduce(function (delta, line) { + return delta.concat(line.delta()); + }, new _quillDelta2.default()); + } + }, { + key: 'getFormat', + value: function getFormat(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var lines = [], + leaves = []; + if (length === 0) { + this.scroll.path(index).forEach(function (path) { + var _path = _slicedToArray(path, 1), + blot = _path[0]; + + if (blot instanceof _block2.default) { + lines.push(blot); + } else if (blot instanceof _parchment2.default.Leaf) { + leaves.push(blot); + } + }); + } else { + lines = this.scroll.lines(index, length); + leaves = this.scroll.descendants(_parchment2.default.Leaf, index, length); + } + var formatsArr = [lines, leaves].map(function (blots) { + if (blots.length === 0) return {}; + var formats = (0, _block.bubbleFormats)(blots.shift()); + while (Object.keys(formats).length > 0) { + var blot = blots.shift(); + if (blot == null) return formats; + formats = combineFormats((0, _block.bubbleFormats)(blot), formats); + } + return formats; + }); + return _extend2.default.apply(_extend2.default, formatsArr); + } + }, { + key: 'getText', + value: function getText(index, length) { + return this.getContents(index, length).filter(function (op) { + return typeof op.insert === 'string'; + }).map(function (op) { + return op.insert; + }).join(''); + } + }, { + key: 'insertEmbed', + value: function insertEmbed(index, embed, value) { + this.scroll.insertAt(index, embed, value); + return this.update(new _quillDelta2.default().retain(index).insert(_defineProperty({}, embed, value))); + } + }, { + key: 'insertText', + value: function insertText(index, text) { + var _this4 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + this.scroll.insertAt(index, text); + Object.keys(formats).forEach(function (format) { + _this4.scroll.formatAt(index, text.length, format, formats[format]); + }); + return this.update(new _quillDelta2.default().retain(index).insert(text, (0, _clone2.default)(formats))); + } + }, { + key: 'isBlank', + value: function isBlank() { + if (this.scroll.children.length == 0) return true; + if (this.scroll.children.length > 1) return false; + var block = this.scroll.children.head; + if (block.statics.blotName !== _block2.default.blotName) return false; + if (block.children.length > 1) return false; + return block.children.head instanceof _break2.default; + } + }, { + key: 'removeFormat', + value: function removeFormat(index, length) { + var text = this.getText(index, length); + + var _scroll$line3 = this.scroll.line(index + length), + _scroll$line4 = _slicedToArray(_scroll$line3, 2), + line = _scroll$line4[0], + offset = _scroll$line4[1]; + + var suffixLength = 0, + suffix = new _quillDelta2.default(); + if (line != null) { + if (!(line instanceof _code2.default)) { + suffixLength = line.length() - offset; + } else { + suffixLength = line.newlineIndex(offset) - offset + 1; + } + suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\n'); + } + var contents = this.getContents(index, length + suffixLength); + var diff = contents.diff(new _quillDelta2.default().insert(text).concat(suffix)); + var delta = new _quillDelta2.default().retain(index).concat(diff); + return this.applyDelta(delta); + } + }, { + key: 'update', + value: function update(change) { + var mutations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + var cursorIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; + + var oldDelta = this.delta; + if (mutations.length === 1 && mutations[0].type === 'characterData' && mutations[0].target.data.match(ASCII) && _parchment2.default.find(mutations[0].target)) { + // Optimization for character changes + var textBlot = _parchment2.default.find(mutations[0].target); + var formats = (0, _block.bubbleFormats)(textBlot); + var index = textBlot.offset(this.scroll); + var oldValue = mutations[0].oldValue.replace(_cursor2.default.CONTENTS, ''); + var oldText = new _quillDelta2.default().insert(oldValue); + var newText = new _quillDelta2.default().insert(textBlot.value()); + var diffDelta = new _quillDelta2.default().retain(index).concat(oldText.diff(newText, cursorIndex)); + change = diffDelta.reduce(function (delta, op) { + if (op.insert) { + return delta.insert(op.insert, formats); + } else { + return delta.push(op); + } + }, new _quillDelta2.default()); + this.delta = oldDelta.compose(change); + } else { + this.delta = this.getDelta(); + if (!change || !(0, _deepEqual2.default)(oldDelta.compose(change), this.delta)) { + change = oldDelta.diff(this.delta, cursorIndex); + } + } + return change; + } + }]); + + return Editor; +}(); + +function combineFormats(formats, combined) { + return Object.keys(combined).reduce(function (merged, name) { + if (formats[name] == null) return merged; + if (combined[name] === formats[name]) { + merged[name] = combined[name]; + } else if (Array.isArray(combined[name])) { + if (combined[name].indexOf(formats[name]) < 0) { + merged[name] = combined[name].concat([formats[name]]); + } + } else { + merged[name] = [combined[name], formats[name]]; + } + return merged; + }, {}); +} + +function normalizeDelta(delta) { + return delta.reduce(function (delta, op) { + if (op.insert === 1) { + var attributes = (0, _clone2.default)(op.attributes); + delete attributes['image']; + return delta.insert({ image: op.attributes.image }, attributes); + } + if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) { + op = (0, _clone2.default)(op); + if (op.attributes.list) { + op.attributes.list = 'ordered'; + } else { + op.attributes.list = 'bullet'; + delete op.attributes.bullet; + } + } + if (typeof op.insert === 'string') { + var text = op.insert.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + return delta.insert(text, op.attributes); + } + return delta.push(op); + }, new _quillDelta2.default()); +} + +exports.default = Editor; + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.Range = undefined; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _emitter3 = __webpack_require__(8); + +var _emitter4 = _interopRequireDefault(_emitter3); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var debug = (0, _logger2.default)('quill:selection'); + +var Range = function Range(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + _classCallCheck(this, Range); + + this.index = index; + this.length = length; +}; + +var Selection = function () { + function Selection(scroll, emitter) { + var _this = this; + + _classCallCheck(this, Selection); + + this.emitter = emitter; + this.scroll = scroll; + this.composing = false; + this.mouseDown = false; + this.root = this.scroll.domNode; + this.cursor = _parchment2.default.create('cursor', this); + // savedRange is last non-null range + this.lastRange = this.savedRange = new Range(0, 0); + this.handleComposition(); + this.handleDragging(); + this.emitter.listenDOM('selectionchange', document, function () { + if (!_this.mouseDown) { + setTimeout(_this.update.bind(_this, _emitter4.default.sources.USER), 1); + } + }); + this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type, delta) { + if (type === _emitter4.default.events.TEXT_CHANGE && delta.length() > 0) { + _this.update(_emitter4.default.sources.SILENT); + } + }); + this.emitter.on(_emitter4.default.events.SCROLL_BEFORE_UPDATE, function () { + if (!_this.hasFocus()) return; + var native = _this.getNativeRange(); + if (native == null) return; + if (native.start.node === _this.cursor.textNode) return; // cursor.restore() will handle + // TODO unclear if this has negative side effects + _this.emitter.once(_emitter4.default.events.SCROLL_UPDATE, function () { + try { + _this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset); + } catch (ignored) {} + }); + }); + this.emitter.on(_emitter4.default.events.SCROLL_OPTIMIZE, function (mutations, context) { + if (context.range) { + var _context$range = context.range, + startNode = _context$range.startNode, + startOffset = _context$range.startOffset, + endNode = _context$range.endNode, + endOffset = _context$range.endOffset; + + _this.setNativeRange(startNode, startOffset, endNode, endOffset); + } + }); + this.update(_emitter4.default.sources.SILENT); + } + + _createClass(Selection, [{ + key: 'handleComposition', + value: function handleComposition() { + var _this2 = this; + + this.root.addEventListener('compositionstart', function () { + _this2.composing = true; + }); + this.root.addEventListener('compositionend', function () { + _this2.composing = false; + if (_this2.cursor.parent) { + var range = _this2.cursor.restore(); + if (!range) return; + setTimeout(function () { + _this2.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset); + }, 1); + } + }); + } + }, { + key: 'handleDragging', + value: function handleDragging() { + var _this3 = this; + + this.emitter.listenDOM('mousedown', document.body, function () { + _this3.mouseDown = true; + }); + this.emitter.listenDOM('mouseup', document.body, function () { + _this3.mouseDown = false; + _this3.update(_emitter4.default.sources.USER); + }); + } + }, { + key: 'focus', + value: function focus() { + if (this.hasFocus()) return; + this.root.focus(); + this.setRange(this.savedRange); + } + }, { + key: 'format', + value: function format(_format, value) { + if (this.scroll.whitelist != null && !this.scroll.whitelist[_format]) return; + this.scroll.update(); + var nativeRange = this.getNativeRange(); + if (nativeRange == null || !nativeRange.native.collapsed || _parchment2.default.query(_format, _parchment2.default.Scope.BLOCK)) return; + if (nativeRange.start.node !== this.cursor.textNode) { + var blot = _parchment2.default.find(nativeRange.start.node, false); + if (blot == null) return; + // TODO Give blot ability to not split + if (blot instanceof _parchment2.default.Leaf) { + var after = blot.split(nativeRange.start.offset); + blot.parent.insertBefore(this.cursor, after); + } else { + blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen + } + this.cursor.attach(); + } + this.cursor.format(_format, value); + this.scroll.optimize(); + this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length); + this.update(); + } + }, { + key: 'getBounds', + value: function getBounds(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var scrollLength = this.scroll.length(); + index = Math.min(index, scrollLength - 1); + length = Math.min(index + length, scrollLength - 1) - index; + var node = void 0, + _scroll$leaf = this.scroll.leaf(index), + _scroll$leaf2 = _slicedToArray(_scroll$leaf, 2), + leaf = _scroll$leaf2[0], + offset = _scroll$leaf2[1]; + if (leaf == null) return null; + + var _leaf$position = leaf.position(offset, true); + + var _leaf$position2 = _slicedToArray(_leaf$position, 2); + + node = _leaf$position2[0]; + offset = _leaf$position2[1]; + + var range = document.createRange(); + if (length > 0) { + range.setStart(node, offset); + + var _scroll$leaf3 = this.scroll.leaf(index + length); + + var _scroll$leaf4 = _slicedToArray(_scroll$leaf3, 2); + + leaf = _scroll$leaf4[0]; + offset = _scroll$leaf4[1]; + + if (leaf == null) return null; + + var _leaf$position3 = leaf.position(offset, true); + + var _leaf$position4 = _slicedToArray(_leaf$position3, 2); + + node = _leaf$position4[0]; + offset = _leaf$position4[1]; + + range.setEnd(node, offset); + return range.getBoundingClientRect(); + } else { + var side = 'left'; + var rect = void 0; + if (node instanceof Text) { + if (offset < node.data.length) { + range.setStart(node, offset); + range.setEnd(node, offset + 1); + } else { + range.setStart(node, offset - 1); + range.setEnd(node, offset); + side = 'right'; + } + rect = range.getBoundingClientRect(); + } else { + rect = leaf.domNode.getBoundingClientRect(); + if (offset > 0) side = 'right'; + } + return { + bottom: rect.top + rect.height, + height: rect.height, + left: rect[side], + right: rect[side], + top: rect.top, + width: 0 + }; + } + } + }, { + key: 'getNativeRange', + value: function getNativeRange() { + var selection = document.getSelection(); + if (selection == null || selection.rangeCount <= 0) return null; + var nativeRange = selection.getRangeAt(0); + if (nativeRange == null) return null; + var range = this.normalizeNative(nativeRange); + debug.info('getNativeRange', range); + return range; + } + }, { + key: 'getRange', + value: function getRange() { + var normalized = this.getNativeRange(); + if (normalized == null) return [null, null]; + var range = this.normalizedToRange(normalized); + return [range, normalized]; + } + }, { + key: 'hasFocus', + value: function hasFocus() { + return document.activeElement === this.root; + } + }, { + key: 'normalizedToRange', + value: function normalizedToRange(range) { + var _this4 = this; + + var positions = [[range.start.node, range.start.offset]]; + if (!range.native.collapsed) { + positions.push([range.end.node, range.end.offset]); + } + var indexes = positions.map(function (position) { + var _position = _slicedToArray(position, 2), + node = _position[0], + offset = _position[1]; + + var blot = _parchment2.default.find(node, true); + var index = blot.offset(_this4.scroll); + if (offset === 0) { + return index; + } else if (blot instanceof _parchment2.default.Container) { + return index + blot.length(); + } else { + return index + blot.index(node, offset); + } + }); + var end = Math.min(Math.max.apply(Math, _toConsumableArray(indexes)), this.scroll.length() - 1); + var start = Math.min.apply(Math, [end].concat(_toConsumableArray(indexes))); + return new Range(start, end - start); + } + }, { + key: 'normalizeNative', + value: function normalizeNative(nativeRange) { + if (!contains(this.root, nativeRange.startContainer) || !nativeRange.collapsed && !contains(this.root, nativeRange.endContainer)) { + return null; + } + var range = { + start: { node: nativeRange.startContainer, offset: nativeRange.startOffset }, + end: { node: nativeRange.endContainer, offset: nativeRange.endOffset }, + native: nativeRange + }; + [range.start, range.end].forEach(function (position) { + var node = position.node, + offset = position.offset; + while (!(node instanceof Text) && node.childNodes.length > 0) { + if (node.childNodes.length > offset) { + node = node.childNodes[offset]; + offset = 0; + } else if (node.childNodes.length === offset) { + node = node.lastChild; + offset = node instanceof Text ? node.data.length : node.childNodes.length + 1; + } else { + break; + } + } + position.node = node, position.offset = offset; + }); + return range; + } + }, { + key: 'rangeToNative', + value: function rangeToNative(range) { + var _this5 = this; + + var indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length]; + var args = []; + var scrollLength = this.scroll.length(); + indexes.forEach(function (index, i) { + index = Math.min(scrollLength - 1, index); + var node = void 0, + _scroll$leaf5 = _this5.scroll.leaf(index), + _scroll$leaf6 = _slicedToArray(_scroll$leaf5, 2), + leaf = _scroll$leaf6[0], + offset = _scroll$leaf6[1]; + var _leaf$position5 = leaf.position(offset, i !== 0); + + var _leaf$position6 = _slicedToArray(_leaf$position5, 2); + + node = _leaf$position6[0]; + offset = _leaf$position6[1]; + + args.push(node, offset); + }); + if (args.length < 2) { + args = args.concat(args); + } + return args; + } + }, { + key: 'scrollIntoView', + value: function scrollIntoView(scrollingContainer) { + var range = this.lastRange; + if (range == null) return; + var bounds = this.getBounds(range.index, range.length); + if (bounds == null) return; + var limit = this.scroll.length() - 1; + + var _scroll$line = this.scroll.line(Math.min(range.index, limit)), + _scroll$line2 = _slicedToArray(_scroll$line, 1), + first = _scroll$line2[0]; + + var last = first; + if (range.length > 0) { + var _scroll$line3 = this.scroll.line(Math.min(range.index + range.length, limit)); + + var _scroll$line4 = _slicedToArray(_scroll$line3, 1); + + last = _scroll$line4[0]; + } + if (first == null || last == null) return; + var scrollBounds = scrollingContainer.getBoundingClientRect(); + if (bounds.top < scrollBounds.top) { + scrollingContainer.scrollTop -= scrollBounds.top - bounds.top; + } else if (bounds.bottom > scrollBounds.bottom) { + scrollingContainer.scrollTop += bounds.bottom - scrollBounds.bottom; + } + } + }, { + key: 'setNativeRange', + value: function setNativeRange(startNode, startOffset) { + var endNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : startNode; + var endOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : startOffset; + var force = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + + debug.info('setNativeRange', startNode, startOffset, endNode, endOffset); + if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) { + return; + } + var selection = document.getSelection(); + if (selection == null) return; + if (startNode != null) { + if (!this.hasFocus()) this.root.focus(); + var native = (this.getNativeRange() || {}).native; + if (native == null || force || startNode !== native.startContainer || startOffset !== native.startOffset || endNode !== native.endContainer || endOffset !== native.endOffset) { + + if (startNode.tagName == "BR") { + startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode); + startNode = startNode.parentNode; + } + if (endNode.tagName == "BR") { + endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode); + endNode = endNode.parentNode; + } + var range = document.createRange(); + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); + selection.removeAllRanges(); + selection.addRange(range); + } + } else { + selection.removeAllRanges(); + this.root.blur(); + document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs) + } + } + }, { + key: 'setRange', + value: function setRange(range) { + var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API; + + if (typeof force === 'string') { + source = force; + force = false; + } + debug.info('setRange', range); + if (range != null) { + var args = this.rangeToNative(range); + this.setNativeRange.apply(this, _toConsumableArray(args).concat([force])); + } else { + this.setNativeRange(null); + } + this.update(source); + } + }, { + key: 'update', + value: function update() { + var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER; + + var oldRange = this.lastRange; + + var _getRange = this.getRange(), + _getRange2 = _slicedToArray(_getRange, 2), + lastRange = _getRange2[0], + nativeRange = _getRange2[1]; + + this.lastRange = lastRange; + if (this.lastRange != null) { + this.savedRange = this.lastRange; + } + if (!(0, _deepEqual2.default)(oldRange, this.lastRange)) { + var _emitter; + + if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) { + this.cursor.restore(); + } + var args = [_emitter4.default.events.SELECTION_CHANGE, (0, _clone2.default)(this.lastRange), (0, _clone2.default)(oldRange), source]; + (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args)); + if (source !== _emitter4.default.sources.SILENT) { + var _emitter2; + + (_emitter2 = this.emitter).emit.apply(_emitter2, args); + } + } + } + }]); + + return Selection; +}(); + +function contains(parent, descendant) { + try { + // Firefox inserts inaccessible nodes around video elements + descendant.parentNode; + } catch (e) { + return false; + } + // IE11 has bug with Text nodes + // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect + if (descendant instanceof Text) { + descendant = descendant.parentNode; + } + return parent.contains(descendant); +} + +exports.Range = Range; +exports.default = Selection; + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Break = function (_Parchment$Embed) { + _inherits(Break, _Parchment$Embed); + + function Break() { + _classCallCheck(this, Break); + + return _possibleConstructorReturn(this, (Break.__proto__ || Object.getPrototypeOf(Break)).apply(this, arguments)); + } + + _createClass(Break, [{ + key: 'insertInto', + value: function insertInto(parent, ref) { + if (parent.children.length === 0) { + _get(Break.prototype.__proto__ || Object.getPrototypeOf(Break.prototype), 'insertInto', this).call(this, parent, ref); + } else { + this.remove(); + } + } + }, { + key: 'length', + value: function length() { + return 0; + } + }, { + key: 'value', + value: function value() { + return ''; + } + }], [{ + key: 'value', + value: function value() { + return undefined; + } + }]); + + return Break; +}(_parchment2.default.Embed); + +Break.blotName = 'break'; +Break.tagName = 'BR'; + +exports.default = Break; + +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var linked_list_1 = __webpack_require__(44); +var shadow_1 = __webpack_require__(30); +var Registry = __webpack_require__(1); +var ContainerBlot = /** @class */ (function (_super) { + __extends(ContainerBlot, _super); + function ContainerBlot(domNode) { + var _this = _super.call(this, domNode) || this; + _this.build(); + return _this; + } + ContainerBlot.prototype.appendChild = function (other) { + this.insertBefore(other); + }; + ContainerBlot.prototype.attach = function () { + _super.prototype.attach.call(this); + this.children.forEach(function (child) { + child.attach(); + }); + }; + ContainerBlot.prototype.build = function () { + var _this = this; + this.children = new linked_list_1.default(); + // Need to be reversed for if DOM nodes already in order + [].slice + .call(this.domNode.childNodes) + .reverse() + .forEach(function (node) { + try { + var child = makeBlot(node); + _this.insertBefore(child, _this.children.head || undefined); + } + catch (err) { + if (err instanceof Registry.ParchmentError) + return; + else + throw err; + } + }); + }; + ContainerBlot.prototype.deleteAt = function (index, length) { + if (index === 0 && length === this.length()) { + return this.remove(); + } + this.children.forEachAt(index, length, function (child, offset, length) { + child.deleteAt(offset, length); + }); + }; + ContainerBlot.prototype.descendant = function (criteria, index) { + var _a = this.children.find(index), child = _a[0], offset = _a[1]; + if ((criteria.blotName == null && criteria(child)) || + (criteria.blotName != null && child instanceof criteria)) { + return [child, offset]; + } + else if (child instanceof ContainerBlot) { + return child.descendant(criteria, offset); + } + else { + return [null, -1]; + } + }; + ContainerBlot.prototype.descendants = function (criteria, index, length) { + if (index === void 0) { index = 0; } + if (length === void 0) { length = Number.MAX_VALUE; } + var descendants = []; + var lengthLeft = length; + this.children.forEachAt(index, length, function (child, index, length) { + if ((criteria.blotName == null && criteria(child)) || + (criteria.blotName != null && child instanceof criteria)) { + descendants.push(child); + } + if (child instanceof ContainerBlot) { + descendants = descendants.concat(child.descendants(criteria, index, lengthLeft)); + } + lengthLeft -= length; + }); + return descendants; + }; + ContainerBlot.prototype.detach = function () { + this.children.forEach(function (child) { + child.detach(); + }); + _super.prototype.detach.call(this); + }; + ContainerBlot.prototype.formatAt = function (index, length, name, value) { + this.children.forEachAt(index, length, function (child, offset, length) { + child.formatAt(offset, length, name, value); + }); + }; + ContainerBlot.prototype.insertAt = function (index, value, def) { + var _a = this.children.find(index), child = _a[0], offset = _a[1]; + if (child) { + child.insertAt(offset, value, def); + } + else { + var blot = def == null ? Registry.create('text', value) : Registry.create(value, def); + this.appendChild(blot); + } + }; + ContainerBlot.prototype.insertBefore = function (childBlot, refBlot) { + if (this.statics.allowedChildren != null && + !this.statics.allowedChildren.some(function (child) { + return childBlot instanceof child; + })) { + throw new Registry.ParchmentError("Cannot insert " + childBlot.statics.blotName + " into " + this.statics.blotName); + } + childBlot.insertInto(this, refBlot); + }; + ContainerBlot.prototype.length = function () { + return this.children.reduce(function (memo, child) { + return memo + child.length(); + }, 0); + }; + ContainerBlot.prototype.moveChildren = function (targetParent, refNode) { + this.children.forEach(function (child) { + targetParent.insertBefore(child, refNode); + }); + }; + ContainerBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + if (this.children.length === 0) { + if (this.statics.defaultChild != null) { + var child = Registry.create(this.statics.defaultChild); + this.appendChild(child); + child.optimize(context); + } + else { + this.remove(); + } + } + }; + ContainerBlot.prototype.path = function (index, inclusive) { + if (inclusive === void 0) { inclusive = false; } + var _a = this.children.find(index, inclusive), child = _a[0], offset = _a[1]; + var position = [[this, index]]; + if (child instanceof ContainerBlot) { + return position.concat(child.path(offset, inclusive)); + } + else if (child != null) { + position.push([child, offset]); + } + return position; + }; + ContainerBlot.prototype.removeChild = function (child) { + this.children.remove(child); + }; + ContainerBlot.prototype.replace = function (target) { + if (target instanceof ContainerBlot) { + target.moveChildren(this); + } + _super.prototype.replace.call(this, target); + }; + ContainerBlot.prototype.split = function (index, force) { + if (force === void 0) { force = false; } + if (!force) { + if (index === 0) + return this; + if (index === this.length()) + return this.next; + } + var after = this.clone(); + this.parent.insertBefore(after, this.next); + this.children.forEachAt(index, this.length(), function (child, offset, length) { + child = child.split(offset, force); + after.appendChild(child); + }); + return after; + }; + ContainerBlot.prototype.unwrap = function () { + this.moveChildren(this.parent, this.next); + this.remove(); + }; + ContainerBlot.prototype.update = function (mutations, context) { + var _this = this; + var addedNodes = []; + var removedNodes = []; + mutations.forEach(function (mutation) { + if (mutation.target === _this.domNode && mutation.type === 'childList') { + addedNodes.push.apply(addedNodes, mutation.addedNodes); + removedNodes.push.apply(removedNodes, mutation.removedNodes); + } + }); + removedNodes.forEach(function (node) { + // Check node has actually been removed + // One exception is Chrome does not immediately remove IFRAMEs + // from DOM but MutationRecord is correct in its reported removal + if (node.parentNode != null && + // @ts-ignore + node.tagName !== 'IFRAME' && + document.body.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) { + return; + } + var blot = Registry.find(node); + if (blot == null) + return; + if (blot.domNode.parentNode == null || blot.domNode.parentNode === _this.domNode) { + blot.detach(); + } + }); + addedNodes + .filter(function (node) { + return node.parentNode == _this.domNode; + }) + .sort(function (a, b) { + if (a === b) + return 0; + if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) { + return 1; + } + return -1; + }) + .forEach(function (node) { + var refBlot = null; + if (node.nextSibling != null) { + refBlot = Registry.find(node.nextSibling); + } + var blot = makeBlot(node); + if (blot.next != refBlot || blot.next == null) { + if (blot.parent != null) { + blot.parent.removeChild(_this); + } + _this.insertBefore(blot, refBlot || undefined); + } + }); + }; + return ContainerBlot; +}(shadow_1.default)); +function makeBlot(node) { + var blot = Registry.find(node); + if (blot == null) { + try { + blot = Registry.create(node); + } + catch (e) { + blot = Registry.create(Registry.Scope.INLINE); + [].slice.call(node.childNodes).forEach(function (child) { + // @ts-ignore + blot.domNode.appendChild(child); + }); + if (node.parentNode) { + node.parentNode.replaceChild(blot.domNode, node); + } + blot.attach(); + } + } + return blot; +} +exports.default = ContainerBlot; + + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +var store_1 = __webpack_require__(31); +var container_1 = __webpack_require__(17); +var Registry = __webpack_require__(1); +var FormatBlot = /** @class */ (function (_super) { + __extends(FormatBlot, _super); + function FormatBlot(domNode) { + var _this = _super.call(this, domNode) || this; + _this.attributes = new store_1.default(_this.domNode); + return _this; + } + FormatBlot.formats = function (domNode) { + if (typeof this.tagName === 'string') { + return true; + } + else if (Array.isArray(this.tagName)) { + return domNode.tagName.toLowerCase(); + } + return undefined; + }; + FormatBlot.prototype.format = function (name, value) { + var format = Registry.query(name); + if (format instanceof attributor_1.default) { + this.attributes.attribute(format, value); + } + else if (value) { + if (format != null && (name !== this.statics.blotName || this.formats()[name] !== value)) { + this.replaceWith(name, value); + } + } + }; + FormatBlot.prototype.formats = function () { + var formats = this.attributes.values(); + var format = this.statics.formats(this.domNode); + if (format != null) { + formats[this.statics.blotName] = format; + } + return formats; + }; + FormatBlot.prototype.replaceWith = function (name, value) { + var replacement = _super.prototype.replaceWith.call(this, name, value); + this.attributes.copy(replacement); + return replacement; + }; + FormatBlot.prototype.update = function (mutations, context) { + var _this = this; + _super.prototype.update.call(this, mutations, context); + if (mutations.some(function (mutation) { + return mutation.target === _this.domNode && mutation.type === 'attributes'; + })) { + this.attributes.build(); + } + }; + FormatBlot.prototype.wrap = function (name, value) { + var wrapper = _super.prototype.wrap.call(this, name, value); + if (wrapper instanceof FormatBlot && wrapper.statics.scope === this.statics.scope) { + this.attributes.move(wrapper); + } + return wrapper; + }; + return FormatBlot; +}(container_1.default)); +exports.default = FormatBlot; + + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var shadow_1 = __webpack_require__(30); +var Registry = __webpack_require__(1); +var LeafBlot = /** @class */ (function (_super) { + __extends(LeafBlot, _super); + function LeafBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + LeafBlot.value = function (domNode) { + return true; + }; + LeafBlot.prototype.index = function (node, offset) { + if (this.domNode === node || + this.domNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) { + return Math.min(offset, 1); + } + return -1; + }; + LeafBlot.prototype.position = function (index, inclusive) { + var offset = [].indexOf.call(this.parent.domNode.childNodes, this.domNode); + if (index > 0) + offset += 1; + return [this.parent.domNode, offset]; + }; + LeafBlot.prototype.value = function () { + var _a; + return _a = {}, _a[this.statics.blotName] = this.statics.value(this.domNode) || true, _a; + }; + LeafBlot.scope = Registry.Scope.INLINE_BLOT; + return LeafBlot; +}(shadow_1.default)); +exports.default = LeafBlot; + + +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { + +var equal = __webpack_require__(11); +var extend = __webpack_require__(3); + + +var lib = { + attributes: { + compose: function (a, b, keepNull) { + if (typeof a !== 'object') a = {}; + if (typeof b !== 'object') b = {}; + var attributes = extend(true, {}, b); + if (!keepNull) { + attributes = Object.keys(attributes).reduce(function (copy, key) { + if (attributes[key] != null) { + copy[key] = attributes[key]; + } + return copy; + }, {}); + } + for (var key in a) { + if (a[key] !== undefined && b[key] === undefined) { + attributes[key] = a[key]; + } + } + return Object.keys(attributes).length > 0 ? attributes : undefined; + }, + + diff: function(a, b) { + if (typeof a !== 'object') a = {}; + if (typeof b !== 'object') b = {}; + var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) { + if (!equal(a[key], b[key])) { + attributes[key] = b[key] === undefined ? null : b[key]; + } + return attributes; + }, {}); + return Object.keys(attributes).length > 0 ? attributes : undefined; + }, + + transform: function (a, b, priority) { + if (typeof a !== 'object') return b; + if (typeof b !== 'object') return undefined; + if (!priority) return b; // b simply overwrites us without priority + var attributes = Object.keys(b).reduce(function (attributes, key) { + if (a[key] === undefined) attributes[key] = b[key]; // null is a valid value + return attributes; + }, {}); + return Object.keys(attributes).length > 0 ? attributes : undefined; + } + }, + + iterator: function (ops) { + return new Iterator(ops); + }, + + length: function (op) { + if (typeof op['delete'] === 'number') { + return op['delete']; + } else if (typeof op.retain === 'number') { + return op.retain; + } else { + return typeof op.insert === 'string' ? op.insert.length : 1; + } + } +}; + + +function Iterator(ops) { + this.ops = ops; + this.index = 0; + this.offset = 0; +} +Iterator.prototype.hasNext = function () { + return this.peekLength() < Infinity; +}; + +Iterator.prototype.next = function (length) { + if (!length) length = Infinity; + var nextOp = this.ops[this.index]; + if (nextOp) { + var offset = this.offset; + var opLength = lib.length(nextOp); + if (length >= opLength - offset) { + length = opLength - offset; + this.index += 1; + this.offset = 0; + } else { + this.offset += length; + } + if (typeof nextOp['delete'] === 'number') { + return { 'delete': length }; + } else { + var retOp = {}; + if (nextOp.attributes) { + retOp.attributes = nextOp.attributes; + } + if (typeof nextOp.retain === 'number') { + retOp.retain = length; + } else if (typeof nextOp.insert === 'string') { + retOp.insert = nextOp.insert.substr(offset, length); + } else { + // offset should === 0, length should === 1 + retOp.insert = nextOp.insert; + } + return retOp; + } + } else { + return { retain: Infinity }; + } +}; + +Iterator.prototype.peek = function () { + return this.ops[this.index]; +}; + +Iterator.prototype.peekLength = function () { + if (this.ops[this.index]) { + // Should never return 0 if our index is being managed correctly + return lib.length(this.ops[this.index]) - this.offset; + } else { + return Infinity; + } +}; + +Iterator.prototype.peekType = function () { + if (this.ops[this.index]) { + if (typeof this.ops[this.index]['delete'] === 'number') { + return 'delete'; + } else if (typeof this.ops[this.index].retain === 'number') { + return 'retain'; + } else { + return 'insert'; + } + } + return 'retain'; +}; + +Iterator.prototype.rest = function () { + if (!this.hasNext()) { + return []; + } else if (this.offset === 0) { + return this.ops.slice(this.index); + } else { + var offset = this.offset; + var index = this.index; + var next = this.next(); + var rest = this.ops.slice(this.index); + this.offset = offset; + this.index = index; + return [next].concat(rest); + } +}; + + +module.exports = lib; + + +/***/ }), +/* 21 */ +/***/ (function(module, exports) { + +var clone = (function() { + +function _instanceof(obj, type) { + return type != null && obj instanceof type; +} + +var nativeMap; +try { + nativeMap = Map; +} catch(_) { + // maybe a reference error because no `Map`. Give it a dummy value that no + // value will ever be an instanceof. + nativeMap = function() {}; +} + +var nativeSet; +try { + nativeSet = Set; +} catch(_) { + nativeSet = function() {}; +} + +var nativePromise; +try { + nativePromise = Promise; +} catch(_) { + nativePromise = function() {}; +} + +/** + * Clones (copies) an Object using deep copying. + * + * This function supports circular references by default, but if you are certain + * there are no circular references in your object, you can save some CPU time + * by calling clone(obj, false). + * + * Caution: if `circular` is false and `parent` contains circular references, + * your program may enter an infinite loop and crash. + * + * @param `parent` - the object to be cloned + * @param `circular` - set to true if the object to be cloned may contain + * circular references. (optional - true by default) + * @param `depth` - set to a number if the object is only to be cloned to + * a particular depth. (optional - defaults to Infinity) + * @param `prototype` - sets the prototype to be used when cloning an object. + * (optional - defaults to parent prototype). + * @param `includeNonEnumerable` - set to true if the non-enumerable properties + * should be cloned as well. Non-enumerable properties on the prototype + * chain will be ignored. (optional - false by default) +*/ +function clone(parent, circular, depth, prototype, includeNonEnumerable) { + if (typeof circular === 'object') { + depth = circular.depth; + prototype = circular.prototype; + includeNonEnumerable = circular.includeNonEnumerable; + circular = circular.circular; + } + // maintain two arrays for circular references, where corresponding parents + // and children have the same index + var allParents = []; + var allChildren = []; + + var useBuffer = typeof Buffer != 'undefined'; + + if (typeof circular == 'undefined') + circular = true; + + if (typeof depth == 'undefined') + depth = Infinity; + + // recurse this function so we don't reset allParents and allChildren + function _clone(parent, depth) { + // cloning null always returns null + if (parent === null) + return null; + + if (depth === 0) + return parent; + + var child; + var proto; + if (typeof parent != 'object') { + return parent; + } + + if (_instanceof(parent, nativeMap)) { + child = new nativeMap(); + } else if (_instanceof(parent, nativeSet)) { + child = new nativeSet(); + } else if (_instanceof(parent, nativePromise)) { + child = new nativePromise(function (resolve, reject) { + parent.then(function(value) { + resolve(_clone(value, depth - 1)); + }, function(err) { + reject(_clone(err, depth - 1)); + }); + }); + } else if (clone.__isArray(parent)) { + child = []; + } else if (clone.__isRegExp(parent)) { + child = new RegExp(parent.source, __getRegExpFlags(parent)); + if (parent.lastIndex) child.lastIndex = parent.lastIndex; + } else if (clone.__isDate(parent)) { + child = new Date(parent.getTime()); + } else if (useBuffer && Buffer.isBuffer(parent)) { + if (Buffer.allocUnsafe) { + // Node.js >= 4.5.0 + child = Buffer.allocUnsafe(parent.length); + } else { + // Older Node.js versions + child = new Buffer(parent.length); + } + parent.copy(child); + return child; + } else if (_instanceof(parent, Error)) { + child = Object.create(parent); + } else { + if (typeof prototype == 'undefined') { + proto = Object.getPrototypeOf(parent); + child = Object.create(proto); + } + else { + child = Object.create(prototype); + proto = prototype; + } + } + + if (circular) { + var index = allParents.indexOf(parent); + + if (index != -1) { + return allChildren[index]; + } + allParents.push(parent); + allChildren.push(child); + } + + if (_instanceof(parent, nativeMap)) { + parent.forEach(function(value, key) { + var keyChild = _clone(key, depth - 1); + var valueChild = _clone(value, depth - 1); + child.set(keyChild, valueChild); + }); + } + if (_instanceof(parent, nativeSet)) { + parent.forEach(function(value) { + var entryChild = _clone(value, depth - 1); + child.add(entryChild); + }); + } + + for (var i in parent) { + var attrs; + if (proto) { + attrs = Object.getOwnPropertyDescriptor(proto, i); + } + + if (attrs && attrs.set == null) { + continue; + } + child[i] = _clone(parent[i], depth - 1); + } + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(parent); + for (var i = 0; i < symbols.length; i++) { + // Don't need to worry about cloning a symbol because it is a primitive, + // like a number or string. + var symbol = symbols[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, symbol); + if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { + continue; + } + child[symbol] = _clone(parent[symbol], depth - 1); + if (!descriptor.enumerable) { + Object.defineProperty(child, symbol, { + enumerable: false + }); + } + } + } + + if (includeNonEnumerable) { + var allPropertyNames = Object.getOwnPropertyNames(parent); + for (var i = 0; i < allPropertyNames.length; i++) { + var propertyName = allPropertyNames[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName); + if (descriptor && descriptor.enumerable) { + continue; + } + child[propertyName] = _clone(parent[propertyName], depth - 1); + Object.defineProperty(child, propertyName, { + enumerable: false + }); + } + } + + return child; + } + + return _clone(parent, depth); +} + +/** + * Simple flat clone using prototype, accepts only objects, usefull for property + * override on FLAT configuration object (no nested props). + * + * USE WITH CAUTION! This may not behave as you wish if you do not know how this + * works. + */ +clone.clonePrototype = function clonePrototype(parent) { + if (parent === null) + return null; + + var c = function () {}; + c.prototype = parent; + return new c(); +}; + +// private utility functions + +function __objToStr(o) { + return Object.prototype.toString.call(o); +} +clone.__objToStr = __objToStr; + +function __isDate(o) { + return typeof o === 'object' && __objToStr(o) === '[object Date]'; +} +clone.__isDate = __isDate; + +function __isArray(o) { + return typeof o === 'object' && __objToStr(o) === '[object Array]'; +} +clone.__isArray = __isArray; + +function __isRegExp(o) { + return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; +} +clone.__isRegExp = __isRegExp; + +function __getRegExpFlags(re) { + var flags = ''; + if (re.global) flags += 'g'; + if (re.ignoreCase) flags += 'i'; + if (re.multiline) flags += 'm'; + return flags; +} +clone.__getRegExpFlags = __getRegExpFlags; + +return clone; +})(); + +if (typeof module === 'object' && module.exports) { + module.exports = clone; +} + + +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _container = __webpack_require__(25); + +var _container2 = _interopRequireDefault(_container); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +function isLine(blot) { + return blot instanceof _block2.default || blot instanceof _block.BlockEmbed; +} + +var Scroll = function (_Parchment$Scroll) { + _inherits(Scroll, _Parchment$Scroll); + + function Scroll(domNode, config) { + _classCallCheck(this, Scroll); + + var _this = _possibleConstructorReturn(this, (Scroll.__proto__ || Object.getPrototypeOf(Scroll)).call(this, domNode)); + + _this.emitter = config.emitter; + if (Array.isArray(config.whitelist)) { + _this.whitelist = config.whitelist.reduce(function (whitelist, format) { + whitelist[format] = true; + return whitelist; + }, {}); + } + // Some reason fixes composition issues with character languages in Windows/Chrome, Safari + _this.domNode.addEventListener('DOMNodeInserted', function () {}); + _this.optimize(); + _this.enable(); + return _this; + } + + _createClass(Scroll, [{ + key: 'batchStart', + value: function batchStart() { + this.batch = true; + } + }, { + key: 'batchEnd', + value: function batchEnd() { + this.batch = false; + this.optimize(); + } + }, { + key: 'deleteAt', + value: function deleteAt(index, length) { + var _line = this.line(index), + _line2 = _slicedToArray(_line, 2), + first = _line2[0], + offset = _line2[1]; + + var _line3 = this.line(index + length), + _line4 = _slicedToArray(_line3, 1), + last = _line4[0]; + + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'deleteAt', this).call(this, index, length); + if (last != null && first !== last && offset > 0) { + if (first instanceof _block.BlockEmbed || last instanceof _block.BlockEmbed) { + this.optimize(); + return; + } + if (first instanceof _code2.default) { + var newlineIndex = first.newlineIndex(first.length(), true); + if (newlineIndex > -1) { + first = first.split(newlineIndex + 1); + if (first === last) { + this.optimize(); + return; + } + } + } else if (last instanceof _code2.default) { + var _newlineIndex = last.newlineIndex(0); + if (_newlineIndex > -1) { + last.split(_newlineIndex + 1); + } + } + var ref = last.children.head instanceof _break2.default ? null : last.children.head; + first.moveChildren(last, ref); + first.remove(); + } + this.optimize(); + } + }, { + key: 'enable', + value: function enable() { + var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + + this.domNode.setAttribute('contenteditable', enabled); + } + }, { + key: 'formatAt', + value: function formatAt(index, length, format, value) { + if (this.whitelist != null && !this.whitelist[format]) return; + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'formatAt', this).call(this, index, length, format, value); + this.optimize(); + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null && this.whitelist != null && !this.whitelist[value]) return; + if (index >= this.length()) { + if (def == null || _parchment2.default.query(value, _parchment2.default.Scope.BLOCK) == null) { + var blot = _parchment2.default.create(this.statics.defaultChild); + this.appendChild(blot); + if (def == null && value.endsWith('\n')) { + value = value.slice(0, -1); + } + blot.insertAt(0, value, def); + } else { + var embed = _parchment2.default.create(value, def); + this.appendChild(embed); + } + } else { + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertAt', this).call(this, index, value, def); + } + this.optimize(); + } + }, { + key: 'insertBefore', + value: function insertBefore(blot, ref) { + if (blot.statics.scope === _parchment2.default.Scope.INLINE_BLOT) { + var wrapper = _parchment2.default.create(this.statics.defaultChild); + wrapper.appendChild(blot); + blot = wrapper; + } + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertBefore', this).call(this, blot, ref); + } + }, { + key: 'leaf', + value: function leaf(index) { + return this.path(index).pop() || [null, -1]; + } + }, { + key: 'line', + value: function line(index) { + if (index === this.length()) { + return this.line(index - 1); + } + return this.descendant(isLine, index); + } + }, { + key: 'lines', + value: function lines() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE; + + var getLines = function getLines(blot, index, length) { + var lines = [], + lengthLeft = length; + blot.children.forEachAt(index, length, function (child, index, length) { + if (isLine(child)) { + lines.push(child); + } else if (child instanceof _parchment2.default.Container) { + lines = lines.concat(getLines(child, index, lengthLeft)); + } + lengthLeft -= length; + }); + return lines; + }; + return getLines(this, index, length); + } + }, { + key: 'optimize', + value: function optimize() { + var mutations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + if (this.batch === true) return; + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'optimize', this).call(this, mutations, context); + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_OPTIMIZE, mutations, context); + } + } + }, { + key: 'path', + value: function path(index) { + return _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'path', this).call(this, index).slice(1); // Exclude self + } + }, { + key: 'update', + value: function update(mutations) { + if (this.batch === true) return; + var source = _emitter2.default.sources.USER; + if (typeof mutations === 'string') { + source = mutations; + } + if (!Array.isArray(mutations)) { + mutations = this.observer.takeRecords(); + } + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_BEFORE_UPDATE, source, mutations); + } + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'update', this).call(this, mutations.concat([])); // pass copy + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_UPDATE, source, mutations); + } + } + }]); + + return Scroll; +}(_parchment2.default.Scroll); + +Scroll.blotName = 'scroll'; +Scroll.className = 'ql-editor'; +Scroll.tagName = 'DIV'; +Scroll.defaultChild = 'block'; +Scroll.allowedChildren = [_block2.default, _block.BlockEmbed, _container2.default]; + +exports.default = Scroll; + +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SHORTKEY = exports.default = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _op = __webpack_require__(20); + +var _op2 = _interopRequireDefault(_op); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:keyboard'); + +var SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey'; + +var Keyboard = function (_Module) { + _inherits(Keyboard, _Module); + + _createClass(Keyboard, null, [{ + key: 'match', + value: function match(evt, binding) { + binding = normalize(binding); + if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function (key) { + return !!binding[key] !== evt[key] && binding[key] !== null; + })) { + return false; + } + return binding.key === (evt.which || evt.keyCode); + } + }]); + + function Keyboard(quill, options) { + _classCallCheck(this, Keyboard); + + var _this = _possibleConstructorReturn(this, (Keyboard.__proto__ || Object.getPrototypeOf(Keyboard)).call(this, quill, options)); + + _this.bindings = {}; + Object.keys(_this.options.bindings).forEach(function (name) { + if (name === 'list autofill' && quill.scroll.whitelist != null && !quill.scroll.whitelist['list']) { + return; + } + if (_this.options.bindings[name]) { + _this.addBinding(_this.options.bindings[name]); + } + }); + _this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter); + _this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function () {}); + if (/Firefox/i.test(navigator.userAgent)) { + // Need to handle delete and backspace for Firefox in the general case #1171 + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete); + } else { + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete); + } + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange); + _this.addBinding({ key: Keyboard.keys.BACKSPACE, altKey: null, ctrlKey: null, metaKey: null, shiftKey: null }, { collapsed: true, offset: 0 }, handleBackspace); + _this.listen(); + return _this; + } + + _createClass(Keyboard, [{ + key: 'addBinding', + value: function addBinding(key) { + var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var handler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var binding = normalize(key); + if (binding == null || binding.key == null) { + return debug.warn('Attempted to add invalid keyboard binding', binding); + } + if (typeof context === 'function') { + context = { handler: context }; + } + if (typeof handler === 'function') { + handler = { handler: handler }; + } + binding = (0, _extend2.default)(binding, context, handler); + this.bindings[binding.key] = this.bindings[binding.key] || []; + this.bindings[binding.key].push(binding); + } + }, { + key: 'listen', + value: function listen() { + var _this2 = this; + + this.quill.root.addEventListener('keydown', function (evt) { + if (evt.defaultPrevented) return; + var which = evt.which || evt.keyCode; + var bindings = (_this2.bindings[which] || []).filter(function (binding) { + return Keyboard.match(evt, binding); + }); + if (bindings.length === 0) return; + var range = _this2.quill.getSelection(); + if (range == null || !_this2.quill.hasFocus()) return; + + var _quill$getLine = _this2.quill.getLine(range.index), + _quill$getLine2 = _slicedToArray(_quill$getLine, 2), + line = _quill$getLine2[0], + offset = _quill$getLine2[1]; + + var _quill$getLeaf = _this2.quill.getLeaf(range.index), + _quill$getLeaf2 = _slicedToArray(_quill$getLeaf, 2), + leafStart = _quill$getLeaf2[0], + offsetStart = _quill$getLeaf2[1]; + + var _ref = range.length === 0 ? [leafStart, offsetStart] : _this2.quill.getLeaf(range.index + range.length), + _ref2 = _slicedToArray(_ref, 2), + leafEnd = _ref2[0], + offsetEnd = _ref2[1]; + + var prefixText = leafStart instanceof _parchment2.default.Text ? leafStart.value().slice(0, offsetStart) : ''; + var suffixText = leafEnd instanceof _parchment2.default.Text ? leafEnd.value().slice(offsetEnd) : ''; + var curContext = { + collapsed: range.length === 0, + empty: range.length === 0 && line.length() <= 1, + format: _this2.quill.getFormat(range), + offset: offset, + prefix: prefixText, + suffix: suffixText + }; + var prevented = bindings.some(function (binding) { + if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false; + if (binding.empty != null && binding.empty !== curContext.empty) return false; + if (binding.offset != null && binding.offset !== curContext.offset) return false; + if (Array.isArray(binding.format)) { + // any format is present + if (binding.format.every(function (name) { + return curContext.format[name] == null; + })) { + return false; + } + } else if (_typeof(binding.format) === 'object') { + // all formats must match + if (!Object.keys(binding.format).every(function (name) { + if (binding.format[name] === true) return curContext.format[name] != null; + if (binding.format[name] === false) return curContext.format[name] == null; + return (0, _deepEqual2.default)(binding.format[name], curContext.format[name]); + })) { + return false; + } + } + if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false; + if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false; + return binding.handler.call(_this2, range, curContext) !== true; + }); + if (prevented) { + evt.preventDefault(); + } + }); + } + }]); + + return Keyboard; +}(_module2.default); + +Keyboard.keys = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + ESCAPE: 27, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46 +}; + +Keyboard.DEFAULTS = { + bindings: { + 'bold': makeFormatHandler('bold'), + 'italic': makeFormatHandler('italic'), + 'underline': makeFormatHandler('underline'), + 'indent': { + // highlight tab or tab at beginning of list, indent or blockquote + key: Keyboard.keys.TAB, + format: ['blockquote', 'indent', 'list'], + handler: function handler(range, context) { + if (context.collapsed && context.offset !== 0) return true; + this.quill.format('indent', '+1', _quill2.default.sources.USER); + } + }, + 'outdent': { + key: Keyboard.keys.TAB, + shiftKey: true, + format: ['blockquote', 'indent', 'list'], + // highlight tab or tab at beginning of list, indent or blockquote + handler: function handler(range, context) { + if (context.collapsed && context.offset !== 0) return true; + this.quill.format('indent', '-1', _quill2.default.sources.USER); + } + }, + 'outdent backspace': { + key: Keyboard.keys.BACKSPACE, + collapsed: true, + shiftKey: null, + metaKey: null, + ctrlKey: null, + altKey: null, + format: ['indent', 'list'], + offset: 0, + handler: function handler(range, context) { + if (context.format.indent != null) { + this.quill.format('indent', '-1', _quill2.default.sources.USER); + } else if (context.format.list != null) { + this.quill.format('list', false, _quill2.default.sources.USER); + } + } + }, + 'indent code-block': makeCodeBlockHandler(true), + 'outdent code-block': makeCodeBlockHandler(false), + 'remove tab': { + key: Keyboard.keys.TAB, + shiftKey: true, + collapsed: true, + prefix: /\t$/, + handler: function handler(range) { + this.quill.deleteText(range.index - 1, 1, _quill2.default.sources.USER); + } + }, + 'tab': { + key: Keyboard.keys.TAB, + handler: function handler(range) { + this.quill.history.cutoff(); + var delta = new _quillDelta2.default().retain(range.index).delete(range.length).insert('\t'); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.history.cutoff(); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + } + }, + 'list empty enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['list'], + empty: true, + handler: function handler(range, context) { + this.quill.format('list', false, _quill2.default.sources.USER); + if (context.format.indent) { + this.quill.format('indent', false, _quill2.default.sources.USER); + } + } + }, + 'checklist enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: { list: 'checked' }, + handler: function handler(range) { + var _quill$getLine3 = this.quill.getLine(range.index), + _quill$getLine4 = _slicedToArray(_quill$getLine3, 2), + line = _quill$getLine4[0], + offset = _quill$getLine4[1]; + + var formats = (0, _extend2.default)({}, line.formats(), { list: 'checked' }); + var delta = new _quillDelta2.default().retain(range.index).insert('\n', formats).retain(line.length() - offset - 1).retain(1, { list: 'unchecked' }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.scrollIntoView(); + } + }, + 'header enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['header'], + suffix: /^$/, + handler: function handler(range, context) { + var _quill$getLine5 = this.quill.getLine(range.index), + _quill$getLine6 = _slicedToArray(_quill$getLine5, 2), + line = _quill$getLine6[0], + offset = _quill$getLine6[1]; + + var delta = new _quillDelta2.default().retain(range.index).insert('\n', context.format).retain(line.length() - offset - 1).retain(1, { header: null }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.scrollIntoView(); + } + }, + 'list autofill': { + key: ' ', + collapsed: true, + format: { list: false }, + prefix: /^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/, + handler: function handler(range, context) { + var length = context.prefix.length; + + var _quill$getLine7 = this.quill.getLine(range.index), + _quill$getLine8 = _slicedToArray(_quill$getLine7, 2), + line = _quill$getLine8[0], + offset = _quill$getLine8[1]; + + if (offset > length) return true; + var value = void 0; + switch (context.prefix.trim()) { + case '[]':case '[ ]': + value = 'unchecked'; + break; + case '[x]': + value = 'checked'; + break; + case '-':case '*': + value = 'bullet'; + break; + default: + value = 'ordered'; + } + this.quill.insertText(range.index, ' ', _quill2.default.sources.USER); + this.quill.history.cutoff(); + var delta = new _quillDelta2.default().retain(range.index - offset).delete(length + 1).retain(line.length() - 2 - offset).retain(1, { list: value }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.history.cutoff(); + this.quill.setSelection(range.index - length, _quill2.default.sources.SILENT); + } + }, + 'code exit': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['code-block'], + prefix: /\n\n$/, + suffix: /^\s+$/, + handler: function handler(range) { + var _quill$getLine9 = this.quill.getLine(range.index), + _quill$getLine10 = _slicedToArray(_quill$getLine9, 2), + line = _quill$getLine10[0], + offset = _quill$getLine10[1]; + + var delta = new _quillDelta2.default().retain(range.index + line.length() - offset - 2).retain(1, { 'code-block': null }).delete(1); + this.quill.updateContents(delta, _quill2.default.sources.USER); + } + }, + 'embed left': makeEmbedArrowHandler(Keyboard.keys.LEFT, false), + 'embed left shift': makeEmbedArrowHandler(Keyboard.keys.LEFT, true), + 'embed right': makeEmbedArrowHandler(Keyboard.keys.RIGHT, false), + 'embed right shift': makeEmbedArrowHandler(Keyboard.keys.RIGHT, true) + } +}; + +function makeEmbedArrowHandler(key, shiftKey) { + var _ref3; + + var where = key === Keyboard.keys.LEFT ? 'prefix' : 'suffix'; + return _ref3 = { + key: key, + shiftKey: shiftKey, + altKey: null + }, _defineProperty(_ref3, where, /^$/), _defineProperty(_ref3, 'handler', function handler(range) { + var index = range.index; + if (key === Keyboard.keys.RIGHT) { + index += range.length + 1; + } + + var _quill$getLeaf3 = this.quill.getLeaf(index), + _quill$getLeaf4 = _slicedToArray(_quill$getLeaf3, 1), + leaf = _quill$getLeaf4[0]; + + if (!(leaf instanceof _parchment2.default.Embed)) return true; + if (key === Keyboard.keys.LEFT) { + if (shiftKey) { + this.quill.setSelection(range.index - 1, range.length + 1, _quill2.default.sources.USER); + } else { + this.quill.setSelection(range.index - 1, _quill2.default.sources.USER); + } + } else { + if (shiftKey) { + this.quill.setSelection(range.index, range.length + 1, _quill2.default.sources.USER); + } else { + this.quill.setSelection(range.index + range.length + 1, _quill2.default.sources.USER); + } + } + return false; + }), _ref3; +} + +function handleBackspace(range, context) { + if (range.index === 0 || this.quill.getLength() <= 1) return; + + var _quill$getLine11 = this.quill.getLine(range.index), + _quill$getLine12 = _slicedToArray(_quill$getLine11, 1), + line = _quill$getLine12[0]; + + var formats = {}; + if (context.offset === 0) { + var _quill$getLine13 = this.quill.getLine(range.index - 1), + _quill$getLine14 = _slicedToArray(_quill$getLine13, 1), + prev = _quill$getLine14[0]; + + if (prev != null && prev.length() > 1) { + var curFormats = line.formats(); + var prevFormats = this.quill.getFormat(range.index - 1, 1); + formats = _op2.default.attributes.diff(curFormats, prevFormats) || {}; + } + } + // Check for astral symbols + var length = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(context.prefix) ? 2 : 1; + this.quill.deleteText(range.index - length, length, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index - length, length, formats, _quill2.default.sources.USER); + } + this.quill.focus(); +} + +function handleDelete(range, context) { + // Check for astral symbols + var length = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(context.suffix) ? 2 : 1; + if (range.index >= this.quill.getLength() - length) return; + var formats = {}, + nextLength = 0; + + var _quill$getLine15 = this.quill.getLine(range.index), + _quill$getLine16 = _slicedToArray(_quill$getLine15, 1), + line = _quill$getLine16[0]; + + if (context.offset >= line.length() - 1) { + var _quill$getLine17 = this.quill.getLine(range.index + 1), + _quill$getLine18 = _slicedToArray(_quill$getLine17, 1), + next = _quill$getLine18[0]; + + if (next) { + var curFormats = line.formats(); + var nextFormats = this.quill.getFormat(range.index, 1); + formats = _op2.default.attributes.diff(curFormats, nextFormats) || {}; + nextLength = next.length(); + } + } + this.quill.deleteText(range.index, length, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index + nextLength - 1, length, formats, _quill2.default.sources.USER); + } +} + +function handleDeleteRange(range) { + var lines = this.quill.getLines(range); + var formats = {}; + if (lines.length > 1) { + var firstFormats = lines[0].formats(); + var lastFormats = lines[lines.length - 1].formats(); + formats = _op2.default.attributes.diff(lastFormats, firstFormats) || {}; + } + this.quill.deleteText(range, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index, 1, formats, _quill2.default.sources.USER); + } + this.quill.setSelection(range.index, _quill2.default.sources.SILENT); + this.quill.focus(); +} + +function handleEnter(range, context) { + var _this3 = this; + + if (range.length > 0) { + this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change + } + var lineFormats = Object.keys(context.format).reduce(function (lineFormats, format) { + if (_parchment2.default.query(format, _parchment2.default.Scope.BLOCK) && !Array.isArray(context.format[format])) { + lineFormats[format] = context.format[format]; + } + return lineFormats; + }, {}); + this.quill.insertText(range.index, '\n', lineFormats, _quill2.default.sources.USER); + // Earlier scroll.deleteAt might have messed up our selection, + // so insertText's built in selection preservation is not reliable + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.focus(); + Object.keys(context.format).forEach(function (name) { + if (lineFormats[name] != null) return; + if (Array.isArray(context.format[name])) return; + if (name === 'link') return; + _this3.quill.format(name, context.format[name], _quill2.default.sources.USER); + }); +} + +function makeCodeBlockHandler(indent) { + return { + key: Keyboard.keys.TAB, + shiftKey: !indent, + format: { 'code-block': true }, + handler: function handler(range) { + var CodeBlock = _parchment2.default.query('code-block'); + var index = range.index, + length = range.length; + + var _quill$scroll$descend = this.quill.scroll.descendant(CodeBlock, index), + _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2), + block = _quill$scroll$descend2[0], + offset = _quill$scroll$descend2[1]; + + if (block == null) return; + var scrollIndex = this.quill.getIndex(block); + var start = block.newlineIndex(offset, true) + 1; + var end = block.newlineIndex(scrollIndex + offset + length); + var lines = block.domNode.textContent.slice(start, end).split('\n'); + offset = 0; + lines.forEach(function (line, i) { + if (indent) { + block.insertAt(start + offset, CodeBlock.TAB); + offset += CodeBlock.TAB.length; + if (i === 0) { + index += CodeBlock.TAB.length; + } else { + length += CodeBlock.TAB.length; + } + } else if (line.startsWith(CodeBlock.TAB)) { + block.deleteAt(start + offset, CodeBlock.TAB.length); + offset -= CodeBlock.TAB.length; + if (i === 0) { + index -= CodeBlock.TAB.length; + } else { + length -= CodeBlock.TAB.length; + } + } + offset += line.length + 1; + }); + this.quill.update(_quill2.default.sources.USER); + this.quill.setSelection(index, length, _quill2.default.sources.SILENT); + } + }; +} + +function makeFormatHandler(format) { + return { + key: format[0].toUpperCase(), + shortKey: true, + handler: function handler(range, context) { + this.quill.format(format, !context.format[format], _quill2.default.sources.USER); + } + }; +} + +function normalize(binding) { + if (typeof binding === 'string' || typeof binding === 'number') { + return normalize({ key: binding }); + } + if ((typeof binding === 'undefined' ? 'undefined' : _typeof(binding)) === 'object') { + binding = (0, _clone2.default)(binding, false); + } + if (typeof binding.key === 'string') { + if (Keyboard.keys[binding.key.toUpperCase()] != null) { + binding.key = Keyboard.keys[binding.key.toUpperCase()]; + } else if (binding.key.length === 1) { + binding.key = binding.key.toUpperCase().charCodeAt(0); + } else { + return null; + } + } + if (binding.shortKey) { + binding[SHORTKEY] = binding.shortKey; + delete binding.shortKey; + } + return binding; +} + +exports.default = Keyboard; +exports.SHORTKEY = SHORTKEY; + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Cursor = function (_Parchment$Embed) { + _inherits(Cursor, _Parchment$Embed); + + _createClass(Cursor, null, [{ + key: 'value', + value: function value() { + return undefined; + } + }]); + + function Cursor(domNode, selection) { + _classCallCheck(this, Cursor); + + var _this = _possibleConstructorReturn(this, (Cursor.__proto__ || Object.getPrototypeOf(Cursor)).call(this, domNode)); + + _this.selection = selection; + _this.textNode = document.createTextNode(Cursor.CONTENTS); + _this.domNode.appendChild(_this.textNode); + _this._length = 0; + return _this; + } + + _createClass(Cursor, [{ + key: 'detach', + value: function detach() { + // super.detach() will also clear domNode.__blot + if (this.parent != null) this.parent.removeChild(this); + } + }, { + key: 'format', + value: function format(name, value) { + if (this._length !== 0) { + return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'format', this).call(this, name, value); + } + var target = this, + index = 0; + while (target != null && target.statics.scope !== _parchment2.default.Scope.BLOCK_BLOT) { + index += target.offset(target.parent); + target = target.parent; + } + if (target != null) { + this._length = Cursor.CONTENTS.length; + target.optimize(); + target.formatAt(index, Cursor.CONTENTS.length, name, value); + this._length = 0; + } + } + }, { + key: 'index', + value: function index(node, offset) { + if (node === this.textNode) return 0; + return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'index', this).call(this, node, offset); + } + }, { + key: 'length', + value: function length() { + return this._length; + } + }, { + key: 'position', + value: function position() { + return [this.textNode, this.textNode.data.length]; + } + }, { + key: 'remove', + value: function remove() { + _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'remove', this).call(this); + this.parent = null; + } + }, { + key: 'restore', + value: function restore() { + if (this.selection.composing || this.parent == null) return; + var textNode = this.textNode; + var range = this.selection.getNativeRange(); + var restoreText = void 0, + start = void 0, + end = void 0; + if (range != null && range.start.node === textNode && range.end.node === textNode) { + var _ref = [textNode, range.start.offset, range.end.offset]; + restoreText = _ref[0]; + start = _ref[1]; + end = _ref[2]; + } + // Link format will insert text outside of anchor tag + while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) { + this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode); + } + if (this.textNode.data !== Cursor.CONTENTS) { + var text = this.textNode.data.split(Cursor.CONTENTS).join(''); + if (this.next instanceof _text2.default) { + restoreText = this.next.domNode; + this.next.insertAt(0, text); + this.textNode.data = Cursor.CONTENTS; + } else { + this.textNode.data = text; + this.parent.insertBefore(_parchment2.default.create(this.textNode), this); + this.textNode = document.createTextNode(Cursor.CONTENTS); + this.domNode.appendChild(this.textNode); + } + } + this.remove(); + if (start != null) { + var _map = [start, end].map(function (offset) { + return Math.max(0, Math.min(restoreText.data.length, offset - 1)); + }); + + var _map2 = _slicedToArray(_map, 2); + + start = _map2[0]; + end = _map2[1]; + + return { + startNode: restoreText, + startOffset: start, + endNode: restoreText, + endOffset: end + }; + } + } + }, { + key: 'update', + value: function update(mutations, context) { + var _this2 = this; + + if (mutations.some(function (mutation) { + return mutation.type === 'characterData' && mutation.target === _this2.textNode; + })) { + var range = this.restore(); + if (range) context.range = range; + } + } + }, { + key: 'value', + value: function value() { + return ''; + } + }]); + + return Cursor; +}(_parchment2.default.Embed); + +Cursor.blotName = 'cursor'; +Cursor.className = 'ql-cursor'; +Cursor.tagName = 'span'; +Cursor.CONTENTS = '\uFEFF'; // Zero width no break space + + +exports.default = Cursor; + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Container = function (_Parchment$Container) { + _inherits(Container, _Parchment$Container); + + function Container() { + _classCallCheck(this, Container); + + return _possibleConstructorReturn(this, (Container.__proto__ || Object.getPrototypeOf(Container)).apply(this, arguments)); + } + + return Container; +}(_parchment2.default.Container); + +Container.allowedChildren = [_block2.default, _block.BlockEmbed, Container]; + +exports.default = Container; + +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ColorStyle = exports.ColorClass = exports.ColorAttributor = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ColorAttributor = function (_Parchment$Attributor) { + _inherits(ColorAttributor, _Parchment$Attributor); + + function ColorAttributor() { + _classCallCheck(this, ColorAttributor); + + return _possibleConstructorReturn(this, (ColorAttributor.__proto__ || Object.getPrototypeOf(ColorAttributor)).apply(this, arguments)); + } + + _createClass(ColorAttributor, [{ + key: 'value', + value: function value(domNode) { + var value = _get(ColorAttributor.prototype.__proto__ || Object.getPrototypeOf(ColorAttributor.prototype), 'value', this).call(this, domNode); + if (!value.startsWith('rgb(')) return value; + value = value.replace(/^[^\d]+/, '').replace(/[^\d]+$/, ''); + return '#' + value.split(',').map(function (component) { + return ('00' + parseInt(component).toString(16)).slice(-2); + }).join(''); + } + }]); + + return ColorAttributor; +}(_parchment2.default.Attributor.Style); + +var ColorClass = new _parchment2.default.Attributor.Class('color', 'ql-color', { + scope: _parchment2.default.Scope.INLINE +}); +var ColorStyle = new ColorAttributor('color', 'color', { + scope: _parchment2.default.Scope.INLINE +}); + +exports.ColorAttributor = ColorAttributor; +exports.ColorClass = ColorClass; +exports.ColorStyle = ColorStyle; + +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sanitize = exports.default = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Link = function (_Inline) { + _inherits(Link, _Inline); + + function Link() { + _classCallCheck(this, Link); + + return _possibleConstructorReturn(this, (Link.__proto__ || Object.getPrototypeOf(Link)).apply(this, arguments)); + } + + _createClass(Link, [{ + key: 'format', + value: function format(name, value) { + if (name !== this.statics.blotName || !value) return _get(Link.prototype.__proto__ || Object.getPrototypeOf(Link.prototype), 'format', this).call(this, name, value); + value = this.constructor.sanitize(value); + this.domNode.setAttribute('href', value); + } + }], [{ + key: 'create', + value: function create(value) { + var node = _get(Link.__proto__ || Object.getPrototypeOf(Link), 'create', this).call(this, value); + value = this.sanitize(value); + node.setAttribute('href', value); + node.setAttribute('rel', 'noopener noreferrer'); + node.setAttribute('target', '_blank'); + return node; + } + }, { + key: 'formats', + value: function formats(domNode) { + return domNode.getAttribute('href'); + } + }, { + key: 'sanitize', + value: function sanitize(url) { + return _sanitize(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL; + } + }]); + + return Link; +}(_inline2.default); + +Link.blotName = 'link'; +Link.tagName = 'A'; +Link.SANITIZED_URL = 'about:blank'; +Link.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel']; + +function _sanitize(url, protocols) { + var anchor = document.createElement('a'); + anchor.href = url; + var protocol = anchor.href.slice(0, anchor.href.indexOf(':')); + return protocols.indexOf(protocol) > -1; +} + +exports.default = Link; +exports.sanitize = _sanitize; + +/***/ }), +/* 28 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _keyboard = __webpack_require__(23); + +var _keyboard2 = _interopRequireDefault(_keyboard); + +var _dropdown = __webpack_require__(107); + +var _dropdown2 = _interopRequireDefault(_dropdown); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var optionsCounter = 0; + +function toggleAriaAttribute(element, attribute) { + element.setAttribute(attribute, !(element.getAttribute(attribute) === 'true')); +} + +var Picker = function () { + function Picker(select) { + var _this = this; + + _classCallCheck(this, Picker); + + this.select = select; + this.container = document.createElement('span'); + this.buildPicker(); + this.select.style.display = 'none'; + this.select.parentNode.insertBefore(this.container, this.select); + + this.label.addEventListener('mousedown', function () { + _this.togglePicker(); + }); + this.label.addEventListener('keydown', function (event) { + switch (event.keyCode) { + // Allows the "Enter" key to open the picker + case _keyboard2.default.keys.ENTER: + _this.togglePicker(); + break; + + // Allows the "Escape" key to close the picker + case _keyboard2.default.keys.ESCAPE: + _this.escape(); + event.preventDefault(); + break; + } + }); + this.select.addEventListener('change', this.update.bind(this)); + } + + _createClass(Picker, [{ + key: 'togglePicker', + value: function togglePicker() { + this.container.classList.toggle('ql-expanded'); + // Toggle aria-expanded and aria-hidden to make the picker accessible + toggleAriaAttribute(this.label, 'aria-expanded'); + toggleAriaAttribute(this.options, 'aria-hidden'); + } + }, { + key: 'buildItem', + value: function buildItem(option) { + var _this2 = this; + + var item = document.createElement('span'); + item.tabIndex = '0'; + item.setAttribute('role', 'button'); + + item.classList.add('ql-picker-item'); + if (option.hasAttribute('value')) { + item.setAttribute('data-value', option.getAttribute('value')); + } + if (option.textContent) { + item.setAttribute('data-label', option.textContent); + } + item.addEventListener('click', function () { + _this2.selectItem(item, true); + }); + item.addEventListener('keydown', function (event) { + switch (event.keyCode) { + // Allows the "Enter" key to select an item + case _keyboard2.default.keys.ENTER: + _this2.selectItem(item, true); + event.preventDefault(); + break; + + // Allows the "Escape" key to close the picker + case _keyboard2.default.keys.ESCAPE: + _this2.escape(); + event.preventDefault(); + break; + } + }); + + return item; + } + }, { + key: 'buildLabel', + value: function buildLabel() { + var label = document.createElement('span'); + label.classList.add('ql-picker-label'); + label.innerHTML = _dropdown2.default; + label.tabIndex = '0'; + label.setAttribute('role', 'button'); + label.setAttribute('aria-expanded', 'false'); + this.container.appendChild(label); + return label; + } + }, { + key: 'buildOptions', + value: function buildOptions() { + var _this3 = this; + + var options = document.createElement('span'); + options.classList.add('ql-picker-options'); + + // Don't want screen readers to read this until options are visible + options.setAttribute('aria-hidden', 'true'); + options.tabIndex = '-1'; + + // Need a unique id for aria-controls + options.id = 'ql-picker-options-' + optionsCounter; + optionsCounter += 1; + this.label.setAttribute('aria-controls', options.id); + + this.options = options; + + [].slice.call(this.select.options).forEach(function (option) { + var item = _this3.buildItem(option); + options.appendChild(item); + if (option.selected === true) { + _this3.selectItem(item); + } + }); + this.container.appendChild(options); + } + }, { + key: 'buildPicker', + value: function buildPicker() { + var _this4 = this; + + [].slice.call(this.select.attributes).forEach(function (item) { + _this4.container.setAttribute(item.name, item.value); + }); + this.container.classList.add('ql-picker'); + this.label = this.buildLabel(); + this.buildOptions(); + } + }, { + key: 'escape', + value: function escape() { + var _this5 = this; + + // Close menu and return focus to trigger label + this.close(); + // Need setTimeout for accessibility to ensure that the browser executes + // focus on the next process thread and after any DOM content changes + setTimeout(function () { + return _this5.label.focus(); + }, 1); + } + }, { + key: 'close', + value: function close() { + this.container.classList.remove('ql-expanded'); + this.label.setAttribute('aria-expanded', 'false'); + this.options.setAttribute('aria-hidden', 'true'); + } + }, { + key: 'selectItem', + value: function selectItem(item) { + var trigger = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var selected = this.container.querySelector('.ql-selected'); + if (item === selected) return; + if (selected != null) { + selected.classList.remove('ql-selected'); + } + if (item == null) return; + item.classList.add('ql-selected'); + this.select.selectedIndex = [].indexOf.call(item.parentNode.children, item); + if (item.hasAttribute('data-value')) { + this.label.setAttribute('data-value', item.getAttribute('data-value')); + } else { + this.label.removeAttribute('data-value'); + } + if (item.hasAttribute('data-label')) { + this.label.setAttribute('data-label', item.getAttribute('data-label')); + } else { + this.label.removeAttribute('data-label'); + } + if (trigger) { + if (typeof Event === 'function') { + this.select.dispatchEvent(new Event('change')); + } else if ((typeof Event === 'undefined' ? 'undefined' : _typeof(Event)) === 'object') { + // IE11 + var event = document.createEvent('Event'); + event.initEvent('change', true, true); + this.select.dispatchEvent(event); + } + this.close(); + } + } + }, { + key: 'update', + value: function update() { + var option = void 0; + if (this.select.selectedIndex > -1) { + var item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex]; + option = this.select.options[this.select.selectedIndex]; + this.selectItem(item); + } else { + this.selectItem(null); + } + var isActive = option != null && option !== this.select.querySelector('option[selected]'); + this.label.classList.toggle('ql-active', isActive); + } + }]); + + return Picker; +}(); + +exports.default = Picker; + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _container = __webpack_require__(25); + +var _container2 = _interopRequireDefault(_container); + +var _cursor = __webpack_require__(24); + +var _cursor2 = _interopRequireDefault(_cursor); + +var _embed = __webpack_require__(35); + +var _embed2 = _interopRequireDefault(_embed); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _scroll = __webpack_require__(22); + +var _scroll2 = _interopRequireDefault(_scroll); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +var _clipboard = __webpack_require__(55); + +var _clipboard2 = _interopRequireDefault(_clipboard); + +var _history = __webpack_require__(42); + +var _history2 = _interopRequireDefault(_history); + +var _keyboard = __webpack_require__(23); + +var _keyboard2 = _interopRequireDefault(_keyboard); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +_quill2.default.register({ + 'blots/block': _block2.default, + 'blots/block/embed': _block.BlockEmbed, + 'blots/break': _break2.default, + 'blots/container': _container2.default, + 'blots/cursor': _cursor2.default, + 'blots/embed': _embed2.default, + 'blots/inline': _inline2.default, + 'blots/scroll': _scroll2.default, + 'blots/text': _text2.default, + + 'modules/clipboard': _clipboard2.default, + 'modules/history': _history2.default, + 'modules/keyboard': _keyboard2.default +}); + +_parchment2.default.register(_block2.default, _break2.default, _cursor2.default, _inline2.default, _scroll2.default, _text2.default); + +exports.default = _quill2.default; + +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var Registry = __webpack_require__(1); +var ShadowBlot = /** @class */ (function () { + function ShadowBlot(domNode) { + this.domNode = domNode; + // @ts-ignore + this.domNode[Registry.DATA_KEY] = { blot: this }; + } + Object.defineProperty(ShadowBlot.prototype, "statics", { + // Hack for accessing inherited static methods + get: function () { + return this.constructor; + }, + enumerable: true, + configurable: true + }); + ShadowBlot.create = function (value) { + if (this.tagName == null) { + throw new Registry.ParchmentError('Blot definition missing tagName'); + } + var node; + if (Array.isArray(this.tagName)) { + if (typeof value === 'string') { + value = value.toUpperCase(); + if (parseInt(value).toString() === value) { + value = parseInt(value); + } + } + if (typeof value === 'number') { + node = document.createElement(this.tagName[value - 1]); + } + else if (this.tagName.indexOf(value) > -1) { + node = document.createElement(value); + } + else { + node = document.createElement(this.tagName[0]); + } + } + else { + node = document.createElement(this.tagName); + } + if (this.className) { + node.classList.add(this.className); + } + return node; + }; + ShadowBlot.prototype.attach = function () { + if (this.parent != null) { + this.scroll = this.parent.scroll; + } + }; + ShadowBlot.prototype.clone = function () { + var domNode = this.domNode.cloneNode(false); + return Registry.create(domNode); + }; + ShadowBlot.prototype.detach = function () { + if (this.parent != null) + this.parent.removeChild(this); + // @ts-ignore + delete this.domNode[Registry.DATA_KEY]; + }; + ShadowBlot.prototype.deleteAt = function (index, length) { + var blot = this.isolate(index, length); + blot.remove(); + }; + ShadowBlot.prototype.formatAt = function (index, length, name, value) { + var blot = this.isolate(index, length); + if (Registry.query(name, Registry.Scope.BLOT) != null && value) { + blot.wrap(name, value); + } + else if (Registry.query(name, Registry.Scope.ATTRIBUTE) != null) { + var parent = Registry.create(this.statics.scope); + blot.wrap(parent); + parent.format(name, value); + } + }; + ShadowBlot.prototype.insertAt = function (index, value, def) { + var blot = def == null ? Registry.create('text', value) : Registry.create(value, def); + var ref = this.split(index); + this.parent.insertBefore(blot, ref); + }; + ShadowBlot.prototype.insertInto = function (parentBlot, refBlot) { + if (refBlot === void 0) { refBlot = null; } + if (this.parent != null) { + this.parent.children.remove(this); + } + var refDomNode = null; + parentBlot.children.insertBefore(this, refBlot); + if (refBlot != null) { + refDomNode = refBlot.domNode; + } + if (this.domNode.parentNode != parentBlot.domNode || + this.domNode.nextSibling != refDomNode) { + parentBlot.domNode.insertBefore(this.domNode, refDomNode); + } + this.parent = parentBlot; + this.attach(); + }; + ShadowBlot.prototype.isolate = function (index, length) { + var target = this.split(index); + target.split(length); + return target; + }; + ShadowBlot.prototype.length = function () { + return 1; + }; + ShadowBlot.prototype.offset = function (root) { + if (root === void 0) { root = this.parent; } + if (this.parent == null || this == root) + return 0; + return this.parent.children.offset(this) + this.parent.offset(root); + }; + ShadowBlot.prototype.optimize = function (context) { + // TODO clean up once we use WeakMap + // @ts-ignore + if (this.domNode[Registry.DATA_KEY] != null) { + // @ts-ignore + delete this.domNode[Registry.DATA_KEY].mutations; + } + }; + ShadowBlot.prototype.remove = function () { + if (this.domNode.parentNode != null) { + this.domNode.parentNode.removeChild(this.domNode); + } + this.detach(); + }; + ShadowBlot.prototype.replace = function (target) { + if (target.parent == null) + return; + target.parent.insertBefore(this, target.next); + target.remove(); + }; + ShadowBlot.prototype.replaceWith = function (name, value) { + var replacement = typeof name === 'string' ? Registry.create(name, value) : name; + replacement.replace(this); + return replacement; + }; + ShadowBlot.prototype.split = function (index, force) { + return index === 0 ? this : this.next; + }; + ShadowBlot.prototype.update = function (mutations, context) { + // Nothing to do by default + }; + ShadowBlot.prototype.wrap = function (name, value) { + var wrapper = typeof name === 'string' ? Registry.create(name, value) : name; + if (this.parent != null) { + this.parent.insertBefore(wrapper, this.next); + } + wrapper.appendChild(this); + return wrapper; + }; + ShadowBlot.blotName = 'abstract'; + return ShadowBlot; +}()); +exports.default = ShadowBlot; + + +/***/ }), +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +var class_1 = __webpack_require__(32); +var style_1 = __webpack_require__(33); +var Registry = __webpack_require__(1); +var AttributorStore = /** @class */ (function () { + function AttributorStore(domNode) { + this.attributes = {}; + this.domNode = domNode; + this.build(); + } + AttributorStore.prototype.attribute = function (attribute, value) { + // verb + if (value) { + if (attribute.add(this.domNode, value)) { + if (attribute.value(this.domNode) != null) { + this.attributes[attribute.attrName] = attribute; + } + else { + delete this.attributes[attribute.attrName]; + } + } + } + else { + attribute.remove(this.domNode); + delete this.attributes[attribute.attrName]; + } + }; + AttributorStore.prototype.build = function () { + var _this = this; + this.attributes = {}; + var attributes = attributor_1.default.keys(this.domNode); + var classes = class_1.default.keys(this.domNode); + var styles = style_1.default.keys(this.domNode); + attributes + .concat(classes) + .concat(styles) + .forEach(function (name) { + var attr = Registry.query(name, Registry.Scope.ATTRIBUTE); + if (attr instanceof attributor_1.default) { + _this.attributes[attr.attrName] = attr; + } + }); + }; + AttributorStore.prototype.copy = function (target) { + var _this = this; + Object.keys(this.attributes).forEach(function (key) { + var value = _this.attributes[key].value(_this.domNode); + target.format(key, value); + }); + }; + AttributorStore.prototype.move = function (target) { + var _this = this; + this.copy(target); + Object.keys(this.attributes).forEach(function (key) { + _this.attributes[key].remove(_this.domNode); + }); + this.attributes = {}; + }; + AttributorStore.prototype.values = function () { + var _this = this; + return Object.keys(this.attributes).reduce(function (attributes, name) { + attributes[name] = _this.attributes[name].value(_this.domNode); + return attributes; + }, {}); + }; + return AttributorStore; +}()); +exports.default = AttributorStore; + + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +function match(node, prefix) { + var className = node.getAttribute('class') || ''; + return className.split(/\s+/).filter(function (name) { + return name.indexOf(prefix + "-") === 0; + }); +} +var ClassAttributor = /** @class */ (function (_super) { + __extends(ClassAttributor, _super); + function ClassAttributor() { + return _super !== null && _super.apply(this, arguments) || this; + } + ClassAttributor.keys = function (node) { + return (node.getAttribute('class') || '').split(/\s+/).map(function (name) { + return name + .split('-') + .slice(0, -1) + .join('-'); + }); + }; + ClassAttributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + this.remove(node); + node.classList.add(this.keyName + "-" + value); + return true; + }; + ClassAttributor.prototype.remove = function (node) { + var matches = match(node, this.keyName); + matches.forEach(function (name) { + node.classList.remove(name); + }); + if (node.classList.length === 0) { + node.removeAttribute('class'); + } + }; + ClassAttributor.prototype.value = function (node) { + var result = match(node, this.keyName)[0] || ''; + var value = result.slice(this.keyName.length + 1); // +1 for hyphen + return this.canAdd(node, value) ? value : ''; + }; + return ClassAttributor; +}(attributor_1.default)); +exports.default = ClassAttributor; + + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +function camelize(name) { + var parts = name.split('-'); + var rest = parts + .slice(1) + .map(function (part) { + return part[0].toUpperCase() + part.slice(1); + }) + .join(''); + return parts[0] + rest; +} +var StyleAttributor = /** @class */ (function (_super) { + __extends(StyleAttributor, _super); + function StyleAttributor() { + return _super !== null && _super.apply(this, arguments) || this; + } + StyleAttributor.keys = function (node) { + return (node.getAttribute('style') || '').split(';').map(function (value) { + var arr = value.split(':'); + return arr[0].trim(); + }); + }; + StyleAttributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + // @ts-ignore + node.style[camelize(this.keyName)] = value; + return true; + }; + StyleAttributor.prototype.remove = function (node) { + // @ts-ignore + node.style[camelize(this.keyName)] = ''; + if (!node.getAttribute('style')) { + node.removeAttribute('style'); + } + }; + StyleAttributor.prototype.value = function (node) { + // @ts-ignore + var value = node.style[camelize(this.keyName)]; + return this.canAdd(node, value) ? value : ''; + }; + return StyleAttributor; +}(attributor_1.default)); +exports.default = StyleAttributor; + + +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Theme = function () { + function Theme(quill, options) { + _classCallCheck(this, Theme); + + this.quill = quill; + this.options = options; + this.modules = {}; + } + + _createClass(Theme, [{ + key: 'init', + value: function init() { + var _this = this; + + Object.keys(this.options.modules).forEach(function (name) { + if (_this.modules[name] == null) { + _this.addModule(name); + } + }); + } + }, { + key: 'addModule', + value: function addModule(name) { + var moduleClass = this.quill.constructor.import('modules/' + name); + this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {}); + return this.modules[name]; + } + }]); + + return Theme; +}(); + +Theme.DEFAULTS = { + modules: {} +}; +Theme.themes = { + 'default': Theme +}; + +exports.default = Theme; + +/***/ }), +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var GUARD_TEXT = '\uFEFF'; + +var Embed = function (_Parchment$Embed) { + _inherits(Embed, _Parchment$Embed); + + function Embed(node) { + _classCallCheck(this, Embed); + + var _this = _possibleConstructorReturn(this, (Embed.__proto__ || Object.getPrototypeOf(Embed)).call(this, node)); + + _this.contentNode = document.createElement('span'); + _this.contentNode.setAttribute('contenteditable', false); + [].slice.call(_this.domNode.childNodes).forEach(function (childNode) { + _this.contentNode.appendChild(childNode); + }); + _this.leftGuard = document.createTextNode(GUARD_TEXT); + _this.rightGuard = document.createTextNode(GUARD_TEXT); + _this.domNode.appendChild(_this.leftGuard); + _this.domNode.appendChild(_this.contentNode); + _this.domNode.appendChild(_this.rightGuard); + return _this; + } + + _createClass(Embed, [{ + key: 'index', + value: function index(node, offset) { + if (node === this.leftGuard) return 0; + if (node === this.rightGuard) return 1; + return _get(Embed.prototype.__proto__ || Object.getPrototypeOf(Embed.prototype), 'index', this).call(this, node, offset); + } + }, { + key: 'restore', + value: function restore(node) { + var range = void 0, + textNode = void 0; + var text = node.data.split(GUARD_TEXT).join(''); + if (node === this.leftGuard) { + if (this.prev instanceof _text2.default) { + var prevLength = this.prev.length(); + this.prev.insertAt(prevLength, text); + range = { + startNode: this.prev.domNode, + startOffset: prevLength + text.length + }; + } else { + textNode = document.createTextNode(text); + this.parent.insertBefore(_parchment2.default.create(textNode), this); + range = { + startNode: textNode, + startOffset: text.length + }; + } + } else if (node === this.rightGuard) { + if (this.next instanceof _text2.default) { + this.next.insertAt(0, text); + range = { + startNode: this.next.domNode, + startOffset: text.length + }; + } else { + textNode = document.createTextNode(text); + this.parent.insertBefore(_parchment2.default.create(textNode), this.next); + range = { + startNode: textNode, + startOffset: text.length + }; + } + } + node.data = GUARD_TEXT; + return range; + } + }, { + key: 'update', + value: function update(mutations, context) { + var _this2 = this; + + mutations.forEach(function (mutation) { + if (mutation.type === 'characterData' && (mutation.target === _this2.leftGuard || mutation.target === _this2.rightGuard)) { + var range = _this2.restore(mutation.target); + if (range) context.range = range; + } + }); + } + }]); + + return Embed; +}(_parchment2.default.Embed); + +exports.default = Embed; + +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.AlignStyle = exports.AlignClass = exports.AlignAttribute = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var config = { + scope: _parchment2.default.Scope.BLOCK, + whitelist: ['right', 'center', 'justify'] +}; + +var AlignAttribute = new _parchment2.default.Attributor.Attribute('align', 'align', config); +var AlignClass = new _parchment2.default.Attributor.Class('align', 'ql-align', config); +var AlignStyle = new _parchment2.default.Attributor.Style('align', 'text-align', config); + +exports.AlignAttribute = AlignAttribute; +exports.AlignClass = AlignClass; +exports.AlignStyle = AlignStyle; + +/***/ }), +/* 37 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.BackgroundStyle = exports.BackgroundClass = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _color = __webpack_require__(26); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var BackgroundClass = new _parchment2.default.Attributor.Class('background', 'ql-bg', { + scope: _parchment2.default.Scope.INLINE +}); +var BackgroundStyle = new _color.ColorAttributor('background', 'background-color', { + scope: _parchment2.default.Scope.INLINE +}); + +exports.BackgroundClass = BackgroundClass; +exports.BackgroundStyle = BackgroundStyle; + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DirectionStyle = exports.DirectionClass = exports.DirectionAttribute = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var config = { + scope: _parchment2.default.Scope.BLOCK, + whitelist: ['rtl'] +}; + +var DirectionAttribute = new _parchment2.default.Attributor.Attribute('direction', 'dir', config); +var DirectionClass = new _parchment2.default.Attributor.Class('direction', 'ql-direction', config); +var DirectionStyle = new _parchment2.default.Attributor.Style('direction', 'direction', config); + +exports.DirectionAttribute = DirectionAttribute; +exports.DirectionClass = DirectionClass; +exports.DirectionStyle = DirectionStyle; + +/***/ }), +/* 39 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.FontClass = exports.FontStyle = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var config = { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['serif', 'monospace'] +}; + +var FontClass = new _parchment2.default.Attributor.Class('font', 'ql-font', config); + +var FontStyleAttributor = function (_Parchment$Attributor) { + _inherits(FontStyleAttributor, _Parchment$Attributor); + + function FontStyleAttributor() { + _classCallCheck(this, FontStyleAttributor); + + return _possibleConstructorReturn(this, (FontStyleAttributor.__proto__ || Object.getPrototypeOf(FontStyleAttributor)).apply(this, arguments)); + } + + _createClass(FontStyleAttributor, [{ + key: 'value', + value: function value(node) { + return _get(FontStyleAttributor.prototype.__proto__ || Object.getPrototypeOf(FontStyleAttributor.prototype), 'value', this).call(this, node).replace(/["']/g, ''); + } + }]); + + return FontStyleAttributor; +}(_parchment2.default.Attributor.Style); + +var FontStyle = new FontStyleAttributor('font', 'font-family', config); + +exports.FontStyle = FontStyle; +exports.FontClass = FontClass; + +/***/ }), +/* 40 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SizeStyle = exports.SizeClass = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var SizeClass = new _parchment2.default.Attributor.Class('size', 'ql-size', { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['small', 'large', 'huge'] +}); +var SizeStyle = new _parchment2.default.Attributor.Style('size', 'font-size', { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['10px', '18px', '32px'] +}); + +exports.SizeClass = SizeClass; +exports.SizeStyle = SizeStyle; + +/***/ }), +/* 41 */ +/***/ (function(module, exports, __webpack_require__) { + + +module.exports = { + 'align': { + '': __webpack_require__(76), + 'center': __webpack_require__(77), + 'right': __webpack_require__(78), + 'justify': __webpack_require__(79) + }, + 'background': __webpack_require__(80), + 'blockquote': __webpack_require__(81), + 'bold': __webpack_require__(82), + 'clean': __webpack_require__(83), + 'code': __webpack_require__(58), + 'code-block': __webpack_require__(58), + 'color': __webpack_require__(84), + 'direction': { + '': __webpack_require__(85), + 'rtl': __webpack_require__(86) + }, + 'float': { + 'center': __webpack_require__(87), + 'full': __webpack_require__(88), + 'left': __webpack_require__(89), + 'right': __webpack_require__(90) + }, + 'formula': __webpack_require__(91), + 'header': { + '1': __webpack_require__(92), + '2': __webpack_require__(93) + }, + 'italic': __webpack_require__(94), + 'image': __webpack_require__(95), + 'indent': { + '+1': __webpack_require__(96), + '-1': __webpack_require__(97) + }, + 'link': __webpack_require__(98), + 'list': { + 'ordered': __webpack_require__(99), + 'bullet': __webpack_require__(100), + 'check': __webpack_require__(101) + }, + 'script': { + 'sub': __webpack_require__(102), + 'super': __webpack_require__(103) + }, + 'strike': __webpack_require__(104), + 'underline': __webpack_require__(105), + 'video': __webpack_require__(106) +}; + +/***/ }), +/* 42 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getLastChangeIndex = exports.default = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var History = function (_Module) { + _inherits(History, _Module); + + function History(quill, options) { + _classCallCheck(this, History); + + var _this = _possibleConstructorReturn(this, (History.__proto__ || Object.getPrototypeOf(History)).call(this, quill, options)); + + _this.lastRecorded = 0; + _this.ignoreChange = false; + _this.clear(); + _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (eventName, delta, oldDelta, source) { + if (eventName !== _quill2.default.events.TEXT_CHANGE || _this.ignoreChange) return; + if (!_this.options.userOnly || source === _quill2.default.sources.USER) { + _this.record(delta, oldDelta); + } else { + _this.transform(delta); + } + }); + _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true }, _this.undo.bind(_this)); + _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true, shiftKey: true }, _this.redo.bind(_this)); + if (/Win/i.test(navigator.platform)) { + _this.quill.keyboard.addBinding({ key: 'Y', shortKey: true }, _this.redo.bind(_this)); + } + return _this; + } + + _createClass(History, [{ + key: 'change', + value: function change(source, dest) { + if (this.stack[source].length === 0) return; + var delta = this.stack[source].pop(); + this.stack[dest].push(delta); + this.lastRecorded = 0; + this.ignoreChange = true; + this.quill.updateContents(delta[source], _quill2.default.sources.USER); + this.ignoreChange = false; + var index = getLastChangeIndex(delta[source]); + this.quill.setSelection(index); + } + }, { + key: 'clear', + value: function clear() { + this.stack = { undo: [], redo: [] }; + } + }, { + key: 'cutoff', + value: function cutoff() { + this.lastRecorded = 0; + } + }, { + key: 'record', + value: function record(changeDelta, oldDelta) { + if (changeDelta.ops.length === 0) return; + this.stack.redo = []; + var undoDelta = this.quill.getContents().diff(oldDelta); + var timestamp = Date.now(); + if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) { + var delta = this.stack.undo.pop(); + undoDelta = undoDelta.compose(delta.undo); + changeDelta = delta.redo.compose(changeDelta); + } else { + this.lastRecorded = timestamp; + } + this.stack.undo.push({ + redo: changeDelta, + undo: undoDelta + }); + if (this.stack.undo.length > this.options.maxStack) { + this.stack.undo.shift(); + } + } + }, { + key: 'redo', + value: function redo() { + this.change('redo', 'undo'); + } + }, { + key: 'transform', + value: function transform(delta) { + this.stack.undo.forEach(function (change) { + change.undo = delta.transform(change.undo, true); + change.redo = delta.transform(change.redo, true); + }); + this.stack.redo.forEach(function (change) { + change.undo = delta.transform(change.undo, true); + change.redo = delta.transform(change.redo, true); + }); + } + }, { + key: 'undo', + value: function undo() { + this.change('undo', 'redo'); + } + }]); + + return History; +}(_module2.default); + +History.DEFAULTS = { + delay: 1000, + maxStack: 100, + userOnly: false +}; + +function endsWithNewlineChange(delta) { + var lastOp = delta.ops[delta.ops.length - 1]; + if (lastOp == null) return false; + if (lastOp.insert != null) { + return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\n'); + } + if (lastOp.attributes != null) { + return Object.keys(lastOp.attributes).some(function (attr) { + return _parchment2.default.query(attr, _parchment2.default.Scope.BLOCK) != null; + }); + } + return false; +} + +function getLastChangeIndex(delta) { + var deleteLength = delta.reduce(function (length, op) { + length += op.delete || 0; + return length; + }, 0); + var changeIndex = delta.length() - deleteLength; + if (endsWithNewlineChange(delta)) { + changeIndex -= 1; + } + return changeIndex; +} + +exports.default = History; +exports.getLastChangeIndex = getLastChangeIndex; + +/***/ }), +/* 43 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.BaseTooltip = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _keyboard = __webpack_require__(23); + +var _keyboard2 = _interopRequireDefault(_keyboard); + +var _theme = __webpack_require__(34); + +var _theme2 = _interopRequireDefault(_theme); + +var _colorPicker = __webpack_require__(59); + +var _colorPicker2 = _interopRequireDefault(_colorPicker); + +var _iconPicker = __webpack_require__(60); + +var _iconPicker2 = _interopRequireDefault(_iconPicker); + +var _picker = __webpack_require__(28); + +var _picker2 = _interopRequireDefault(_picker); + +var _tooltip = __webpack_require__(61); + +var _tooltip2 = _interopRequireDefault(_tooltip); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ALIGNS = [false, 'center', 'right', 'justify']; + +var COLORS = ["#000000", "#e60000", "#ff9900", "#ffff00", "#008a00", "#0066cc", "#9933ff", "#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff", "#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff", "#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2", "#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466"]; + +var FONTS = [false, 'serif', 'monospace']; + +var HEADERS = ['1', '2', '3', false]; + +var SIZES = ['small', false, 'large', 'huge']; + +var BaseTheme = function (_Theme) { + _inherits(BaseTheme, _Theme); + + function BaseTheme(quill, options) { + _classCallCheck(this, BaseTheme); + + var _this = _possibleConstructorReturn(this, (BaseTheme.__proto__ || Object.getPrototypeOf(BaseTheme)).call(this, quill, options)); + + var listener = function listener(e) { + if (!document.body.contains(quill.root)) { + return document.body.removeEventListener('click', listener); + } + if (_this.tooltip != null && !_this.tooltip.root.contains(e.target) && document.activeElement !== _this.tooltip.textbox && !_this.quill.hasFocus()) { + _this.tooltip.hide(); + } + if (_this.pickers != null) { + _this.pickers.forEach(function (picker) { + if (!picker.container.contains(e.target)) { + picker.close(); + } + }); + } + }; + quill.emitter.listenDOM('click', document.body, listener); + return _this; + } + + _createClass(BaseTheme, [{ + key: 'addModule', + value: function addModule(name) { + var module = _get(BaseTheme.prototype.__proto__ || Object.getPrototypeOf(BaseTheme.prototype), 'addModule', this).call(this, name); + if (name === 'toolbar') { + this.extendToolbar(module); + } + return module; + } + }, { + key: 'buildButtons', + value: function buildButtons(buttons, icons) { + buttons.forEach(function (button) { + var className = button.getAttribute('class') || ''; + className.split(/\s+/).forEach(function (name) { + if (!name.startsWith('ql-')) return; + name = name.slice('ql-'.length); + if (icons[name] == null) return; + if (name === 'direction') { + button.innerHTML = icons[name][''] + icons[name]['rtl']; + } else if (typeof icons[name] === 'string') { + button.innerHTML = icons[name]; + } else { + var value = button.value || ''; + if (value != null && icons[name][value]) { + button.innerHTML = icons[name][value]; + } + } + }); + }); + } + }, { + key: 'buildPickers', + value: function buildPickers(selects, icons) { + var _this2 = this; + + this.pickers = selects.map(function (select) { + if (select.classList.contains('ql-align')) { + if (select.querySelector('option') == null) { + fillSelect(select, ALIGNS); + } + return new _iconPicker2.default(select, icons.align); + } else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) { + var format = select.classList.contains('ql-background') ? 'background' : 'color'; + if (select.querySelector('option') == null) { + fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000'); + } + return new _colorPicker2.default(select, icons[format]); + } else { + if (select.querySelector('option') == null) { + if (select.classList.contains('ql-font')) { + fillSelect(select, FONTS); + } else if (select.classList.contains('ql-header')) { + fillSelect(select, HEADERS); + } else if (select.classList.contains('ql-size')) { + fillSelect(select, SIZES); + } + } + return new _picker2.default(select); + } + }); + var update = function update() { + _this2.pickers.forEach(function (picker) { + picker.update(); + }); + }; + this.quill.on(_emitter2.default.events.EDITOR_CHANGE, update); + } + }]); + + return BaseTheme; +}(_theme2.default); + +BaseTheme.DEFAULTS = (0, _extend2.default)(true, {}, _theme2.default.DEFAULTS, { + modules: { + toolbar: { + handlers: { + formula: function formula() { + this.quill.theme.tooltip.edit('formula'); + }, + image: function image() { + var _this3 = this; + + var fileInput = this.container.querySelector('input.ql-image[type=file]'); + if (fileInput == null) { + fileInput = document.createElement('input'); + fileInput.setAttribute('type', 'file'); + fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'); + fileInput.classList.add('ql-image'); + fileInput.addEventListener('change', function () { + if (fileInput.files != null && fileInput.files[0] != null) { + var reader = new FileReader(); + reader.onload = function (e) { + var range = _this3.quill.getSelection(true); + _this3.quill.updateContents(new _quillDelta2.default().retain(range.index).delete(range.length).insert({ image: e.target.result }), _emitter2.default.sources.USER); + _this3.quill.setSelection(range.index + 1, _emitter2.default.sources.SILENT); + fileInput.value = ""; + }; + reader.readAsDataURL(fileInput.files[0]); + } + }); + this.container.appendChild(fileInput); + } + fileInput.click(); + }, + video: function video() { + this.quill.theme.tooltip.edit('video'); + } + } + } + } +}); + +var BaseTooltip = function (_Tooltip) { + _inherits(BaseTooltip, _Tooltip); + + function BaseTooltip(quill, boundsContainer) { + _classCallCheck(this, BaseTooltip); + + var _this4 = _possibleConstructorReturn(this, (BaseTooltip.__proto__ || Object.getPrototypeOf(BaseTooltip)).call(this, quill, boundsContainer)); + + _this4.textbox = _this4.root.querySelector('input[type="text"]'); + _this4.listen(); + return _this4; + } + + _createClass(BaseTooltip, [{ + key: 'listen', + value: function listen() { + var _this5 = this; + + this.textbox.addEventListener('keydown', function (event) { + if (_keyboard2.default.match(event, 'enter')) { + _this5.save(); + event.preventDefault(); + } else if (_keyboard2.default.match(event, 'escape')) { + _this5.cancel(); + event.preventDefault(); + } + }); + } + }, { + key: 'cancel', + value: function cancel() { + this.hide(); + } + }, { + key: 'edit', + value: function edit() { + var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'link'; + var preview = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + this.root.classList.remove('ql-hidden'); + this.root.classList.add('ql-editing'); + if (preview != null) { + this.textbox.value = preview; + } else if (mode !== this.root.getAttribute('data-mode')) { + this.textbox.value = ''; + } + this.position(this.quill.getBounds(this.quill.selection.savedRange)); + this.textbox.select(); + this.textbox.setAttribute('placeholder', this.textbox.getAttribute('data-' + mode) || ''); + this.root.setAttribute('data-mode', mode); + } + }, { + key: 'restoreFocus', + value: function restoreFocus() { + var scrollTop = this.quill.scrollingContainer.scrollTop; + this.quill.focus(); + this.quill.scrollingContainer.scrollTop = scrollTop; + } + }, { + key: 'save', + value: function save() { + var value = this.textbox.value; + switch (this.root.getAttribute('data-mode')) { + case 'link': + { + var scrollTop = this.quill.root.scrollTop; + if (this.linkRange) { + this.quill.formatText(this.linkRange, 'link', value, _emitter2.default.sources.USER); + delete this.linkRange; + } else { + this.restoreFocus(); + this.quill.format('link', value, _emitter2.default.sources.USER); + } + this.quill.root.scrollTop = scrollTop; + break; + } + case 'video': + { + value = extractVideoUrl(value); + } // eslint-disable-next-line no-fallthrough + case 'formula': + { + if (!value) break; + var range = this.quill.getSelection(true); + if (range != null) { + var index = range.index + range.length; + this.quill.insertEmbed(index, this.root.getAttribute('data-mode'), value, _emitter2.default.sources.USER); + if (this.root.getAttribute('data-mode') === 'formula') { + this.quill.insertText(index + 1, ' ', _emitter2.default.sources.USER); + } + this.quill.setSelection(index + 2, _emitter2.default.sources.USER); + } + break; + } + } + this.textbox.value = ''; + this.hide(); + } + }]); + + return BaseTooltip; +}(_tooltip2.default); + +function extractVideoUrl(url) { + var match = url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/) || url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/); + if (match) { + return (match[1] || 'https') + '://www.youtube.com/embed/' + match[2] + '?showinfo=0'; + } + if (match = url.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/)) { + // eslint-disable-line no-cond-assign + return (match[1] || 'https') + '://player.vimeo.com/video/' + match[2] + '/'; + } + return url; +} + +function fillSelect(select, values) { + var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + values.forEach(function (value) { + var option = document.createElement('option'); + if (value === defaultValue) { + option.setAttribute('selected', 'selected'); + } else { + option.setAttribute('value', value); + } + select.appendChild(option); + }); +} + +exports.BaseTooltip = BaseTooltip; +exports.default = BaseTheme; + +/***/ }), +/* 44 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var LinkedList = /** @class */ (function () { + function LinkedList() { + this.head = this.tail = null; + this.length = 0; + } + LinkedList.prototype.append = function () { + var nodes = []; + for (var _i = 0; _i < arguments.length; _i++) { + nodes[_i] = arguments[_i]; + } + this.insertBefore(nodes[0], null); + if (nodes.length > 1) { + this.append.apply(this, nodes.slice(1)); + } + }; + LinkedList.prototype.contains = function (node) { + var cur, next = this.iterator(); + while ((cur = next())) { + if (cur === node) + return true; + } + return false; + }; + LinkedList.prototype.insertBefore = function (node, refNode) { + if (!node) + return; + node.next = refNode; + if (refNode != null) { + node.prev = refNode.prev; + if (refNode.prev != null) { + refNode.prev.next = node; + } + refNode.prev = node; + if (refNode === this.head) { + this.head = node; + } + } + else if (this.tail != null) { + this.tail.next = node; + node.prev = this.tail; + this.tail = node; + } + else { + node.prev = null; + this.head = this.tail = node; + } + this.length += 1; + }; + LinkedList.prototype.offset = function (target) { + var index = 0, cur = this.head; + while (cur != null) { + if (cur === target) + return index; + index += cur.length(); + cur = cur.next; + } + return -1; + }; + LinkedList.prototype.remove = function (node) { + if (!this.contains(node)) + return; + if (node.prev != null) + node.prev.next = node.next; + if (node.next != null) + node.next.prev = node.prev; + if (node === this.head) + this.head = node.next; + if (node === this.tail) + this.tail = node.prev; + this.length -= 1; + }; + LinkedList.prototype.iterator = function (curNode) { + if (curNode === void 0) { curNode = this.head; } + // TODO use yield when we can + return function () { + var ret = curNode; + if (curNode != null) + curNode = curNode.next; + return ret; + }; + }; + LinkedList.prototype.find = function (index, inclusive) { + if (inclusive === void 0) { inclusive = false; } + var cur, next = this.iterator(); + while ((cur = next())) { + var length = cur.length(); + if (index < length || + (inclusive && index === length && (cur.next == null || cur.next.length() !== 0))) { + return [cur, index]; + } + index -= length; + } + return [null, 0]; + }; + LinkedList.prototype.forEach = function (callback) { + var cur, next = this.iterator(); + while ((cur = next())) { + callback(cur); + } + }; + LinkedList.prototype.forEachAt = function (index, length, callback) { + if (length <= 0) + return; + var _a = this.find(index), startNode = _a[0], offset = _a[1]; + var cur, curIndex = index - offset, next = this.iterator(startNode); + while ((cur = next()) && curIndex < index + length) { + var curLength = cur.length(); + if (index > curIndex) { + callback(cur, index - curIndex, Math.min(length, curIndex + curLength - index)); + } + else { + callback(cur, 0, Math.min(curLength, index + length - curIndex)); + } + curIndex += curLength; + } + }; + LinkedList.prototype.map = function (callback) { + return this.reduce(function (memo, cur) { + memo.push(callback(cur)); + return memo; + }, []); + }; + LinkedList.prototype.reduce = function (callback, memo) { + var cur, next = this.iterator(); + while ((cur = next())) { + memo = callback(memo, cur); + } + return memo; + }; + return LinkedList; +}()); +exports.default = LinkedList; + + +/***/ }), +/* 45 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var container_1 = __webpack_require__(17); +var Registry = __webpack_require__(1); +var OBSERVER_CONFIG = { + attributes: true, + characterData: true, + characterDataOldValue: true, + childList: true, + subtree: true, +}; +var MAX_OPTIMIZE_ITERATIONS = 100; +var ScrollBlot = /** @class */ (function (_super) { + __extends(ScrollBlot, _super); + function ScrollBlot(node) { + var _this = _super.call(this, node) || this; + _this.scroll = _this; + _this.observer = new MutationObserver(function (mutations) { + _this.update(mutations); + }); + _this.observer.observe(_this.domNode, OBSERVER_CONFIG); + _this.attach(); + return _this; + } + ScrollBlot.prototype.detach = function () { + _super.prototype.detach.call(this); + this.observer.disconnect(); + }; + ScrollBlot.prototype.deleteAt = function (index, length) { + this.update(); + if (index === 0 && length === this.length()) { + this.children.forEach(function (child) { + child.remove(); + }); + } + else { + _super.prototype.deleteAt.call(this, index, length); + } + }; + ScrollBlot.prototype.formatAt = function (index, length, name, value) { + this.update(); + _super.prototype.formatAt.call(this, index, length, name, value); + }; + ScrollBlot.prototype.insertAt = function (index, value, def) { + this.update(); + _super.prototype.insertAt.call(this, index, value, def); + }; + ScrollBlot.prototype.optimize = function (mutations, context) { + var _this = this; + if (mutations === void 0) { mutations = []; } + if (context === void 0) { context = {}; } + _super.prototype.optimize.call(this, context); + // We must modify mutations directly, cannot make copy and then modify + var records = [].slice.call(this.observer.takeRecords()); + // Array.push currently seems to be implemented by a non-tail recursive function + // so we cannot just mutations.push.apply(mutations, this.observer.takeRecords()); + while (records.length > 0) + mutations.push(records.pop()); + // TODO use WeakMap + var mark = function (blot, markParent) { + if (markParent === void 0) { markParent = true; } + if (blot == null || blot === _this) + return; + if (blot.domNode.parentNode == null) + return; + // @ts-ignore + if (blot.domNode[Registry.DATA_KEY].mutations == null) { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations = []; + } + if (markParent) + mark(blot.parent); + }; + var optimize = function (blot) { + // Post-order traversal + if ( + // @ts-ignore + blot.domNode[Registry.DATA_KEY] == null || + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations == null) { + return; + } + if (blot instanceof container_1.default) { + blot.children.forEach(optimize); + } + blot.optimize(context); + }; + var remaining = mutations; + for (var i = 0; remaining.length > 0; i += 1) { + if (i >= MAX_OPTIMIZE_ITERATIONS) { + throw new Error('[Parchment] Maximum optimize iterations reached'); + } + remaining.forEach(function (mutation) { + var blot = Registry.find(mutation.target, true); + if (blot == null) + return; + if (blot.domNode === mutation.target) { + if (mutation.type === 'childList') { + mark(Registry.find(mutation.previousSibling, false)); + [].forEach.call(mutation.addedNodes, function (node) { + var child = Registry.find(node, false); + mark(child, false); + if (child instanceof container_1.default) { + child.children.forEach(function (grandChild) { + mark(grandChild, false); + }); + } + }); + } + else if (mutation.type === 'attributes') { + mark(blot.prev); + } + } + mark(blot); + }); + this.children.forEach(optimize); + remaining = [].slice.call(this.observer.takeRecords()); + records = remaining.slice(); + while (records.length > 0) + mutations.push(records.pop()); + } + }; + ScrollBlot.prototype.update = function (mutations, context) { + var _this = this; + if (context === void 0) { context = {}; } + mutations = mutations || this.observer.takeRecords(); + // TODO use WeakMap + mutations + .map(function (mutation) { + var blot = Registry.find(mutation.target, true); + if (blot == null) + return null; + // @ts-ignore + if (blot.domNode[Registry.DATA_KEY].mutations == null) { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations = [mutation]; + return blot; + } + else { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations.push(mutation); + return null; + } + }) + .forEach(function (blot) { + if (blot == null || + blot === _this || + //@ts-ignore + blot.domNode[Registry.DATA_KEY] == null) + return; + // @ts-ignore + blot.update(blot.domNode[Registry.DATA_KEY].mutations || [], context); + }); + // @ts-ignore + if (this.domNode[Registry.DATA_KEY].mutations != null) { + // @ts-ignore + _super.prototype.update.call(this, this.domNode[Registry.DATA_KEY].mutations, context); + } + this.optimize(mutations, context); + }; + ScrollBlot.blotName = 'scroll'; + ScrollBlot.defaultChild = 'block'; + ScrollBlot.scope = Registry.Scope.BLOCK_BLOT; + ScrollBlot.tagName = 'DIV'; + return ScrollBlot; +}(container_1.default)); +exports.default = ScrollBlot; + + +/***/ }), +/* 46 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var format_1 = __webpack_require__(18); +var Registry = __webpack_require__(1); +// Shallow object comparison +function isEqual(obj1, obj2) { + if (Object.keys(obj1).length !== Object.keys(obj2).length) + return false; + // @ts-ignore + for (var prop in obj1) { + // @ts-ignore + if (obj1[prop] !== obj2[prop]) + return false; + } + return true; +} +var InlineBlot = /** @class */ (function (_super) { + __extends(InlineBlot, _super); + function InlineBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + InlineBlot.formats = function (domNode) { + if (domNode.tagName === InlineBlot.tagName) + return undefined; + return _super.formats.call(this, domNode); + }; + InlineBlot.prototype.format = function (name, value) { + var _this = this; + if (name === this.statics.blotName && !value) { + this.children.forEach(function (child) { + if (!(child instanceof format_1.default)) { + child = child.wrap(InlineBlot.blotName, true); + } + _this.attributes.copy(child); + }); + this.unwrap(); + } + else { + _super.prototype.format.call(this, name, value); + } + }; + InlineBlot.prototype.formatAt = function (index, length, name, value) { + if (this.formats()[name] != null || Registry.query(name, Registry.Scope.ATTRIBUTE)) { + var blot = this.isolate(index, length); + blot.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + InlineBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + var formats = this.formats(); + if (Object.keys(formats).length === 0) { + return this.unwrap(); // unformatted span + } + var next = this.next; + if (next instanceof InlineBlot && next.prev === this && isEqual(formats, next.formats())) { + next.moveChildren(this); + next.remove(); + } + }; + InlineBlot.blotName = 'inline'; + InlineBlot.scope = Registry.Scope.INLINE_BLOT; + InlineBlot.tagName = 'SPAN'; + return InlineBlot; +}(format_1.default)); +exports.default = InlineBlot; + + +/***/ }), +/* 47 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var format_1 = __webpack_require__(18); +var Registry = __webpack_require__(1); +var BlockBlot = /** @class */ (function (_super) { + __extends(BlockBlot, _super); + function BlockBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + BlockBlot.formats = function (domNode) { + var tagName = Registry.query(BlockBlot.blotName).tagName; + if (domNode.tagName === tagName) + return undefined; + return _super.formats.call(this, domNode); + }; + BlockBlot.prototype.format = function (name, value) { + if (Registry.query(name, Registry.Scope.BLOCK) == null) { + return; + } + else if (name === this.statics.blotName && !value) { + this.replaceWith(BlockBlot.blotName); + } + else { + _super.prototype.format.call(this, name, value); + } + }; + BlockBlot.prototype.formatAt = function (index, length, name, value) { + if (Registry.query(name, Registry.Scope.BLOCK) != null) { + this.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + BlockBlot.prototype.insertAt = function (index, value, def) { + if (def == null || Registry.query(value, Registry.Scope.INLINE) != null) { + // Insert text or inline + _super.prototype.insertAt.call(this, index, value, def); + } + else { + var after = this.split(index); + var blot = Registry.create(value, def); + after.parent.insertBefore(blot, after); + } + }; + BlockBlot.prototype.update = function (mutations, context) { + if (navigator.userAgent.match(/Trident/)) { + this.build(); + } + else { + _super.prototype.update.call(this, mutations, context); + } + }; + BlockBlot.blotName = 'block'; + BlockBlot.scope = Registry.Scope.BLOCK_BLOT; + BlockBlot.tagName = 'P'; + return BlockBlot; +}(format_1.default)); +exports.default = BlockBlot; + + +/***/ }), +/* 48 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var leaf_1 = __webpack_require__(19); +var EmbedBlot = /** @class */ (function (_super) { + __extends(EmbedBlot, _super); + function EmbedBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + EmbedBlot.formats = function (domNode) { + return undefined; + }; + EmbedBlot.prototype.format = function (name, value) { + // super.formatAt wraps, which is what we want in general, + // but this allows subclasses to overwrite for formats + // that just apply to particular embeds + _super.prototype.formatAt.call(this, 0, this.length(), name, value); + }; + EmbedBlot.prototype.formatAt = function (index, length, name, value) { + if (index === 0 && length === this.length()) { + this.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + EmbedBlot.prototype.formats = function () { + return this.statics.formats(this.domNode); + }; + return EmbedBlot; +}(leaf_1.default)); +exports.default = EmbedBlot; + + +/***/ }), +/* 49 */ +/***/ (function(module, exports, __webpack_require__) { + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var leaf_1 = __webpack_require__(19); +var Registry = __webpack_require__(1); +var TextBlot = /** @class */ (function (_super) { + __extends(TextBlot, _super); + function TextBlot(node) { + var _this = _super.call(this, node) || this; + _this.text = _this.statics.value(_this.domNode); + return _this; + } + TextBlot.create = function (value) { + return document.createTextNode(value); + }; + TextBlot.value = function (domNode) { + var text = domNode.data; + // @ts-ignore + if (text['normalize']) + text = text['normalize'](); + return text; + }; + TextBlot.prototype.deleteAt = function (index, length) { + this.domNode.data = this.text = this.text.slice(0, index) + this.text.slice(index + length); + }; + TextBlot.prototype.index = function (node, offset) { + if (this.domNode === node) { + return offset; + } + return -1; + }; + TextBlot.prototype.insertAt = function (index, value, def) { + if (def == null) { + this.text = this.text.slice(0, index) + value + this.text.slice(index); + this.domNode.data = this.text; + } + else { + _super.prototype.insertAt.call(this, index, value, def); + } + }; + TextBlot.prototype.length = function () { + return this.text.length; + }; + TextBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + this.text = this.statics.value(this.domNode); + if (this.text.length === 0) { + this.remove(); + } + else if (this.next instanceof TextBlot && this.next.prev === this) { + this.insertAt(this.length(), this.next.value()); + this.next.remove(); + } + }; + TextBlot.prototype.position = function (index, inclusive) { + return [this.domNode, index]; + }; + TextBlot.prototype.split = function (index, force) { + if (force === void 0) { force = false; } + if (!force) { + if (index === 0) + return this; + if (index === this.length()) + return this.next; + } + var after = Registry.create(this.domNode.splitText(index)); + this.parent.insertBefore(after, this.next); + this.text = this.statics.value(this.domNode); + return after; + }; + TextBlot.prototype.update = function (mutations, context) { + var _this = this; + if (mutations.some(function (mutation) { + return mutation.type === 'characterData' && mutation.target === _this.domNode; + })) { + this.text = this.statics.value(this.domNode); + } + }; + TextBlot.prototype.value = function () { + return this.text; + }; + TextBlot.blotName = 'text'; + TextBlot.scope = Registry.Scope.INLINE_BLOT; + return TextBlot; +}(leaf_1.default)); +exports.default = TextBlot; + + +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { + + +var elem = document.createElement('div'); +elem.classList.toggle('test-class', false); +if (elem.classList.contains('test-class')) { + var _toggle = DOMTokenList.prototype.toggle; + DOMTokenList.prototype.toggle = function (token, force) { + if (arguments.length > 1 && !this.contains(token) === !force) { + return force; + } else { + return _toggle.call(this, token); + } + }; +} + +if (!String.prototype.startsWith) { + String.prototype.startsWith = function (searchString, position) { + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; +} + +if (!String.prototype.endsWith) { + String.prototype.endsWith = function (searchString, position) { + var subjectString = this.toString(); + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }; +} + +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, "find", { + value: function value(predicate) { + if (this === null) { + throw new TypeError('Array.prototype.find called on null or undefined'); + } + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + var list = Object(this); + var length = list.length >>> 0; + var thisArg = arguments[1]; + var value; + + for (var i = 0; i < length; i++) { + value = list[i]; + if (predicate.call(thisArg, value, i, list)) { + return value; + } + } + return undefined; + } + }); +} + +document.addEventListener("DOMContentLoaded", function () { + // Disable resizing in Firefox + document.execCommand("enableObjectResizing", false, false); + // Disable automatic linkifying in IE11 + document.execCommand("autoUrlDetect", false, false); +}); + +/***/ }), +/* 51 */ +/***/ (function(module, exports) { + +/** + * This library modifies the diff-patch-match library by Neil Fraser + * by removing the patch and match functionality and certain advanced + * options in the diff function. The original license is as follows: + * + * === + * + * Diff Match and Patch + * + * Copyright 2006 Google Inc. + * http://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ +var DIFF_DELETE = -1; +var DIFF_INSERT = 1; +var DIFF_EQUAL = 0; + + +/** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {Int} cursor_pos Expected edit position in text1 (optional) + * @return {Array} Array of diff tuples. + */ +function diff_main(text1, text2, cursor_pos) { + // Check for equality (speedup). + if (text1 == text2) { + if (text1) { + return [[DIFF_EQUAL, text1]]; + } + return []; + } + + // Check cursor_pos within bounds + if (cursor_pos < 0 || text1.length < cursor_pos) { + cursor_pos = null; + } + + // Trim off common prefix (speedup). + var commonlength = diff_commonPrefix(text1, text2); + var commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = diff_commonSuffix(text1, text2); + var commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + var diffs = diff_compute_(text1, text2); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift([DIFF_EQUAL, commonprefix]); + } + if (commonsuffix) { + diffs.push([DIFF_EQUAL, commonsuffix]); + } + diff_cleanupMerge(diffs); + if (cursor_pos != null) { + diffs = fix_cursor(diffs, cursor_pos); + } + diffs = fix_emoji(diffs); + return diffs; +} + +/** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @return {Array} Array of diff tuples. + */ +function diff_compute_(text1, text2) { + var diffs; + + if (!text1) { + // Just add some text (speedup). + return [[DIFF_INSERT, text2]]; + } + + if (!text2) { + // Just delete some text (speedup). + return [[DIFF_DELETE, text1]]; + } + + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + var i = longtext.indexOf(shorttext); + if (i != -1) { + // Shorter text is inside the longer text (speedup). + diffs = [[DIFF_INSERT, longtext.substring(0, i)], + [DIFF_EQUAL, shorttext], + [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length == 1) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + } + + // Check to see if the problem can be split in two. + var hm = diff_halfMatch_(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + var text1_a = hm[0]; + var text1_b = hm[1]; + var text2_a = hm[2]; + var text2_b = hm[3]; + var mid_common = hm[4]; + // Send both pairs off for separate processing. + var diffs_a = diff_main(text1_a, text2_a); + var diffs_b = diff_main(text1_b, text2_b); + // Merge the results. + return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b); + } + + return diff_bisect_(text1, text2); +} + +/** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @return {Array} Array of diff tuples. + * @private + */ +function diff_bisect_(text1, text2) { + // Cache the text lengths to prevent multiple calls. + var text1_length = text1.length; + var text2_length = text2.length; + var max_d = Math.ceil((text1_length + text2_length) / 2); + var v_offset = max_d; + var v_length = 2 * max_d; + var v1 = new Array(v_length); + var v2 = new Array(v_length); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (var x = 0; x < v_length; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[v_offset + 1] = 0; + v2[v_offset + 1] = 0; + var delta = text1_length - text2_length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + var front = (delta % 2 != 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + var k1start = 0; + var k1end = 0; + var k2start = 0; + var k2end = 0; + for (var d = 0; d < max_d; d++) { + // Walk the front path one step. + for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + var k1_offset = v_offset + k1; + var x1; + if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { + x1 = v1[k1_offset + 1]; + } else { + x1 = v1[k1_offset - 1] + 1; + } + var y1 = x1 - k1; + while (x1 < text1_length && y1 < text2_length && + text1.charAt(x1) == text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1_offset] = x1; + if (x1 > text1_length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2_length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + var k2_offset = v_offset + delta - k1; + if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { + // Mirror x2 onto top-left coordinate system. + var x2 = text1_length - v2[k2_offset]; + if (x1 >= x2) { + // Overlap detected. + return diff_bisectSplit_(text1, text2, x1, y1); + } + } + } + } + + // Walk the reverse path one step. + for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + var k2_offset = v_offset + k2; + var x2; + if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { + x2 = v2[k2_offset + 1]; + } else { + x2 = v2[k2_offset - 1] + 1; + } + var y2 = x2 - k2; + while (x2 < text1_length && y2 < text2_length && + text1.charAt(text1_length - x2 - 1) == + text2.charAt(text2_length - y2 - 1)) { + x2++; + y2++; + } + v2[k2_offset] = x2; + if (x2 > text1_length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2_length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + var k1_offset = v_offset + delta - k2; + if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { + var x1 = v1[k1_offset]; + var y1 = v_offset + x1 - k1_offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1_length - x2; + if (x1 >= x2) { + // Overlap detected. + return diff_bisectSplit_(text1, text2, x1, y1); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; +} + +/** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @return {Array} Array of diff tuples. + */ +function diff_bisectSplit_(text1, text2, x, y) { + var text1a = text1.substring(0, x); + var text2a = text2.substring(0, y); + var text1b = text1.substring(x); + var text2b = text2.substring(y); + + // Compute both diffs serially. + var diffs = diff_main(text1a, text2a); + var diffsb = diff_main(text1b, text2b); + + return diffs.concat(diffsb); +} + +/** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ +function diff_commonPrefix(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerstart = 0; + while (pointermin < pointermid) { + if (text1.substring(pointerstart, pointermid) == + text2.substring(pointerstart, pointermid)) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +} + +/** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ +function diff_commonSuffix(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || + text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerend = 0; + while (pointermin < pointermid) { + if (text1.substring(text1.length - pointermid, text1.length - pointerend) == + text2.substring(text2.length - pointermid, text2.length - pointerend)) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +} + +/** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + */ +function diff_halfMatch_(text1, text2) { + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diff_halfMatchI_(longtext, shorttext, i) { + // Start with a 1/4 length substring at position i as a seed. + var seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + var j = -1; + var best_common = ''; + var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; + while ((j = shorttext.indexOf(seed, j + 1)) != -1) { + var prefixLength = diff_commonPrefix(longtext.substring(i), + shorttext.substring(j)); + var suffixLength = diff_commonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (best_common.length < suffixLength + prefixLength) { + best_common = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + best_longtext_a = longtext.substring(0, i - suffixLength); + best_longtext_b = longtext.substring(i + prefixLength); + best_shorttext_a = shorttext.substring(0, j - suffixLength); + best_shorttext_b = shorttext.substring(j + prefixLength); + } + } + if (best_common.length * 2 >= longtext.length) { + return [best_longtext_a, best_longtext_b, + best_shorttext_a, best_shorttext_b, best_common]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + var hm1 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + var hm2 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 2)); + var hm; + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + var text1_a, text1_b, text2_a, text2_b; + if (text1.length > text2.length) { + text1_a = hm[0]; + text1_b = hm[1]; + text2_a = hm[2]; + text2_b = hm[3]; + } else { + text2_a = hm[0]; + text2_b = hm[1]; + text1_a = hm[2]; + text1_b = hm[3]; + } + var mid_common = hm[4]; + return [text1_a, text1_b, text2_a, text2_b, mid_common]; +} + +/** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {Array} diffs Array of diff tuples. + */ +function diff_cleanupMerge(diffs) { + diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end. + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + var commonlength; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete + count_insert > 1) { + if (count_delete !== 0 && count_insert !== 0) { + // Factor out any common prefixies. + commonlength = diff_commonPrefix(text_insert, text_delete); + if (commonlength !== 0) { + if ((pointer - count_delete - count_insert) > 0 && + diffs[pointer - count_delete - count_insert - 1][0] == + DIFF_EQUAL) { + diffs[pointer - count_delete - count_insert - 1][1] += + text_insert.substring(0, commonlength); + } else { + diffs.splice(0, 0, [DIFF_EQUAL, + text_insert.substring(0, commonlength)]); + pointer++; + } + text_insert = text_insert.substring(commonlength); + text_delete = text_delete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = diff_commonSuffix(text_insert, text_delete); + if (commonlength !== 0) { + diffs[pointer][1] = text_insert.substring(text_insert.length - + commonlength) + diffs[pointer][1]; + text_insert = text_insert.substring(0, text_insert.length - + commonlength); + text_delete = text_delete.substring(0, text_delete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + if (count_delete === 0) { + diffs.splice(pointer - count_insert, + count_delete + count_insert, [DIFF_INSERT, text_insert]); + } else if (count_insert === 0) { + diffs.splice(pointer - count_delete, + count_delete + count_insert, [DIFF_DELETE, text_delete]); + } else { + diffs.splice(pointer - count_delete - count_insert, + count_delete + count_insert, [DIFF_DELETE, text_delete], + [DIFF_INSERT, text_insert]); + } + pointer = pointer - count_delete - count_insert + + (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + } + if (diffs[diffs.length - 1][1] === '') { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + var changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if (diffs[pointer][1].substring(diffs[pointer][1].length - + diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == + diffs[pointer + 1][1]) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + diff_cleanupMerge(diffs); + } +} + +var diff = diff_main; +diff.INSERT = DIFF_INSERT; +diff.DELETE = DIFF_DELETE; +diff.EQUAL = DIFF_EQUAL; + +module.exports = diff; + +/* + * Modify a diff such that the cursor position points to the start of a change: + * E.g. + * cursor_normalize_diff([[DIFF_EQUAL, 'abc']], 1) + * => [1, [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'bc']]] + * cursor_normalize_diff([[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xyz']], 2) + * => [2, [[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xy'], [DIFF_DELETE, 'z']]] + * + * @param {Array} diffs Array of diff tuples + * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds! + * @return {Array} A tuple [cursor location in the modified diff, modified diff] + */ +function cursor_normalize_diff (diffs, cursor_pos) { + if (cursor_pos === 0) { + return [DIFF_EQUAL, diffs]; + } + for (var current_pos = 0, i = 0; i < diffs.length; i++) { + var d = diffs[i]; + if (d[0] === DIFF_DELETE || d[0] === DIFF_EQUAL) { + var next_pos = current_pos + d[1].length; + if (cursor_pos === next_pos) { + return [i + 1, diffs]; + } else if (cursor_pos < next_pos) { + // copy to prevent side effects + diffs = diffs.slice(); + // split d into two diff changes + var split_pos = cursor_pos - current_pos; + var d_left = [d[0], d[1].slice(0, split_pos)]; + var d_right = [d[0], d[1].slice(split_pos)]; + diffs.splice(i, 1, d_left, d_right); + return [i + 1, diffs]; + } else { + current_pos = next_pos; + } + } + } + throw new Error('cursor_pos is out of bounds!') +} + +/* + * Modify a diff such that the edit position is "shifted" to the proposed edit location (cursor_position). + * + * Case 1) + * Check if a naive shift is possible: + * [0, X], [ 1, Y] -> [ 1, Y], [0, X] (if X + Y === Y + X) + * [0, X], [-1, Y] -> [-1, Y], [0, X] (if X + Y === Y + X) - holds same result + * Case 2) + * Check if the following shifts are possible: + * [0, 'pre'], [ 1, 'prefix'] -> [ 1, 'pre'], [0, 'pre'], [ 1, 'fix'] + * [0, 'pre'], [-1, 'prefix'] -> [-1, 'pre'], [0, 'pre'], [-1, 'fix'] + * ^ ^ + * d d_next + * + * @param {Array} diffs Array of diff tuples + * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds! + * @return {Array} Array of diff tuples + */ +function fix_cursor (diffs, cursor_pos) { + var norm = cursor_normalize_diff(diffs, cursor_pos); + var ndiffs = norm[1]; + var cursor_pointer = norm[0]; + var d = ndiffs[cursor_pointer]; + var d_next = ndiffs[cursor_pointer + 1]; + + if (d == null) { + // Text was deleted from end of original string, + // cursor is now out of bounds in new string + return diffs; + } else if (d[0] !== DIFF_EQUAL) { + // A modification happened at the cursor location. + // This is the expected outcome, so we can return the original diff. + return diffs; + } else { + if (d_next != null && d[1] + d_next[1] === d_next[1] + d[1]) { + // Case 1) + // It is possible to perform a naive shift + ndiffs.splice(cursor_pointer, 2, d_next, d); + return merge_tuples(ndiffs, cursor_pointer, 2) + } else if (d_next != null && d_next[1].indexOf(d[1]) === 0) { + // Case 2) + // d[1] is a prefix of d_next[1] + // We can assume that d_next[0] !== 0, since d[0] === 0 + // Shift edit locations.. + ndiffs.splice(cursor_pointer, 2, [d_next[0], d[1]], [0, d[1]]); + var suffix = d_next[1].slice(d[1].length); + if (suffix.length > 0) { + ndiffs.splice(cursor_pointer + 2, 0, [d_next[0], suffix]); + } + return merge_tuples(ndiffs, cursor_pointer, 3) + } else { + // Not possible to perform any modification + return diffs; + } + } +} + +/* + * Check diff did not split surrogate pairs. + * Ex. [0, '\uD83D'], [-1, '\uDC36'], [1, '\uDC2F'] -> [-1, '\uD83D\uDC36'], [1, '\uD83D\uDC2F'] + * '\uD83D\uDC36' === '🐶', '\uD83D\uDC2F' === '🐯' + * + * @param {Array} diffs Array of diff tuples + * @return {Array} Array of diff tuples + */ +function fix_emoji (diffs) { + var compact = false; + var starts_with_pair_end = function(str) { + return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF; + }; + var ends_with_pair_start = function(str) { + return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF; + }; + for (var i = 2; i < diffs.length; i += 1) { + if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) && + diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) && + diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) { + compact = true; + + diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1]; + diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1]; + + diffs[i-2][1] = diffs[i-2][1].slice(0, -1); + } + } + if (!compact) { + return diffs; + } + var fixed_diffs = []; + for (var i = 0; i < diffs.length; i += 1) { + if (diffs[i][1].length > 0) { + fixed_diffs.push(diffs[i]); + } + } + return fixed_diffs; +} + +/* + * Try to merge tuples with their neigbors in a given range. + * E.g. [0, 'a'], [0, 'b'] -> [0, 'ab'] + * + * @param {Array} diffs Array of diff tuples. + * @param {Int} start Position of the first element to merge (diffs[start] is also merged with diffs[start - 1]). + * @param {Int} length Number of consecutive elements to check. + * @return {Array} Array of merged diff tuples. + */ +function merge_tuples (diffs, start, length) { + // Check from (start-1) to (start+length). + for (var i = start + length - 1; i >= 0 && i >= start - 1; i--) { + if (i + 1 < diffs.length) { + var left_d = diffs[i]; + var right_d = diffs[i+1]; + if (left_d[0] === right_d[1]) { + diffs.splice(i, 2, [left_d[0], left_d[1] + right_d[1]]); + } + } + } + return diffs; +} + + +/***/ }), +/* 52 */ +/***/ (function(module, exports) { + +exports = module.exports = typeof Object.keys === 'function' + ? Object.keys : shim; + +exports.shim = shim; +function shim (obj) { + var keys = []; + for (var key in obj) keys.push(key); + return keys; +} + + +/***/ }), +/* 53 */ +/***/ (function(module, exports) { + +var supportsArgumentsClass = (function(){ + return Object.prototype.toString.call(arguments) +})() == '[object Arguments]'; + +exports = module.exports = supportsArgumentsClass ? supported : unsupported; + +exports.supported = supported; +function supported(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} +exports.unsupported = unsupported; +function unsupported(object){ + return object && + typeof object == 'object' && + typeof object.length == 'number' && + Object.prototype.hasOwnProperty.call(object, 'callee') && + !Object.prototype.propertyIsEnumerable.call(object, 'callee') || + false; +} + +/***/ }), +/* 54 */ +/***/ (function(module, exports) { + +var has = Object.prototype.hasOwnProperty + , prefix = '~'; + +/** + * Constructor to create a storage for our `EE` objects. + * An `Events` instance is a plain object whose properties are event names. + * + * @constructor + * @api private + */ +function Events() {} + +// +// We try to not inherit from `Object.prototype`. In some engines creating an +// instance in this way is faster than calling `Object.create(null)` directly. +// If `Object.create(null)` is not supported we prefix the event names with a +// character to make sure that the built-in object properties are not +// overridden or used as an attack vector. +// +if (Object.create) { + Events.prototype = Object.create(null); + + // + // This hack is needed because the `__proto__` property is still inherited in + // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. + // + if (!new Events().__proto__) prefix = false; +} + +/** + * Representation of a single event listener. + * + * @param {Function} fn The listener function. + * @param {Mixed} context The context to invoke the listener with. + * @param {Boolean} [once=false] Specify if the listener is a one-time listener. + * @constructor + * @api private + */ +function EE(fn, context, once) { + this.fn = fn; + this.context = context; + this.once = once || false; +} + +/** + * Minimal `EventEmitter` interface that is molded against the Node.js + * `EventEmitter` interface. + * + * @constructor + * @api public + */ +function EventEmitter() { + this._events = new Events(); + this._eventsCount = 0; +} + +/** + * Return an array listing the events for which the emitter has registered + * listeners. + * + * @returns {Array} + * @api public + */ +EventEmitter.prototype.eventNames = function eventNames() { + var names = [] + , events + , name; + + if (this._eventsCount === 0) return names; + + for (name in (events = this._events)) { + if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); + } + + if (Object.getOwnPropertySymbols) { + return names.concat(Object.getOwnPropertySymbols(events)); + } + + return names; +}; + +/** + * Return the listeners registered for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Boolean} exists Only check if there are listeners. + * @returns {Array|Boolean} + * @api public + */ +EventEmitter.prototype.listeners = function listeners(event, exists) { + var evt = prefix ? prefix + event : event + , available = this._events[evt]; + + if (exists) return !!available; + if (!available) return []; + if (available.fn) return [available.fn]; + + for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) { + ee[i] = available[i].fn; + } + + return ee; +}; + +/** + * Calls each of the listeners registered for a given event. + * + * @param {String|Symbol} event The event name. + * @returns {Boolean} `true` if the event had listeners, else `false`. + * @api public + */ +EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return false; + + var listeners = this._events[evt] + , len = arguments.length + , args + , i; + + if (listeners.fn) { + if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); + + switch (len) { + case 1: return listeners.fn.call(listeners.context), true; + case 2: return listeners.fn.call(listeners.context, a1), true; + case 3: return listeners.fn.call(listeners.context, a1, a2), true; + case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; + case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; + case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; + } + + for (i = 1, args = new Array(len -1); i < len; i++) { + args[i - 1] = arguments[i]; + } + + listeners.fn.apply(listeners.context, args); + } else { + var length = listeners.length + , j; + + for (i = 0; i < length; i++) { + if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); + + switch (len) { + case 1: listeners[i].fn.call(listeners[i].context); break; + case 2: listeners[i].fn.call(listeners[i].context, a1); break; + case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; + case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; + default: + if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { + args[j - 1] = arguments[j]; + } + + listeners[i].fn.apply(listeners[i].context, args); + } + } + } + + return true; +}; + +/** + * Add a listener for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn The listener function. + * @param {Mixed} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.on = function on(event, fn, context) { + var listener = new EE(fn, context || this) + , evt = prefix ? prefix + event : event; + + if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++; + else if (!this._events[evt].fn) this._events[evt].push(listener); + else this._events[evt] = [this._events[evt], listener]; + + return this; +}; + +/** + * Add a one-time listener for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn The listener function. + * @param {Mixed} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.once = function once(event, fn, context) { + var listener = new EE(fn, context || this, true) + , evt = prefix ? prefix + event : event; + + if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++; + else if (!this._events[evt].fn) this._events[evt].push(listener); + else this._events[evt] = [this._events[evt], listener]; + + return this; +}; + +/** + * Remove the listeners of a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn Only remove the listeners that match this function. + * @param {Mixed} context Only remove the listeners that have this context. + * @param {Boolean} once Only remove one-time listeners. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return this; + if (!fn) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + return this; + } + + var listeners = this._events[evt]; + + if (listeners.fn) { + if ( + listeners.fn === fn + && (!once || listeners.once) + && (!context || listeners.context === context) + ) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + } else { + for (var i = 0, events = [], length = listeners.length; i < length; i++) { + if ( + listeners[i].fn !== fn + || (once && !listeners[i].once) + || (context && listeners[i].context !== context) + ) { + events.push(listeners[i]); + } + } + + // + // Reset the array, or remove it completely if we have no more listeners. + // + if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; + else if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + + return this; +}; + +/** + * Remove all listeners, or those of the specified event. + * + * @param {String|Symbol} [event] The event name. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { + var evt; + + if (event) { + evt = prefix ? prefix + event : event; + if (this._events[evt]) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + } else { + this._events = new Events(); + this._eventsCount = 0; + } + + return this; +}; + +// +// Alias methods names because people roll like that. +// +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; +EventEmitter.prototype.addListener = EventEmitter.prototype.on; + +// +// This function doesn't apply anymore. +// +EventEmitter.prototype.setMaxListeners = function setMaxListeners() { + return this; +}; + +// +// Expose the prefix. +// +EventEmitter.prefixed = prefix; + +// +// Allow `EventEmitter` to be imported as module namespace. +// +EventEmitter.EventEmitter = EventEmitter; + +// +// Expose the module. +// +if ('undefined' !== typeof module) { + module.exports = EventEmitter; +} + + +/***/ }), +/* 55 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.matchText = exports.matchSpacing = exports.matchNewline = exports.matchBlot = exports.matchAttributor = exports.default = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _extend2 = __webpack_require__(3); + +var _extend3 = _interopRequireDefault(_extend2); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +var _align = __webpack_require__(36); + +var _background = __webpack_require__(37); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _color = __webpack_require__(26); + +var _direction = __webpack_require__(38); + +var _font = __webpack_require__(39); + +var _size = __webpack_require__(40); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:clipboard'); + +var DOM_KEY = '__ql-matcher'; + +var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]]; + +var ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) { + memo[attr.keyName] = attr; + return memo; +}, {}); + +var STYLE_ATTRIBUTORS = [_align.AlignStyle, _background.BackgroundStyle, _color.ColorStyle, _direction.DirectionStyle, _font.FontStyle, _size.SizeStyle].reduce(function (memo, attr) { + memo[attr.keyName] = attr; + return memo; +}, {}); + +var Clipboard = function (_Module) { + _inherits(Clipboard, _Module); + + function Clipboard(quill, options) { + _classCallCheck(this, Clipboard); + + var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options)); + + _this.quill.root.addEventListener('paste', _this.onPaste.bind(_this)); + _this.container = _this.quill.addContainer('ql-clipboard'); + _this.container.setAttribute('contenteditable', true); + _this.container.setAttribute('tabindex', -1); + _this.matchers = []; + CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + selector = _ref2[0], + matcher = _ref2[1]; + + if (!options.matchVisual && matcher === matchSpacing) return; + _this.addMatcher(selector, matcher); + }); + return _this; + } + + _createClass(Clipboard, [{ + key: 'addMatcher', + value: function addMatcher(selector, matcher) { + this.matchers.push([selector, matcher]); + } + }, { + key: 'convert', + value: function convert(html) { + if (typeof html === 'string') { + this.container.innerHTML = html.replace(/\>\r?\n +\<'); // Remove spaces between tags + return this.convert(); + } + var formats = this.quill.getFormat(this.quill.selection.savedRange.index); + if (formats[_code2.default.blotName]) { + var text = this.container.innerText; + this.container.innerHTML = ''; + return new _quillDelta2.default().insert(text, _defineProperty({}, _code2.default.blotName, formats[_code2.default.blotName])); + } + + var _prepareMatching = this.prepareMatching(), + _prepareMatching2 = _slicedToArray(_prepareMatching, 2), + elementMatchers = _prepareMatching2[0], + textMatchers = _prepareMatching2[1]; + + var delta = traverse(this.container, elementMatchers, textMatchers); + // Remove trailing newline + if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) { + delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1)); + } + debug.log('convert', this.container.innerHTML, delta); + this.container.innerHTML = ''; + return delta; + } + }, { + key: 'dangerouslyPasteHTML', + value: function dangerouslyPasteHTML(index, html) { + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _quill2.default.sources.API; + + if (typeof index === 'string') { + this.quill.setContents(this.convert(index), html); + this.quill.setSelection(0, _quill2.default.sources.SILENT); + } else { + var paste = this.convert(html); + this.quill.updateContents(new _quillDelta2.default().retain(index).concat(paste), source); + this.quill.setSelection(index + paste.length(), _quill2.default.sources.SILENT); + } + } + }, { + key: 'onPaste', + value: function onPaste(e) { + var _this2 = this; + + if (e.defaultPrevented || !this.quill.isEnabled()) return; + var range = this.quill.getSelection(); + var delta = new _quillDelta2.default().retain(range.index); + var scrollTop = this.quill.scrollingContainer.scrollTop; + this.container.focus(); + this.quill.selection.update(_quill2.default.sources.SILENT); + setTimeout(function () { + delta = delta.concat(_this2.convert()).delete(range.length); + _this2.quill.updateContents(delta, _quill2.default.sources.USER); + // range.length contributes to delta.length() + _this2.quill.setSelection(delta.length() - range.length, _quill2.default.sources.SILENT); + _this2.quill.scrollingContainer.scrollTop = scrollTop; + _this2.quill.focus(); + }, 1); + } + }, { + key: 'prepareMatching', + value: function prepareMatching() { + var _this3 = this; + + var elementMatchers = [], + textMatchers = []; + this.matchers.forEach(function (pair) { + var _pair = _slicedToArray(pair, 2), + selector = _pair[0], + matcher = _pair[1]; + + switch (selector) { + case Node.TEXT_NODE: + textMatchers.push(matcher); + break; + case Node.ELEMENT_NODE: + elementMatchers.push(matcher); + break; + default: + [].forEach.call(_this3.container.querySelectorAll(selector), function (node) { + // TODO use weakmap + node[DOM_KEY] = node[DOM_KEY] || []; + node[DOM_KEY].push(matcher); + }); + break; + } + }); + return [elementMatchers, textMatchers]; + } + }]); + + return Clipboard; +}(_module2.default); + +Clipboard.DEFAULTS = { + matchers: [], + matchVisual: true +}; + +function applyFormat(delta, format, value) { + if ((typeof format === 'undefined' ? 'undefined' : _typeof(format)) === 'object') { + return Object.keys(format).reduce(function (delta, key) { + return applyFormat(delta, key, format[key]); + }, delta); + } else { + return delta.reduce(function (delta, op) { + if (op.attributes && op.attributes[format]) { + return delta.push(op); + } else { + return delta.insert(op.insert, (0, _extend3.default)({}, _defineProperty({}, format, value), op.attributes)); + } + }, new _quillDelta2.default()); + } +} + +function computeStyle(node) { + if (node.nodeType !== Node.ELEMENT_NODE) return {}; + var DOM_KEY = '__ql-computed-style'; + return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node)); +} + +function deltaEndsWith(delta, text) { + var endText = ""; + for (var i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) { + var op = delta.ops[i]; + if (typeof op.insert !== 'string') break; + endText = op.insert + endText; + } + return endText.slice(-1 * text.length) === text; +} + +function isLine(node) { + if (node.childNodes.length === 0) return false; // Exclude embed blocks + var style = computeStyle(node); + return ['block', 'list-item'].indexOf(style.display) > -1; +} + +function traverse(node, elementMatchers, textMatchers) { + // Post-order + if (node.nodeType === node.TEXT_NODE) { + return textMatchers.reduce(function (delta, matcher) { + return matcher(node, delta); + }, new _quillDelta2.default()); + } else if (node.nodeType === node.ELEMENT_NODE) { + return [].reduce.call(node.childNodes || [], function (delta, childNode) { + var childrenDelta = traverse(childNode, elementMatchers, textMatchers); + if (childNode.nodeType === node.ELEMENT_NODE) { + childrenDelta = elementMatchers.reduce(function (childrenDelta, matcher) { + return matcher(childNode, childrenDelta); + }, childrenDelta); + childrenDelta = (childNode[DOM_KEY] || []).reduce(function (childrenDelta, matcher) { + return matcher(childNode, childrenDelta); + }, childrenDelta); + } + return delta.concat(childrenDelta); + }, new _quillDelta2.default()); + } else { + return new _quillDelta2.default(); + } +} + +function matchAlias(format, node, delta) { + return applyFormat(delta, format, true); +} + +function matchAttributor(node, delta) { + var attributes = _parchment2.default.Attributor.Attribute.keys(node); + var classes = _parchment2.default.Attributor.Class.keys(node); + var styles = _parchment2.default.Attributor.Style.keys(node); + var formats = {}; + attributes.concat(classes).concat(styles).forEach(function (name) { + var attr = _parchment2.default.query(name, _parchment2.default.Scope.ATTRIBUTE); + if (attr != null) { + formats[attr.attrName] = attr.value(node); + if (formats[attr.attrName]) return; + } + attr = ATTRIBUTE_ATTRIBUTORS[name]; + if (attr != null && (attr.attrName === name || attr.keyName === name)) { + formats[attr.attrName] = attr.value(node) || undefined; + } + attr = STYLE_ATTRIBUTORS[name]; + if (attr != null && (attr.attrName === name || attr.keyName === name)) { + attr = STYLE_ATTRIBUTORS[name]; + formats[attr.attrName] = attr.value(node) || undefined; + } + }); + if (Object.keys(formats).length > 0) { + delta = applyFormat(delta, formats); + } + return delta; +} + +function matchBlot(node, delta) { + var match = _parchment2.default.query(node); + if (match == null) return delta; + if (match.prototype instanceof _parchment2.default.Embed) { + var embed = {}; + var value = match.value(node); + if (value != null) { + embed[match.blotName] = value; + delta = new _quillDelta2.default().insert(embed, match.formats(node)); + } + } else if (typeof match.formats === 'function') { + delta = applyFormat(delta, match.blotName, match.formats(node)); + } + return delta; +} + +function matchBreak(node, delta) { + if (!deltaEndsWith(delta, '\n')) { + delta.insert('\n'); + } + return delta; +} + +function matchIgnore() { + return new _quillDelta2.default(); +} + +function matchIndent(node, delta) { + var match = _parchment2.default.query(node); + if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\n')) { + return delta; + } + var indent = -1, + parent = node.parentNode; + while (!parent.classList.contains('ql-clipboard')) { + if ((_parchment2.default.query(parent) || {}).blotName === 'list') { + indent += 1; + } + parent = parent.parentNode; + } + if (indent <= 0) return delta; + return delta.compose(new _quillDelta2.default().retain(delta.length() - 1).retain(1, { indent: indent })); +} + +function matchNewline(node, delta) { + if (!deltaEndsWith(delta, '\n')) { + if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) { + delta.insert('\n'); + } + } + return delta; +} + +function matchSpacing(node, delta) { + if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) { + var nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom); + if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight * 1.5) { + delta.insert('\n'); + } + } + return delta; +} + +function matchStyles(node, delta) { + var formats = {}; + var style = node.style || {}; + if (style.fontStyle && computeStyle(node).fontStyle === 'italic') { + formats.italic = true; + } + if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) { + formats.bold = true; + } + if (Object.keys(formats).length > 0) { + delta = applyFormat(delta, formats); + } + if (parseFloat(style.textIndent || 0) > 0) { + // Could be 0.5in + delta = new _quillDelta2.default().insert('\t').concat(delta); + } + return delta; +} + +function matchText(node, delta) { + var text = node.data; + // Word represents empty line with   + if (node.parentNode.tagName === 'O:P') { + return delta.insert(text.trim()); + } + if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) { + return delta; + } + if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) { + // eslint-disable-next-line func-style + var replacer = function replacer(collapse, match) { + match = match.replace(/[^\u00a0]/g, ''); // \u00a0 is nbsp; + return match.length < 1 && collapse ? ' ' : match; + }; + text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' '); + text = text.replace(/\s\s+/g, replacer.bind(replacer, true)); // collapse whitespace + if (node.previousSibling == null && isLine(node.parentNode) || node.previousSibling != null && isLine(node.previousSibling)) { + text = text.replace(/^\s+/, replacer.bind(replacer, false)); + } + if (node.nextSibling == null && isLine(node.parentNode) || node.nextSibling != null && isLine(node.nextSibling)) { + text = text.replace(/\s+$/, replacer.bind(replacer, false)); + } + } + return delta.insert(text); +} + +exports.default = Clipboard; +exports.matchAttributor = matchAttributor; +exports.matchBlot = matchBlot; +exports.matchNewline = matchNewline; +exports.matchSpacing = matchSpacing; +exports.matchText = matchText; + +/***/ }), +/* 56 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Bold = function (_Inline) { + _inherits(Bold, _Inline); + + function Bold() { + _classCallCheck(this, Bold); + + return _possibleConstructorReturn(this, (Bold.__proto__ || Object.getPrototypeOf(Bold)).apply(this, arguments)); + } + + _createClass(Bold, [{ + key: 'optimize', + value: function optimize(context) { + _get(Bold.prototype.__proto__ || Object.getPrototypeOf(Bold.prototype), 'optimize', this).call(this, context); + if (this.domNode.tagName !== this.statics.tagName[0]) { + this.replaceWith(this.statics.blotName); + } + } + }], [{ + key: 'create', + value: function create() { + return _get(Bold.__proto__ || Object.getPrototypeOf(Bold), 'create', this).call(this); + } + }, { + key: 'formats', + value: function formats() { + return true; + } + }]); + + return Bold; +}(_inline2.default); + +Bold.blotName = 'bold'; +Bold.tagName = ['STRONG', 'B']; + +exports.default = Bold; + +/***/ }), +/* 57 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.addControls = exports.default = undefined; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:toolbar'); + +var Toolbar = function (_Module) { + _inherits(Toolbar, _Module); + + function Toolbar(quill, options) { + _classCallCheck(this, Toolbar); + + var _this = _possibleConstructorReturn(this, (Toolbar.__proto__ || Object.getPrototypeOf(Toolbar)).call(this, quill, options)); + + if (Array.isArray(_this.options.container)) { + var container = document.createElement('div'); + addControls(container, _this.options.container); + quill.container.parentNode.insertBefore(container, quill.container); + _this.container = container; + } else if (typeof _this.options.container === 'string') { + _this.container = document.querySelector(_this.options.container); + } else { + _this.container = _this.options.container; + } + if (!(_this.container instanceof HTMLElement)) { + var _ret; + + return _ret = debug.error('Container required for toolbar', _this.options), _possibleConstructorReturn(_this, _ret); + } + _this.container.classList.add('ql-toolbar'); + _this.controls = []; + _this.handlers = {}; + Object.keys(_this.options.handlers).forEach(function (format) { + _this.addHandler(format, _this.options.handlers[format]); + }); + [].forEach.call(_this.container.querySelectorAll('button, select'), function (input) { + _this.attach(input); + }); + _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (type, range) { + if (type === _quill2.default.events.SELECTION_CHANGE) { + _this.update(range); + } + }); + _this.quill.on(_quill2.default.events.SCROLL_OPTIMIZE, function () { + var _this$quill$selection = _this.quill.selection.getRange(), + _this$quill$selection2 = _slicedToArray(_this$quill$selection, 1), + range = _this$quill$selection2[0]; // quill.getSelection triggers update + + + _this.update(range); + }); + return _this; + } + + _createClass(Toolbar, [{ + key: 'addHandler', + value: function addHandler(format, handler) { + this.handlers[format] = handler; + } + }, { + key: 'attach', + value: function attach(input) { + var _this2 = this; + + var format = [].find.call(input.classList, function (className) { + return className.indexOf('ql-') === 0; + }); + if (!format) return; + format = format.slice('ql-'.length); + if (input.tagName === 'BUTTON') { + input.setAttribute('type', 'button'); + } + if (this.handlers[format] == null) { + if (this.quill.scroll.whitelist != null && this.quill.scroll.whitelist[format] == null) { + debug.warn('ignoring attaching to disabled format', format, input); + return; + } + if (_parchment2.default.query(format) == null) { + debug.warn('ignoring attaching to nonexistent format', format, input); + return; + } + } + var eventName = input.tagName === 'SELECT' ? 'change' : 'click'; + input.addEventListener(eventName, function (e) { + var value = void 0; + if (input.tagName === 'SELECT') { + if (input.selectedIndex < 0) return; + var selected = input.options[input.selectedIndex]; + if (selected.hasAttribute('selected')) { + value = false; + } else { + value = selected.value || false; + } + } else { + if (input.classList.contains('ql-active')) { + value = false; + } else { + value = input.value || !input.hasAttribute('value'); + } + e.preventDefault(); + } + _this2.quill.focus(); + + var _quill$selection$getR = _this2.quill.selection.getRange(), + _quill$selection$getR2 = _slicedToArray(_quill$selection$getR, 1), + range = _quill$selection$getR2[0]; + + if (_this2.handlers[format] != null) { + _this2.handlers[format].call(_this2, value); + } else if (_parchment2.default.query(format).prototype instanceof _parchment2.default.Embed) { + value = prompt('Enter ' + format); + if (!value) return; + _this2.quill.updateContents(new _quillDelta2.default().retain(range.index).delete(range.length).insert(_defineProperty({}, format, value)), _quill2.default.sources.USER); + } else { + _this2.quill.format(format, value, _quill2.default.sources.USER); + } + _this2.update(range); + }); + // TODO use weakmap + this.controls.push([format, input]); + } + }, { + key: 'update', + value: function update(range) { + var formats = range == null ? {} : this.quill.getFormat(range); + this.controls.forEach(function (pair) { + var _pair = _slicedToArray(pair, 2), + format = _pair[0], + input = _pair[1]; + + if (input.tagName === 'SELECT') { + var option = void 0; + if (range == null) { + option = null; + } else if (formats[format] == null) { + option = input.querySelector('option[selected]'); + } else if (!Array.isArray(formats[format])) { + var value = formats[format]; + if (typeof value === 'string') { + value = value.replace(/\"/g, '\\"'); + } + option = input.querySelector('option[value="' + value + '"]'); + } + if (option == null) { + input.value = ''; // TODO make configurable? + input.selectedIndex = -1; + } else { + option.selected = true; + } + } else { + if (range == null) { + input.classList.remove('ql-active'); + } else if (input.hasAttribute('value')) { + // both being null should match (default values) + // '1' should match with 1 (headers) + var isActive = formats[format] === input.getAttribute('value') || formats[format] != null && formats[format].toString() === input.getAttribute('value') || formats[format] == null && !input.getAttribute('value'); + input.classList.toggle('ql-active', isActive); + } else { + input.classList.toggle('ql-active', formats[format] != null); + } + } + }); + } + }]); + + return Toolbar; +}(_module2.default); + +Toolbar.DEFAULTS = {}; + +function addButton(container, format, value) { + var input = document.createElement('button'); + input.setAttribute('type', 'button'); + input.classList.add('ql-' + format); + if (value != null) { + input.value = value; + } + container.appendChild(input); +} + +function addControls(container, groups) { + if (!Array.isArray(groups[0])) { + groups = [groups]; + } + groups.forEach(function (controls) { + var group = document.createElement('span'); + group.classList.add('ql-formats'); + controls.forEach(function (control) { + if (typeof control === 'string') { + addButton(group, control); + } else { + var format = Object.keys(control)[0]; + var value = control[format]; + if (Array.isArray(value)) { + addSelect(group, format, value); + } else { + addButton(group, format, value); + } + } + }); + container.appendChild(group); + }); +} + +function addSelect(container, format, values) { + var input = document.createElement('select'); + input.classList.add('ql-' + format); + values.forEach(function (value) { + var option = document.createElement('option'); + if (value !== false) { + option.setAttribute('value', value); + } else { + option.setAttribute('selected', 'selected'); + } + input.appendChild(option); + }); + container.appendChild(input); +} + +Toolbar.DEFAULTS = { + container: null, + handlers: { + clean: function clean() { + var _this3 = this; + + var range = this.quill.getSelection(); + if (range == null) return; + if (range.length == 0) { + var formats = this.quill.getFormat(); + Object.keys(formats).forEach(function (name) { + // Clean functionality in existing apps only clean inline formats + if (_parchment2.default.query(name, _parchment2.default.Scope.INLINE) != null) { + _this3.quill.format(name, false); + } + }); + } else { + this.quill.removeFormat(range, _quill2.default.sources.USER); + } + }, + direction: function direction(value) { + var align = this.quill.getFormat()['align']; + if (value === 'rtl' && align == null) { + this.quill.format('align', 'right', _quill2.default.sources.USER); + } else if (!value && align === 'right') { + this.quill.format('align', false, _quill2.default.sources.USER); + } + this.quill.format('direction', value, _quill2.default.sources.USER); + }, + indent: function indent(value) { + var range = this.quill.getSelection(); + var formats = this.quill.getFormat(range); + var indent = parseInt(formats.indent || 0); + if (value === '+1' || value === '-1') { + var modifier = value === '+1' ? 1 : -1; + if (formats.direction === 'rtl') modifier *= -1; + this.quill.format('indent', indent + modifier, _quill2.default.sources.USER); + } + }, + link: function link(value) { + if (value === true) { + value = prompt('Enter link URL:'); + } + this.quill.format('link', value, _quill2.default.sources.USER); + }, + list: function list(value) { + var range = this.quill.getSelection(); + var formats = this.quill.getFormat(range); + if (value === 'check') { + if (formats['list'] === 'checked' || formats['list'] === 'unchecked') { + this.quill.format('list', false, _quill2.default.sources.USER); + } else { + this.quill.format('list', 'unchecked', _quill2.default.sources.USER); + } + } else { + this.quill.format('list', value, _quill2.default.sources.USER); + } + } + } +}; + +exports.default = Toolbar; +exports.addControls = addControls; + +/***/ }), +/* 58 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 59 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _picker = __webpack_require__(28); + +var _picker2 = _interopRequireDefault(_picker); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ColorPicker = function (_Picker) { + _inherits(ColorPicker, _Picker); + + function ColorPicker(select, label) { + _classCallCheck(this, ColorPicker); + + var _this = _possibleConstructorReturn(this, (ColorPicker.__proto__ || Object.getPrototypeOf(ColorPicker)).call(this, select)); + + _this.label.innerHTML = label; + _this.container.classList.add('ql-color-picker'); + [].slice.call(_this.container.querySelectorAll('.ql-picker-item'), 0, 7).forEach(function (item) { + item.classList.add('ql-primary'); + }); + return _this; + } + + _createClass(ColorPicker, [{ + key: 'buildItem', + value: function buildItem(option) { + var item = _get(ColorPicker.prototype.__proto__ || Object.getPrototypeOf(ColorPicker.prototype), 'buildItem', this).call(this, option); + item.style.backgroundColor = option.getAttribute('value') || ''; + return item; + } + }, { + key: 'selectItem', + value: function selectItem(item, trigger) { + _get(ColorPicker.prototype.__proto__ || Object.getPrototypeOf(ColorPicker.prototype), 'selectItem', this).call(this, item, trigger); + var colorLabel = this.label.querySelector('.ql-color-label'); + var value = item ? item.getAttribute('data-value') || '' : ''; + if (colorLabel) { + if (colorLabel.tagName === 'line') { + colorLabel.style.stroke = value; + } else { + colorLabel.style.fill = value; + } + } + } + }]); + + return ColorPicker; +}(_picker2.default); + +exports.default = ColorPicker; + +/***/ }), +/* 60 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _picker = __webpack_require__(28); + +var _picker2 = _interopRequireDefault(_picker); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var IconPicker = function (_Picker) { + _inherits(IconPicker, _Picker); + + function IconPicker(select, icons) { + _classCallCheck(this, IconPicker); + + var _this = _possibleConstructorReturn(this, (IconPicker.__proto__ || Object.getPrototypeOf(IconPicker)).call(this, select)); + + _this.container.classList.add('ql-icon-picker'); + [].forEach.call(_this.container.querySelectorAll('.ql-picker-item'), function (item) { + item.innerHTML = icons[item.getAttribute('data-value') || '']; + }); + _this.defaultItem = _this.container.querySelector('.ql-selected'); + _this.selectItem(_this.defaultItem); + return _this; + } + + _createClass(IconPicker, [{ + key: 'selectItem', + value: function selectItem(item, trigger) { + _get(IconPicker.prototype.__proto__ || Object.getPrototypeOf(IconPicker.prototype), 'selectItem', this).call(this, item, trigger); + item = item || this.defaultItem; + this.label.innerHTML = item.innerHTML; + } + }]); + + return IconPicker; +}(_picker2.default); + +exports.default = IconPicker; + +/***/ }), +/* 61 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Tooltip = function () { + function Tooltip(quill, boundsContainer) { + var _this = this; + + _classCallCheck(this, Tooltip); + + this.quill = quill; + this.boundsContainer = boundsContainer || document.body; + this.root = quill.addContainer('ql-tooltip'); + this.root.innerHTML = this.constructor.TEMPLATE; + if (this.quill.root === this.quill.scrollingContainer) { + this.quill.root.addEventListener('scroll', function () { + _this.root.style.marginTop = -1 * _this.quill.root.scrollTop + 'px'; + }); + } + this.hide(); + } + + _createClass(Tooltip, [{ + key: 'hide', + value: function hide() { + this.root.classList.add('ql-hidden'); + } + }, { + key: 'position', + value: function position(reference) { + var left = reference.left + reference.width / 2 - this.root.offsetWidth / 2; + // root.scrollTop should be 0 if scrollContainer !== root + var top = reference.bottom + this.quill.root.scrollTop; + this.root.style.left = left + 'px'; + this.root.style.top = top + 'px'; + this.root.classList.remove('ql-flip'); + var containerBounds = this.boundsContainer.getBoundingClientRect(); + var rootBounds = this.root.getBoundingClientRect(); + var shift = 0; + if (rootBounds.right > containerBounds.right) { + shift = containerBounds.right - rootBounds.right; + this.root.style.left = left + shift + 'px'; + } + if (rootBounds.left < containerBounds.left) { + shift = containerBounds.left - rootBounds.left; + this.root.style.left = left + shift + 'px'; + } + if (rootBounds.bottom > containerBounds.bottom) { + var height = rootBounds.bottom - rootBounds.top; + var verticalShift = reference.bottom - reference.top + height; + this.root.style.top = top - verticalShift + 'px'; + this.root.classList.add('ql-flip'); + } + return shift; + } + }, { + key: 'show', + value: function show() { + this.root.classList.remove('ql-editing'); + this.root.classList.remove('ql-hidden'); + } + }]); + + return Tooltip; +}(); + +exports.default = Tooltip; + +/***/ }), +/* 62 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _base = __webpack_require__(43); + +var _base2 = _interopRequireDefault(_base); + +var _link = __webpack_require__(27); + +var _link2 = _interopRequireDefault(_link); + +var _selection = __webpack_require__(15); + +var _icons = __webpack_require__(41); + +var _icons2 = _interopRequireDefault(_icons); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var TOOLBAR_CONFIG = [[{ header: ['1', '2', '3', false] }], ['bold', 'italic', 'underline', 'link'], [{ list: 'ordered' }, { list: 'bullet' }], ['clean']]; + +var SnowTheme = function (_BaseTheme) { + _inherits(SnowTheme, _BaseTheme); + + function SnowTheme(quill, options) { + _classCallCheck(this, SnowTheme); + + if (options.modules.toolbar != null && options.modules.toolbar.container == null) { + options.modules.toolbar.container = TOOLBAR_CONFIG; + } + + var _this = _possibleConstructorReturn(this, (SnowTheme.__proto__ || Object.getPrototypeOf(SnowTheme)).call(this, quill, options)); + + _this.quill.container.classList.add('ql-snow'); + return _this; + } + + _createClass(SnowTheme, [{ + key: 'extendToolbar', + value: function extendToolbar(toolbar) { + toolbar.container.classList.add('ql-snow'); + this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), _icons2.default); + this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), _icons2.default); + this.tooltip = new SnowTooltip(this.quill, this.options.bounds); + if (toolbar.container.querySelector('.ql-link')) { + this.quill.keyboard.addBinding({ key: 'K', shortKey: true }, function (range, context) { + toolbar.handlers['link'].call(toolbar, !context.format.link); + }); + } + } + }]); + + return SnowTheme; +}(_base2.default); + +SnowTheme.DEFAULTS = (0, _extend2.default)(true, {}, _base2.default.DEFAULTS, { + modules: { + toolbar: { + handlers: { + link: function link(value) { + if (value) { + var range = this.quill.getSelection(); + if (range == null || range.length == 0) return; + var preview = this.quill.getText(range); + if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) { + preview = 'mailto:' + preview; + } + var tooltip = this.quill.theme.tooltip; + tooltip.edit('link', preview); + } else { + this.quill.format('link', false); + } + } + } + } + } +}); + +var SnowTooltip = function (_BaseTooltip) { + _inherits(SnowTooltip, _BaseTooltip); + + function SnowTooltip(quill, bounds) { + _classCallCheck(this, SnowTooltip); + + var _this2 = _possibleConstructorReturn(this, (SnowTooltip.__proto__ || Object.getPrototypeOf(SnowTooltip)).call(this, quill, bounds)); + + _this2.preview = _this2.root.querySelector('a.ql-preview'); + return _this2; + } + + _createClass(SnowTooltip, [{ + key: 'listen', + value: function listen() { + var _this3 = this; + + _get(SnowTooltip.prototype.__proto__ || Object.getPrototypeOf(SnowTooltip.prototype), 'listen', this).call(this); + this.root.querySelector('a.ql-action').addEventListener('click', function (event) { + if (_this3.root.classList.contains('ql-editing')) { + _this3.save(); + } else { + _this3.edit('link', _this3.preview.textContent); + } + event.preventDefault(); + }); + this.root.querySelector('a.ql-remove').addEventListener('click', function (event) { + if (_this3.linkRange != null) { + var range = _this3.linkRange; + _this3.restoreFocus(); + _this3.quill.formatText(range, 'link', false, _emitter2.default.sources.USER); + delete _this3.linkRange; + } + event.preventDefault(); + _this3.hide(); + }); + this.quill.on(_emitter2.default.events.SELECTION_CHANGE, function (range, oldRange, source) { + if (range == null) return; + if (range.length === 0 && source === _emitter2.default.sources.USER) { + var _quill$scroll$descend = _this3.quill.scroll.descendant(_link2.default, range.index), + _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2), + link = _quill$scroll$descend2[0], + offset = _quill$scroll$descend2[1]; + + if (link != null) { + _this3.linkRange = new _selection.Range(range.index - offset, link.length()); + var preview = _link2.default.formats(link.domNode); + _this3.preview.textContent = preview; + _this3.preview.setAttribute('href', preview); + _this3.show(); + _this3.position(_this3.quill.getBounds(_this3.linkRange)); + return; + } + } else { + delete _this3.linkRange; + } + _this3.hide(); + }); + } + }, { + key: 'show', + value: function show() { + _get(SnowTooltip.prototype.__proto__ || Object.getPrototypeOf(SnowTooltip.prototype), 'show', this).call(this); + this.root.removeAttribute('data-mode'); + } + }]); + + return SnowTooltip; +}(_base.BaseTooltip); + +SnowTooltip.TEMPLATE = ['', '', '', ''].join(''); + +exports.default = SnowTheme; + +/***/ }), +/* 63 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _core = __webpack_require__(29); + +var _core2 = _interopRequireDefault(_core); + +var _align = __webpack_require__(36); + +var _direction = __webpack_require__(38); + +var _indent = __webpack_require__(64); + +var _blockquote = __webpack_require__(65); + +var _blockquote2 = _interopRequireDefault(_blockquote); + +var _header = __webpack_require__(66); + +var _header2 = _interopRequireDefault(_header); + +var _list = __webpack_require__(67); + +var _list2 = _interopRequireDefault(_list); + +var _background = __webpack_require__(37); + +var _color = __webpack_require__(26); + +var _font = __webpack_require__(39); + +var _size = __webpack_require__(40); + +var _bold = __webpack_require__(56); + +var _bold2 = _interopRequireDefault(_bold); + +var _italic = __webpack_require__(68); + +var _italic2 = _interopRequireDefault(_italic); + +var _link = __webpack_require__(27); + +var _link2 = _interopRequireDefault(_link); + +var _script = __webpack_require__(69); + +var _script2 = _interopRequireDefault(_script); + +var _strike = __webpack_require__(70); + +var _strike2 = _interopRequireDefault(_strike); + +var _underline = __webpack_require__(71); + +var _underline2 = _interopRequireDefault(_underline); + +var _image = __webpack_require__(72); + +var _image2 = _interopRequireDefault(_image); + +var _video = __webpack_require__(73); + +var _video2 = _interopRequireDefault(_video); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _formula = __webpack_require__(74); + +var _formula2 = _interopRequireDefault(_formula); + +var _syntax = __webpack_require__(75); + +var _syntax2 = _interopRequireDefault(_syntax); + +var _toolbar = __webpack_require__(57); + +var _toolbar2 = _interopRequireDefault(_toolbar); + +var _icons = __webpack_require__(41); + +var _icons2 = _interopRequireDefault(_icons); + +var _picker = __webpack_require__(28); + +var _picker2 = _interopRequireDefault(_picker); + +var _colorPicker = __webpack_require__(59); + +var _colorPicker2 = _interopRequireDefault(_colorPicker); + +var _iconPicker = __webpack_require__(60); + +var _iconPicker2 = _interopRequireDefault(_iconPicker); + +var _tooltip = __webpack_require__(61); + +var _tooltip2 = _interopRequireDefault(_tooltip); + +var _bubble = __webpack_require__(108); + +var _bubble2 = _interopRequireDefault(_bubble); + +var _snow = __webpack_require__(62); + +var _snow2 = _interopRequireDefault(_snow); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +_core2.default.register({ + 'attributors/attribute/direction': _direction.DirectionAttribute, + + 'attributors/class/align': _align.AlignClass, + 'attributors/class/background': _background.BackgroundClass, + 'attributors/class/color': _color.ColorClass, + 'attributors/class/direction': _direction.DirectionClass, + 'attributors/class/font': _font.FontClass, + 'attributors/class/size': _size.SizeClass, + + 'attributors/style/align': _align.AlignStyle, + 'attributors/style/background': _background.BackgroundStyle, + 'attributors/style/color': _color.ColorStyle, + 'attributors/style/direction': _direction.DirectionStyle, + 'attributors/style/font': _font.FontStyle, + 'attributors/style/size': _size.SizeStyle +}, true); + +_core2.default.register({ + 'formats/align': _align.AlignClass, + 'formats/direction': _direction.DirectionClass, + 'formats/indent': _indent.IndentClass, + + 'formats/background': _background.BackgroundStyle, + 'formats/color': _color.ColorStyle, + 'formats/font': _font.FontClass, + 'formats/size': _size.SizeClass, + + 'formats/blockquote': _blockquote2.default, + 'formats/code-block': _code2.default, + 'formats/header': _header2.default, + 'formats/list': _list2.default, + + 'formats/bold': _bold2.default, + 'formats/code': _code.Code, + 'formats/italic': _italic2.default, + 'formats/link': _link2.default, + 'formats/script': _script2.default, + 'formats/strike': _strike2.default, + 'formats/underline': _underline2.default, + + 'formats/image': _image2.default, + 'formats/video': _video2.default, + + 'formats/list/item': _list.ListItem, + + 'modules/formula': _formula2.default, + 'modules/syntax': _syntax2.default, + 'modules/toolbar': _toolbar2.default, + + 'themes/bubble': _bubble2.default, + 'themes/snow': _snow2.default, + + 'ui/icons': _icons2.default, + 'ui/picker': _picker2.default, + 'ui/icon-picker': _iconPicker2.default, + 'ui/color-picker': _colorPicker2.default, + 'ui/tooltip': _tooltip2.default +}, true); + +exports.default = _core2.default; + +/***/ }), +/* 64 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.IndentClass = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var IdentAttributor = function (_Parchment$Attributor) { + _inherits(IdentAttributor, _Parchment$Attributor); + + function IdentAttributor() { + _classCallCheck(this, IdentAttributor); + + return _possibleConstructorReturn(this, (IdentAttributor.__proto__ || Object.getPrototypeOf(IdentAttributor)).apply(this, arguments)); + } + + _createClass(IdentAttributor, [{ + key: 'add', + value: function add(node, value) { + if (value === '+1' || value === '-1') { + var indent = this.value(node) || 0; + value = value === '+1' ? indent + 1 : indent - 1; + } + if (value === 0) { + this.remove(node); + return true; + } else { + return _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'add', this).call(this, node, value); + } + } + }, { + key: 'canAdd', + value: function canAdd(node, value) { + return _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'canAdd', this).call(this, node, value) || _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'canAdd', this).call(this, node, parseInt(value)); + } + }, { + key: 'value', + value: function value(node) { + return parseInt(_get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'value', this).call(this, node)) || undefined; // Don't return NaN + } + }]); + + return IdentAttributor; +}(_parchment2.default.Attributor.Class); + +var IndentClass = new IdentAttributor('indent', 'ql-indent', { + scope: _parchment2.default.Scope.BLOCK, + whitelist: [1, 2, 3, 4, 5, 6, 7, 8] +}); + +exports.IndentClass = IndentClass; + +/***/ }), +/* 65 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Blockquote = function (_Block) { + _inherits(Blockquote, _Block); + + function Blockquote() { + _classCallCheck(this, Blockquote); + + return _possibleConstructorReturn(this, (Blockquote.__proto__ || Object.getPrototypeOf(Blockquote)).apply(this, arguments)); + } + + return Blockquote; +}(_block2.default); + +Blockquote.blotName = 'blockquote'; +Blockquote.tagName = 'blockquote'; + +exports.default = Blockquote; + +/***/ }), +/* 66 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Header = function (_Block) { + _inherits(Header, _Block); + + function Header() { + _classCallCheck(this, Header); + + return _possibleConstructorReturn(this, (Header.__proto__ || Object.getPrototypeOf(Header)).apply(this, arguments)); + } + + _createClass(Header, null, [{ + key: 'formats', + value: function formats(domNode) { + return this.tagName.indexOf(domNode.tagName) + 1; + } + }]); + + return Header; +}(_block2.default); + +Header.blotName = 'header'; +Header.tagName = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']; + +exports.default = Header; + +/***/ }), +/* 67 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.ListItem = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _container = __webpack_require__(25); + +var _container2 = _interopRequireDefault(_container); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ListItem = function (_Block) { + _inherits(ListItem, _Block); + + function ListItem() { + _classCallCheck(this, ListItem); + + return _possibleConstructorReturn(this, (ListItem.__proto__ || Object.getPrototypeOf(ListItem)).apply(this, arguments)); + } + + _createClass(ListItem, [{ + key: 'format', + value: function format(name, value) { + if (name === List.blotName && !value) { + this.replaceWith(_parchment2.default.create(this.statics.scope)); + } else { + _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'format', this).call(this, name, value); + } + } + }, { + key: 'remove', + value: function remove() { + if (this.prev == null && this.next == null) { + this.parent.remove(); + } else { + _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'remove', this).call(this); + } + } + }, { + key: 'replaceWith', + value: function replaceWith(name, value) { + this.parent.isolate(this.offset(this.parent), this.length()); + if (name === this.parent.statics.blotName) { + this.parent.replaceWith(name, value); + return this; + } else { + this.parent.unwrap(); + return _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'replaceWith', this).call(this, name, value); + } + } + }], [{ + key: 'formats', + value: function formats(domNode) { + return domNode.tagName === this.tagName ? undefined : _get(ListItem.__proto__ || Object.getPrototypeOf(ListItem), 'formats', this).call(this, domNode); + } + }]); + + return ListItem; +}(_block2.default); + +ListItem.blotName = 'list-item'; +ListItem.tagName = 'LI'; + +var List = function (_Container) { + _inherits(List, _Container); + + _createClass(List, null, [{ + key: 'create', + value: function create(value) { + var tagName = value === 'ordered' ? 'OL' : 'UL'; + var node = _get(List.__proto__ || Object.getPrototypeOf(List), 'create', this).call(this, tagName); + if (value === 'checked' || value === 'unchecked') { + node.setAttribute('data-checked', value === 'checked'); + } + return node; + } + }, { + key: 'formats', + value: function formats(domNode) { + if (domNode.tagName === 'OL') return 'ordered'; + if (domNode.tagName === 'UL') { + if (domNode.hasAttribute('data-checked')) { + return domNode.getAttribute('data-checked') === 'true' ? 'checked' : 'unchecked'; + } else { + return 'bullet'; + } + } + return undefined; + } + }]); + + function List(domNode) { + _classCallCheck(this, List); + + var _this2 = _possibleConstructorReturn(this, (List.__proto__ || Object.getPrototypeOf(List)).call(this, domNode)); + + var listEventHandler = function listEventHandler(e) { + if (e.target.parentNode !== domNode) return; + var format = _this2.statics.formats(domNode); + var blot = _parchment2.default.find(e.target); + if (format === 'checked') { + blot.format('list', 'unchecked'); + } else if (format === 'unchecked') { + blot.format('list', 'checked'); + } + }; + + domNode.addEventListener('touchstart', listEventHandler); + domNode.addEventListener('mousedown', listEventHandler); + return _this2; + } + + _createClass(List, [{ + key: 'format', + value: function format(name, value) { + if (this.children.length > 0) { + this.children.tail.format(name, value); + } + } + }, { + key: 'formats', + value: function formats() { + // We don't inherit from FormatBlot + return _defineProperty({}, this.statics.blotName, this.statics.formats(this.domNode)); + } + }, { + key: 'insertBefore', + value: function insertBefore(blot, ref) { + if (blot instanceof ListItem) { + _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'insertBefore', this).call(this, blot, ref); + } else { + var index = ref == null ? this.length() : ref.offset(this); + var after = this.split(index); + after.parent.insertBefore(blot, after); + } + } + }, { + key: 'optimize', + value: function optimize(context) { + _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'optimize', this).call(this, context); + var next = this.next; + if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && next.domNode.tagName === this.domNode.tagName && next.domNode.getAttribute('data-checked') === this.domNode.getAttribute('data-checked')) { + next.moveChildren(this); + next.remove(); + } + } + }, { + key: 'replace', + value: function replace(target) { + if (target.statics.blotName !== this.statics.blotName) { + var item = _parchment2.default.create(this.statics.defaultChild); + target.moveChildren(item); + this.appendChild(item); + } + _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'replace', this).call(this, target); + } + }]); + + return List; +}(_container2.default); + +List.blotName = 'list'; +List.scope = _parchment2.default.Scope.BLOCK_BLOT; +List.tagName = ['OL', 'UL']; +List.defaultChild = 'list-item'; +List.allowedChildren = [ListItem]; + +exports.ListItem = ListItem; +exports.default = List; + +/***/ }), +/* 68 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _bold = __webpack_require__(56); + +var _bold2 = _interopRequireDefault(_bold); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Italic = function (_Bold) { + _inherits(Italic, _Bold); + + function Italic() { + _classCallCheck(this, Italic); + + return _possibleConstructorReturn(this, (Italic.__proto__ || Object.getPrototypeOf(Italic)).apply(this, arguments)); + } + + return Italic; +}(_bold2.default); + +Italic.blotName = 'italic'; +Italic.tagName = ['EM', 'I']; + +exports.default = Italic; + +/***/ }), +/* 69 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Script = function (_Inline) { + _inherits(Script, _Inline); + + function Script() { + _classCallCheck(this, Script); + + return _possibleConstructorReturn(this, (Script.__proto__ || Object.getPrototypeOf(Script)).apply(this, arguments)); + } + + _createClass(Script, null, [{ + key: 'create', + value: function create(value) { + if (value === 'super') { + return document.createElement('sup'); + } else if (value === 'sub') { + return document.createElement('sub'); + } else { + return _get(Script.__proto__ || Object.getPrototypeOf(Script), 'create', this).call(this, value); + } + } + }, { + key: 'formats', + value: function formats(domNode) { + if (domNode.tagName === 'SUB') return 'sub'; + if (domNode.tagName === 'SUP') return 'super'; + return undefined; + } + }]); + + return Script; +}(_inline2.default); + +Script.blotName = 'script'; +Script.tagName = ['SUB', 'SUP']; + +exports.default = Script; + +/***/ }), +/* 70 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Strike = function (_Inline) { + _inherits(Strike, _Inline); + + function Strike() { + _classCallCheck(this, Strike); + + return _possibleConstructorReturn(this, (Strike.__proto__ || Object.getPrototypeOf(Strike)).apply(this, arguments)); + } + + return Strike; +}(_inline2.default); + +Strike.blotName = 'strike'; +Strike.tagName = 'S'; + +exports.default = Strike; + +/***/ }), +/* 71 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Underline = function (_Inline) { + _inherits(Underline, _Inline); + + function Underline() { + _classCallCheck(this, Underline); + + return _possibleConstructorReturn(this, (Underline.__proto__ || Object.getPrototypeOf(Underline)).apply(this, arguments)); + } + + return Underline; +}(_inline2.default); + +Underline.blotName = 'underline'; +Underline.tagName = 'U'; + +exports.default = Underline; + +/***/ }), +/* 72 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _link = __webpack_require__(27); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ATTRIBUTES = ['alt', 'height', 'width']; + +var Image = function (_Parchment$Embed) { + _inherits(Image, _Parchment$Embed); + + function Image() { + _classCallCheck(this, Image); + + return _possibleConstructorReturn(this, (Image.__proto__ || Object.getPrototypeOf(Image)).apply(this, arguments)); + } + + _createClass(Image, [{ + key: 'format', + value: function format(name, value) { + if (ATTRIBUTES.indexOf(name) > -1) { + if (value) { + this.domNode.setAttribute(name, value); + } else { + this.domNode.removeAttribute(name); + } + } else { + _get(Image.prototype.__proto__ || Object.getPrototypeOf(Image.prototype), 'format', this).call(this, name, value); + } + } + }], [{ + key: 'create', + value: function create(value) { + var node = _get(Image.__proto__ || Object.getPrototypeOf(Image), 'create', this).call(this, value); + if (typeof value === 'string') { + node.setAttribute('src', this.sanitize(value)); + } + return node; + } + }, { + key: 'formats', + value: function formats(domNode) { + return ATTRIBUTES.reduce(function (formats, attribute) { + if (domNode.hasAttribute(attribute)) { + formats[attribute] = domNode.getAttribute(attribute); + } + return formats; + }, {}); + } + }, { + key: 'match', + value: function match(url) { + return (/\.(jpe?g|gif|png)$/.test(url) || /^data:image\/.+;base64/.test(url) + ); + } + }, { + key: 'sanitize', + value: function sanitize(url) { + return (0, _link.sanitize)(url, ['http', 'https', 'data']) ? url : '//:0'; + } + }, { + key: 'value', + value: function value(domNode) { + return domNode.getAttribute('src'); + } + }]); + + return Image; +}(_parchment2.default.Embed); + +Image.blotName = 'image'; +Image.tagName = 'IMG'; + +exports.default = Image; + +/***/ }), +/* 73 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _block = __webpack_require__(4); + +var _link = __webpack_require__(27); + +var _link2 = _interopRequireDefault(_link); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ATTRIBUTES = ['height', 'width']; + +var Video = function (_BlockEmbed) { + _inherits(Video, _BlockEmbed); + + function Video() { + _classCallCheck(this, Video); + + return _possibleConstructorReturn(this, (Video.__proto__ || Object.getPrototypeOf(Video)).apply(this, arguments)); + } + + _createClass(Video, [{ + key: 'format', + value: function format(name, value) { + if (ATTRIBUTES.indexOf(name) > -1) { + if (value) { + this.domNode.setAttribute(name, value); + } else { + this.domNode.removeAttribute(name); + } + } else { + _get(Video.prototype.__proto__ || Object.getPrototypeOf(Video.prototype), 'format', this).call(this, name, value); + } + } + }], [{ + key: 'create', + value: function create(value) { + var node = _get(Video.__proto__ || Object.getPrototypeOf(Video), 'create', this).call(this, value); + node.setAttribute('frameborder', '0'); + node.setAttribute('allowfullscreen', true); + node.setAttribute('src', this.sanitize(value)); + return node; + } + }, { + key: 'formats', + value: function formats(domNode) { + return ATTRIBUTES.reduce(function (formats, attribute) { + if (domNode.hasAttribute(attribute)) { + formats[attribute] = domNode.getAttribute(attribute); + } + return formats; + }, {}); + } + }, { + key: 'sanitize', + value: function sanitize(url) { + return _link2.default.sanitize(url); + } + }, { + key: 'value', + value: function value(domNode) { + return domNode.getAttribute('src'); + } + }]); + + return Video; +}(_block.BlockEmbed); + +Video.blotName = 'video'; +Video.className = 'ql-video'; +Video.tagName = 'IFRAME'; + +exports.default = Video; + +/***/ }), +/* 74 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.FormulaBlot = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _embed = __webpack_require__(35); + +var _embed2 = _interopRequireDefault(_embed); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var FormulaBlot = function (_Embed) { + _inherits(FormulaBlot, _Embed); + + function FormulaBlot() { + _classCallCheck(this, FormulaBlot); + + return _possibleConstructorReturn(this, (FormulaBlot.__proto__ || Object.getPrototypeOf(FormulaBlot)).apply(this, arguments)); + } + + _createClass(FormulaBlot, null, [{ + key: 'create', + value: function create(value) { + var node = _get(FormulaBlot.__proto__ || Object.getPrototypeOf(FormulaBlot), 'create', this).call(this, value); + if (typeof value === 'string') { + window.katex.render(value, node, { + throwOnError: false, + errorColor: '#f00' + }); + node.setAttribute('data-value', value); + } + return node; + } + }, { + key: 'value', + value: function value(domNode) { + return domNode.getAttribute('data-value'); + } + }]); + + return FormulaBlot; +}(_embed2.default); + +FormulaBlot.blotName = 'formula'; +FormulaBlot.className = 'ql-formula'; +FormulaBlot.tagName = 'SPAN'; + +var Formula = function (_Module) { + _inherits(Formula, _Module); + + _createClass(Formula, null, [{ + key: 'register', + value: function register() { + _quill2.default.register(FormulaBlot, true); + } + }]); + + function Formula() { + _classCallCheck(this, Formula); + + var _this2 = _possibleConstructorReturn(this, (Formula.__proto__ || Object.getPrototypeOf(Formula)).call(this)); + + if (window.katex == null) { + throw new Error('Formula module requires KaTeX.'); + } + return _this2; + } + + return Formula; +}(_module2.default); + +exports.FormulaBlot = FormulaBlot; +exports.default = Formula; + +/***/ }), +/* 75 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.CodeToken = exports.CodeBlock = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var SyntaxCodeBlock = function (_CodeBlock) { + _inherits(SyntaxCodeBlock, _CodeBlock); + + function SyntaxCodeBlock() { + _classCallCheck(this, SyntaxCodeBlock); + + return _possibleConstructorReturn(this, (SyntaxCodeBlock.__proto__ || Object.getPrototypeOf(SyntaxCodeBlock)).apply(this, arguments)); + } + + _createClass(SyntaxCodeBlock, [{ + key: 'replaceWith', + value: function replaceWith(block) { + this.domNode.textContent = this.domNode.textContent; + this.attach(); + _get(SyntaxCodeBlock.prototype.__proto__ || Object.getPrototypeOf(SyntaxCodeBlock.prototype), 'replaceWith', this).call(this, block); + } + }, { + key: 'highlight', + value: function highlight(_highlight) { + var text = this.domNode.textContent; + if (this.cachedText !== text) { + if (text.trim().length > 0 || this.cachedText == null) { + this.domNode.innerHTML = _highlight(text); + this.domNode.normalize(); + this.attach(); + } + this.cachedText = text; + } + } + }]); + + return SyntaxCodeBlock; +}(_code2.default); + +SyntaxCodeBlock.className = 'ql-syntax'; + +var CodeToken = new _parchment2.default.Attributor.Class('token', 'hljs', { + scope: _parchment2.default.Scope.INLINE +}); + +var Syntax = function (_Module) { + _inherits(Syntax, _Module); + + _createClass(Syntax, null, [{ + key: 'register', + value: function register() { + _quill2.default.register(CodeToken, true); + _quill2.default.register(SyntaxCodeBlock, true); + } + }]); + + function Syntax(quill, options) { + _classCallCheck(this, Syntax); + + var _this2 = _possibleConstructorReturn(this, (Syntax.__proto__ || Object.getPrototypeOf(Syntax)).call(this, quill, options)); + + if (typeof _this2.options.highlight !== 'function') { + throw new Error('Syntax module requires highlight.js. Please include the library on the page before Quill.'); + } + var timer = null; + _this2.quill.on(_quill2.default.events.SCROLL_OPTIMIZE, function () { + clearTimeout(timer); + timer = setTimeout(function () { + _this2.highlight(); + timer = null; + }, _this2.options.interval); + }); + _this2.highlight(); + return _this2; + } + + _createClass(Syntax, [{ + key: 'highlight', + value: function highlight() { + var _this3 = this; + + if (this.quill.selection.composing) return; + this.quill.update(_quill2.default.sources.USER); + var range = this.quill.getSelection(); + this.quill.scroll.descendants(SyntaxCodeBlock).forEach(function (code) { + code.highlight(_this3.options.highlight); + }); + this.quill.update(_quill2.default.sources.SILENT); + if (range != null) { + this.quill.setSelection(range, _quill2.default.sources.SILENT); + } + } + }]); + + return Syntax; +}(_module2.default); + +Syntax.DEFAULTS = { + highlight: function () { + if (window.hljs == null) return null; + return function (text) { + var result = window.hljs.highlightAuto(text); + return result.value; + }; + }(), + interval: 1000 +}; + +exports.CodeBlock = SyntaxCodeBlock; +exports.CodeToken = CodeToken; +exports.default = Syntax; + +/***/ }), +/* 76 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 77 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 78 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 79 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 80 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 81 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 82 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 83 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 84 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 85 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 86 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 87 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 88 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 89 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 90 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 91 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 92 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 93 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 94 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 95 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 96 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 97 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 98 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 99 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 100 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 101 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 102 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 103 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 104 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 105 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 106 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 107 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 108 */ +/***/ (function(module, exports, __webpack_require__) { + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.BubbleTooltip = undefined; + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _base = __webpack_require__(43); + +var _base2 = _interopRequireDefault(_base); + +var _selection = __webpack_require__(15); + +var _icons = __webpack_require__(41); + +var _icons2 = _interopRequireDefault(_icons); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var TOOLBAR_CONFIG = [['bold', 'italic', 'link'], [{ header: 1 }, { header: 2 }, 'blockquote']]; + +var BubbleTheme = function (_BaseTheme) { + _inherits(BubbleTheme, _BaseTheme); + + function BubbleTheme(quill, options) { + _classCallCheck(this, BubbleTheme); + + if (options.modules.toolbar != null && options.modules.toolbar.container == null) { + options.modules.toolbar.container = TOOLBAR_CONFIG; + } + + var _this = _possibleConstructorReturn(this, (BubbleTheme.__proto__ || Object.getPrototypeOf(BubbleTheme)).call(this, quill, options)); + + _this.quill.container.classList.add('ql-bubble'); + return _this; + } + + _createClass(BubbleTheme, [{ + key: 'extendToolbar', + value: function extendToolbar(toolbar) { + this.tooltip = new BubbleTooltip(this.quill, this.options.bounds); + this.tooltip.root.appendChild(toolbar.container); + this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), _icons2.default); + this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), _icons2.default); + } + }]); + + return BubbleTheme; +}(_base2.default); + +BubbleTheme.DEFAULTS = (0, _extend2.default)(true, {}, _base2.default.DEFAULTS, { + modules: { + toolbar: { + handlers: { + link: function link(value) { + if (!value) { + this.quill.format('link', false); + } else { + this.quill.theme.tooltip.edit(); + } + } + } + } + } +}); + +var BubbleTooltip = function (_BaseTooltip) { + _inherits(BubbleTooltip, _BaseTooltip); + + function BubbleTooltip(quill, bounds) { + _classCallCheck(this, BubbleTooltip); + + var _this2 = _possibleConstructorReturn(this, (BubbleTooltip.__proto__ || Object.getPrototypeOf(BubbleTooltip)).call(this, quill, bounds)); + + _this2.quill.on(_emitter2.default.events.EDITOR_CHANGE, function (type, range, oldRange, source) { + if (type !== _emitter2.default.events.SELECTION_CHANGE) return; + if (range != null && range.length > 0 && source === _emitter2.default.sources.USER) { + _this2.show(); + // Lock our width so we will expand beyond our offsetParent boundaries + _this2.root.style.left = '0px'; + _this2.root.style.width = ''; + _this2.root.style.width = _this2.root.offsetWidth + 'px'; + var lines = _this2.quill.getLines(range.index, range.length); + if (lines.length === 1) { + _this2.position(_this2.quill.getBounds(range)); + } else { + var lastLine = lines[lines.length - 1]; + var index = _this2.quill.getIndex(lastLine); + var length = Math.min(lastLine.length() - 1, range.index + range.length - index); + var _bounds = _this2.quill.getBounds(new _selection.Range(index, length)); + _this2.position(_bounds); + } + } else if (document.activeElement !== _this2.textbox && _this2.quill.hasFocus()) { + _this2.hide(); + } + }); + return _this2; + } + + _createClass(BubbleTooltip, [{ + key: 'listen', + value: function listen() { + var _this3 = this; + + _get(BubbleTooltip.prototype.__proto__ || Object.getPrototypeOf(BubbleTooltip.prototype), 'listen', this).call(this); + this.root.querySelector('.ql-close').addEventListener('click', function () { + _this3.root.classList.remove('ql-editing'); + }); + this.quill.on(_emitter2.default.events.SCROLL_OPTIMIZE, function () { + // Let selection be restored by toolbar handlers before repositioning + setTimeout(function () { + if (_this3.root.classList.contains('ql-hidden')) return; + var range = _this3.quill.getSelection(); + if (range != null) { + _this3.position(_this3.quill.getBounds(range)); + } + }, 1); + }); + } + }, { + key: 'cancel', + value: function cancel() { + this.show(); + } + }, { + key: 'position', + value: function position(reference) { + var shift = _get(BubbleTooltip.prototype.__proto__ || Object.getPrototypeOf(BubbleTooltip.prototype), 'position', this).call(this, reference); + var arrow = this.root.querySelector('.ql-tooltip-arrow'); + arrow.style.marginLeft = ''; + if (shift === 0) return shift; + arrow.style.marginLeft = -1 * shift - arrow.offsetWidth / 2 + 'px'; + } + }]); + + return BubbleTooltip; +}(_base.BaseTooltip); + +BubbleTooltip.TEMPLATE = ['', '
', '', '', '
'].join(''); + +exports.BubbleTooltip = BubbleTooltip; +exports.default = BubbleTheme; + +/***/ }), +/* 109 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(63); + + +/***/ }) +/******/ ])["default"]; +}); +}); + +var Quill = /*@__PURE__*/getDefaultExportFromCjs(quill); + +var randomColor = createCommonjsModule(function (module, exports) { +(function(root, factory) { + + // Support CommonJS + { + var randomColor = factory(); + + // Support NodeJS & Component, which allow module.exports to be a function + if (module && module.exports) { + exports = module.exports = randomColor; + } + + // Support CommonJS 1.1.1 spec + exports.randomColor = randomColor; + + // Support AMD + } + +}(commonjsGlobal, function() { + + // Seed to get repeatable colors + var seed = null; + + // Shared color dictionary + var colorDictionary = {}; + + // Populate the color dictionary + loadColorBounds(); + + // check if a range is taken + var colorRanges = []; + + var randomColor = function (options) { + + options = options || {}; + + // Check if there is a seed and ensure it's an + // integer. Otherwise, reset the seed value. + if (options.seed !== undefined && options.seed !== null && options.seed === parseInt(options.seed, 10)) { + seed = options.seed; + + // A string was passed as a seed + } else if (typeof options.seed === 'string') { + seed = stringToInteger(options.seed); + + // Something was passed as a seed but it wasn't an integer or string + } else if (options.seed !== undefined && options.seed !== null) { + throw new TypeError('The seed value must be an integer or string'); + + // No seed, reset the value outside. + } else { + seed = null; + } + + var H,S,B; + + // Check if we need to generate multiple colors + if (options.count !== null && options.count !== undefined) { + + var totalColors = options.count, + colors = []; + // Value false at index i means the range i is not taken yet. + for (var i = 0; i < options.count; i++) { + colorRanges.push(false); + } + options.count = null; + + while (totalColors > colors.length) { + + var color = randomColor(options); + + if (seed !== null) { + options.seed = seed; + } + + colors.push(color); + } + + options.count = totalColors; + + return colors; + } + + // First we pick a hue (H) + H = pickHue(options); + + // Then use H to determine saturation (S) + S = pickSaturation(H, options); + + // Then use S and H to determine brightness (B). + B = pickBrightness(H, S, options); + + // Then we return the HSB color in the desired format + return setFormat([H,S,B], options); + }; + + function pickHue(options) { + if (colorRanges.length > 0) { + var hueRange = getRealHueRange(options.hue); + + var hue = randomWithin(hueRange); + + //Each of colorRanges.length ranges has a length equal approximatelly one step + var step = (hueRange[1] - hueRange[0]) / colorRanges.length; + + var j = parseInt((hue - hueRange[0]) / step); + + //Check if the range j is taken + if (colorRanges[j] === true) { + j = (j + 2) % colorRanges.length; + } + else { + colorRanges[j] = true; + } + + var min = (hueRange[0] + j * step) % 359, + max = (hueRange[0] + (j + 1) * step) % 359; + + hueRange = [min, max]; + + hue = randomWithin(hueRange); + + if (hue < 0) {hue = 360 + hue;} + return hue + } + else { + var hueRange = getHueRange(options.hue); + + hue = randomWithin(hueRange); + // Instead of storing red as two seperate ranges, + // we group them, using negative numbers + if (hue < 0) { + hue = 360 + hue; + } + + return hue; + } + } + + function pickSaturation (hue, options) { + + if (options.hue === 'monochrome') { + return 0; + } + + if (options.luminosity === 'random') { + return randomWithin([0,100]); + } + + var saturationRange = getSaturationRange(hue); + + var sMin = saturationRange[0], + sMax = saturationRange[1]; + + switch (options.luminosity) { + + case 'bright': + sMin = 55; + break; + + case 'dark': + sMin = sMax - 10; + break; + + case 'light': + sMax = 55; + break; + } + + return randomWithin([sMin, sMax]); + + } + + function pickBrightness (H, S, options) { + + var bMin = getMinimumBrightness(H, S), + bMax = 100; + + switch (options.luminosity) { + + case 'dark': + bMax = bMin + 20; + break; + + case 'light': + bMin = (bMax + bMin)/2; + break; + + case 'random': + bMin = 0; + bMax = 100; + break; + } + + return randomWithin([bMin, bMax]); + } + + function setFormat (hsv, options) { + + switch (options.format) { + + case 'hsvArray': + return hsv; + + case 'hslArray': + return HSVtoHSL(hsv); + + case 'hsl': + var hsl = HSVtoHSL(hsv); + return 'hsl('+hsl[0]+', '+hsl[1]+'%, '+hsl[2]+'%)'; + + case 'hsla': + var hslColor = HSVtoHSL(hsv); + var alpha = options.alpha || Math.random(); + return 'hsla('+hslColor[0]+', '+hslColor[1]+'%, '+hslColor[2]+'%, ' + alpha + ')'; + + case 'rgbArray': + return HSVtoRGB(hsv); + + case 'rgb': + var rgb = HSVtoRGB(hsv); + return 'rgb(' + rgb.join(', ') + ')'; + + case 'rgba': + var rgbColor = HSVtoRGB(hsv); + var alpha = options.alpha || Math.random(); + return 'rgba(' + rgbColor.join(', ') + ', ' + alpha + ')'; + + default: + return HSVtoHex(hsv); + } + + } + + function getMinimumBrightness(H, S) { + + var lowerBounds = getColorInfo(H).lowerBounds; + + for (var i = 0; i < lowerBounds.length - 1; i++) { + + var s1 = lowerBounds[i][0], + v1 = lowerBounds[i][1]; + + var s2 = lowerBounds[i+1][0], + v2 = lowerBounds[i+1][1]; + + if (S >= s1 && S <= s2) { + + var m = (v2 - v1)/(s2 - s1), + b = v1 - m*s1; + + return m*S + b; + } + + } + + return 0; + } + + function getHueRange (colorInput) { + + if (typeof parseInt(colorInput) === 'number') { + + var number = parseInt(colorInput); + + if (number < 360 && number > 0) { + return [number, number]; + } + + } + + if (typeof colorInput === 'string') { + + if (colorDictionary[colorInput]) { + var color = colorDictionary[colorInput]; + if (color.hueRange) {return color.hueRange;} + } else if (colorInput.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i)) { + var hue = HexToHSB(colorInput)[0]; + return [ hue, hue ]; + } + } + + return [0,360]; + + } + + function getSaturationRange (hue) { + return getColorInfo(hue).saturationRange; + } + + function getColorInfo (hue) { + + // Maps red colors to make picking hue easier + if (hue >= 334 && hue <= 360) { + hue-= 360; + } + + for (var colorName in colorDictionary) { + var color = colorDictionary[colorName]; + if (color.hueRange && + hue >= color.hueRange[0] && + hue <= color.hueRange[1]) { + return colorDictionary[colorName]; + } + } return 'Color not found'; + } + + function randomWithin (range) { + if (seed === null) { + //generate random evenly destinct number from : https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/ + var golden_ratio = 0.618033988749895; + var r=Math.random(); + r += golden_ratio; + r %= 1; + return Math.floor(range[0] + r*(range[1] + 1 - range[0])); + } else { + //Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/ + var max = range[1] || 1; + var min = range[0] || 0; + seed = (seed * 9301 + 49297) % 233280; + var rnd = seed / 233280.0; + return Math.floor(min + rnd * (max - min)); +} + } + + function HSVtoHex (hsv){ + + var rgb = HSVtoRGB(hsv); + + function componentToHex(c) { + var hex = c.toString(16); + return hex.length == 1 ? '0' + hex : hex; + } + + var hex = '#' + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]); + + return hex; + + } + + function defineColor (name, hueRange, lowerBounds) { + + var sMin = lowerBounds[0][0], + sMax = lowerBounds[lowerBounds.length - 1][0], + + bMin = lowerBounds[lowerBounds.length - 1][1], + bMax = lowerBounds[0][1]; + + colorDictionary[name] = { + hueRange: hueRange, + lowerBounds: lowerBounds, + saturationRange: [sMin, sMax], + brightnessRange: [bMin, bMax] + }; + + } + + function loadColorBounds () { + + defineColor( + 'monochrome', + null, + [[0,0],[100,0]] + ); + + defineColor( + 'red', + [-26,18], + [[20,100],[30,92],[40,89],[50,85],[60,78],[70,70],[80,60],[90,55],[100,50]] + ); + + defineColor( + 'orange', + [18,46], + [[20,100],[30,93],[40,88],[50,86],[60,85],[70,70],[100,70]] + ); + + defineColor( + 'yellow', + [46,62], + [[25,100],[40,94],[50,89],[60,86],[70,84],[80,82],[90,80],[100,75]] + ); + + defineColor( + 'green', + [62,178], + [[30,100],[40,90],[50,85],[60,81],[70,74],[80,64],[90,50],[100,40]] + ); + + defineColor( + 'blue', + [178, 257], + [[20,100],[30,86],[40,80],[50,74],[60,60],[70,52],[80,44],[90,39],[100,35]] + ); + + defineColor( + 'purple', + [257, 282], + [[20,100],[30,87],[40,79],[50,70],[60,65],[70,59],[80,52],[90,45],[100,42]] + ); + + defineColor( + 'pink', + [282, 334], + [[20,100],[30,90],[40,86],[60,84],[80,80],[90,75],[100,73]] + ); + + } + + function HSVtoRGB (hsv) { + + // this doesn't work for the values of 0 and 360 + // here's the hacky fix + var h = hsv[0]; + if (h === 0) {h = 1;} + if (h === 360) {h = 359;} + + // Rebase the h,s,v values + h = h/360; + var s = hsv[1]/100, + v = hsv[2]/100; + + var h_i = Math.floor(h*6), + f = h * 6 - h_i, + p = v * (1 - s), + q = v * (1 - f*s), + t = v * (1 - (1 - f)*s), + r = 256, + g = 256, + b = 256; + + switch(h_i) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } + + var result = [Math.floor(r*255), Math.floor(g*255), Math.floor(b*255)]; + return result; + } + + function HexToHSB (hex) { + hex = hex.replace(/^#/, ''); + hex = hex.length === 3 ? hex.replace(/(.)/g, '$1$1') : hex; + + var red = parseInt(hex.substr(0, 2), 16) / 255, + green = parseInt(hex.substr(2, 2), 16) / 255, + blue = parseInt(hex.substr(4, 2), 16) / 255; + + var cMax = Math.max(red, green, blue), + delta = cMax - Math.min(red, green, blue), + saturation = cMax ? (delta / cMax) : 0; + + switch (cMax) { + case red: return [ 60 * (((green - blue) / delta) % 6) || 0, saturation, cMax ]; + case green: return [ 60 * (((blue - red) / delta) + 2) || 0, saturation, cMax ]; + case blue: return [ 60 * (((red - green) / delta) + 4) || 0, saturation, cMax ]; + } + } + + function HSVtoHSL (hsv) { + var h = hsv[0], + s = hsv[1]/100, + v = hsv[2]/100, + k = (2-s)*v; + + return [ + h, + Math.round(s*v / (k<1 ? k : 2-k) * 10000) / 100, + k/2 * 100 + ]; + } + + function stringToInteger (string) { + var total = 0; + for (var i = 0; i !== string.length; i++) { + if (total >= Number.MAX_SAFE_INTEGER) break; + total += string.charCodeAt(i); + } + return total + } + + // get The range of given hue when options.count!=0 + function getRealHueRange(colorHue) + { if (!isNaN(colorHue)) { + var number = parseInt(colorHue); + + if (number < 360 && number > 0) { + return getColorInfo(colorHue).hueRange + } + } + else if (typeof colorHue === 'string') { + + if (colorDictionary[colorHue]) { + var color = colorDictionary[colorHue]; + + if (color.hueRange) { + return color.hueRange + } + } else if (colorHue.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i)) { + var hue = HexToHSB(colorHue)[0]; + return getColorInfo(hue).hueRange + } + } + + return [0,360] +} + return randomColor; +})); +}); + +class TextInput { + // The input element + el; + quill; + groups; + parentElement; + droppedText; + constructor() { + this.el = document.createElement('div'); + this.el.id = 'regex-input'; + this.el.classList.add('regex-input'); + this.quill = null; + this.groups = new Array(); + // this.el.classList.add('regex-textbox') + // this.el.setAttribute("rows", "1"); + this.parentElement = null; + this.droppedText = false; + } + initQuill = () => { + // initializing quill + this.quill = new Quill('#regex-input', { + modules: { + toolbar: false + }, + }); + this.quill.keyboard.addBinding({ + key: 'C', + shortKey: true, + }, (range, context) => { + const freeKeyboardEvent = { + 'event-type': 'free-input-keyboard', + range: range, + keys: ['ctrl', 'c'] + }; + if (this.parentElement) { + this.parentElement.logEvent(freeKeyboardEvent); + } + return true; + }); + this.quill.keyboard.addBinding({ + key: 'V', + shortKey: true, + }, (range, context) => { + const freeKeyboardEvent = { + 'event-type': 'free-input-keyboard', + range: range, + keys: ['ctrl', 'v'] + }; + if (this.parentElement) { + this.parentElement.logEvent(freeKeyboardEvent); + } + return true; + }); + this.el.ondrop = (event) => { + this.droppedText = true; + }; + }; + getText = () => { + if (this.quill != null) { + return this.quill.getText(0, this.quill.getLength() - 1); + } + else { + return ''; + } + }; + highlightGroup = (colors) => { + this.calculateGroup(); + console.log(this.groups); + this.quill?.removeFormat(0, this.quill.getLength() - 1, 'silent'); + // generate new colors for group if current colors are not enough + const groupCount = this.groups.length; + if (colors.length < groupCount) { + this.generateColor(colors, groupCount - colors.length); + } + // highlight the groups + for (let i = 0; i < this.groups.length; ++i) { + this.quill?.formatText(this.groups[i].start, this.groups[i].end - this.groups[i].start, { + 'background': colors[i] + }, 'silent'); + } + }; + calculateGroup = () => { + let stack = new Array(); + this.groups = new Array(); + const pattern = this.getText(); + this.groups.push({ start: 0, end: pattern.length }); + for (let i = 0; i < pattern.length; ++i) { + if (pattern[i] == '(') { + stack.push(this.groups.length); + this.groups.push({ start: i, end: -1 }); + } + else if (pattern[i] == ')') { + const startIndex = stack.pop(); + if (startIndex) { + this.groups[startIndex].end = i + 1; + } + } + } + }; + // TODO: (structure) move this function to the main element after adding highlight to input + generateColor = (colors, cnt) => { + for (let i = 0; i < cnt; ++i) { + colors.push(randomColor.randomColor()); + } + }; + updateTestStatus = (result) => { + if (this.el.classList.contains(result)) { + return; + } + if (this.el.classList.contains('Pass')) { + this.el.classList.remove('Pass'); + } + else if (this.el.classList.contains('Fail')) { + this.el.classList.remove('Fail'); + } + else if (this.el.classList.contains('Error')) { + this.el.classList.remove('Error'); + } + this.el.classList.add(result); + }; + removeFormat = () => { + this.quill?.removeFormat(0, this.quill.getLength() - 1, 'silent'); + }; + highlightError = (position) => { + this.quill?.formatText(position, 1, { + 'background': '#ff99b3' + }, 'silent'); + }; +} + +// import {Quill} from '../types/Quill'; +// experiments: wrapping classes +let Inline = Quill.import('blots/inline'); +class GroupBlot extends Inline { + static create(value) { + let node = super.create(); + node.setAttribute('class', 'group0'); + return node; + } + static formats(domNode) { + return true; + } +} +GroupBlot.blotName = 'grouping'; +GroupBlot.className = 'group1'; +GroupBlot.tagName = 'span'; +Quill.register(GroupBlot); +class TestStringInput { + // The input element + el; + quill; + droppedText; + parentElement; + slotName; + highlightMode; + constructor(slotName) { + this.el = document.createElement('div'); + this.slotName = slotName; + this.el.id = 'test-string-input' + slotName; + this.el.classList.add('regex-test-string'); + this.quill = null; + // console.log(Quill); + this.droppedText = false; + this.parentElement = null; + this.highlightMode = 're.finditer'; + } + initQuill = () => { + // initializing quill + this.quill = new Quill('#test-string-input' + this.slotName, { + modules: { + toolbar: false + }, + placeholder: 'type the test string', + }); + this.quill.keyboard.addBinding({ + key: 'C', + shortKey: true, + }, (range, context) => { + const testStringKeyboardEvent = { + 'event-type': 'test-string-keyboard', + 'slot': this.slotName, + range: range, + keys: ['ctrl', 'c'] + }; + if (this.parentElement) { + this.parentElement.logEvent(testStringKeyboardEvent); + } + return true; + }); + this.quill.keyboard.addBinding({ + key: 'V', + shortKey: true, + }, (range, context) => { + const testStringKeyboardEvent = { + 'event-type': 'test-string-keyboard', + 'slot': this.slotName, + range: range, + keys: ['ctrl', 'v'] + }; + if (this.parentElement) { + this.parentElement.logEvent(testStringKeyboardEvent); + } + return true; + }); + this.el.ondrop = (event) => { + this.droppedText = true; + // console.log('ondrop'); + // console.log(event); + }; + }; + getText = () => { + if (this.quill != null) { + return this.quill.getText(); + } + else { + return ''; + } + }; + updateMatchResult = (matches) => { + this.quill?.removeFormat(0, this.quill.getLength() - 1, 'silent'); + let startPos = 0; + let matchPos; + const testString = this.getText(); + for (let i = 0; i < matches.length; ++i) { + matchPos = testString.indexOf(matches[i], startPos); + startPos = matchPos + matches[i].length; + this.quill?.formatText(matchPos, matches[i].length, { + 'background': 'rgb(251, 255, 130)' + }, 'silent'); + } + }; + // TODO: (UI) differentiate between different matches. + // update match result with group info. Each group is a different color. + updateGroupedMatchResult = (matches, colors) => { + this.quill?.removeFormat(0, this.quill.getLength() - 1, 'silent'); + if (matches.length == 0) { + return; + } + // generate new colors for group if current colors are not enough + const groupCount = matches[0].length; + if (colors.length < groupCount) { + this.generateColor(colors, groupCount - colors.length); + } + // highlight the matches in a group sequence such that inner groups' color will cover outer groups'. + let index = 0; + if (this.highlightMode == 're.findall') { + if (this.parentElement?.matchFindall && groupCount > 1) { + index = 1; + } + } + for (index; index < groupCount; ++index) { + for (let j = 0; j < matches.length; ++j) { + this.quill?.formatText(matches[j][index].start, matches[j][index].end - matches[j][index].start, { + 'background': colors[index] + }, 'silent'); + } + } + }; + // update match result that does not has group info. Each match is a different color. + updateMatchResultNoGroup = (matches, colors) => { + this.quill?.removeFormat(0, this.quill.getLength() - 1, 'silent'); + if (matches.length == 0) { + return; + } + const groupCount = matches.length; + if (colors.length < groupCount) { + this.generateColor(colors, groupCount - colors.length); + } + // highlight the matches in a group sequence such that inner groups' color will cover outer groups'. + let index = 0; + for (index; index < matches.length; ++index) { + this.quill?.formatText(matches[index].st, matches[index].ed - matches[index].st, { + 'background': colors[index] + }, 'silent'); + } + }; + updateGroupedMatchResult_exp = (matches) => { + let i = 0; + for (let j = 0; j < matches.length; ++j) { + this.quill?.formatText(matches[j][i].start, matches[j][i].end - matches[j][i].start, { + 'grouping': true + }, 'silent'); + } + }; + // TODO: (structure) move this function to the main element after adding highlight to input + generateColor = (colors, cnt) => { + const newcolors = randomColor.randomColor({ count: 10, luminosity: 'light' }); + for (let i = 0; i < cnt; ++i) { + colors.push(newcolors[i]); + } + }; + setText(text) { + this.quill?.setText(text); + } +} + +class StatusOutput { + // The input element + el; + text; + constructor() { + this.el = document.createElement('div'); + this.text = document.createElement('textarea'); + this.el.appendChild(this.text); + this.text.id = 'status-output'; + this.el.classList.add('regex-textbox'); + // this.text.setAttribute("rows", "10"); + this.text.value = 'initializing...\n'; + } +} + +class TestButton { + // The input element + el; + constructor() { + this.el = document.createElement('button'); + this.el.id = 'test-button'; + this.el.innerText = 'match!'; + } +} + +class RegexOptions { + el; + triggerButton; + containerDiv; + flags; + buttons; + selectedFlags; + constructor() { + this.el = document.createElement('div'); + this.el.classList.add('regex-options-dropdown'); + this.triggerButton = document.createElement('button'); + this.triggerButton.classList.add('regex-options-dropdown-btn'); + this.triggerButton.innerText = 'Flags:\n'; + this.el.appendChild(this.triggerButton); + this.containerDiv = document.createElement('div'); + this.el.appendChild(this.containerDiv); + this.containerDiv.classList.add('regex-options-container'); + this.flags = ['re.MULTILINE', 're.ASCII', 're.IGNORECASE', 're.LOCALE', 're.DOTALL']; + const defaultFlags = [0]; + this.selectedFlags = []; + this.buttons = []; + for (let flag = 0; flag < this.flags.length; ++flag) { + let newButton = document.createElement('button'); + this.buttons.push(newButton); + this.containerDiv.appendChild(newButton); + newButton.innerText = this.flags[flag]; + newButton.onclick = () => { + newButton.classList.toggle('selected'); + this.updateButton(); + }; + if (defaultFlags.indexOf(flag) != -1) { + newButton.classList.add('selected'); + } + } + this.updateButton(); + this.triggerButton.onclick = this.onTriggerButtonClicked; + } + getFlags = () => { + let flags = ''; + this.buttons.forEach(button => { + if (button.classList.contains('selected')) { + flags += button.innerText + ' | '; + } + }); + if (flags == '') { + return null; + } + else { + return flags.slice(0, -3); + } + }; + getFlagList = () => { + let flags = []; + this.buttons.forEach(button => { + if (button.classList.contains('selected')) { + flags.push(button.innerText); + } + }); + return flags; + }; + onTriggerButtonClicked = () => { + this.containerDiv.classList.toggle('show'); + }; + updateButton = () => { + let flags = ''; + this.buttons.forEach(button => { + if (button.classList.contains('selected')) { + flags += button.innerText[3]; + } + }); + this.triggerButton.innerText = 'Flags: ' + flags; + }; +} + +class UnitTestTable { + // The input element + el; + table; + flags; + testCases; + testCaseCount; + // if the matched groups should also be identical + strictGroup; + hintRevealed; + // when true: match the whole string from the beginning to the end. When disabled: use findall. + strictMatch; + // used for problem 3 in the thinkaloud. + // TODO: refactor this into strictgroup. + noGroupsAllowed; + latestResults; + columnsEnabled; + parentElement; + // for saving current index + testcaseIndex; + constructor() { + // init the element in HTML + this.el = document.createElement('div'); + this.el.id = 'unittest-table'; + this.el.classList.add('regex-unittest'); + // the element is hidden initially. + this.el.classList.add('collapse'); + // columns enabled besides the status column + // TODO: only enabled notes for study 0 and 1 + this.columnsEnabled = ['actualOutput', 'expectedOutput', 'input', 'notes']; + // this.columnsEnabled = ['notes']; + this.table = document.createElement('table'); + this.el.appendChild(this.table); + const tableHead = document.createElement('tr'); + tableHead.innerHTML = this._getTableHead(); + this.table.appendChild(tableHead); + // no flags specified by default + this.flags = ''; + // this.testCases = [{input: 'unicorn', expect: ['unicorn'], notes:'testing unicorn'}, {input: 'element', expect:['element']}, {input: 'banana', expect: []}, {input: 'apple', expect: []}]; + this.testCases = []; + this.testCaseCount = 0; + this.hintRevealed = []; + this.latestResults = []; + // not matching groups strictly by default + this.strictGroup = false; + // using strict match by default + this.strictMatch = true; + // allow groups by default + this.noGroupsAllowed = false; + this.parentElement = null; + this.testcaseIndex = 0; + } + // not used: Return value: 'Pass' if all pass, 'Error' if one error, 'Fail' if no error but at least one fail + // return number of test cases passed + check = (regex) => { + let passCount = 0; + if (this.el.classList.contains('collapse')) { + this.el.classList.remove('collapse'); + } + // clean up previous unit test result + this.table.innerHTML = this.table.rows[0].innerHTML; + this.latestResults = []; + const statusList = []; + for (let index = 0; index < this.testCases.length; ++index) { + // const caseStatus = this.match(index, regex, this.testCases[index]); + const caseStatus = this._match_SK(index, regex, this.testCases[index]); + if (caseStatus == 'Pass') { + passCount += 1; + } + statusList.push(caseStatus); + } + // logging + const unittestRunEvent = { + 'event-type': 'unittest-run', + 'status': statusList + }; + if (this.parentElement) { + this.parentElement.logEvent(unittestRunEvent); + } + return passCount; + }; + _match_SK_output = (text, index, testCase) => { + // convert the output result to list of string + let splitMatches = text.slice(1, text.length - 2).split(', '); + let matches = []; + for (let i = 0; i < splitMatches.length; ++i) { + if (splitMatches[i].length >= 2) { + matches.push(splitMatches[i].slice(1, splitMatches[i].length - 1)); + } + } + const result = { success: true, match: matches, errorMessage: null }; + this._createRow(index, testCase, result); + }; + builtinRead(x) { + if (window.Sk.builtinFiles === undefined || window.Sk.builtinFiles["files"][x] === undefined) + throw "File not found: '" + x + "'"; + return window.Sk.builtinFiles["files"][x]; + } + /** + * Runs re.findall() with data from regex and test string input; + * Creates the unittest Table + */ + _match_SK = (index, regex, testCase) => { + // TODO: default to using global and multiline + let pyCode = ''; + if (this.strictMatch) { + pyCode = 'import re\nprint(re.findall(\'^' + regex + '$\', ' + '\'' + testCase.input + '\', re.MULTILINE))'; + } + else { + pyCode = 'import re\nprint(re.findall(\'' + regex + '\', ' + '\'' + testCase.input + '\', re.MULTILINE))'; + } + this.testcaseIndex = index; + window.Sk.configure({ + output: (text) => { this._match_SK_output(text, index, testCase); }, + read: this.builtinRead + }); + window.Sk.importMainWithBody("", false, pyCode, true); + return 'Pass'; + }; + // returns: 'Pass' if pass, 'Fail' if fail, 'Error' if error + _createRow = (index, testCase, result) => { + // console.log(testCase); + // console.log(result); + this.latestResults.push(result); + // creating the status(the first) column + const row = document.createElement('tr'); + let status = result.success ? (JSON.stringify(result.match) === JSON.stringify(testCase.expect) ? 'Pass' : 'Fail') : 'Error'; + // if (status == 'Pass' && JSON.stringify(testCase.expect) != '[]' && this.noGroupsAllowed && window.pyodide.globals.unit_match_group_cnt != 1) { + // status = 'Fail' + // // fail because no group is allowed + // } + const rowContent = '' + status + ''; + row.innerHTML = rowContent; + this.table.append(row); + if (!result.success) { + row.firstChild.style.backgroundColor = 'red'; + } + else if (status == 'Pass') { + row.firstChild.style.backgroundColor = 'green'; + } + else { + row.firstChild.style.backgroundColor = 'orange'; + } + if (this.hintRevealed[index]) { + row.innerHTML += this._getRevealedRow(testCase, result); + } + else { + row.appendChild(this._getUnrevealedRow(index)); + } + return status; + }; + // html for a revealed row + _getRevealedRow = (testCase, result) => { + let actualOutput = this.columnsEnabled.indexOf('actualOutput') == -1 ? '' : '' + (result.success ? JSON.stringify(result.match) : String(result.errorMessage)) + ''; + let expectedOutput = this.columnsEnabled.indexOf('expectedOutput') == -1 ? '' : '' + JSON.stringify(testCase.expect) + ''; + let input = this.columnsEnabled.indexOf('input') == -1 ? '' : '' + testCase.input + ''; + let notes = this.columnsEnabled.indexOf('notes') == -1 ? '' : '' + (testCase.notes ? String(testCase.notes) : '--') + ''; + return actualOutput + expectedOutput + input + notes; + }; + _getUnrevealedRow = (index) => { + const td = document.createElement('td'); + td.colSpan = 4; + const button = document.createElement('button'); + td.appendChild(button); + button.innerText = 'Reveal Test Case ' + index.toString(); + button.setAttribute('id', index.toString()); + button.onclick = () => { + this._revealRow(index); + }; + return td; + }; + // TODO + _revealRow = (index) => { + console.log('reveal row'); + console.log(index); + this.hintRevealed[index] = true; + const row = this.table.rows[index + 1]; + if (row.lastChild) { + row.removeChild(row.lastChild); + } + row.innerHTML += this._getRevealedRow(this.testCases[index], this.latestResults[index]); + }; + setTestCases = (testCases) => { + console.log('unittest 242'); + this.testCases = testCases; + // TODO: make this an option. Set all revealed for study 0 and 1. + this.hintRevealed = Array(testCases.length).fill(true); + this.testCaseCount = testCases.length; + // logging + // const unittestSetEvent: RegexEvent.UnittestSet = { + // 'event-type': 'unittest-set', + // 'test-cases': testCases + // } + // if (this.parentElement) { + // this.parentElement.logEvent(unittestSetEvent); + // } + }; + _getTableHead = () => { + let actualOutput = this.columnsEnabled.indexOf('actualOutput') == -1 ? '' : 'Actual Value'; + let expectedOutput = this.columnsEnabled.indexOf('expectedOutput') == -1 ? '' : 'Expected Value'; + let input = this.columnsEnabled.indexOf('input') == -1 ? '' : 'Test Case'; + let notes = this.columnsEnabled.indexOf('notes') == -1 ? '' : 'Notes'; + return 'Result' + actualOutput + expectedOutput + input + notes; + }; + setError = () => { + if (this.el.classList.contains('collapse')) { + this.el.classList.remove('collapse'); + } + // clean up previous unit test result + this.table.innerHTML = this.table.rows[0].innerHTML; + this.latestResults = []; + for (let index = 0; index < this.testCases.length; ++index) { + this._createRow(index, this.testCases[index], { success: false, match: [], errorMessage: 'Pattern Error' }); + } + }; +} + +class RegexStatusTag { + // The input element + el; + status; + constructor() { + this.el = document.createElement('span'); + this.el.classList.add('regex-status'); + this.status = ''; + } + updateStatus = (status) => { + // currently supporting: '', 'error', 'valid' + this.el.innerText = 'pattern ' + status; + if (status === '') { + if (this.el.classList.contains('error')) { + this.el.classList.remove('error'); + } + else if (this.el.classList.contains('valid')) { + this.el.classList.remove('valid'); + } + } + else if (this.el.classList.contains(status)) { + return; + } + else { + if (this.el.classList.contains('error')) { + this.el.classList.remove('error'); + } + else if (this.el.classList.contains('valid')) { + this.el.classList.remove('valid'); + } + this.el.classList.add(status); + } + }; +} + +class RegexElement extends HTMLElement { + root; + _parsonsData; + parsonsExplanation; + regexInput; + inputType; + regexStatus; + // private regexErrorMessage: HTMLDivElement; + // private regexErrorPosition: number; + // The input box for positive test strings (with highlight) + positiveTestStringInput; + positiveInitialTestString; + // Saving previous checked text, used with checkWhileTyping + positivePrevText; + // The input box for negative test strings (with highlight) + negativeTestStringInput; + negativeInitialTestString; + // Saving previous checked text, used with checkWhileTyping + negativePrevText; + // Python output + statusOutput; + // The button to trigger matching + testButton; + // *temporary: The checkbox to enable always check (will be integrated in options later) + checkWhileTyping; + positiveMatchResult; + negativeMatchResult; + // random color representations for groups + groupColor; + regexOptions; + // unit tests + unitTestTable; + // private logger: Logger; + patternValidFlag; + // data for logging + studentId; + problemId; + temporaryInputEvent; + _testStatusDiv; + // highlights the result using findall. used for study 1 and 2. + matchFindall; + outf(text) { + console.log('sk output'); + console.log(text); + } + builtinRead(x) { + if (window.Sk.builtinFiles === undefined || window.Sk.builtinFiles["files"][x] === undefined) + throw "File not found: '" + x + "'"; + return window.Sk.builtinFiles["files"][x]; + } + // copied from Skulpt source code + static _Sk_validGroups = ["(?:", "(?=", "(?!"]; + _Sk_convert = (pattern) => { + var newpattern; + var match; + var i; + // Look for disallowed constructs + match = pattern.match(/\(\?./g); + if (match) { + for (i = 0; i < match.length; i++) { + if (RegexElement._Sk_validGroups.indexOf(match[i]) == -1) { + throw new window.Sk.builtin.ValueError("Disallowed group in pattern: '" + + match[i] + "'"); + } + } + } + newpattern = pattern.replace("/\\/g", "\\\\"); + newpattern = pattern.replace(/([^\\]){,(?![^\[]*\])/g, "$1{0,"); + return newpattern; + }; + // adapted from Skulpt source code (findall) + // problem: can only show the first and last match of the position, no grouping information + _Sk_finditer = (pattern, string, flags = 'gm') => { + let pat = pattern; + let str = string; + pat = this._Sk_convert(pat); + // flags can only be global, ignorecase, and multiline + let regex = new RegExp(pat, flags); + if (pat.match(/\$/)) { + var newline_at_end = new RegExp(/\n$/); + if (str.match(newline_at_end)) { + str = str.slice(0, -1); + } + } + let match_result = []; + let pos_result = []; + let match; + while ((match = regex.exec(str)) != null) { + // console.log(match) + console.log("Matched '" + match[0] + "' at position " + match.index + + "; next search at " + regex.lastIndex); + console.log("match: " + JSON.stringify(match)); + pos_result.push({ st: match.index, ed: regex.lastIndex }); + if (match.length < 2) { + match_result.push(new window.Sk.builtin.str(match[0])); + } + else if (match.length == 2) { + match_result.push(new window.Sk.builtin.str(match[1])); + } + else { + var groups = []; + for (var i = 1; i < match.length; i++) { + groups.push(new window.Sk.builtin.str(match[i])); + } + match_result.push(new window.Sk.builtin.tuple(groups)); + } + if (match.index === regex.lastIndex) { + regex.lastIndex += 1; + } + } + return pos_result; + // return new window.Sk.builtin.list(match_result); + }; + constructor() { + super(); + this.root = this.attachShadow({ mode: 'open' }); + window.Sk.configure({ + output: this.outf, + read: this.builtinRead + }); + // time limit for running each run + window.Sk.execLimit = 1000; + // add style + this.addStyle(); + // init element: button for unittest + // const unitTestButton = document.createElement('button'); + // unitTestButton.innerText = 'Run Unit Test'; + // this.root.appendChild(unitTestButton); + // unitTestButton.onclick = () => this.unitTestTable.check(this.regexInput.getText()); + // init elements: button for match + // TODO: disabled the button for study 0 and 1. + // this.root.appendChild(document.createElement('br')); + this.testButton = new TestButton(); + // this.root.appendChild(this.testButton.el); + // this.testButton.el.onclick = this.match; + // init elements: checkbox + // TODO[feature]: replace this with an option module + // const checkbox = document.createElement('input'); + // checkbox.setAttribute('type', 'checkbox'); + // checkbox.checked = false; + // this.root.appendChild(checkbox); + // this.root.append('always check on input'); + // this.checkWhileTyping = false; + // checkbox.addEventListener('change', () => { + // this.checkWhileTyping = checkbox.checked; + // if (this.checkWhileTyping) { + // this.match(); + // } + // }) + // TODO: make this an option; for now always enabled the 'always check' for study 0 and 1. + this.checkWhileTyping = true; + // a div wrapping the input and the test case status + const inputAndTestStatusDiv = document.createElement('div'); + this.root.append(inputAndTestStatusDiv); + inputAndTestStatusDiv.classList.add('regex-input-and-test-status'); + // init regex input based on the input type + const inputDiv = document.createElement('div'); + inputAndTestStatusDiv.appendChild(inputDiv); + inputDiv.classList.add('regex-input-div'); + this.regexOptions = new RegexOptions(); + this.patternValidFlag = true; + this._parsonsData = new Array(); + this.parsonsExplanation = null; + this.regexStatus = new RegexStatusTag(); + this.regexInput = new ParsonsInput(); + this.inputType = 'parsons'; + // this.regexErrorMessage = document.createElement('div'); + // this.regexErrorPosition = -1; + this.initRegexInput(inputDiv); + this._testStatusDiv = document.createElement('div'); + inputAndTestStatusDiv.appendChild(this._testStatusDiv); + this._testStatusDiv.classList.add('regex-test-status'); + // init elements: test string input + // TODO: (bug) stylesheet isn't working with shadowroot + const quillLinkRef = document.createElement('link'); + quillLinkRef.href = 'https://cdn.quilljs.com/1.3.7/quill.bubble.css'; + quillLinkRef.rel = 'stylesheet'; + this.appendChild(quillLinkRef); + const testStringDiv = document.createElement('div'); + testStringDiv.classList.add('regex-test-string-container'); + this.root.append('Feel free to experiment with your own test cases.'); + this.root.append(testStringDiv); + const positiveTestStringDiv = document.createElement('div'); + testStringDiv.append(positiveTestStringDiv); + positiveTestStringDiv.classList.add('regex-test-string-div'); + positiveTestStringDiv.append('Match:'); + this.positiveInitialTestString = ''; + const positiveSlot = document.createElement('slot'); + positiveSlot.name = 'positive-test-string-input'; + positiveTestStringDiv.appendChild(positiveSlot); + const resetPositiveTestStringButton = document.createElement('button'); + positiveTestStringDiv.appendChild(resetPositiveTestStringButton); + resetPositiveTestStringButton.innerText = 'Reset'; + resetPositiveTestStringButton.onclick = this.resetPositiveTestString; + this.positiveTestStringInput = new TestStringInput('positive'); + this.positiveTestStringInput.slotName = 'positive'; + this.appendChild(this.positiveTestStringInput.el); + this.positiveTestStringInput.el.slot = 'positive-test-string-input'; + this.positiveTestStringInput.initQuill(); + this.positivePrevText = this.positiveTestStringInput.getText(); + this.positiveTestStringInput.parentElement = this; + this.positiveTestStringInput.quill?.on('text-change', (delta, _, source) => { + if (source == 'user') { + const testStringInputEvent = { + 'event-type': 'test-string-input', + 'slot': 'positive', + dropped: this.positiveTestStringInput.droppedText, + delta: delta, + 'test-string': this.positiveTestStringInput.getText() + }; + this.logEvent(testStringInputEvent); + } + this.positiveTestStringInput.droppedText = false; + if (this.positiveTestStringInput.getText() != this.positivePrevText) { + this.positivePrevText = this.positiveTestStringInput.getText(); + // updating test_string in pyodide + // window.pyodide.globals.test_string = this.testStringInput.getText().slice(0, -1); + if (this.checkWhileTyping && this.patternValidFlag) { + // this.match(); + this._match_with_Sk(); + } + else { + this.positiveTestStringInput.quill?.removeFormat(0, this.positiveTestStringInput.quill.getLength() - 1, 'silent'); + } + } + }); + const negativeTestStringDiv = document.createElement('div'); + testStringDiv.append(negativeTestStringDiv); + negativeTestStringDiv.classList.add('regex-test-string-div'); + negativeTestStringDiv.append('Do not match:'); + this.negativeInitialTestString = ''; + const negativeSlot = document.createElement('slot'); + negativeSlot.name = 'negative-test-string-input'; + negativeTestStringDiv.appendChild(negativeSlot); + const resetNegativeTestStringButton = document.createElement('button'); + negativeTestStringDiv.appendChild(resetNegativeTestStringButton); + resetNegativeTestStringButton.innerText = 'Reset'; + resetNegativeTestStringButton.onclick = this.resetNegativeTestString; + this.negativeTestStringInput = new TestStringInput('negative'); + this.negativeTestStringInput.slotName = 'negative'; + this.appendChild(this.negativeTestStringInput.el); + this.negativeTestStringInput.el.slot = 'negative-test-string-input'; + this.negativeTestStringInput.initQuill(); + this.negativePrevText = this.negativeTestStringInput.getText(); + this.negativeTestStringInput.parentElement = this; + this.negativeTestStringInput.quill?.on('text-change', (delta, _, source) => { + if (source == 'user') { + const testStringInputEvent = { + 'event-type': 'test-string-input', + 'slot': 'negative', + dropped: this.negativeTestStringInput.droppedText, + delta: delta, + 'test-string': this.negativeTestStringInput.getText() + }; + this.logEvent(testStringInputEvent); + } + this.negativeTestStringInput.droppedText = false; + if (this.negativeTestStringInput.getText() != this.negativePrevText) { + this.negativePrevText = this.negativeTestStringInput.getText(); + // updating test_string in pyodide + // window.pyodide.globals.test_string = this.testStringInput.getText().slice(0, -1); + if (this.checkWhileTyping && this.patternValidFlag) { + // this.match(); + this._match_with_Sk(); + } + else { + this.negativeTestStringInput.quill?.removeFormat(0, this.negativeTestStringInput.quill.getLength() - 1, 'silent'); + } + } + }); + // init element: unit test table + this.unitTestTable = new UnitTestTable(); + this.unitTestTable.parentElement = this; + this.root.appendChild(this.unitTestTable.el); + // init element: python output + this.statusOutput = new StatusOutput(); + this.root.appendChild(this.statusOutput.el); + // initialize the match result array + this.positiveMatchResult = new Array(); + this.negativeMatchResult = new Array(); + // initialize the color array + // TODO: (UI) only light colors that do not obfuscates the word + this.groupColor = new Array(); + // logging page visibility + if (typeof document.addEventListener === "undefined" || document.hidden === undefined) { + const visibilityStatusEvent = { + 'event-type': 'page-visibility-status', + enabled: false + }; + this.logEvent(visibilityStatusEvent); + console.log("visibility not working"); + } + else { + const visibilityStatusEvent = { + 'event-type': 'page-visibility-status', + enabled: true + }; + this.logEvent(visibilityStatusEvent); + console.log("add visibility change"); + document.addEventListener("visibilitychange", (event) => { + let pageStatusEvent; + if (document.hidden) { + pageStatusEvent = { + 'event-type': 'page-status', + 'status-type': RegexEvent.PageStatus.VISIBILITY, + result: false + }; + } + else { + pageStatusEvent = { + 'event-type': 'page-status', + 'status-type': RegexEvent.PageStatus.VISIBILITY, + result: true + }; + } + this.logEvent(pageStatusEvent); + }, false); + } + // logging window blur & focus + window.addEventListener('blur', () => { + const blurEvent = { + 'event-type': 'page-status', + 'status-type': RegexEvent.PageStatus.FOCUS, + result: false + }; + this.logEvent(blurEvent); + }); + window.addEventListener('focus', () => { + const focusEvent = { + 'event-type': 'page-status', + 'status-type': RegexEvent.PageStatus.FOCUS, + result: true + }; + this.logEvent(focusEvent); + }); + // stub for student and problem id + this.studentId = this._getStudentIdFromURL(); + this.problemId = this.getAttribute('problem-id') || ''; + console.log(this.studentId); + console.log(this.problemId); + this.temporaryInputEvent = null; + this.matchFindall = true; + } + set parsonsData(data) { + this._parsonsData = data; + if (this.inputType == 'parsons') { + this.regexInput.setSourceBlocks(data, this.parsonsExplanation); + } + } + get parsonsData() { + return this._parsonsData; + } + // TODO[refactor]: put stylesheet in a separate css/scss file + addStyle = () => { + const sheet = document.createElement('style'); + sheet.innerHTML += '.regex-textbox {width: 100%; display:none;}\n'; + sheet.innerHTML += '.regex-input-and-test-status {display: flex; flex-wrap: nowrap;}\n'; + sheet.innerHTML += '.regex-test-status {font-family: monospace; font-size: 15px; color:black; padding:20px 0 0 10px;height: fit-content;}\n'; + sheet.innerHTML += '.regex-test-status.Fail {font-family: monospace; font-size: 15px; color:#ebd071;}\n'; + sheet.innerHTML += '.regex-test-status.Pass {font-family: monospace; font-size: 15px; color:green;}\n'; + // regex status tag + sheet.innerHTML += '.regex-status { border-radius: 4px; visibility: collapse; font-family: monospace; padding: 3px 6px; margin: 2px 10px; color: #fff; }\n'; + sheet.innerHTML += '.regex-status.error { visibility: visible; background-color: red; }\n'; + sheet.innerHTML += '.regex-status.valid { visibility: visible; background-color: green; }\n'; + sheet.innerHTML += '.parsons-selected {background-color: red;}\n'; + // regex error message + sheet.innerHTML += '.regex-error-message {color: red; font-family: monospace; font-size: 15px;}\n'; + sheet.innerHTML += '.regex-error-message.hidden {visibility: collapse;}\n'; + // parsons block + sheet.innerHTML += '.parsons-block {display: inline-block; font-family: monospace; font-size: large; background-color: white; padding: 1px 2px; border: 1px solid; border-color:gray; margin: 0 1px; border-radius: 2px; position: relative;}\n'; + sheet.innerHTML += '.parsons-block:hover, .parsons-block:focus { border-color: black; padding: 0 6px; border: 2px solid;}\n'; + sheet.innerHTML += '.drop-area { background-color: #b1dafa; }\n'; + sheet.innerHTML += '.drop-area.Pass { background-color: #bcebd7; }\n'; + sheet.innerHTML += '.drop-area.Fail { background-color: #ebd071; }\n'; + sheet.innerHTML += '.drop-area.Error { background-color: #ff99b3; }\n'; + // TODO:(UI) move the tooltip to the top of the line + sheet.innerHTML += '.parsons-block .tooltip { visibility: hidden; width: 200px; background-color: black; color: #fff; text-align: center; padding: 5px 0; border-radius: 6px; position: absolute; z-index: 1; margin: 0 10px; bottom: 120%; margin-left: -100px;}\n'; + sheet.innerHTML += '.parsons-block .tooltip::after {content: " ";position: absolute; top: 100%;left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: black transparent transparent transparent;}\n'; + sheet.innerHTML += '.drag-area .parsons-block:hover .tooltip { visibility: visible;}\n'; + sheet.innerHTML += '.drag-area{ width: 510px;}\n'; + sheet.innerHTML += '.regex-test-string-container {display:flex;}\n'; + sheet.innerHTML += '.regex-test-string-div, .regex-input-div { margin: 8px 0; height: fit-content; }\n'; + sheet.innerHTML += '.regex-test-string-div { flex: 1; }\n'; + sheet.innerHTML += '.regex-input-div { width: 80%; }\n'; + sheet.innerHTML += '.regex-input-div > div {display:inline-block;}\n'; + // the dropdown menu for regex options + sheet.innerHTML += '.regex-options-dropdown-btn { background-color: #3498DB; color: white; padding: 10px; font-size: 16px; border: none; cursor: pointer;}\n'; + sheet.innerHTML += '.regex-options-dropdown-btn:hover, .regex-options-dropdown-btn:focus { background-color: #2980B9;}\n'; + sheet.innerHTML += '.regex-options-dropdown { position: relative; display: inline-block; }\n'; + sheet.innerHTML += '.regex-options-container { display: none; position: absolute; background-color: #f1f1f1; min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);z-index: 1;}\n'; + sheet.innerHTML += '.regex-options-container > button{ color: black; padding: 12px 16px; text-decoration: none; display: block; width: -webkit-fill-available;}\n'; + sheet.innerHTML += '.regex-options-container .selected{ background-color: pink;}\n'; + sheet.innerHTML += '.show {display:block;}\n'; + // unittest + sheet.innerHTML += '.regex-unittest > table, .regex-unittest td {border: 1px solid black; padding: 3px; text-align: center; border-collapse: collapse;}\n'; + // TODO: showing the table for now + // sheet.innerHTML += '.regex-unittest > table, .regex-unittest td {border: 1px solid black; padding: 3px; text-align: center;}\n' + // sheet.innerHTML += '.regex-unittest.collapse{display:none;}\n' + // for study 0: hide the table + // sheet.innerHTML += '.regex-unittest{display:none;}\n' + document.body.appendChild(sheet); + this.root.appendChild(sheet); + const global_sheet = document.createElement('style'); + global_sheet.innerHTML += '.regex-test-string .ql-editor, .regex-input .ql-editor { padding: 5px; border: 1px solid; border-radius: 3px; font-family: monospace; font-size: 14px; box-shadow: inset 0 1px 2px rgb(0 0 0 / 10%); line-height: 18px; letter-spacing: 0.5px;}\n'; + global_sheet.innerHTML += '.regex-input .ql-editor {height: fit-content;}\n'; + global_sheet.innerHTML += '.ql-editor { box-shadow: 0 0 2px 5px #b1dafa; margin: 5px; }\n'; + global_sheet.innerHTML += '.Pass .ql-editor { box-shadow: 0 0 2px 5px #bcebd7; margin: 5px; }\n'; + global_sheet.innerHTML += '.Fail .ql-editor { box-shadow: 0 0 2px 5px #ebd071; margin: 5px; }\n'; + global_sheet.innerHTML += '.Error .ql-editor { box-shadow: 0 0 2px 5px #ff99b3; margin: 5px; }\n'; + this.appendChild(global_sheet); + }; + /** + * Runs a function similar to re.finditer() with data from regex and test string input. + * Highlights the result in the test string input. + */ + // TODO: separate positive and negative test string to prevent additional calculation when test string is changed + _match_with_Sk = () => { + let positiveTestString = this.positivePrevText; + let negativeTestString = this.negativePrevText; + let regex = this.regexInput.getText(); + if (regex == '') { + // ignore empty regex + return; + } + // TODO: add flags. defaulted to gm for now + // something to do with regexOptions + // updates test string highlight + this.positiveTestStringInput.updateMatchResultNoGroup(this._Sk_finditer(regex, positiveTestString), this.groupColor); + this.negativeTestStringInput.updateMatchResultNoGroup(this._Sk_finditer(regex, negativeTestString), this.groupColor); + // TODO: add log + // the code below is the one for pyodide + // const positiveMatchTestStringEvent: RegexEvent.MatchTestStringEvent = { + // 'event-type': 'match', + // 'slot': 'positive', + // trigger: RegexEvent.MatchTriggerType.AUTO, + // regex: this.regexInput.getText(), + // 'test-string': this.positivePrevText, + // flags: this.regexOptions.getFlagList(), + // "match-result": this.positiveMatchResult + // } + // this.logEvent(positiveMatchTestStringEvent); + // const negativeMatchTestStringEvent: RegexEvent.MatchTestStringEvent = { + // 'event-type': 'match', + // 'slot': 'negative', + // trigger: RegexEvent.MatchTriggerType.AUTO, + // regex: this.regexInput.getText(), + // 'test-string': this.negativePrevText, + // flags: this.regexOptions.getFlagList(), + // "match-result": this.negativeMatchResult + // } + // this.logEvent(negativeMatchTestStringEvent); + // negative test strings + }; + /** + * Runs re.findall() with data from regex and test string input; + * Highlights the result in the test string input; + * Prints python output. + */ + // public match = (): void => { + // this.statusOutput.text.value = '' + // let pydata = 'import re\n'; + // window.pyodide.globals.positive_test_string = this.positivePrevText; + // window.pyodide.globals.negative_test_string = this.negativePrevText; + // if (this.regexInput.getText() != '') { + // window.pyodide.globals.regex_input = this.regexInput.getText(); + // } else { + // return; + // } + // if (this.regexOptions.getFlags() != null) { + // pydata += 'pattern = re.compile(regex_input, ' + this.regexOptions.getFlags() + ')\n'; + // } else { + // pydata += 'pattern = re.compile(regex_input)\n'; + // } + // pydata += 'source = positive_test_string\n'; + // pydata += 'global positive_match_result\n'; + // pydata += 'positive_match_result = []\n'; + // // TODO: (performance)try to reduce assigning data here + // pydata += 'for match_obj in re.finditer(pattern, source):\n'; + // pydata += ' match_data = []\n'; + // pydata += ' positive_match_result.append(match_data)\n'; + // pydata += ' for group_id in range(pattern.groups + 1):\n'; + // pydata += ' group_data = {}\n'; + // pydata += ' match_data.append(group_data)\n'; + // pydata += ' group_data[\'group_id\'] = group_id\n'; + // pydata += ' group_data[\'start\'] = match_obj.start(group_id)\n'; + // pydata += ' group_data[\'end\'] = match_obj.end(group_id)\n'; + // pydata += ' group_data[\'data\'] = match_obj.group(group_id)\n'; + // pydata += ' for name, index in pattern.groupindex.items():\n'; + // pydata += ' match_data[index][\'name\'] = name\n'; + // pydata += 'source = negative_test_string\n'; + // pydata += 'global negative_match_result\n'; + // pydata += 'negative_match_result = []\n'; + // // TODO: (performance)try to reduce assigning data here + // pydata += 'for match_obj in re.finditer(pattern, source):\n'; + // pydata += ' match_data = []\n'; + // pydata += ' negative_match_result.append(match_data)\n'; + // pydata += ' for group_id in range(pattern.groups + 1):\n'; + // pydata += ' group_data = {}\n'; + // pydata += ' match_data.append(group_data)\n'; + // pydata += ' group_data[\'group_id\'] = group_id\n'; + // pydata += ' group_data[\'start\'] = match_obj.start(group_id)\n'; + // pydata += ' group_data[\'end\'] = match_obj.end(group_id)\n'; + // pydata += ' group_data[\'data\'] = match_obj.group(group_id)\n'; + // pydata += ' for name, index in pattern.groupindex.items():\n'; + // pydata += ' match_data[index][\'name\'] = name\n'; + // window.pyodide.runPythonAsync(pydata) + // .then(_ => { + // // TODO: (robustness)test edge cases with no match + // this.positiveMatchResult = window.pyodide.globals.positive_match_result as Array>; + // this.negativeMatchResult = window.pyodide.globals.negative_match_result as Array>; + // this.addMatchResultToOutput(); + // // TODO: (feature)fix highlighting with group information + // this.positiveTestStringInput.updateGroupedMatchResult(this.positiveMatchResult, this.groupColor); + // this.negativeTestStringInput.updateGroupedMatchResult(this.negativeMatchResult, this.groupColor); + // // this.testStringInput.updateGroupedMatchResult_exp(this.matchResult); + // // log match result + // // TODO: it is always auto in this study, but could change in other studies + // const positiveMatchTestStringEvent: RegexEvent.MatchTestStringEvent = { + // 'event-type': 'match', + // 'slot': 'positive', + // trigger: RegexEvent.MatchTriggerType.AUTO, + // regex: this.regexInput.getText(), + // 'test-string': this.positivePrevText, + // flags: this.regexOptions.getFlagList(), + // "match-result": this.positiveMatchResult + // } + // this.logEvent(positiveMatchTestStringEvent); + // const negativeMatchTestStringEvent: RegexEvent.MatchTestStringEvent = { + // 'event-type': 'match', + // 'slot': 'negative', + // trigger: RegexEvent.MatchTriggerType.AUTO, + // regex: this.regexInput.getText(), + // 'test-string': this.negativePrevText, + // flags: this.regexOptions.getFlagList(), + // "match-result": this.negativeMatchResult + // } + // this.logEvent(negativeMatchTestStringEvent); + // }) + // .catch((err) => { this.addTextToOutput(err) }); + // } + _compilePattern_Sk = () => { + let regex = this.regexInput.getText(); + if (regex == '') { + // this.regexErrorMessage.classList.add('hidden'); + return false; + } + try { + new RegExp(regex); + return true; + } + catch (e) { + console.log(e); + return false; + } + }; + // /** + // * Runs re.compile() without flags + // */ + // public pyodide_compilePattern = (): boolean => { + // // console.log('compile') + // if (this.regexInput.getText() == '') { + // this.regexErrorMessage.classList.add('hidden'); + // return false; + // } + // this.statusOutput.text.value = '' + // let pydata = 'import re\n'; + // window.pyodide.globals.regex_input = this.regexInput.getText(); + // pydata += 'compiled_pattern = re.compile(regex_input)\n'; + // let successFlag = false; + // try { + // window.pyodide.runPython(pydata); + // successFlag = true; + // this.regexErrorMessage.classList.add('hidden'); + // this.regexErrorMessage.innerText = 'No Error'; + // this.regexErrorPosition = -1; + // } catch (err) { + // successFlag = false; + // // updates error message + // const regexError = String(err).split('\n'); + // const errorMessage = regexError[regexError.length - 2]; + // const errorMessageSplit = errorMessage.split(' '); + // this.regexErrorPosition = parseInt(errorMessageSplit[errorMessageSplit.length - 1]); + // this.regexErrorMessage.innerText = errorMessage; + // if (this.regexErrorMessage.classList.contains('hidden')) { + // this.regexErrorMessage.classList.remove('hidden'); + // } + // } + // return successFlag; + // } + // private addToOutput = (originalOutput: Array>): void => { + // let output = ''; + // originalOutput.forEach(element => { + // output += '(' + element.toString() + '),'; + // }); + // this.statusOutput.text.value += '>>>' + this.regexInput.getText() + '\n' + output + '\n'; + // } + // private addTextToOutput = (output: string): void => { + // this.statusOutput.text.value += '>>>' + this.regexInput.getText() + '\n' + output + '\n'; + // } + // private addMatchResultToOutput = (): void => { + // let output = ''; + // // currently disabling output + // // for (let i = 0; i < this.matchResult.length; ++i) { + // // output += 'Match ' + i.toString() + ':\n'; + // // for (let j = 0; j < this.matchResult[i].length; ++j) { + // // output += 'Group ' + j.toString() + ': '; + // // output += this.matchResult[i][j].data + ' span(' + this.matchResult[i][j].start + ', ' + this.matchResult[i][j].end + ') '; + // // if (this.matchResult[i][j].name) { + // // output += this.matchResult[i][j].name; + // // } + // // output += '\n'; + // // } + // // output += '\n'; + // // } + // // this.statusOutput.text.value += '>>>\n' + output; + // } + setPositiveInitialTestString(text) { + this.positiveTestStringInput.setText(text); + this.positiveInitialTestString = text; + } + setNegativeInitialTestString(text) { + this.negativeTestStringInput.setText(text); + this.negativeInitialTestString = text; + } + setTestCases(testCases) { + console.log('set test cases'); + this.unitTestTable.setTestCases(testCases); + } + resetPositiveTestString = () => { + const testStringResetEvent = { + 'event-type': 'test-string-reset', + 'slot': 'positive', + 'test-string': this.positiveInitialTestString + }; + this.logEvent(testStringResetEvent); + this.positiveTestStringInput.setText(this.positiveInitialTestString); + }; + resetNegativeTestString = () => { + const testStringResetEvent = { + 'event-type': 'test-string-reset', + 'slot': 'negative', + 'test-string': this.negativeInitialTestString + }; + this.logEvent(testStringResetEvent); + this.negativeTestStringInput.setText(this.negativeInitialTestString); + }; + logEvent = (eventContent) => { + ({ + 'student-id': window.regexStudentId || 'stub-id', + 'course-id': window.regexCourseId || 'stub-course-id', + 'problem-id': this.problemId, + 'input-type': this.inputType, + 'client-timestamp': this._getTimestamp() + }); + // console.log({...basicEvent, ...eventContent}); + }; + _getTimestamp = () => { + const timestamp = new Date(); + return timestamp.getFullYear() + '/' + (timestamp.getMonth() + 1) + '/' + timestamp.getDate() + '/' + timestamp.getHours() + '/' + timestamp.getMinutes() + '/' + timestamp.getSeconds() + '/' + timestamp.getMilliseconds(); + }; + // log regex input event along with compilation result + _logRegexInputEvent = () => { + // TODO: just using any here. but regexInputEvent is actually RegexEvent.ParsonsInputEvent or FreeInputEvent... so much trouble with typing!! + let regexInputEvent = { + ...this.temporaryInputEvent, + valid: this.patternValidFlag + }; + if (!this.patternValidFlag) ; + this.logEvent(regexInputEvent); + }; + _getStudentIdFromURL = () => { + const queryString = document.location.search; + const urlParams = new URLSearchParams(queryString); + const studentId = urlParams.get('student-id') || 'stub-student-id'; + return studentId; + }; + logCognitiveLoad = (cl) => { + const cognitiveLoad = { + 'event-type': 'cognitive-load', + 'data': cl + }; + this.logEvent(cognitiveLoad); + }; + logProblemFinished = (completed) => { + const problemFinished = { + 'event-type': 'problem-finished', + 'completed': completed + }; + this.logEvent(problemFinished); + }; + static get observedAttributes() { return ['input-type', 'problem-id']; } + attributeChangedCallback(name, oldValue, newValue) { + switch (name) { + case 'input-type': { + this.initRegexInput(this.root.querySelector('.regex-input-div')); + break; + } + case 'problem-id': { + this.problemId = newValue; + break; + } + } + } + initRegexInput(inputDiv) { + inputDiv.innerHTML = ''; + this.patternValidFlag = true; + let inputType = this.getAttribute('input-type'); + this.inputType = inputType == 'parsons' ? 'parsons' : 'text'; + this._parsonsData = new Array(); + this.parsonsExplanation = null; + inputDiv.append('Your regular expression:'); + this.regexStatus = new RegexStatusTag(); + inputDiv.appendChild(this.regexStatus.el); + inputDiv.appendChild(document.createElement('br')); + // todo:(UI) fix the css for the input + if (this.inputType == 'parsons') { + // init elements: parsons regex input + this.regexInput = new ParsonsInput(); + inputDiv.appendChild(this.regexInput.el); + this.regexInput.el.addEventListener('regexChanged', () => { + this.regexInput.removeFormat(); + if (this.checkWhileTyping) { + // this.patternValidFlag = this.pyodide_compilePattern(); + this.patternValidFlag = this._compilePattern_Sk(); + // log regex input event + this._logRegexInputEvent(); + if (this.patternValidFlag) { + this.regexStatus.updateStatus('valid'); + // check and update the background color of the parsons input based on the unit test results + const passCount = this.unitTestTable.check(this.regexInput.getText()); + this.regexInput.updateTestStatus(passCount == this.unitTestTable.testCaseCount ? 'Pass' : 'Fail'); + this._testStatusDiv.className = ''; + this._testStatusDiv.classList.add('regex-test-status'); + this._testStatusDiv.classList.add(passCount == this.unitTestTable.testCaseCount ? 'Pass' : 'Fail'); + this._testStatusDiv.innerText = 'Test cases passed: ' + passCount + '/' + this.unitTestTable.testCaseCount; + // this.match(); + this._match_with_Sk(); + if (passCount === this.unitTestTable.testCaseCount) { + // console.log('dispatch'); + this.dispatchEvent(new CustomEvent('passed-all-testcases')); + } + } + else { + // if (this.regexErrorMessage.classList.contains('hidden')) { + // // it means the regex is actually empty + // this.regexStatus.updateStatus(''); + // } else { + // this.regexStatus.updateStatus('error'); + // this.regexInput.updateTestStatus('Error'); + // this.regexInput.highlightError(this.regexErrorPosition); + // // console.log('highlight error: '); + // // console.log(this.regexErrorPosition); + // } + if (this.regexInput.getText() == '') { + this.regexStatus.updateStatus(''); + } + else { + this.regexStatus.updateStatus('error'); + this.regexInput.updateTestStatus('Error'); + // this.regexInput.highlightError(this.regexErrorPosition); + // console.log('highlight error: '); + // console.log(this.regexErrorPosition); + } + this.positiveTestStringInput.quill?.removeFormat(0, this.positiveTestStringInput.quill.getLength() - 1, 'silent'); + this.negativeTestStringInput.quill?.removeFormat(0, this.negativeTestStringInput.quill.getLength() - 1, 'silent'); + this._testStatusDiv.innerText = 'Test cases passed: 0/' + this.unitTestTable.testCaseCount; + this.unitTestTable.setError(); + } + } + }, false); + } + else { + // (this.inputType == 'text') + const regex_slot = document.createElement('slot'); + regex_slot.name = 'regex-input'; + inputDiv.appendChild(regex_slot); + // TODO: (refactor) rename RegexInput + this.regexInput = new TextInput(); + this.appendChild(this.regexInput.el); + this.regexInput.el.slot = 'regex-input'; + this.regexInput.initQuill(); + this.regexInput.quill?.on('text-change', (delta) => { + this.regexInput.removeFormat(); + // logging free input event + this.temporaryInputEvent = { + 'event-type': 'free-input', + dropped: this.regexInput.droppedText, + delta: delta, + answer: this.regexInput.getText() + }; + this.regexInput.droppedText = false; + // update status indicator + // this.patternValidFlag = this.pyodide_compilePattern(); + this.patternValidFlag = this._compilePattern_Sk(); + if (this.patternValidFlag) { + this.regexStatus.updateStatus('valid'); + } + else { + this.regexStatus.updateStatus('error'); + // this.regexInput.highlightError(this.regexErrorPosition); + } + // console.log(this.patternValidFlag); + this._logRegexInputEvent(); + if (this.checkWhileTyping) { + if (this.patternValidFlag) { + // only match when the pattern is valid + const passCount = this.unitTestTable.check(this.regexInput.getText()); + this.regexInput.updateTestStatus(passCount == this.unitTestTable.testCaseCount ? 'Pass' : 'Fail'); + this._testStatusDiv.innerText = 'Test cases passed: ' + passCount + '/' + this.unitTestTable.testCaseCount; + this._testStatusDiv.className = ''; + this._testStatusDiv.classList.add('regex-test-status'); + this._testStatusDiv.classList.add(passCount == this.unitTestTable.testCaseCount ? 'Pass' : 'Fail'); + // this.match(); + this._match_with_Sk(); + if (passCount === this.unitTestTable.testCaseCount) { + // console.log('dispatch'); + this.dispatchEvent(new CustomEvent('passed-all-testcases')); + } + } + else { + // if (this.regexErrorMessage.classList.contains('hidden')) { + if (this.regexInput.getText() == '') { + // it means the regex is actually empty + this.regexStatus.updateStatus(''); + } + else { + this.regexStatus.updateStatus('error'); + this.regexInput.updateTestStatus('Error'); + // this.regexInput.highlightError(this.regexErrorPosition); + } + this.positiveTestStringInput.quill?.removeFormat(0, this.positiveTestStringInput.quill.getLength() - 1, 'silent'); + this.negativeTestStringInput.quill?.removeFormat(0, this.negativeTestStringInput.quill.getLength() - 1, 'silent'); + this._testStatusDiv.innerText = 'Test cases passed: 0/' + this.unitTestTable.testCaseCount; + this.unitTestTable.setError(); + } + // check and update the background color of the parsons input based on the unit test results + } + }); + } + this.regexInput.parentElement = this; + // this.regexErrorMessage = document.createElement('div'); + // this.regexErrorMessage.classList.add('regex-error-message'); + // inputDiv.appendChild(this.regexErrorMessage); + // this.regexErrorPosition = -1; + // init elements: regex options dropdown + this.regexOptions = new RegexOptions(); + // inputDiv.appendChild(this.regexOptions.el); + } + resetTool() { + if (this.inputType != 'parsons') { + const regexInput = this.regexInput; + regexInput.quill?.setText('', 'silent'); + } + // this.regexErrorMessage.classList.add('hidden'); + this.regexStatus.updateStatus(''); + this.positiveTestStringInput.quill?.removeFormat(0, this.positiveTestStringInput.quill.getLength() - 1, 'silent'); + this.negativeTestStringInput.quill?.removeFormat(0, this.negativeTestStringInput.quill.getLength() - 1, 'silent'); + this._testStatusDiv.innerText = ''; + this.unitTestTable.setError(); + } +} +customElements.define('regex-element', RegexElement); + +export { RegexElement }; +//# sourceMappingURL=regex-element.js.map diff --git a/runestone/hparsons/js/sa_template.html b/runestone/hparsons/js/sa_template.html new file mode 100644 index 000000000..caaf4f8f2 --- /dev/null +++ b/runestone/hparsons/js/sa_template.html @@ -0,0 +1,32 @@ + + +
+
+
+
+ Short Answer +
+

+ +

+
+
+
+ +
+
+
+
+ +
+
Your current saved answer is shown above.
+
+
+

shortanswer (question1)

+
+
\ No newline at end of file diff --git a/runestone/hparsons/js/shortanswer.js b/runestone/hparsons/js/shortanswer.js new file mode 100644 index 000000000..1836ea7c6 --- /dev/null +++ b/runestone/hparsons/js/shortanswer.js @@ -0,0 +1,295 @@ +/*========================================== +======= Master shortanswer.js ======== +============================================ +=== This file contains the JS for === +=== the Runestone shortanswer component. === +============================================ +=== Created by === +=== Isaiah Mayerchak === +=== 7/2/15 === +=== Brad Miller === +=== 2019 === +==========================================*/ + +import RunestoneBase from "../../common/js/runestonebase.js"; +import "./../css/shortanswer.css"; + +export var saList; +if (saList === undefined) saList = {}; // Dictionary that contains all instances of shortanswer objects + +export default class ShortAnswer extends RunestoneBase { + constructor(opts) { + super(opts); + if (opts) { + var orig = opts.orig; // entire

element that will be replaced by new HTML + this.useRunestoneServices = + opts.useRunestoneServices || eBookConfig.useRunestoneServices; + this.origElem = orig; + this.divid = orig.id; + this.question = this.origElem.innerHTML; + this.optional = false; + if ($(this.origElem).is("[data-optional]")) { + this.optional = true; + } + if ($(this.origElem).is("[data-mathjax]")) { + this.mathjax = true; + } + this.renderHTML(); + this.caption = "shortanswer"; + this.addCaption("runestone"); + this.checkServer("shortanswer", true); + } + } + + renderHTML() { + this.containerDiv = document.createElement("div"); + this.containerDiv.id = this.divid; + $(this.containerDiv).addClass(this.origElem.getAttribute("class")); + this.newForm = document.createElement("form"); + this.newForm.id = this.divid + "_journal"; + this.newForm.name = this.newForm.id; + this.newForm.action = ""; + this.containerDiv.appendChild(this.newForm); + this.fieldSet = document.createElement("fieldset"); + this.newForm.appendChild(this.fieldSet); + this.legend = document.createElement("legend"); + this.legend.innerHTML = "Short Answer"; + this.fieldSet.appendChild(this.legend); + this.firstLegendDiv = document.createElement("div"); + this.firstLegendDiv.innerHTML = this.question; + $(this.firstLegendDiv).addClass("journal-question"); + this.fieldSet.appendChild(this.firstLegendDiv); + this.jInputDiv = document.createElement("div"); + this.jInputDiv.id = this.divid + "_journal_input"; + this.fieldSet.appendChild(this.jInputDiv); + this.jOptionsDiv = document.createElement("div"); + $(this.jOptionsDiv).addClass("journal-options"); + this.jInputDiv.appendChild(this.jOptionsDiv); + this.jLabel = document.createElement("label"); + $(this.jLabel).addClass("radio-inline"); + this.jOptionsDiv.appendChild(this.jLabel); + this.jTextArea = document.createElement("textarea"); + let self = this; + this.jTextArea.onchange = function () { + self.isAnswered = true; + }; + this.jTextArea.id = this.divid + "_solution"; + $(this.jTextArea).attr("aria-label", "textarea"); + $(this.jTextArea).css("display:inline, width:530px"); + $(this.jTextArea).addClass("form-control"); + this.jTextArea.rows = 4; + this.jTextArea.cols = 50; + this.jLabel.appendChild(this.jTextArea); + this.jTextArea.onchange = function () { + this.feedbackDiv.innerHTML = "Your answer has not been saved yet!"; + $(this.feedbackDiv).removeClass("alert-success"); + $(this.feedbackDiv).addClass("alert alert-danger"); + }.bind(this); + this.fieldSet.appendChild(document.createElement("br")); + if (this.mathjax) { + this.renderedAnswer = document.createElement("div"); + $(this.renderedAnswer).addClass("latexoutput"); + this.fieldSet.appendChild(this.renderedAnswer); + } + this.buttonDiv = document.createElement("div"); + this.fieldSet.appendChild(this.buttonDiv); + this.submitButton = document.createElement("button"); + $(this.submitButton).addClass("btn btn-success"); + this.submitButton.type = "button"; + this.submitButton.textContent = "Save"; + this.submitButton.onclick = function () { + this.checkCurrentAnswer(); + this.logCurrentAnswer(); + this.renderFeedback(); + }.bind(this); + this.buttonDiv.appendChild(this.submitButton); + this.randomSpan = document.createElement("span"); + this.randomSpan.innerHTML = "Instructor's Feedback"; + this.fieldSet.appendChild(this.randomSpan); + this.otherOptionsDiv = document.createElement("div"); + $(this.otherOptionsDiv).css("padding-left:20px"); + $(this.otherOptionsDiv).addClass("journal-options"); + this.fieldSet.appendChild(this.otherOptionsDiv); + // add a feedback div to give user feedback + this.feedbackDiv = document.createElement("div"); + //$(this.feedbackDiv).addClass("bg-info form-control"); + //$(this.feedbackDiv).css("width:530px, background-color:#eee, font-style:italic"); + $(this.feedbackDiv).css("width:530px, font-style:italic"); + this.feedbackDiv.id = this.divid + "_feedback"; + this.feedbackDiv.innerHTML = "You have not answered this question yet."; + $(this.feedbackDiv).addClass("alert alert-danger"); + //this.otherOptionsDiv.appendChild(this.feedbackDiv); + this.fieldSet.appendChild(this.feedbackDiv); + //this.fieldSet.appendChild(document.createElement("br")); + $(this.origElem).replaceWith(this.containerDiv); + // This is a stopgap measure for when MathJax is not loaded at all. There is another + // more difficult case that when MathJax is loaded asynchronously we will get here + // before MathJax is loaded. In that case we will need to implement something + // like `the solution described here `_ + if (typeof MathJax !== "undefined") { + this.queueMathJax(this.containerDiv) + } + } + + renderMath(value) { + if (this.mathjax) { + value = value.replace(/\$\$(.*?)\$\$/g, "\\[ $1 \\]"); + value = value.replace(/\$(.*?)\$/g, "\\( $1 \\)"); + $(this.renderedAnswer).text(value); + this.queueMathJax(this.renderedAnswer) + } + } + + checkCurrentAnswer() { } + + async logCurrentAnswer(sid) { + let value = $(document.getElementById(this.divid + "_solution")).val(); + this.renderMath(value); + this.setLocalStorage({ + answer: value, + timestamp: new Date(), + }); + let data = { + event: "shortanswer", + act: value, + answer: value, + div_id: this.divid, + }; + if (typeof sid !== "undefined") { + data.sid = sid; + } + await this.logBookEvent(data); + } + + renderFeedback() { + this.feedbackDiv.innerHTML = "Your answer has been saved."; + $(this.feedbackDiv).removeClass("alert-danger"); + $(this.feedbackDiv).addClass("alert alert-success"); + } + setLocalStorage(data) { + if (!this.graderactive) { + let key = this.localStorageKey(); + localStorage.setItem(key, JSON.stringify(data)); + } + } + checkLocalStorage() { + // Repopulates the short answer text + // which was stored into local storage. + var answer = ""; + if (this.graderactive) { + return; + } + var len = localStorage.length; + if (len > 0) { + var ex = localStorage.getItem(this.localStorageKey()); + if (ex !== null) { + try { + var storedData = JSON.parse(ex); + answer = storedData.answer; + } catch (err) { + // error while parsing; likely due to bad value stored in storage + console.log(err.message); + localStorage.removeItem(this.localStorageKey()); + return; + } + let solution = $("#" + this.divid + "_solution"); + solution.text(answer); + this.renderMath(answer); + this.feedbackDiv.innerHTML = + "Your current saved answer is shown above."; + $(this.feedbackDiv).removeClass("alert-danger"); + $(this.feedbackDiv).addClass("alert alert-success"); + } + } + } + restoreAnswers(data) { + // Restore answers from storage retrieval done in RunestoneBase + // sometimes data.answer can be null + if (!data.answer) { + data.answer = ""; + } + this.answer = data.answer; + this.jTextArea.value = this.answer; + this.renderMath(this.answer); + + let p = document.createElement("p"); + this.jInputDiv.appendChild(p); + var tsString = ""; + if (data.timestamp) { + tsString = new Date(data.timestamp).toLocaleString(); + } else { + tsString = ""; + } + $(p).text(tsString); + if (data.last_answer) { + this.current_answer = "ontime"; + let toggle_answer_button = document.createElement("button"); + toggle_answer_button.type = "button"; + $(toggle_answer_button).text("Show Late Answer"); + $(toggle_answer_button).addClass("btn btn-warning"); + $(toggle_answer_button).css("margin-left", "5px"); + + $(toggle_answer_button).click( + function () { + var display_timestamp, button_text; + if (this.current_answer === "ontime") { + this.jTextArea.value = data.last_answer; + this.answer = data.last_answer; + display_timestamp = new Date( + data.last_timestamp + ).toLocaleString(); + button_text = "Show on-Time Answer"; + this.current_answer = "late"; + } else { + this.jTextArea.value = data.answer; + this.answer = data.answer; + display_timestamp = tsString; + button_text = "Show Late Answer"; + this.current_answer = "ontime"; + } + this.renderMath(this.answer); + $(p).text(`Submitted: ${display_timestamp}`); + $(toggle_answer_button).text(button_text); + }.bind(this) + ); + + this.buttonDiv.appendChild(toggle_answer_button); + } + let feedbackStr = "Your current saved answer is shown above."; + if (typeof data.score !== "undefined") { + feedbackStr = `Score: ${data.score}`; + } + if (data.comment) { + feedbackStr += ` -- ${data.comment}`; + } + this.feedbackDiv.innerHTML = feedbackStr; + + $(this.feedbackDiv).removeClass("alert-danger"); + $(this.feedbackDiv).addClass("alert alert-success"); + } + + disableInteraction() { + this.jTextArea.disabled = true; + } +} + +/*================================= +== Find the custom HTML tags and == +== execute our code on them == +=================================*/ +$(document).bind("runestone:login-complete", function () { + $("[data-component=shortanswer]").each(function () { + if ($(this).closest("[data-component=timedAssessment]").length == 0) { + // If this element exists within a timed component, don't render it here + try { + saList[this.id] = new ShortAnswer({ + orig: this, + useRunestoneServices: eBookConfig.useRunestoneServices, + }); + } catch (err) { + console.log(`Error rendering ShortAnswer Problem ${this.id} + Details: ${err}`); + } + } + }); +}); diff --git a/runestone/hparsons/js/timed_shortanswer.js b/runestone/hparsons/js/timed_shortanswer.js new file mode 100644 index 000000000..4ecfde956 --- /dev/null +++ b/runestone/hparsons/js/timed_shortanswer.js @@ -0,0 +1,43 @@ +import ShortAnswer from "./shortanswer.js"; + +export default class TimedShortAnswer extends ShortAnswer { + constructor(opts) { + super(opts); + this.renderTimedIcon(this.containerDiv); + this.hideButtons(); + } + hideButtons() { + $(this.submitButton).hide(); + } + renderTimedIcon(component) { + // renders the clock icon on timed components. The component parameter + // is the element that the icon should be appended to. + var timeIconDiv = document.createElement("div"); + var timeIcon = document.createElement("img"); + $(timeIcon).attr({ + src: "../_static/clock.png", + style: "width:15px;height:15px", + }); + timeIconDiv.className = "timeTip"; + timeIconDiv.title = ""; + timeIconDiv.appendChild(timeIcon); + $(component).prepend(timeIconDiv); + } + checkCorrectTimed() { + return "I"; // we ignore this in the grading + } + hideFeedback() { + $(this.feedbackDiv).hide(); + } +} + +if (typeof window.component_factory === "undefined") { + window.component_factory = {}; +} + +window.component_factory.shortanswer = function (opts) { + if (opts.timed) { + return new TimedShortAnswer(opts); + } + return new ShortAnswer(opts); +}; diff --git a/runestone/hparsons/shortanswer.py b/runestone/hparsons/shortanswer.py new file mode 100755 index 000000000..61d795dba --- /dev/null +++ b/runestone/hparsons/shortanswer.py @@ -0,0 +1,131 @@ +# Copyright (C) 2011 Bradley N. Miller +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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 the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +__author__ = "" + +from docutils import nodes +from docutils.parsers.rst import directives +from runestone.mchoice import Assessment +from runestone.server.componentdb import ( + addQuestionToDB, + addHTMLToDB, + maybeAddToAssignment, +) +from runestone.common.runestonedirective import RunestoneDirective, RunestoneIdNode + + +def setup(app): + app.add_directive("shortanswer", JournalDirective) + app.add_node(JournalNode, html=(visit_journal_node, depart_journal_node)) + app.add_config_value("shortanswer_div_class", "journal alert alert-warning", "html") + app.add_config_value( + "shortanswer_optional_div_class", "journal alert alert-success", "html" + ) + + +TEXT_START = """ +

+
+""" + +TEXT_END = """ +
+
+""" + + +class JournalNode(nodes.General, nodes.Element, RunestoneIdNode): + def __init__(self, options, **kwargs): + super(JournalNode, self).__init__(**kwargs) + self.runestone_options = options + + +def visit_journal_node(self, node): + div_id = node.runestone_options["divid"] + components = dict(node.runestone_options) + components.update({"divid": div_id}) + + node.delimiter = "_start__{}_".format(node.runestone_options["divid"]) + self.body.append(node.delimiter) + + res = TEXT_START % components + + self.body.append(res) + + +def depart_journal_node(self, node): + + components = dict(node.runestone_options) + + res = TEXT_END % components + self.body.append(res) + + addHTMLToDB( + node.runestone_options["divid"], + components["basecourse"], + "".join(self.body[self.body.index(node.delimiter) + 1 :]), + ) + + self.body.remove(node.delimiter) + + +class JournalDirective(Assessment): + """ +.. shortanswer:: uniqueid + :optional: + + text of the question goes here + + +config values (conf.py): + +- shortanswer_div_class - custom CSS class of the component's outermost div + """ + + required_arguments = 1 # the div id + optional_arguments = 0 + final_argument_whitespace = True + has_content = True + option_spec = Assessment.option_spec.copy() + option_spec.update({"mathjax": directives.flag}) + + node_class = JournalNode + + def run(self): + super(JournalDirective, self).run() + addQuestionToDB(self) + # Raise an error if the directive does not have contents. + self.assert_has_content() + + self.options["mathjax"] = "data-mathjax" if "mathjax" in self.options else "" + + journal_node = JournalNode(self.options, rawsource=self.block_text) + journal_node.source, journal_node.line = self.state_machine.get_source_and_line( + self.lineno + ) + + self.updateContent() + + self.state.nested_parse(self.content, self.content_offset, journal_node) + + env = self.state.document.settings.env + if self.options["optional"]: + self.options["divclass"] = env.config.shortanswer_optional_div_class + else: + self.options["divclass"] = env.config.shortanswer_div_class + + maybeAddToAssignment(self) + + return [journal_node] diff --git a/runestone/hparsons/test/__init__.py b/runestone/hparsons/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/runestone/hparsons/test/_sources/Figures/LutherBellPic.jpg b/runestone/hparsons/test/_sources/Figures/LutherBellPic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c6469a3ba0f5f7ee053902add1188bf99b1e4478 GIT binary patch literal 64480 zcmbTdcRZV4{5Kr6Y8SOv?Y8!=T~)jIqH0Uks@gjUX{o)6qIT7usl7sMwTq&tktnSZ z5;KT+^8Ky*em$?}kLSKmUgvdva$V;-=kxwtXMDyvH$QJS0C)7Y^|S#*L_`2x!VhqR zAbO+)a&rLy3=IJf0RX^l08y+PfP_F15i&a=0|0j-a{;7;>%Vt4A`AY9{u{>!ApVd0 zgeP&r|49>_0018V0CK{85=m)k$q&SUTLk(pA)`N#{5MTRl1>7k{Ev45M6Btg|6M21 zy>#;bs*})S`tARs2?f(B{x_QPf6CwgeCxltCHnB0_`hku$rqOYD*tcm|CtC}l07Dn z|A9_gQu2|Kq_mQx{5>goC0ThT89BiJ*S-JsE`WhR|0{=ouS7R10L_2p_`juF#JB!U z#Q*+Ch)GEPBa@PmlKh)VNXf~_$jAwag!K09+vKbWo|J)CN`s8i#DSd8_pbE2wBpiCC#PrU*o(`5aS;KC|3~KkiR}M{i=Ke%*1y6hh?9f>I{07U^dzLbQe+Go zCgcvjjC|7XZr^>9R@~4>!7pQqW_sl}L&+>4ix9;83++FU{r>|N`G1A%e}MfjT+0Az zVj{xFBc=za11@tJ$`*9=rUp@uiVv8F-6?sn%nYYeP7H`KYT*6;?R+M zTE2+xJs5%%fl}T8`i0AWO{#diiM2orNcT-C=$4#y^{66ii$}lNri5D9)T%*|C<|i< z8`j$#9i7}%p6j_k@$mT9^lx3h1m6yA1Kxl7EDv}i9DoL~hzW6;2jtxYJoDO*uSb?NYD#OH|HAM<8Ec8?o_py2)++SXa0DMlAqpqvt1OsRQrS2vO=c{q z0Y6F#V3&?3ja!BVr8Y4MG%pQ)Y#JMF%>>j4>(4DZ7`%i5wvwJ}I)> z$IbL*eLri@_0Q`r%=Io&LGm#D!bWKZ!GdPxlBFp~UMXklBxsWoF;E{&`40O$o(?VE zxB)P^IX+$}&xcu5aZOOSi6>0$-TbNSBJAA~FxMWw&SjB02o%OKOKylC)|25gqhO ztwjXi459^|vi)fGNIBrK#3$vByYxW;NsU0zeSG*RRJU_O0y`~9kX|Ff^j z(1|ynoqoTIlrh3W>i5>fdh+`5_t5vz5{VuK;!amuKg=xG-X~RL^O{=4Jw_3xh|v+v zlT;ip^O|T^%)&N)sKqt7E57i%BIUWIq-e=Yhr7i-K?f`B2cmv97d_X_H-L?%OXK~? zD}AuRbs@|S?P%7F5$;DbCZ7t>eQB6x)txiESh0$mIj6#suLEJ(XZkw`it^us-;=w zmF2-r8^(#Jp2laHn2{J2tqnNq>&4K(x~*zELlS%2nGjz+vL~3k$<$t-M1!j(0-)Pn zc)rS(w#cdb5kw%r6U)kI>hs_Y5^CChK;INO&}jZ$;0=IW=Wnq6{WeVW^#0byN_ddG zmV$`TX8};}OQsx4HZralT9BiC7NcNIZ1P7BU!;_HQn*CN-T}4=(SznQ7M}HHz1DI0$_DyZU zM29}=%il3Bo2z}_S3A|GsucmL`@n(Rq1~?aH$mqM%y`MBW=oX9ik*#tj81kYXUqrs zZ6UhVhm|@iMRM48G_4irbNlZt2IXkg;nN@pn-k_aFLB zLLP()<2Y{s=)8e+-81zje3z^R9ql*Qc~=b=M{RR@B6>>csi2Ic27n{$0_+A5RocU8 z`?t+nA4j!N3-ykCXggB*?V$ZHEtbQ_8`(FdbSI>4XJb!mhfz@r-Z;GzhE$KndCF&& zVqNtx3hx6AfrB*UO9uj9(*)zZ;+;h9d9K*S>{A0?J>v2|67FB8-^(f#n^$Ae+eDOu zq73E*K%!coK*BjP+adl*vYvbgl)9c{zA1Xpp%C?rzlYIZPzUu9Bu0$`u4g=J(=r~n zw4I&Fc#m&suub#Vh43V*Z~T^s!b@Nnu+`@qt4Sr=_!m2^vA zI_s*scV=tw;mpmBiVza4Yb{c-4@wSaf|c7mU}M)EwYr@_EWmVM zQ*HQhPy^l<#zW}Bnph4aJ4#h!L}ttAOl?n}KoELWzye`z!mZo-)q?W&ucXg8bX=tR zkd1AL7(KOOWDQ+(u2&**$wP3?u{~h{@jKGRlzHdrSIJ)-)EeIeSy+dBpO0gdS_8C8 z1;4~@;ayKm!G@To!3a@|T59E+2vQ7f_E}Ks)AkHK(34+U{Ha2gUw-X&@dbR))i72x z(*Jrl`_f{l${Y87+i0G+~3iopeiEN@+#PCi%7@PM`wD2)M1>Z!uFm1hGxCP~1%#<7)$GL*u^6&u5 zNDRDK_}p}jw4L%wYW%fPnQ{KlaE{_=|GP!b+$l>=YxgG~cb0L`%Zs8PJ=r$^is{(R znU$eX&yhqAD7*Wg_Kt?O)crd8U!LPDZ~NLjoL@^AZp%qv6>AV)#iXzxI2mI01yzav_Tb_!8kS@=);W}-GrkUx;PjUo z-y1KF>U;sNzX3=btJmhOv$zotLBGF?N?N`IL$SKNpme5OS70ME>&OMGDf#47IfD_BtQ z;glN;R?2?~b|}=d1Q8dDc(9gua2aj*M!ic|NyXEKqk*x3*QQqnSo1Ff~Ul%9V=<-Btf8y$=75wkDgm#Ejqi zzHKHg@*@cNvJunK3`Z;aSd(%NsxEf}x3m_jk*6sOEb%zt}f^95?vjD$&toOmngkgLiEZu-khQDutlkRZe zoAq>fy?V&Bowd75Z_Z_9^7siG&vuFt?uAiWJ$76_Ex;J7^>QX%WK`k+-1R`SSQt(^IU&wi72Mj=7OAJ!e2%?c`*Ct|M%Nk9V|Gs-h>d&ZRy-Ns0u1 z`c|fA2Te<&|91D<22ZPToekD-3@hD(PSdyQG$CL!A-aeSom zBCXFn`k>xex}bZZN9QEFe3yqh@U<9B#<70ob?5uVBW?aHP?qafwo}4Z7Eor=zCyTC zILXIi)Ej9NZaVF=PTv95Rw3V0EdKTvVN8{^Z@~4nz~cXwT;!n z$NQ%QTgg65mtun=ez)J@_l4KD3}FH}CL0ji4tCm4j5C zo}DqcWs}*|bb-p;6~7cTezC359&J06z>(n9ch}MW!uw@NF7X?{GYn6ZiaFGB_f0Ww zuTGibUaI5oDn$!>BW=IQQSuZCJm0)#~wNbeKV z&y`P({Gv*4MJPYnKJ&;2ep_IwG7OrlhAeJC9^=Ipz3at9<>6> zRp#`aX$32m2jmXW!W)1Kbome&i~^Q}j;+xwz24kzMYJtl1{~vI1uh37^0{1EwwnI0 z)8dm72Bk3i%tc=vXg=kX#~;FHFj9{L>(*~vUZyn17P$tXS)1|`HKy1 z-(+|IB@%!C7q+H9S39Q;#QzKpjX5=@UmazPm)&Mq>r#nlG*aN)D#~|46rjyRm}=8BlT7{3*k zAHy?3K7g)EvMzvKh8J{2j~uGhj5_8%c=GAIe%Lmt>cBrs&D>g?)tRYb1T2qf%|Wa) zzwWs;-!e(3GY?G<%Ne}A?wlKB+dA*tq1oASe}p&xR znOd&JQ3DUX2=<@vpRCDh5X&&0uWjCAD_qzzeTqhYhyGr|0b4?d{E{@@4!-fBT|NH#hZE47`zs@*<1S2oIzaY6-ThhrhkHR!`9*kBBA#J!(? zm+^U}{aTsVhm)ed@lvhN{DvhbBOhPT9z1r8?~&LG9Z6E*M^ZtEu`kYT^NiavMq#F{ z=-L}VPmC6=O+Ke&?@Mi&zRA$+#1ij#Zq^R;{K;imcTCl*l`=D~=3r^h7f<{&NlB94 zju$HO^Yw^g!!T|t^oU6v1iodnJrGEbjo|nsG0^9c*RLJ#F*Z}smBYmf{sNaP^=d|~AX)3aE{l5CHQVz6S9?o6v>-a8nay@0hvv@td!3m-x2#gu1>W7^ z50$QpxHdqb7l(`PERBRgaREQ%^uT&SZRtDgY9F)Y7t8tNOV96Q7T$5t`s>5ydk}s# zpeAU1*X|8BJ-QX@=v~fnQTnlOE7sr{{zwL({G%!MAHXm%kN$TIFrk@_>@RyXG)`jw z=_Pp5FGKwI@l5O_6^pw37RepUmv2LpERsb#GqPo!epv+F04BxgG+Y?B^aEz|TC*-r zjiJV3bhqJHMf9qtr*J1)A~p{>RVUyxTDvP`r@zH-YW_BNFW|F-B!DvbC7{&Z|J{InZN25EbpWclKpSlhMe{S|Jt|^Ae zghPm}-^eRDJ)HT~mMyJfLQ1W`EY^LT7DJU(7oh`sHbC8Ex7(J@(z(#>FSpd5L?%<( z#-Q(VpEmf;Mn+lz&L7A%=xeE@XJv0}thK%!E^ls*-2J;QY6iy^WECb{1u7ffI@hq0 zEnrwQZusF6PvY0dU)LDpSv>)mdQ8$_pa0S#38&HOezG=0c^jfe8@SGu&Hj74&d z?e`cIcrrUrpdqs;l+(v)?jpdClWz;IAH{JE8w~>9!!skoU!kPU^R=be84SB};E!>4 zzDJ@ie$Ra6y4yudPl?6rMmI?o{}?fR?CAV?IU>7kAB>}`S7Tk-z1%!B|2qRy&J>Xf% z$#G3a(aVImR8czlXQxM^DHjnavh+&&qEV)-kK6Xm-N;)7mE?~V)z|pCr?}+9#9sWd zYUvO*&3&GGY}nEP}~%rI4rZ}x%`n;Vy5kJ zB)O4N$|q={QB(1*q;Y_k#87!QrEa0-$25hP`@i3{QGe#lCl#PK#;~Lwlw;j%K#lza zFIr~pI1()h!S83*NL8Z~_88NtbwHOG=r{to@U|~P{)D)vQh=4w z!9#F*oGy0M4Sj`V`4CW`iDy*4(E3wEJk8DIWso$&k?}z)i5mpI+2%M#SxBRN!Qg)T4cs>30G+V3?wtrUBa0BSO2UF_x zx~{^RY!%a8!DB<01m694r3QQMifkKZV-10`s6A+s;@`#yl@1iu{wP8>;)TY{%dvSF z`s43vUErln=;iXf&%!JCwiIMT>IU$e^tvg^;ChMRvNI+8Oa4h9Qq|ZC%B1o05lT4+ z+61pnf7_yMy8yR8+caMb-whyA4kVRX0DnUl&*04wVH_R0|A~7kHsxAsp+jo3h;#C8 zVQ?zg?@tnO?P{BHjHZjxr|F&3Qi73MGvv(digAr!)gYL~@tLElko5|D?KaN|+YKOn z9G7Pkpi|n%NEEB(MJ^M3Y*8qoEVn5!j{_v9;CpkqBXu( zT3}zly7mwVmE1tgH1O2UJw}axQP61>$>mUxN1A(S%dO)#Zy4B3a28npu>FINa8m7- z`KKKyMd*lj5G*z1f__}NuTHJH=(=0tSk(}+kyu3QV#NnZy#X`}jA37q_piDag7%oh z=2NkL(*yU*+k&xIS5LKs(V1AAni%rMxSBC|i#R$dH}hCtCRG)CdWiQmwtI5S#&W)8 zqkcpXtT^1dY~a!P4L}Nd10YJhU=-cEomof^`B&gFKh+UqMbyiRR(N&%tVi7<4^?3H zROs&z`*|5iACnq~ZDr7PvAP?%n@rjbfU?HwS|`(9MNTC1k-^pVHAXopqGc6Cg;*Sd zCMuQuy#Y{zB{q#YcIoN=h!-|JYejU1G&<>UhYWACZ8t#&Kw}2EA<@~vtScA|9mRop z@Q1$DGt=xX|FRoEtN>Uhl~(aaT_uATb*rZUlbC&9O$P& zVsCCBN(NKPmxNoVXj!8(zWDQ;(2XMRVHF~_t}F3^|}!&6;t&RW0COAPsIXh`QwkkE(_pv@4Uvjzas&D<&8Js!9VTu<)&RffM3YM zC-%1q_40w7s4Gx5n*zO8vtaQ>3(iXvvN{#ZN0xL{m3Y?I7567feZUjsxOa2|P?}5} zUXzJ7TIayFa^)xI*3A}n$3=*kE+W+j1E~an3~Idmu0AXE_DOX8`hxvSxuK4bBg-4W z(Jfoes2jj%Ag&O&t>I6QtwO5zr8=qx(d@AWJ_PK|q)v!A+7br(ofbh>;EFrKtnj@7 zLN%0euKZaDRI?4+g$exuT~d<(TeJ!jJ+&A5QKAZ2%IzGNNGak<;|F5xZvdSC5NGkA zKfS>P+yKgP&kbUZ^+Ri|37#m&8^AaA0o7h(yh9P5Tph1_u?xiVk;mTvw${()vA3B* zDVkT`04mlc^IrrMp*7+0#yx%x8sCPc*v3pIz1Ayd%16D2KzS?t@R}4B za;1YcBLt)dmzuk^*DbzAy|4;!&nb&JTQGfcH*JQ4Br2WUCW4W?>9U%ut3-=SI z8YL)-0*dorP!kv`-({aFLyor4kev@G++HXjHqa{-o*;Y5|NCmmlOQ5+#->p`qy(oy zV9E!lL$m$s%^Fh=I;M&D_u(wHZ|goShyQ5OIgd3=jx&g?SrXf^UbuT^&{LyT9xEzsA;$efHta2TW=+36#@M+rsCVkCy#ODVy!vptlj;Dg$O` zHLIv_nxnBMlhD&3Stqn8GlT-nFEOB9G++T=Av;^DuP;?hYW_2xQ)V}snLYk@qK;4+9G$o{vnF9xcC%!>#?MUT%S>-n_Kbf)MdBfxT>0!3b5FaC*C{o_Ut5Y zn6JX}_ixkunNG{>OTAB+=2MVZ;Vlzj7U4tvQ}BdFLO;0BuR7+dHgWdUI)CARH(9CCs%Y!-TN#`1%-JAMyLL zQW~**c#+A5fb(pt7&?ayNzOJ3s^Bvu(THbz#yO@IPqGW{1X1=nrUVm--jVzZqCc@YQ->} z)CIH-w#PdqH4QKQAw6QLyvXKmS$+S}Z}((RTi&hWTqdEJTj}GD-m2V1jnq0bUKJFq zf!@w@Enj`Zvb=g?5Jpp9Bp_`p`^14`IAP$=s2GPN@oT{@%8OexLBJ$#^&X5@TE&n< zJt%T&-LU0ryZ0}|y|ASK4hvUoT(2tyLj{xqfkjGpGrGayJKBbG?Tu>ut0b z#OqzJoJ-jQy!h9tAC{#{d34Bsf!+gKkl| zm1hUd&f3H|yDZ9_vduVeK(&n)`vRWTCL5@3I%avk*>;BwpF zOYb;uv03@6z;0N|nt49xsd$oY&HWeP#s(LPR7dP6hVdzSBnKI8Y&Gk>FQ+qRyB3pE823BXP8>2^X`FkpmUVLc{W@_5#!`nh zdz`b~hK47#JWBG{l33y}{Is9+Fcv(SPiA}$&@VF{l ztpfFkI$Ao-bNGdbnZI8NcuRg*e;C!ytreukk2y?K6SUrS3vsUnKR`dr))EtHZ%G$3 zFf+e+{Pe@ar?p$p$pkul0Dapra5wRH#m3AfNH~k=i?4duMUI{hj<2gx9gfN7cN&?$ zQCl79aqYaYUoH~xW_PpOZLx>i1cw_FlRx}WS@WW)axGU?&1CkP;c6l^^|EDTAeH=$ zHp2cdHVxUpQh!c6qs7_daV{<|qrv_~4VY>Aip$a!v+{Hv>>ge`T~QGmaF#p9CK@r@ z*{RiD@}d5Pvd;m9sh=*1>jK;WuWYA<8mQ83^U+_f^{FKv)#S^P%4ohzU3xr9UKg0P z#?~?CI=@d^w;}+6x54aYGsm@cP4cDs)5&z`-W5h=dp#F8vEY8*O12V7I*YBT9}mX5ik^YhoE~LV{3G$b0iXnFdx(d+Ee z5eL6NhNhu9q!Yn>aw@gn43SS3)9l5Qsz>25V$gQf=aLt#?DA{67s_}_)hgLemHW1g zbvQ%J-SLb*=1`ZM*D`y_zb$^(46{E`JDcenk-PM~iU2}h6U@g(jmWm;^AtHX->B_9 zhuB|EepV!ZjTEV0L5x5BPqPn7G4purl!5a`33_OT7Z)-_o=C2*JE6 zrH9JP1TD7Ls5XUrjEQ3*t0(Is1SB(`tg5tse2e|K`&r%VobXc!#5s@$hR$Tk$ ziaVQ2@AQOKyxQsMAxaEcM1#A9rrV$9iNsUlC_5L`_(0rq)(aAHKpOF4+3Z_u>!KYW z?or!~--<1KZ!rOEtB7qerozt^vAfLkI;9Q<16PsFm8h4 z8^63>V`#5?w8IG{>>hSF zx;J;K)TFd-Q9fI_c_{V@-r3bsyo?nYe&9^Nh{YTZbwS9%(%DF|Yz(dYZHWVzMENea zVawoI6HM7PdbroiS&RIj9xzNA5ZrBgEPkO3~->&8rXC zX%tNOkh+YfEzzS#dd^8o4@9y*(4B&>nXc~bz$svwR|I{Q1-o%s%0mM)#JC0QYY0^YD_Ed?I2nOr z)%Emx2)vk@-4Mj8+A5fUYjn z^A{#bM@ejl!_6YEvm?mg@H7Vx8`I+C+PXUD0&@7mbu7E$yu$3%7$UWw_z^ehalX;L zCtDq|V_$Ox1Usg$jBxW}8PWA_U>dXzLU5u}t2M23>G*!>kp^5nXIGmd(|QL-APBc;Pa zujPIc10@e1ZaMp>uEhSt#e{*Uq6;<&zmfraeQyXP*vxSe;jdDL}EGCQpJ?P){Q-t!)ooe0iF^^LqDp1>R&7eZBY2v37a3I(%I zVoNE0Tz{%Ug6(CyA3BR>Z8!T>NI2R`PGEp1X^ExdCK-f#b<$WWYZ!6E6sI zYAwhf0do9Zr%`tW{@fd0qrzIDO=&cd#<6TIHF*TD+s-*E#FzTTu7KbiZ@l<~#;8Ds zy&D?RYfX+~6dp8QrrH#Lhk%oU*|GG+!^|~eT@@Db^pu@ZE(ql3G}L0#-%B&dtP{_^IB7WI!2ghMe2o`Em*-u{ z<0mgA-mB<2s=hbTEFKpsDUUU6m}V{(n3TwmrM7xUt#M(&&*Nz2Zcm5i!7(0h1P{}2 zY(RS9?ASH$JHN2*FJOx&%rFIdEy1->)&bF{qeoB65;@<7GUs0iy^%g4TG6e}+dph*^I-b%#W{HYySLEvg#iVfD+If=ed92l(*Jb0i4w1SNtz$kaGBI&3PQzAN=Toci=Idfb&0L)aggyT6Qv z`F(T9RdgaMxiF{^k~)f7gMCOZ61m0JbwFI$a)P~GX`19f&a||<&;vbINqU}__i=0D z>IR_4BLUW6QKP_SmS7Qradny3^-%6v;s24_*a)h5*4`Q1 zBkW}TPwILMSAF^M8sEF0JW8AqOc#roJncE}TGXk?q&9~^90D}UKqdEWlTxaeSmoON zL;)H2t5EVfwMW{0uQ6OlyBRHNF+!H5nlPKEMp($|(!p456pjQMOnqROxG z{oo_QW0E)I2MX)&;lfC~l{K*0TY8}?a(sTymf4`YjvS$Ii}Sxx;m_kyf%? z%DM|eZ>@ffy4QEYKj}K9g9Zu5L7r5)ur5my4nGaxzZczBF~c+=?53j*DJFFNI zP#xFBm+G%erU2k!Bj5ac3XEY^qOGU{IX*|{?T7f~~l^s=_+cbzxZAA=h!C9m_IKCTmt%qIIQKL4n00tp>g%Y$UGo>El;nUJ``ynikhcJan@YZQp@3 zr1}SzO;CuzS;w}ZnAxqm=9O6YIPc=lG@V0iPR@7NOcz0oN$?T^m>}G@b{|a$^&P<2 zDu3Fofc#KM*z*{7E`!mYwk-K9jzITSr59E?xH(QSe#eLnN{bKnV2@5r_K?K>Cp-^m z`?x=g#lkveWJWl0u&XlV0FkA2bkOVT%co19ND4U8pnyVC-Uv1as^0Vj*K>x!JNQ#`_iI&1f!dW7Ly zLSl2%!6T6btOQbG0GM*dE?Ov$mO!!9fFGqyKTjF4$EYlj;q9J{^1=RD*>9o$1xEO( z&9#XtP&4!I9mcFT9u7ljK{A7D%Gkqt+zX8Abdpf|L0I2sYbyh;T;ks}hi z=ODvE5@Vy|wLIA@0=NgX*a}^}3#}=vNS&ynobI*Cn24m>lkin0|kPk}&&$ zT!5L;lqWih9sF%EsC)u0!A1dmhqe8Ck+7=)b!wbq_3O3>epu09XXfb2nN{)pfinwz zts8&M){&h-8-VlOVZvXqc#H2`OlqzKo^`ggdrv4ON)Ky*F!*F@ z(^bSgEQX|IUVPp`#jA3s)|QlbJtJ*c#9N;bow7QF*8Oys>Q1~zz1+Qh$|&Wfjmh6g z53gD-8l+)&6FsF8#)w5W-UUT}OiCIoSYo~CL>sH*&NX7+eVO$2_J>q}@=V?~hG1y8 zO}e#_l1n3O8$LSRClw}E3Jjk+3hJ%BiLg|9EQ_s5+;w$t^pqbI5JZ&DI^eZzsb<5b zO2oMsj4&nv%e8kN%NYZ9%`~f)M?@MHp#A#=SDS~| zNNSiA)Sfd04=F6;Vvf7a+hFO3Q-k!kaKH^?YD2dgkQYTD)sr^Db~aFaIyy=qxx-Fj z(4vEX4>$!(jF9{bY+1xHx7lOc5Ckn71-%Vo(d(XV4*OijR97cE-6;Fc=Abrx;_%%F zKL%d_ah&F`v;{v-?YCg(NjyuE;P17PQ_R$S{*=H-19`7XTo3A!W9MsG$?cGqlTqKsgeM}p#HcE_O zUoHjDn?pg0$D8-HP7I;xuG`tDY(H^vHB#-eG8S%z_W6{@JaYK1%GH=@`xj5;Fe=49 z+O#@7GQh^=wzn!wNG^sm32qzmM(^hfl45c{0)HpHASIh=&Hppr<}6e4hVfS{qY+6?rC-D z-i1szJ+0<-N5Tg%$jf`MkD4j#=md%jaAvz)JibhSOxR(&0zZ3&oyS;vzq-Bb(u&Hdl3D-tO<6?manjW}@0 zduXPEgEayG5r^cLCo?l+y0v3`taP#0o@B4^Gpmg-ipRIq`wh zv6ioY1PWU#G_};H_gwnRK{FGp8yHO9yLH(q1my*-*zoV(0I&~OmcSNQZdh(H9+SC1 zD&=bljh41tj!(a%Q_u3R-hGgtiShCSA={_154Ec*_S9QcE>kk9+&ati!HN~y8OydK z!5o=x=ME*JpCcdht53mb)Lm9xwE6KYboLcra{&i11jrsm8s zU7q+oW-b4kOzNo6z2p|UUC|_?zNjfNWo5noGL$hc8vOjB&eBS*#jh)iO}atC@f?Ca z4%zs$_GoT82JBXi6PzVIoyz8};0RHmE3<@|{0b4Z=Pwu`A^ynU0L@gp7cqMSpivXR zPTOFkHYJv9df6A_aXON@XArC&Si?)mCn2v@(=YCk$&Ah)+c5lO?&n_|n>_pb2O|ZA zVv)wcgR1IT{G?LNA9a=<68Qr)<2$g9k*lxy3m{H;yI8%cx~=-EF(rkt*SJY3IjKyJ zt(aW5gKkgvU07-Z&JaV6m?i|OH*)dHb2t%%cMRdIjkhbtNoE95Ct(v-lh}^zZb~pf zW}9}8D>FF@WW0~I^bz^c4MR_(-d&0O&8>*T0TEnuXZhIMMroX#8ldW{D|+r4oeri)RC zZV4lZ5L+=|;TGr^?$syQTtGL@clJn;r^$0O6^hzb-XS_ol*kOL<%=@~y zq}9M3&S3LXuL=}4m`BE7@G%*-r>FBo_aO_9^2S4KEl;Cb(<3`LE1nh*LS$p(2N6@p z)X`xbuLIJ@F;NIbkL&q13^7A^L-}Gu%J56y#$;p9aZ_bIVTfVvVunsZhe6k1-jI=L2y+uffk1$O%|v$wAoN-^w3le;!0H;zw1rgc;BTx=75*v zj5lYB|Ak7^CebIJd==o;({*=y${%XXU|BTX`v^*$^rEXlx7o89=dR8o>ny3OrjU?e zfo#SvjG0MaANvHI)YmVbNnuDvQWGIm{KHf{ji3*Ie&9M?<)C!?HqftxzE1G_cZB?5 z=Dsg{127DnjDco5bnj+{l!dKLikxrRqZY$B7E4l~2mQ>+b?y}v?rmYG^*EJ3++08y$nE_D_n)KQH})Ysslo z4JvY?v3~1(*5rCS-+xoO6!8(7tMy-bD_@yyYj-}V)io~@{2=|5bYF*FJFxwWJ~jM3 z_;;wR*1BzD!xpN-plEg=7lvwaR1?MnERGOK;e(#6Sc801IE?sxzLy z*&ZtxJ$q?Yxo!Iv_=9#I;=jX&Cb_C zqwMMOq^-PP;(BMpZ-BoIyjAgE;r{@{ui3-n&%)O5j*H?wH&~Oyel^ucg>~%?7_Xw5 zTW3ks!E{j-)m|x1{0ssP>isGBi>LTA#P3*vs)pQP6d-56yexO*YmW z%kS)~Z#6`ZCee?v!lk+b{NleQ$Lw3;9oc`2KMYu+j=FD(4?JgsrFzwt{f#x}jZ}PE z_+DIL2JJh<{{W9&FWR{Aiw%pMX7Rrbt$4fW)79yHyB#=Hh{LA7thL_K({9}ij~IT+ z9yRa>fILs}8^m7`z5?CZcyL>v?7bt#7FzB7+e>pCAjK7?^$Mm<>{*5ap0)CvmkYyu zg@EG&IRNLcuRoQ2V&AdWy&AsHi@ypu{{W|75uxkvrgW+@e#n~eRgu0fd?*Bc+@`vn zdwkk{ee39Onf4x%e%BJD{{X_*`Tl2(iOQ?a+>|Vrnt!SJ)$x|@7SnXAeXeGEg28|U z01$=;+~j^$`ZN1JTc?dbXI~O{UT9#wZybCUyNMD{yDip%uWA7@J(pCsA3!Udi{LG< z!tWb=H}ThtHGkR5<1T^Wy=oiV?+j~qnysy_vu=vp?cLZk&$0Od??nu zC3*3a!j>Kr(>@*eGfwd?vEz@k_*cU>_ZqdXg{5kCx^>ic##t@k8$|X}fV&h8;3xwE zxww}&rH{f!od>G|v_4o)*!3QJ`xVUKj9u zuDXx*W$Cb%@rA23x-{W`c7i~rMP5pLqnEGh{{X@N00Vz#-y3*G;orm5xVX@@eK|HNDBT*&5KJ~R zs<2EL+yHQE$l^Gvs+CD6Cij0X$3{%Ol&4KQC4bEHtry{M!CwR3X?otB;h8j>i`jh3 zFEajXnFLK3VC&{FJnlsSMOnLe@)k`&77c zg-4oqILfv#8*v>gGr-;j)ip1LE%4*xAH&ay8m_&i>lc?=MeEw#x1AKx?IB@|gSTqF z5O4q|A9&Y~YJUj)X(-fu4dMIG75p>N{{XZt@3cAW^oy-lX%%CZMQH7%DA5Aq-bV_= zH$&bJH`QPKk9pL8W{-k)2g0_RXNB&ruQj>ifE#UFQh{NLN7?3; zNdv|=0#C|R;B(I#{tx|={t)Aemq~TqXaT!4lX`yD=*9m8@ z@Qjzzc-r#%;uniU`%Tj7K4jMHE@iN5o3|`to;gb)46l(m>vmAJL0#DG|!8l3_MGq zX?OlTk6F^M?KN)>OQ~q{=+6!9yA~FUB+*26N~&Tw`L;RtYCt3U({Y5QI0<=!q%4Y=38je z>usN_?0zH0IQp0jU20Ef%J0@K_Is_bVDW~Tf8dQ@!TuKT5!9_Cw6l!k{-G^5A}xs=I*{PInB2kk52*t}w%9n!pOq(gPCXwyL*)Q3@$+HB5;Y?k{PM}gZD zf)z&fC*~xUAA4Q+147gMGijlClj26B4!#~~?O#oqBQj1Ve=1xy=YNxG9idbZ;{Y1) z&k%eTwrhJ`e^}DAuOE#T*8c!f)h{k$lJ%`H-eIy!d8B4&)rrexLGsB1<_f={;%H66 zTJO>9zYo0mY!)&Kq#c#lZQtJd^wA#<{1f;~X{zbk2aSFr$>A+G;y#p(b39%+)GoX) z29Yi?0fNCii>r5vQMDc1x5=CkS8vu{3Ap&t@mIu?d@k0$EO<-eFT<@Nt)#oS(eM8N zwe9}^wM_fg)+RGG)Ukw9GcgQQ2Irx#rG7Vf8{zGzg4*N6wo`b4C$`&ZqUx4&c!tYX zxh=X6A{izNYd4#YRf2Pz90I;Q@Qe0u@!y9$8>Z^t5iGnPeHM+VzNmElKTWpNbzLqd zfccFUsH`Mu!5fO169Jhwfq`EvxLTYXFqds=S?1RN0JALiCC&QcK{FR-mRtsm z24V=t02!_=^{smCAX($J)UV`_k-TkGT;uNK;Ep=v`*y2(EHG>SEAh9E{?U1H{uUXS{-8#Y+mOE=FNTd#vi9?4Ej!p^51fQt`-oGZ| zY+V+plg-HXFNNuEXg1xyF^HecWmaaN%dv&Nc zw5|7^TlT$f`gm=uV|@yHi2S}d2msE~qmR4JbAge^HR*b6jpo|fU+U7_%L^;S5z2Y2 z00%fDfzKW5l(Nw7V7$3;bXqA;!4t6xVqd%fa5K{*KkW7CUXiExTTQgSvy=Nq=Jo#0 z(ueaFK=1rJNyr^KbU6Cf^{U1Tmttta$@3g1hP-PP*Tnw-97(0yTR~%GyuHLI+dPbk z6<-Ghnc0Xvf}c}f<=}r6YlWb&Yt@=NV^cN-eYO4J%FY1iuaZXRmk`#^YG{kKp@f)%-sgcMo-_+9;W9lqbx5 z_|{u;0m&1Q*1wKl2!7BX5`GDMR`|j3#e5~B`0K}?7T)U9T|OlHM1(B*gI!8wSmahG z%=uU`KA5lV%l2|(XFY|>+j+LK$kP_aO0#5lJm7TiU&l}FfACAiUjlz=FOPo+{B@(< zTi?a0-(P8W4;w66wx4q|O$FQ-$Yo&Tp*vWzoHCF+8Z={3)TbrAukt-7Rne&l#o5`| z@~i&<+q1^@6WBp*`ze0Jw#})zL}~mjaNrOL1nih{KqPb6*A3$T0Qe}+#_tDN$*7;% zul6GrPz?Ey_&W9Rh5&*-+PJ_u`d5xYW_4{A<_Bq7`$}Q7l_2iv9(#bneLJ5@@}Csi zS!>=A*P7wxmd&k+iA#vKu;xeZTL5jr^lmw?N}gX!XCK9xRm|(Pq|aV&_$%MXb(E!_ z!Ee}_)*v|B;CuZ25By9;T#x<=$NN>?usUDE&)Cw&9{W5QsH=Sk{7et0t$qk<-xA~S z?d|rBe}&XE$yx@^=4>h8=kd=bou__z94>GjXp2@xhLHZydJ7>&yfLxhxk)kyuLGL1Z}Iy;Q-##xU8}b)u0v;&#y_QR9HN7E zbUy}iOE|=Itp4-&GB5ZlzwNM*qMsZ56N*+pdJhK-0rYE=QMddRJNEp$E%slE{{ROq zLT5XC60ULBoZKJfUx**@zI-ZVi~c8wiU#5YFNHt%&1g&U{{X`d7C80D5k{v3ukSx+8~zGy{{RIAyAlWZ+wi6}RqCDzx8UcVvYsl0pYT!- z+vKYm{yls>L7WDU!8RNW{o}e?{BU27o)w5lgIkS|oSX(7dgFuMsNH;f@Z73Idf|_Q z!p3>_$Q5rR!gZj&Chw%35PTkTqZ+DF58cj^eVu>SyrJ4;D#9{}wH2HL!woum)Ozajid@w_ga*4j3u zaeH@hl^X$1EMt?C(4L%h`q$2v+SaG2&hy;gsFrpt$0gW(Smd9?*3|MWEB^qO5&Mx3 zI>jZed41>Wgn#f{U-&3~6K}*VHHRHE4I~eL`E~xkoqbvGv;GR9`)GVh(5-a^{i!s~ zA6!xe-k9DI(aexBJR+Bgcjp{11bb$`ogXrC>Z^GfSzXN zJCK|l;BF_acmDwJ(>@(6hD~B}2<1dmfIUBh(xRSCguR^z{{Y}9bu)Zg+OzxF`%5qF z`}=+&9#8Fc6Cm{PHik&_{u;Y!{{RI5{l5^uoBLT#;Q=T9D6I)M^yqxEUyAX5(tZ#C zyt>WF+xJJ4HhqWO{&f)jrTi|kGyec$- zU+_>k(90+7X(YqZd>Nnvj=BE;P=A$v8{PiW+62ny_st-3%Ye(CdXtaGk6Oxa+ABz% zCRwJPNCyqE+dT$)X0WZ5;9<5If8ZbR4XEZ=y1UnF)?e^X+g(fk3a$SD1rnC&Y;+IW znXl)Wi3u-*v>)B5BXKMs0G@al86&lNE`|R91qlBDf{%EDSz)&QteNLnB%T=0f;2Q~ zgn8V6`B>kcfSzz`@xS018r_`Q@k^F{R^{RbR%OB}Wn?F+DslLmn*uE||_!mWHx142NK2*zXAdK{?5PsDkw~va>scN6J)K?bP(kyXF zCxNtuTY_>JugS;<1B~OndHu$h=GsA~X>T;Q*Vz-wr(jieB)J4|cN_u+dXt`OsqbB-JM;rMl=L#5ei@2kT--IlR3d7E>I zGDEU6j1Io%hOZz00D^M9bD^s!Y9lX)bvMdUh2^{7Z0dl)tv4O}e21YC7 zs>rS?w^5V|p&uc}JHECikou?dv zNL~{;?Oe{0eX7BCWpVwhsIP*w^CK011`$iqq-3iX?^ZtnFIgjs2?Xs8)KP($)#X)JEDM(8jAZ(n{Mh}QA5+mk zXGjH}fSv?T8hDRcp6&H`<0cK7jOb#BZFrQbTls2a11hMYN!n}ge};b*HBT7nJ}lSt zyI3__4+iQBro&<+HqJb$BlAF!n{3PF?>S`|`=y3Tuf!fA@n?cODn2WG9iLURZyxBL zAk<~jw1DX~#1?v!{{U**yQ3134YzUSm@|_ZRl#Mi+&mu46)a6$ES~F`C;fj`KQZwe z2`bbnRJMv%Z{*Ks(X751wu}9>r)rH!gNQM03NLE}|xBM-iO85oinY>ft z9|8?FTRSPDy73Lkd)YN~Wri3I!ewYMf!(8NRiyjJ=ivU)TOCp{yw@hZ?yd4)KVGN8 z(aPw>wFx?!cC)^n8^6xmbUS?u_L0!OD{5W{_>b`q;YY_k7rIDn5!tsIR+bH|LylfH< zF|^mue;EELd{F(IG!GE#S6&=c@V>8iZ>Q;ZTJ6QEc@pXu2^ZPk+S_(kEX_XB;dW#l zpbY7Q$5F%K+|^!t>g2V*pSz`(;CzN-hOdvrDpRDXTS&pXUtP7`x31^K-UiWsvUE!w zF?87e$zvE&i~F!tjl_&~$tUuv-Xrkrx0fqtrhS>M?uPhLV%`vpw++>O$4vTH4SVo+ z;q>~A?yEMHXZFkarkFf?;*DA4EJC6jD)WV2c)-n8xA<4#q=sq#0I*Wp*3sTFvxy<| zoH-vZ2_O&@jx+NN*Yg%LE%ue=Cp)XVd7si4e8Nzr6ys-QeVUvI{jP=o~!NYvb(>=56T~GWdPlhaB0pTkYlnG=qDPpgKv|%%Y z)21?i3gE@yl?h$H>+d@#;$3Lo+n%(Rv0K{5X??3&OLEXEs>^X0Wp&FEGmv@6;~l+g zrqXZDmu+%wZgkB?HiR2^gow_Y!TCt%I4zt3#dy45v&X|#7sd|?L}ZCWNhQ6+u|{7# z89~VYE~NK8>u1A12>c7A!*6+~X=|qHGDyuEi^<)T?d{JVfS;{$`&g+YnX9cR@qZgpt3x18>`w+Le@K6l!#GI4{~u70(nrs+N$)vjf@KW>`Ic7oGg zO)E+YGYo>qu~CDZX1eD}Q*xS?jrRWlf^$)+$dp`mKU=?K>+8P}X_7@{D3|>zK?~1- zWJu07;~exVNgckG@L&8KOYrBy(fm36o4zhvM{RHMGRs-H@ai&*$#bSjX7@6J*!hG~ z!kaXKV{&h*>c%y+y|Po`YCM03fFN+OZinEC((e9EZdiIvs0vEPk{ zI;ClBu74t0hM#R^;(I-RN0qI#`K{8@`XTa*fVn9mo<47xfg`ni^WxjMw5U8!Wu{DJ zvYzg5v9zG7ra&(qPu~hjJ$VPUeIuaWT6lr(V7q~$@?F_iD}oL;^*_Q_{{SBP*U!Hi z^!Ag&7cirwv-oN^mKN9)$hZI%7(BUNPHX94-iYF?uDOrOABsN*ms9Xp#Qy+}cY5K| z@3cKy-qkekvI$o7&nS$t%e-t<0G?5^kT}L`;?iY4>h0)!&SM2mX)egHZl-Rm zoOJmYkJ6v^>yE#<=tugb@Ay?pa-%zKMQ3B!4A-R6IC+_EZ|)a37T-6l5ug2*gQ#2_ z@o&%lF^~KyBeiyK-YEbcMna#=)V@?uF)rMdIL8Efb){r$%{gxW01=CQagst>W%$pN z7{}9&Dp#6x1Up*{+4g)d*%6|3`(-D*5LH3lk6=-cFZoWxEwGbbDytKPCAv! zs-g=Wq+k*PL2)Mrz?IQiii^oB9~g+;R7KngoX#Pngu=T+PlI3R**To+*GZ>UKl%H~N3 zEZuMb?d@NqKe6BJV`KYJe$RilSHZY$DJ9r zSlTG^+a)K>0|Oba&N}_^{{Vs>>9>A1@VERF!{KBP46yi@;J@~{wR6SE)3wVfTO*Ci zu*-1m$oW*)UJendJ4tkZeVxA(DwSnVm8WldH}vLL{12CZX8U`KdwZ)}k}T}tXnftv zjDQN9E_(0>u;#wp@YMQFtFFYCs|DAGVMztNOu}X%KyBnP&ma;p=)aG~o)Ylw{{Z|Z z=fZL6*ZOSIPSV_?w#R}ltq>T=ZMXpT`G^M<_XJm#wzrxkt-4!U-C3?JWK;(sS9TmR z1AxSH>M_#3dQ!Ajy$@m#YEJ8C_?`BF9;F@Tq2eTX?qIl_CB>|)pv7*0$Fu@Ej20*I z;Da!n{>K(xs}bF8djJ*)-G^Li~_{BRwItRPAl|R{t3OU z=%eGT)*dzR{*QU5c9gkY7vBE zDD!tef4nQDYx=*!Z-)9O#2Z~BP`#S`*=i5{oK>1=6%qv|ISR6$n8sCejG1-66T&eGRF~G{0XzBa8UGdE>7X>5X~t_u@B!t+lO2D{l~3SzIltf$w5< zNh9-2fz_l&kwGMY0o({RoAC3-GwRPiMz?!W34yEx^MDY%*X{ubuV*^VZMc3@=jDA?kM7NK<(>?{=N#Q#$8q25n8(7rsrnQ1?K`tb)yHLp1xs#WQa*dYV&~g`n zUzWe{Q(q7Gqf-5&^bZ|;F4v)l#LuBvUf*e&Y*x3?>WOW0I$X1X^BEEt?LzK+kpKq^ zwAbthh z9MwEE`%nBm({xvDO4m}*FJqqKZ5<|>Xj$SnZwzvX1dRmt=;aw%-Vnr=!1`_%!~IVk zPIaelmt8j0{{#6JzSn=b>~>T?@SMKwKQ zU2@9CS!NPR6tOz8p<*ITE07Jch9?1A=}&|o3cPLcTgAR4_;smx=IZ|dz_&WI8l}?e z8it_xjnZd|;Srm1$k8NH1Yx-ab8Zn-5y^* z&9)XZ!4BD_l1GCGM2-U;-X5erMzO>XV?QJUUWW z?Q_cH6odM|=4HElb@{vw}2)9kG!)GhVfljOlT4ok!UP^`(h2ml?~7|8~| zo&NxgF5WTyt^7~oy+2pHvC=iX+znP_MRb`QL5_|xtqK&J zl$2)nO6kf!s`dMmJ`;eQOv1EOpydVKX&2L1v(f0R&uY?^%JRlLDDGyswqYB{&RI{H z8Ddovs8Tuu)DS@<8TPp>W4N)po9r-06}e{(aHa(koUv>&fK)KSY+!}XO?W?t{4b?g z+<0moZr0mJ(=D|ZyMk7}p5Vw#3>rQ11p+lPC<^@Nzsr&CTKv{9*lKsTH!|8;KAct- zLabYO&i*pE$-w^r3jAWVUS;Nf;GpAn&xO7Qym}bcv}7xtvTS%96y$P_8)) z2s!pO^tFwLnQ>#O*hwl2hVr6t;o@BhO&Bo@BB?Vzc*!G6 zEN5!I-cB*bM?suou!Shfrz}|KHyGWV%5BuGHS3$(3$6Ef5Ibb~qj@hbaB?sdbLf3) zZv$J|T-)Cuxw$QCZM)1+7k&`!j~`Lk=O@~|2W{LsDYmzG)+a_$BDO+CMgoJ_au4BL z*TYXDEBl*Skh8Ng#z5Gty90LPuJ6z3UdohV7t8sce=BlVKKcETtZZ(zty9Byx09`f z#*_AdVq)?`DhCAR~&fTVGr*cq=O__eD= zY;^rWZlRw}zktNUISQXKQH=A(>>PIIiu)T|nRO+e#rB(f*xWPV6+4Cs5#Q8(4Ssug z*3?Ztz2IxOOKN&(GQt-wW{Y9@RCLL~9Qur!`y3y=@g6QIa;qhyD1O&E#iq0W00gl3 zRjS7)g_`Q~;xG1eut#&U_Sx($Zs0C$zyr#aOmc7JWbGKmehk;{FYWQ+=zJ^t0shL# zZF_YUpNf1>sLyL^&@4-H8d=2h-GGh zzIYY(_ze7k6axavUReaY!to*nRBr{XJ+uoaHf95kn)K7e=n^r~*k>`nAI{-*#IJ-zFz z(Yy`f&k#t7rf8S5d53~fu5;W2$@~GYsC*yrcTKdK2WxenVm@Ynn6U@{03GSr3i}7Z z-w&&Wp#K2DKXt3~Z~dp^+c;#n+xxgIrjV{q4oMiV(=P=0b4a#ncH7%<%12Ilnj zh5W?Y%aBROUs6f*#eK`+h46IG4c3?9e-E@uRIGk#i6tkJ_$QyGeC9P)p?0VF9-b1d zI+9Uw{LkdyR{sEkEPm7L2c6+vN5ftsatkbOv)R0Z9fKhr+>u`yd}sZJKWd+VMW5nl z!aow|_6_$h5d`+~4`~pU$@C{a)&84bvYwCOzY$#8uBj%I1*(9`(qY3K^v^i_>-F!( zf3xT8@$p08w!7h<8h9VTJ|?oivXL$!zPE;DkOSCq4A)MV7G($}%; zJQXU5HFbZP{vTI1cXl(iwe7XVoU7F(LmLmkimG~o+2`87TEA&80r)dp_>JRh{TIOc z7lu40`n29gm8Y4S+fRqhDya#cNhAXUb~lg-AYfn*&(9frInwm&rkg>$xQ5NT#dD0I z1Lg|Z`LXZKeKjdMuu`?Icyp43oL@u62dU$=Oe7fcobQi0&vEZsx7r=eoJn%5mlo2D zheP=CKM(VY#vEYx6{M4kvnzc>kv29nl{|y$EBX-s0E1~SbXmXPp?)Rse~53Rv(dbL z@VmuYZNkX=BD;_5LFBcR?P2B0it(|*z-;!f=gmHN-3jJDnSZbN*X$?!5HrQMz7zid zg0B9`O=S(W!SNoWd!7SQ59N++Jn)tWj8T60ZQ{vu+mdmJkpTKvoW2vxs;sWj`D>Q>^ z?a2zSfZfk(?L0LR)%Ag{+KX7@w;}tjd5}E?vY#;WnKWl1CP$Vt48rKvx`k{-OC(y%e;o% zsuX0gImU3@h3EmUFH(6Whl?-m7*Lqx@{i1NxMdON=E=`W!_h3{u!3z0D2SI!l1SUi zCk8jh*w;N+s2Rp_$vlxgcjBpbO*nZW0e4%~6e z0qI|D{3P+^{{V&lC22k{wAFRL3{7Whwy|Eq%WPu}AQGu19Gu1-a1`~(!2pkmY&52^ zhRWg)Be=G7U7I*5kOH6`)TsSy?4JT@81XlRMbFvgx7IIh?XEu8wW5MMnLM_TZ^|93 zCUK5=2ZNgS>BcaGlRU>MH@W>n_!r_2gRgY^4RcJli(S+;{Uc9UXVa&Rt#4=ZKpT`0 ztG7GbA2AY~08|6$o)OpeuMfefcz?xGSsxV7XJ(RJBABF_`ZRAZF{uUA8A7R3^8!Xy zg?>`_AK)MDDeyPqrLTmvPZoG0$^1X} z9aH}RGwokQ4Lz|$Yy`GldCGRE1omVY2G&YedFC5S@HZ@mZ58Z71TO4 z?Y+&V!%d}MdB!0Lhi|N$$%3I-zHPg!mV)JW=#8qorQFi+e}y7T--p+lzMHIT8pp$l z8EtRO>vt`LQz{9an6Uv$sKWBV06N#pdJeB+;UC)P_Kv)@fvqB&Ky7`bj@nDMo?!28 z%&Rgvz6_qk{E(8K^6*c02a>xg~uQY z_)GSP)h;|c`$T*X@rIRnjcxHd!sEj}d=Bu(aQ;|y$wyLwk|N4M%Cdq7YSxsws>wn0 z{KgpI_J`fu^7EjL{FfncRC zCIeBqg>3AK+D@`<-77q>NSje&GsMo{FPkvVe6yd*58K1xFNC}~;9m&*Z~d9I-xYXU z;wQrWZ6ux%CH3^uSjbTfrrvYpvw4p5Jex5fiI3f7Blql19#p8)uNyvI?WuCxWh?8a zNB5ta(X6Xgoh!9_+Q~TcCf=*3RDP*=vghD7x%*kK!H)%NUOUo!4dBf+wJl3W(w_Ez z4;#NP%ril1T-jL^SA_w$h&%nFLIdr{{QB^3jlMVhL~o25&&B@$*>7L)_M@qIicLP( zOtaK2?d>jYqP4e*Jwg|iQbMvAR!$cKc>|Roe!%#z{t4spE5W)xtMMAc_Jr^*tlkRK zC%Cm5--p)P%2yDv3nQDB$puE|WK)J1&N%$O(tK6%8(6Z7!`>vl((imz;s~I+)UNdS zS_acDW`xV+DwiRAkQy9kor?Jx3tyaLa~$6t?Q7#GMx12T-!hZYSx3og+tYKiCCf6* zx-_uZOhl^65o*pZH*QyQ)o)~$`Xsw1ZcF1YiW2D>c8fQ`O-|k|8Ut+&y!z#{7a$$4 zlO9U|dY&*p8tgP*9eA(Ax?S#%sC+ThJU<1xp_*mXBD*n=%e4xHPyxm(*FRm zbzMJ6w$`;N;j{5Ry%M2@Nf3vTq+hxm7Dd~JY!iZ8*w;CCq21~lo}*_y+G+X#@RO3lhHES1m`#ljMvjP9vjl{bo)!o*SWZs z<}{Rff*6))@VK7O>c)c*jqCwpH4YH(jk3IHLN-aY6DAQcCZ^5Z_WZVhU7 z)GoBP@aC%x{Cq8!+8~vR?F2G{;DLr8OpI5yXj*gW_a|D^FPBi7HC5ttWQYbNvh3jf z;8%`8>t8$Tx3?A_82nqU{kZ9RboTLETgw{dHvOfNLO~;D6#>XSJJE;0(yKP0wM)AS z*s0S|=6W-_wbV6%x*IJs#P-oP!10ug%BUNPf(u{_gURkXS1k^vbWZQ25_Xg3j12vy*8n$>)8f5OOlZCnN)f`B;7c zdQOLLC5E%0U))V;d2b6fv~jN0D<%ShcqEOS4xJ5kEWQ-4uC_VeB5kA8e`bw;M%Q&{ zyi;qgORnjcD)*XA!3HC;M|9Xq2LEQxHc>+SL%BrSFjAf&E-GOD=?SR;BL9fj-G~rdoTlYTC z329SJAI8`1ckuq(;D7BK@vFc#R`A=D}lYwl_9}AhZt_fGtgqcpkMedH-U7$6UBc7J}6i; zk?LM1(zS~{MiSUZOBpP7$?Qs9GXDU;0s8*{Ik$R-tK!cT-AkuKsB3yXgqJ``jKv?6 zuqlv9)0YwfgT_xzqwTV4p5BydBjs_rO~Ld%7xw=E_#^T2;t%Yl`x$s2;opJ$Veucs zUIDh(b-CY3^R-mA^Q1{GXYy3DTSBa|FgO5=p4@&ZC)tmiWo0@&)G)l$MA<(fTm#bp zbDV;D*Y<7uGx&$Zz9apRzB>4KTC;}g$HAT-zSFeWY~@RPtwC1G<)z>D^75R0Om$k&ohJ@QWqzwocH6a^KA`ZunOC;R3V`9+5E3(vJS@|yK(XYHpZA0k z0Q6q<^iPKL?IzaR*)H|nF6Q<*MQIohqB-5Z3-ubt+evY zuBc%1lmZ9M!{l`xkLh0?>6e}%y?a=uv9i5~Ya+~b5E%zxNStSOIIpk17e%Yt{gpy! z5ToqOmsg&Dmh3^n2JB?@Imh#_lFTYvc5VIN@DEE0udR>N{{VuY4>j#<+1T3nGr*d? z!UTBy)%y!L9N;#|M_lqnex3MV;ZMaK8^OzaeWLjK!%w(~BE_d`D}4lE&cm1{cvJY8 zac`Lo%%^sn0w*$c++qkIMN z#+a*nCA2!t~l+|85pvJl@=)3o;5C2RBuupcK!DU zq z);QbQv%myxBk=V<+LJ@nFTOZ<;^nm4i7X#nyofCE7;d47T!?rW1NUweuG}s#1$h(S z*=bhx(yB>qtLaJhO{nA-R!7P)_Xl|NIIqt0Iy{(~l<#{pobudfzCJZfa3v(>A_*RrDs!>vC=B-uKovwSHi{{*27-C;8A-Ud7zd7fklaM{@(6w)fo*L79E2Z4u zK-!kKXL00O*`sCTnkg00&g|f0YZmG=k<&JMqR( zLHJU5Q$o7cwJAJNtE4x2!>Gh?VT431vy%gBj@sd74xxKw9=KD*L9Q)A#S9qF3Bv3afOHhM0db9;RN zw36goMw1{nY^n^7rFLYlcA*T!V+ZpLT)F2jd8B=|8ZH!|(z~)gWBA3W_?O_J_(R}t z5%}9()u8x`szKpRF8ug@)=4b9#5*LJ=56E;IzS5L%V(}@HeU?froF28i^Ja?G-yVv zFOhwJXW>mg+IEs4b_tX!kWd0Xbk8{@xT~M=QN3!%#eNmjH48mT#v8wf-X+j=xbC3a zaSg0;NZW3axdD{yV~lXBNaPyb(!2wxYbo(M^IGs7rlH`^iMKO}8%MlAB?v@oa)gj# zW{Nl^#z;9l=OA&@Zb?e|YIW3%VF?UxFPEQnm2)xVp6v zA1FC!?icq#56;YTLlAOX74%-Kp!lYJKSa|Zvc0$QKZkU}mZwp9ZkF-mJAio>M9VNl zAxU5fatX&AXT#r%>Ea(2p96eX@W#L4j}+b9%AR$@1-JR3!j)$V$ag7aKQJ4xPZhN$ z>CW(4kvOTx-ut~#!GFRXE%x zE|Y3=O{dElw7lA>DR|sC&Q8X}vL@l^Go7{MT3?SJM)4Pi^sQ%2HumiTTjWfL*13Vs z!mW&`Ah5v)_oP=%8uF99{{RyiQjAu|RpN_S^gTMlbA93aU+g*8Q+vHbPP)1}*|=Z| zJhtp*XJ;-#2&7SuyUDMdzC3825BPv}r+*UoyF!cNABV4?y^m2dNRi7mtd{L_1W19r zq-j!5omN1thD0NeMe(P{e}`TT_|f4nhvUbO;H!;WLOQ;Oezw_<#4iM<7t-2LzGQPQ ze8f!catP|6*PeLS<43~3k3R@CKM3j8ULmviyL9(6$2Os0`hBjYcN}s6L_xd0Lc1df zWS4Um!zkX$HnAm1U+KJ;H2&Dj=*m{-<=k;C#~L}t3OVE zX&>4<;-|;09{1p{>{;O3ZCg|Eq%R%MiZvTSExa(v^8KPX363>ZAdw};&_X!~=K%gj zUwA9yzKMOOORD()0K*>;yle%-_@et;g68Toda@X{Lpf-?(Gf5n5SzVQbfX4)c<2e}k9QO@Z3r;H&JGaqYMQwX6 z@3ykPm9{->ilr*mW%XEEDJOSk-R_pVTT9wLSG|r^B>3d5YhpC-gEtVs#KN*cs4)e) zMi|^2`i1Mqwkn0sjD9g&#T+`Vm%}J+VhwM));rUJS0!>LVn#T~*17Su=`B2 zPi>;lB!VV)wwgJnknLsxfRq6%WSrnA#w*7DH(dttr-M8fqFBLWG>ImoaNjk$e31=^ zRC2^K=Yy6c0(h^9f{dh~m67%`=7PI=mbC8^d~Lb0)1!jR;jXErSy3gyNGXAx#OEA)RtJalAGK@#CW~2? z?nx}|gb>9P%^Vhyg~KwT3K7_C2R#Yk@YR`Tp!lie{?YL5#HppDy0w9F0uTUKTyYo# zhGW-;#{?QG@=>!<-+tzlqbj;{THkTMW#fN|s`fBJp?DVFJHzLBD|al!WS0uqA1@s9 zgO8NcJ(pIo@VzVN1tE!CnGoGz+7yJA?GRQ!aVNB|T0_NCyBQ%BK!Lq44PEYRxq zS^HG;qRekgM!^Xv-GBfDz#t5cwcXskj;VBY8z;XO8it@Q3pft0$_l-~L!JKsK0ZhO z9-}p`8N!~iR==5sDw~$>_SmnaYyLY+yNf#y2I+TgEW%+N7qG&tqYOaZ5CK5PM&pl4 z=`?Q~c;0PN81K9UmeyKC-)Yny-&VD}o-oKtn4%bF0WxqpkVbP_o;J9bN7F9j)2wYw zD$eUB*H+krS6eJbC>8ylJRT{7p$wev4*rE8gd(;Eo!xpsq)F@_2cAQmL? zSz<8}gt=R_&WbpBREM-$*y#K{@h?Z#En3PWWNuMnxU#snod?^HvOe%K3K926JmfIz z!27LyXLqW2XF^14S$t1@XT1VBj$4a(n0cEcCkj=bFvp+1q=CM_8@@BxY99mqXQueR z4KC|Z_$lMfM%}erO9mWovw+?Z||S04eKEp^}YK9eqLak|^Dsrg0! z00p44)4WgMKiNaX(mL2W*?5XuxTIz>g2PB;ibW$BC(K@Pk`JYSJ--OpUfydy7YP(= zYV5H>c?7s8%g0g($qGAyJJ;{W{1(sRrP|qi3HVaFKA)#*8ts+GiM3+ruHo(HYdE2Z zla)eE!EnHD{mJUJ{NfLY9uxRye!e;J4~b^dEynWsvYKMgxddzsDp_#&AfKgs+&4-~ z3c0s?9xiKAGOqhA{{RE`-{8)*rFNjJ+{u}Vb5xT*uG?HAm05_2q)kBl>0%ZrMP`q(p zr8@rr?U@(EZv^T;27VGt@fO?RHIrEC8kN?vENo)ebqMXig=7gMQ(X3Cw(Hez4qPcw6(gQ5O{~fHd>gLKw*Mn(qt&XAV}Pv-2VWL zc?|v^mqoPHWtw3$j|*Udke@3aPrlLKzL=UomJH$Y5i-MW?b*re+rN78KNcxEW#!!4 znkI$z09)nC?HR|v;ZB7#si){?S>AeipU@ZW39o3M@asPbXdAbZ+w5h|iNYSRgCsPFEy=Pu!2MP7h4_*XZ}{ zA>qFf=pV74g{>}pRTaWZrD*WpLbpC+h`iWZ1Yyt>-GR3xDD9qpJ@_Z#RJqh(TX;{| ztU|vk6av73(03<3yk@+KRm94YSh@2`?#=JZ(Kpu1O?9=6FjT2Juh^?D-hEnLd-~k^ zFT)-QzSM25;DXjAc+?X$)JF4RI4oH@AH(`r(cTU4ezZ(t^TisM+0cIS8+MM;83>h! z-H39eaB@hi-T||_D-q1LQ6@~PcD_bWCyevg8LsuMStNz*WRS|NzcFH=lsU*F=uhd* zc4YH)Yb9g5Fv3QiCnnEF@a~Uoq-t7s*)6om6(x>GEfVgO1>LZb*Pzd?YuiSHYvLQr zJE)_xvRGse(IPMkrz~R%#PCleIj$?gmNu6xx{IGN;ALo;*B}t3agWF8(!Kux!*;U0 z?4M}+G;KJHWsoQYpy%4ZQmH{Ydacia`o}Ey*zf!u;N2Gb7^0rSD~RK8FjSDFe7XEjt$wn2WA<$D)vtzhgqr5&*Ib6pQSL1Q+ZkUZmVKa>&O)AX z#~81Ne`oC$*ZW@5>do$6FEM|4&IU)^#(l@@U$2&Hvew->J5UeOzFvk~Lls&|q!ZBk z3F7KuVIE0X{{S~{_&8I;J`B^pZB1Lko*mI79wP8gjb~}EY7HTobs3s3J~F7oa*w^pKSZr^_Bks2MK7`7XJY7P%n=6%XYT& zUE1lgBBXr0XH10~C)edX{XHxBVDa6&cQ0~PPnZ|x&QBn9$?KZ>Jf965Jt)FEM*Hj4 zo(6FVVk$~aZj<~G`jh_v1fKXiH^y&=KeT3v;k^&VUNP~Og?FW1TWEGqZkG2+aNbi% zbLYtz^3+CGD#d}2ag3V&dc1X`OX2?j6?mILx{63Hv^`Sp&UncO%aS>YGURY@K^%Hl z^>P0I2HJR!;kJw7&)U-4;*W~1d_AXWx@Lu_K=DZ?EY#i4Z8h@4AN+kXEzZv^g8(Gl z6qFeLZa-|_3)*-$_NV=(d@*G^URr2gKGvaza;%0}{@pg#A^u^J&ph*ARhi(Z)5B1& z2-eA0?_v&#E}wO9StPP(mjW`b94Z{0hCZZ* zubaGIduJ54x<#C`MLn)Tj^Kt8HsQH)G6MkKy+|I__;v8VYefpePiJ36@u2n<_oL4t$SD7=gTXW3aSV0 zKp(<4=Z;DBtY}c?O61Ai>U(CF;B7wFThg^l`*`wFz`eiLAdw7=#e{Bgg%~^z2+lpb z))=MDr$l#QUni;k9Dl(ut#q#ue$V$a{6y3|N8&wSQP*#wv$g)ydpkIMw1!W!MgiE5 z^T^>y%N_fA8vP2?KjBPxb58JG_PcH4jU!Lj#gS6SP{9oW!>`+nBbcH!#agpRcFf9=h@{>6QvGo z-&2?IpMri7YTg#V&^%3Tr(bA3DbA4;H?4AusJjR9kS^kLxBwVFbC582uO-ueXK#Z( z8t@(6^}mOFSEuTlWx`uq+DGDRsbZT2B68tn1%77gNZLB&_2jh=6Y3rx_=WKM!u~7p z--o5}R-dZJKAkm|tK`XVCg&w2-SV=Akh#h0$YEZesCcVDv6^}8d|BXo-4{{1ovv+W zySEZZrhFyB2a^ulqE;t4!s9s=$qC8Z`u#UMWlbmPZ|l(Uj}-g}_+LJgZK-LRYT0SxjJ-&f;;Anxm)w%)bP64NFV#*NFTG@c4^UwQIO! ziuQY`X4pWLLrI;uVm5=GGtV5w@n_;zucLV9OlvF8huZgrCU?8@E%gg!xPnGjl330d zj58hyQIf=DbUg=${{U#uhxS*RR=aR4R?U)27?j>aA-0NG!F3Ii7%N8@9SO-j4R+J0 z+OsmNPgCVN{vP-v!M1)heOJJGLTla&@lD-@<*9dv&(qPtNh5EZ7BReR&5j6eF^cx{ z@H61Ju}hh}E2aagI<}p2x6TqHsu;%8kld0;$420eqv!7y_?u1FKWM)g9ZSR-bY2&@ z)O7t;dt1whW4D_4-dx_@D4wSkXK?0P)v?^!sfR=2)#` zmgQn0mA1t)F6`k!P&<62js2bxLDbGw&YwziJqL;(=6g=82|ic0=u`~tu6mbt2U-^1zR4JZB= zH-#>uwSp_nMEM$unO1j}3)3(Rk{~xBN=y&VFfe1U-sbp?;VZ8PYr^|f(L7CaC9IZA z!ukMLym214b7|qPiTXqqzGmAPq|tKqPk0HTJEq{1Yd^ z{{RBM4nyPJN8+M*kH;PyL2sei=$5BR(QK~f%rZ+dPV-#dx|Vqil1K*V!ScZeK1rr{ zIdtpq4R}h^UW-smhPbo5yPi1bYleyGlIH8O)30T@;eI3V@5cykTGs302ZD7JzIF$9hw|l; zGbTi0X%Wq92rigQ2gatPQ@Q z@e1?9R@!!}HU5tgSB6AMFxy5Tcz^}+*v42F z+2}eBs`E(=?w_w~nxBewBct40CERF&Xzim(qVkRecrFkW0B7dIa8G@u{3p}p)b%T9 z^;_k(uu~GXYr~#xr=_CC)yELD6;Gl9e|Z=FatO|@&U-vEPQ+8E4^Dq(r55>yq+A~w-Vk%cAU6S z`*6&EiEudU>@oKl@K=cR4-VU0#B``O#@6v5cer>XMkSRx7Tg49B=PTopOC&5Nv!y* z#U3-&H0yP?u(!LqV=rHradbQ)J6Yk!MvtJ>+U z;va_kwe7qs6GuI@%*Em-Zq_ZyB&K`xHPGDO{95pByXsyc()>B7crp|aUrVLJ;q=&9 zz!Qky<(e(qi5SX;Y-Dj==Y_m7E@QngX_MV1ri4p#<`L&HVC^t03hcuX*Md}ZuSU~6 zYiinFy=vBJb)?$ww7^4btrD&T+vFt~N!S6-8zUWS3|1bWD^St*H=&8C87D1&uOk)- z?KKN1FYPWZHBC2OiYEQh6Qjhjw$h-IstaYha&hTgFYMRjt#|f(`1bz*1^yyk>CNz? zTEZdKP2{&t;hDmXir!q4J-cze5GD(uCxG!sgf)MLHt~47NVU}C@I2q? z*H*C!C!0fekSwx8f-?~g&hg{rIV+qIUEhLi{2%d`NbwKB-x^u?lTz@$y0;G%+@+Kh z4I9IKX>}e9E{!yD!s@}7XuycW||ifl2TbC+;@`MLC|DZK}n+Bd_vSLbb)KB zTTd*j;%zPqb8Bs@e)B9QVG9|fEOtuL4a_!^^Y)$lG580>I&2z=)O=~D>N@T1j4vD$ z+rDSJ&lK)*h8PrKPWLUd;KN!cxRJLFumj?cEOe4dPud zQt*7%Q9MgFkP#(T7~I=I4mju2{sUe``$g)o+}-GwP)iltM;KEgKbXUH${mLvcN-V4 zt$JUKJ_KsNvm$Cp&3m{^H#JXtN4>neo>ne*ig8y> ztxkJT5L?>99u|1r6k~Bv-!4vnI`OXkEjbgT{RvBXv|31w;HT#WDzGv5{K7kZ!C zL^3gn;E772mg)Y{>DT;gr~RA06ltHdKkak-Hhd7(Z>?>#Ux{8X)2(%x+0038rpIyT zUrbmqz*rVm$RPgjiu&4EW|EJ4A3a&PyYxrtpZpXD!XFR(3Gh?Fx>v)m7x=rxx<;mS z_-<^q{SMzpz0mI!n7yQp0xWkRq)8xgxL}Mi2alJ21^8Jc)1lOzoMK-&fyfQDN`=Qf zWahs`{{Y~z+84sF2x%X)--Nyy_;cZ(hvd~SF7EH+@a_DUYD89+>I}kSXAb6HF!{J4 z+0+a#9|(Lbi&ck4OQ^4w%GM=Sjw5cfd7SccPDcZ`t$CbRhn60*f^oi|4ehs`{{YC} zh1#tLIVb-BZ(95LTl(JT)_NI^Es^=j3KislKmd+-$2hI?A~dQY^JVh$Cgxs9Uij^i z&*5Hg;raCmL&}!lBx)EEO9PMyQP6kw>J4`{1?6kW3@>jKF^#1=5;-lN-Twd@d_1{o z#dSRl=3LaDZ<+KT!`({W*jqI5+daIJM`%~-QEj4OYjz@$WGdKflaSnY z!2^ys2ESfj`sKE#<#PAubnL&br`z5EzH@VFZ8QhZc14&w6v-!{IL|$Jue$sd=V|lX ztnjUte7OVxNC962b?uJ-09yDT;YP2bj}J_<={A>3X(~%B%OaMRJ)kU2z#x3ReHfl| zUu$>+Uelqy(dUy!^O3EG?u@KfF|>`tj9_xZKb?Fga~@XLDfU@fyjA3Wy!-{eg5Ohy z>c;j5vbZ8muq$FLbyBC1k6!(2^_#<&qVr6)my_nGV1A>eeiM8o@qdTBzp=x2_9=wu z;^Af_O&DNG;E~P_IP|a9UxWIU?xSUJ*8U(a?rWA;X}_0Fp*?o&|gtwEDee zGwNp#ZAq)_e<2_6bT`8JygmCs{4e;WXKm$Jd`IxJT1`1sVAnPm7-hlkBe;bABONRF zyYXG5Xk<4-YTm~ojj@~%NFPCwgJ0=K{{RODd=T(IkAGslW5<6Iuk|}05d15W~e0#Z5*0qtI$ z@SjqX!@n5(AE?_tqJGrVSh#_pW z4KGviR-F`=R`*a%1hF`b6f!KJGPA)X4gu`IfTgh7I<@dwy*~`b?Hnq}xS!Jnd_Ff#gxLHEU zaWr%JNjAojRaAkJtU=w|HTxOj4~SZwrm&w3z7cWsre|1OT~UG@r%92rLAftNpX2F0Yf%(g;p)K2LnF&uV3+Ri=oo){5$q*n}e=> ztuCU|ZHhC`5sPe*%fZ^D7IBV@0ptT-7klv{{_j(pz`qQxZFOyCIQ+|-;KEC7E-Zy@ zq-V^NIlvj`uW??L@Xtxoye)5h-hcMLj4oIUi`m$D^TL0< z>qDY=h|~9AwLeh*0N|S*7x2f#ui7EBPmWr3&x!3deFpkn4@CP*4L3}_iQ$oDxCzgt}$D&GxZt9*-FNBr)C_aU2e;xsVgQ?i`3; zJ2(gMxAqIsZ(H`7O-D<<)wK)%01xsF+X=0uj)?>Sje``f7Ej)|m?HC|RyQ9g-!Q`p03pv3;m1`x@ z9+p{)l;GQadi;;7v@Z*38rOiITGl)n;gr|o)0*xme2C?i*zetj$T&R%1IMw(Ps2Y8 zelmPG@lKEMlE+=}PKe$n@qOIUTx!4B*6RbyYO~1Ne8><4EL5N&e(DU6Ywc?-YHx=c zFNgHqfBq6b7%sJ_m|ke@(&p;apDH6Qz=W$bDXvZa z=>x%@E7Yy6pu4pZwv!#)@-Vqa$RjIpfC0y-Bd?K$psUJIeyjTVo|ReqI&qhszW)ID zWA$F?{3B&7&Eg*g_zqERb3Ar&i%VtuOj%zpRAh~TU5|l~gpr;=u1Y@;d^6N^+bJOM zj)md*?wLx#E|nCx+K0>uDlWiO?Z!w9H(>Er{tnnfpg~3rq8937OvA;#5c^5(kyjjp&Z?sAVickmnp4 z&ZRlgZZcY+r#RJI$nw7od`?}NzROQ3ZnG9`zJ z7;Ov*zyQ`%7d|q$*Gy-@{{Rf_ydiYU<(o}1HI~&M<&Oj@j$At6<6@D!fsdQj{u}%? z@XoiT>YBCQlVz;xy4CAk$$bs8S#3(5-*@@vfDx=<~~WsK_G|yhwPrigKhJ zG322qI6THbALk?Dhrn+f{3iXOzAktNK=CKUpM%=v*1xG<_(t*Lhs3%rxf)+Ip&_)M zBG&g!Zzz+=hG^S!mIzdF_IxLtPY*fKskZmMlK%jQ`>uG|z5+EVs<7QPrMLZbvHQW| zt!v|?ad;cSUl8@3bH_dp@htjWNn@gE!VMnYN$mzMCAgJlxte&92g~O}7UD34-vj8I zpNH_JhKVk(_BGTZwU_Mz!9+4!$jppm{qqn;)z1F_m4H#4;}xIdRM5qTf^B>?@Mpwc z4Vy^St}pJBU6Ke(+N`bQ?2;>qL~M?+%Atacso`^7#;vSsmYyHb^$jdXsaR<@FJ{ur z5#hZtzn3HvJZ@e&8Pgv!1tVzcaa0>+(ru;lC4IXj6Eu!tcsvy3_Rs^X=o6#E4~! zVgzL@4oScSXXU}JpTgSrgYLXnEu6P6V`;6$4Xv8qh@mdgI{>7kuo4`ebB5r9#t(+- zb832Om9$6Co*D48UKY``iFC<-w{=T0BC@r+xkXEMX#`7>M(4vvk{p)#M?lp^^S}{m zCsehui6>7BG-6Ac;PX-L$=<{u;3*+Cg**^CV<+j)AAAw8cp|dgCuH3qMxQ@;T{&(;2UtyfxtO8_O??remD6%N@I&yAX4T)UpxO9p}=s z!{S{jNio)$lPcD%LlV_#c*xHt}TbZp9Okp=C3?o%N&Isony{pZ1n=3nQPgb_O z(%tOsRnk?Ob9%B6LZitU`F9eFu=9*CIK_I_gL$IOaXsgYq|`L&-eno~@3tte#^2HLf6`V%tCz+DGgS2DWi0Er-xR%wgsf=*< z*2j)&NfdVXmU(uvi2SWD&F1diyYfAV?_N*vqv8F(gEj9ECW{5$q2OI~W^FnPg-LXV zVj1pjvJU9C9mXsjjmnH=YtnCKw@8^SqJrQSaCQ%qA@PiM=bvh$@b>Lt@#TqGe6fi< z!?+*dImkIU7zaN}`EDbp8uVnJO;2YesVX%SU35PWzu>zb8StOQUyWY^QSCI{N5sDk z{3kDya^+-edFQ>FXwjH}4pKC5xc>l_r21FpuflsRXHT(@!+QEQp{?Ddk*p0Wu#(uv zAV7_PdY!zUHZC#W^w<6iIq<$ehhH9i8}TPjjV=5w@jFO|QNOf`R+d{^*kpToF5yB+ zP*GEIDZ$!E0PfGl-URUOk>ei}{5!RRX>DUVg}tnI_RPO6ywj|5Hs)nk{n>V4#4pQ} ziuQPK_n0a%z5f8=_B`z0-o(^=yS-0c(f$v9%nu&B2gL6a-d}hx#yTuk*3fFA)6bO> zB^Q!?q8DK_&`;*75fZxv3zc#h{#p3{0R9Pc@i$f1JV2kbCyI1GjJ_4{^_8x-cX_DI z{{RTP>B}VP7`EGd(VMA5NU`Bpc0p~R0M+|t@$>cw);<&4{2TH1wcyMD01$j7_=n;X zr1*Xd4Om!fdV?RfNpEfBhmtE*mBUUE0cH!sM$E#s{2TBO!d?^jU!iz+;~$7L-`Q`) z9w5{&EFjdiEgQs9>KcS-(@K`(RQqb!d4*t(FE3)I)_Fxa5 z#V?yaYwHRz{luKTEdK!VT_oP!kHCKif59IA0N|p(6?I*AN$@|wOHUJco5RA{@AQT7 zSBIg$w-;^_+jXRui!ynbETxnwRCdl0i2W!1hdA;slB-Q90Wqsodj&IUjxa$&(Z{{RJ;(>@;QpSK0S!+(XgUJ`@D9suyhnAel&w+80c z`%${Gj(MjCVuW+LC1eBTWFX|`zH|6}3tD)3#t9->Cm^-}+;-=VdJ4h)p!`Lkc>e(Q zg7No={5PYG1HzKuv>H`{-P~#kV{D8Ba0%{NrkEL={4=-a6w0 z(>}t!H$SMcIH@?TFVp(@pGA|=cw91C{P#V7Mvhdsh3+PJzQ@e#yIc6=~-5@NMk_ErN4*{F*> zoF75SuTzI6#X~_Gh`ejvI)spmf;E`&K7b2R!vS9=NY@&~=uwhkUac zZ`wh=A{Pt_h5PwDjN<~nOw+Yl2$7ut^B6Hi!t?lhSFULK_>MG=PnjI$4suhbbB^a9 zrGB-AqMfdOYhJ%peu8)-T-7WsG;4c%+ZPgrxQaD&kf;Qzf&j{#N1QiM1$~d;Umjmi zXD!^irL2j_}Wm zt}WIpmb^Yj^>Puo>(;!?<05#NbE|#FPFJ2RHAPBS`}F*e+s}l)JD$>QB3pZiF11@n za2Hh$>?~d=aJ3k^17+Kg}*nZ_9Uo5~P zE08edWQ~DhNjzlNFArAasNvsJyEUioDJ?DfpMjBI-S}6&^?i}>ImJqtNUD*GYU@<&{{Sgp=VjfWh*JLm!5aQ0$!Fo8_(Q%U_zU9I z=ZI2K_CE$%wyZ6gBiwf~xGVz)jJC!3qig~&N#L*eC0>c6>w10H$1fD?R{DI_&|F#A z+r<@}da|_2rZm0WVplxnau@_)2sp2`d@ZhOo)Y-gsakv)@s6?Zw#&u$657m~gUGfz zEu5ESW=nx>8Ee~Xbr=AZ-asW(GXeS=@#o=(ioPFex=y8_d{fdsJO0lE!8V2BEe;EP ziK@=4y3Q$XG4@qcZlxh-+wz4CkWG1*oE{Gy3Dm>Irxw#ue9Qb=``vHT=VQ{#@l|lO zp+^$=xsqy9k1xsDa^Bi#pL6pj%i*_$z7YH?x%hkFy+1;442@*o!9A-qADTRQG@dB%*NeP8pz0#eMAT$!>2Ixq#U!!>RGFh}5&>m=pS`q^ zoB>~>zZ1MOapOH7#-9^(zXacI;S#Ul}dF zgQa*6K#m8G?0Tl3_F-ilA?3Sgfzm`(TnO1@a>c>p2P9X;VrbOBzh_fd(c0JZKEBY! zKWk0eJ(IdyXuEzEIll;aN5kI?yfJ2U)YEjWH8j*RUX~?QSY@}r@iR795Kd36arc)V zEclst_IiAmUKa2z%d(`3n{Mxuampy`8lDDNWapgY71n9m6{MH?RhNo_ULx5}wpLro zg3ef+0^5J(tKsv#fdKRaBOS($3*bNWjFM=V^Ep-0?26*2`@rS{gEqvWS>=x}U{2SaY&B2hYkzBhu zd2r(;gPuzq_&J>2Sw^F0tAPFAG!oGJPr-gM)HUq}PY*oTcDFY-6GCUTksHcc1gjj% zK4(A-Y~2YVUl=?O%72P+YKife;*Y{z4^+C6^TiiB9ItVFBZyuv_G*Tk&5$d3hsu>O zz~zW11QB0zH^%RTmb(7{hVN~BMX$karRlKg_aYqwQu2;hPb;bHg4V}fEuA{2hG;b3!eAeU?C+0f{Wa?vW+yP#{ zVezxVwiY+qGwA;S6m=*y2}Q%pABQd=5&##5G9SP4?Ie+tUS+Cke*!!~uj?1yBKQ~M zjau_pLSEuc63*UVQ6T>SN<)yul0eT+agcGXTiQv=dYVzDp%oaPYkWBISB0NPk`&eL z?jgiB_fbZC^K9bZZ<+!RGzP)i*xS<>BD`PX2a7xzu0dgC1+uH^R~HuTH<88MVg{9N z5Xhn|WdZrfIb3Zg73RJb@t&FB30CvM9|nFR+*;}z#oT^HhwoSr%JFZzIBbmLa^N0v z4QWC9M))_x_xgv1H1CC;JBM0yGQEsvNiS|ot2E6T-MRA4<{8`0;5yb4lTyCO+MIb} z*Xnvl!RfU7Zxw5D*+X-wcv|OAhFd$8x=0nul^B@Pf61CJG&ISk5aN{*kg`mh559eFC&SgWPG?FIb2ukb-mT( zT6U%3{TtyvyRg>9$ZHIv<1*m!p8>fx;2#^PI{s8dyho^i^)hxB! ztqNaL2V7q)ssrs zR`Ja;EXF)eTn(EMN2!aodI#-g;rTRw*~7(>cup%wQ&%>2mn(EqNq)Tw7j>wW}IImF4c-e&&;{%34GylJN2z$Qliz%Mr$ZSTBLavtDvrp{h4#8 z_{YUFonl9dD>oLc6Bc8*XUh%zTe|Q_70GJf2Xwoe%|gdnzn11Gq4L!yNPs9dkU+n60`wKVkOrT&d&E<@KUZDBsjrHNI!uD42M*btS zFy6I`<~5WS+>ES7FylOAf(JFEt4->R6$o0)@~M0arlyHwX>G1+nw7Pp8$?MGJKf4j zVH1gRFrl|8##9>fFNwN`#2*Fd_8PUNy`PD^BWW>$3R*3IB*?&p+Cs3$ z1mg$Wz8Uxf#CKO3-OjarcN}*uCB$}d5F-+Ac=o&U+qMPC9Y;Lm0qOoF@P&oHgBwn} zwvnu^EDxLu2@#)paUeZUT-PkIQ;X!2*ZTgtn>;jH=U<8WrKb3kL;D@Zx1?R2Q%SqJ zVR3Wjs(p>g!$$FP%F5U!SOfE9`}IE$FHFA>bVU%h%ebbUWeK?$bmhOi+wupo^sg8A zi}2P#t7?8M@J6c_g1mEiFcaNJ61*CrkB=(u9t5DOmEDl)IyG<~20kkIqWj`~kAXGq zChNj}In!P(_9x4l8~99seWub}gER+z)AEN*eAqp3&aoH6&P#h2mD0UvFEi#p_%0Tq zX{h``_*tgK_L$_;ydLkowgR-5_XBt0xNoL8ugXt=dd8h6j(iWH$*M=A%dTpcf+xH& z#cKzdDxsCyR*e;NxtlJE*cl_5{TcrNg17kV!hSsX<7;7{U)$VkehSc{)3s%SbeSNz zS>=#MLY(DRMGU8qax3zyMY7&6=e$VDq+@0kNy!JyT({lh2PF5eQ-)_M7&=mGZFTiH zGf1kKncK|%m^^jy%fo*f{8R9=;V;9TW57Nm_{y5ihl-@OU0zLgtys=d9g*7DcPcaXkU=O2w9@K0}n9u)W?seDHGGvOZ@e{cLm@IIRN);iya zjg^i4*nK(fA+yuqUoz!!1dQQ9-WY`=Ij?&78T&5$8u)eaXW>2H#{U2q{CN1At@!j< z+iD&PF6pOfwxZ2^y%yHq{99c|6i*~FHX2j4STNa|{)(qcq|{|Ty{>*iJVaBJV;6Pa z+Wh-^YJG$8*Y<7y0D^vMUjckOu3G#q@IBXqd{w7g>3$pWPl0qhTf3b;@9a{n+T{1p zBu*fVxOSP{l$b$o!oQX;i#`+ht@|DP3h`%-CHep*Bm|#;b{GE!@Fc|6W*YwSHwz`(8TYR@yaHD#ZPI$-}IX$c6DAD%QP2R`Q z*TkwZNi}ob{{XceNRbE%a5BHk)8FtlpL2gHLHph8%eaQe19wkzUS`+w;xaB~o%W0` z?;9X=Cx8d9wRAdkNq)de`aQL*AcvA^Vn<_-;YV=9*V$$GidY1jiSqf3Dq`QncJ`W@ zTM$*vynqvsdbT=s_pY}{@j3>NHzk>vWD~dM&)3?##9moREU|C3cw)xVUegEX$m1+~ z4E<|z$61CHNkF$k#E?Ar2t1xJ0LNPUIytPV#Wf$t{sHn;FiFY#*7<+IK7+9Fnm=&b6%8TrNKkx*ys%}b7SNVUzKJV}k#xbN^+P9p) zJb);ZC0vZ0bmP5#oqQerrUaKZ-h7tw%!35Hr)cA`_+32h8(CX_w!C~{?WB!dJ4GN=1wqJURsfuX;a>><0BOr36_u6F zzhxUp5<(h%5UC-RhSg_6IUIs|4tV0e)6~3g;lC1Di>*FwH(7%5caQWPB1Vk&T-x8r zM&4*o89L;exXZ(Gum_6<_wMY@*Sc~%*qQ7m$@ z9imXO0f0X8s|=7w9Q`Z%2maH3KmDA%ZQ_C8Zxw2~4wdmwMVrGqu#F>fw>#=QZccON^>&UOrGTh1->S}OpORtgh7>X{8 zxg}Z0@o_Ya*itGDnE8>m$3C;C1}B_)+8U1^ipR zYrhoT>3TGK^Z?uaz&Ca2DG?!Ao41;8{^9)K!FraxE_9@5SJ?rcriysNR zX+MX&N8=rP#@ZgGb)(p=^@D2Hi8cJ|ZD8^UjE$CAOn*3y&g`P@$Sa?i^vgt=_Qv;7 zhV%PUD_A$&0<9MJGN^W7K4H035;(y_?_S0-v~d!1j9PjvTj}cEjs+P~QG<6{y^{Qr zxyE?0rnXBBNSi?vmytjiPnhSZ z?Outc{AbZLWgZ>SEoAYJi5QrgI9oA7K`a^M>(i5t;PNZviv;m!i}5dswV3ohF@X5zK(O$y zg?u&OonK9Z`z2vfJg`kFJDXGHUQ-pi zD;scm8%W?|BP(A|_&efufo0(>8$`GG#s2^Z%$@_kYk4G++xtN;BEb&QfS@d9c+@!D zNFablK^zsR#}Ne;q0tG-oc*KJ{Ycilb>a(aeFFMF4!jGaI%&e%)nm0~m@5+O$}#1X zbVBWeoGL#RW)LjVzyN3ST4y^)K&3`Y5>#$Xd`s~!M8EN7k9mFJpBm`+ zOD!pwuHQc1D}}a`b3R!4gzf+k11L~dzID-dzhl@{dLKvlAL3O0I`CGPuW3m7W#d~3 zra-9WZVS3jjHCp1ZLf(Hm~K4%;ww+!wwHh6HTa!*@e(PlwTsO{^5ajqvz>~=4njo8 z#O{T>zbHIvxH}6H#{_wYg>?%^^nneQpQvc#Sh2OgxIm2`+OGV_73Gda1Ivw(WF#Cn z?!g%Of%=j^3}`yP!~Xyk=yN5{_IztA$>rh~LSzSW%bWtsGlR|uyaxB~KH6TG~TH?U<)7RE;IeSb23GHO=dAN{L-BY6JN zFKqQK8$z>@ZEaaqu475n%WtZOh|t2ACk z#8cl%b!}u6fb7`;giK_^Hs+ID zdodFU{>9X7%%Ye!^ z38IcCh>}VG?mCdopM$?=kJ!t{zY4$M71N^lwXI$FXIGjXXITydue00wvA;n z?TBtvWF>ive&dYS=qJE`5$YZo_=n;cyg>F*Z-L?o7+1DP1i>u)mjE0x?>!DqdCBH6 z@wHiUyZ-=QW)vmQ1!b>G^w{gaYL>OPU)lcv;O3l=Uc>P##eQ}6o>Y0W+3D~}c_e$7 zk{8cN7?alk4h}1?)U>OQ3`?kZt6bCWj8i;T@lR|bSm1=I5lYD;saB169dKBZF`Dsz z+BV!=e$(HwUym)dn+uIT!0Q%%T8}m%y3%K9gN%mCt_bP~uYUNYFNrn(01s&JO>L%| zeND0D`J7B2h}h+D%YvYcgUPQ48zmP6owPf!Dp8H?eO=B=U5`Myp6^rmNi>`G0%Lp$#Gi3#o_WR?O8;RNAEb+?{?r;d=W>y=$QGo$jlk_;U8z z`7Hde3rjRmEwmVlJ8iL*fwPjNV`w~sxRHW0L>p1MI(b$1EqK>i)%*=ITx%X1yVQIo ztJ})0cV`dUzQaG3LPv8522&pZE2wM*B#~Y-rqAJhTUyk0A*v$k_ZoCdr!zq!M>&aE z_#_oYCS~CBf-C6_Q(M!uO(R&!xYX1Odk4w{a zVq}KZzG57c86%T{gd}oCA2qY_LRsPPR=snl+ZmqMPjepCKfFF!E%Ns3jC)r-quD`k z;!8JyrZ6xdDQbp6r*Z>VokUa9dl;fGFWts_b1)I6x;i4$+g{7CypOdxar z00A}4UrDJop|n4T%hde5*Yv2QxV&(JLS%9Q=PXp^zdi+g@uTSa7Ng@W3d6=$?7D5$ z-;o{IeuFys~Q-|g=l=-O9>?H^LrZey~wW_b&QZb$_79kX8%clEV1wvtF)d=E(|V&iE7-K{q5RYQUDJG5qj>)S#@eOs zqo{b7N!9g9Mb@V_WxhZsl(XE%7c#RGmXTL>a!Ti#*Z66oYMv(5G^@BY%cUBCf;ChQ z$t;r)tsc-$Sy%-C1Iu9JB-gKeV%9ADRq;2(x}*ygou7y=?$pB+YO98Skb%%0pyc;C zHOl-HzVVH&vEa>9!`E8ApMRxlw)SlHaYuV1w08g{=#E0DWg9X+UCSbgML2iek$ei#)@{$u<~_~G$);!{beY4)05!aV|eb%CRb=I2q> zH8cx>CH|v!@?Bj>dXUlx*PZV^bvoVp_{HysL zt9U=b+FiWY5_p8(Y6N}MRO^pc4&RRI>TkS11k#qj*0n7{Mdg{CoaE<|nw@+(p`^P$ z!uTL^{d$q#+P*@rVg037+D5ArIk+J>cY zk7r;mQTP1EVFhvDC-SeP?YtY|_5(57KsfuvoM30Cucz0wXs5%S8)<02<*DbMa7U(j z{{ZXPvxbsx(TCu9Sc&poxA>nhrk$$d-AYc+Aj$d2=y~tK&OWubX`*WK1&zkXTocGW z@t*$tSJJlr0JldgAIr&M@{H%H#(4h#3=CJGXhal2_SoQ=a0s{c+$G& zx$~2+DNmM7G<;8Oqw0w4hBBqp5O-vQj&YCk(zT_w)UEB7QnEfWKv>IQW2qowzT2_* zPc9vXmvDz}7x!!c{KsCum0!E~MAOS07Zy_mP;#oJLmr&^5Am$(QsrCg>Te8Ml+tq0 z_*U0ZxDcT>_das+I(ui3Po-;rZe3hvR{sD)h>jVbZcj`f$o`e~i+lvSk~B8@RNxR< zc0_t}&T)_b099dL{0fzzMH)UMqO__dO^DbT+mbqwoDagfD<9eWU%pt_+ucXz9tW3>agaKC3iIk^Ue-F< zqtaQtXv=n)4ww5u_~Pv>^xN5PJct`~OtWm19%&3{{Y$7^S$-8{j0*daEQ$|!Elm9WKoTUu;(l>&PPmFg8suF@KNs*{>Pp;(6t{K z=sq9#!SK&mv!2%C+V;-c?)yv=L}HRlS!Et#OCJPATq6QPl-^ptit)e2o3D-DH2ACW zhr##OdbFN2zI{qN(`6Q%CW*=@Qv_v%cvUO3GZ0sjbDHLIGwgqmEZpBa#6tmHa^rFb_4R9$x9qRcoQ?-VyNjk1nHcaU53V z;r={llr5I}kUH0`#|D$HT_v8aWS3e_lpvAGMk~yj4nY{eJ9F#}VE9MFi)&|k*Lt13 zqkQ`d1}v=DE0O#|{5|XFpA7g$3*9D77BzWezO@TI${?CQF|aTJ{{VoHF^|T%F&LG# z?_;+QhLpV-&RBS3!#0WH9V<$CZsv~Wo?Op+piVujK`QbOU*@>nocJmm9<7lm~lKUeVHoquySsb{O% z+(8&{uuHl(NtPycKQS?x7Z~@=ebxIlYyJ&7=ZQQWs?VnQb@jVjhl186-p>J#h>elO7@$IZyG|`_9T|~Fi*;~qf(z49!4Z@@B8EDov z3ZRuTNDm`zTYm(2FT`FFpTj!$_I0<29`)I7PMml`B-(Qp4o6+XA9!+daYs(k{BvdD zi;Zi-g2P$V6dCQb+q+xcHu4LYWQ-#_#3YJ!7?h^`03|r&ZLgcfV&azio~0Z+ZCz>p zN6>!`JYC^kR`1513hHI*;z|@{J*AQe<7h!m!z#9rzE%IwII#_(S4Or+nc&#MHE-8aYtl zFcQeP+TiiD3=@Deiu2t|>&>~_6>qcsedm&Rd&5$ENYQ*fs_HjZz8==>1Q)k97w|#TzyOZ!2fb9~o9B<^hd)kL`V@P2kN#_FB2tb(5-U{uA+R*wm+BvaPP*;>h_x z1_CzNNM3~x8RONVO*2E+o5ogO0z7F3rE4C@@2nuxAqs|Mc@W6=XiOQwWg(Z4NK&NM zNA0KJKNbGgeiQg`o(%BY_&db@64YM$f9)&VMMDgcs4iaGNkQZ&1QXY%HEy&eIHxT? zBI(m{QquZ8x}GVeTU==$5(wZ8kyd+S?^$=*vjEu;jXjTI2c zi~)7uk6*dIJos0`I+yI<`(W63uJ2dypNCVx+T53S)}k`bZLFr1t|8yJG4o;~#>wPXOAV27ETsbianTQ(jI}U%7a-d#j0qK_Q6RAQG$WMPuA>MoFjs z)7C$=o&Nyr+xt-I-wC`4qAY{@7W{@ z*cQ>p9Ge7tdnyGRiSJ2g@WV>qe{rv;h%~KKK^*>MTJE7ZGd;%7C9=cI>lZod%yGv! zp8(s$9vt{%r(AevLeX^l>qw%PRng|1VV1>!#P;XpZLB~Ck~b+`rx`W5H^kXBTTMs8 zI**5^i^P{L_KDz~OE`ngVgN6*E46UA?xg3_&BjI4sVVcFRAU-xLMv^z=yAUh^v{LX z`Yo1=GwS~UWB7YC(%Ie(V)D}B!v0cx)>$_K5sZ(VwoXaLII0?i9vYs6d`8|O(}#gHzYl9sYWl=dSy-*=t-O&g z!hmx4cLN1maXb!9W@NdGPw^IyaTd)g!SC$w#f7wSIF?mr$`_4xu>cbI zJe*fGII4{*j{g88ZyghG{MqbFp=nx#Hj6R~tj}+;B$~i)DLrsoS-ducX|=su<#( zWfGy@BP>{MLJ#ntm!=yv(Re@NHJz`D^j$fvwA&f=$Ue;TOPJC8$X$!W<~9_mIBXWe zhR7A9)1>VkSEu{}qXkoa{a^W+SJu!=;>}mWej4$wj*=e_={EYDlAG04m(RHJjA0C9 zVN_*~PtXo)Ep#m-Q~v;pTIPu5 zE~WXTi9)O`2^sSl;?C9E^I-^X2s+x~k_e=X2Qf3qNWy?eCmG}p=Cu4_Z}E4*&!$N>mcJG>-7%yhXs+SYuaZA3uHB(L zyN6xueq$B~a!qG`&#!IbTRk_%+J%?)b>^`@*@e7`!oRS7gJu?kbL=92fhXXZ0C+}d-N6O;_0fAjC-T16+Mhxzu=#azBKr&@aB7m&~+VI zG(QksEP&}6$xADYxhKjB=WM-yfVbVR%0G{qt)77e{r#P_yc*TiO#zWWQRNeWgb%zi z&>jXW`WF4Izh~cu{xk4h)z^r;F{5kttrU?Dv_g{}N#}RoTw^_W+nWA9{{U&f4EU$v z=ZbtoYw*LynuF*%Z2Jb8sxj0o<6N*S5&r;L3XXtoJx4Veb_HT;a<{Jk0N3=4u~^S# z9&TUP`utCr^*a^5*Oo~HqioVRV8`WSxPjN^$2jf9JHTErZ-(CmE90fP>o}!*o<-vS03YjL6+R?*r{c$nG*)jD_`dpk)e8G<>_m=PVTEL3*ib;+ z*(Vt!oSMODZ*@1#)z!qaG24$Y$Q0wa09U9*@SDY2BnWklD%V01jQ;?pURtcoJD)Gi zGLOY;YF~!B?Y@w8S@nAhJuG2Wi_8Jwoa0~w3=xsWNF7adP|L9xx~~ycHs4lxwQ+ES zRVsQ#E}!t{jaq4s4#`(d)LKT!z=@+I9xw|6KOaidk4>DqEykYdk&!IZsr<>o{{Zz? zKDDms8Yj;-qjz@EG5Mo`+zcM0EHZs7&ToD+cwbD7gnCzquO#eNIHSSNGxI+q+v{Ib zgQTgxSxfUiYY`_WbsxQ>>HQx>vPFg)iF_0QW|J)}v&wP@2WiKqIQ6cs*GJnTT*n2J zF$V+%jhGCMqag9eL+f7@pW1^?u|<1t2WqplBn96rQ5WYqBm?St@lPMN_OYf&4WGmN zNn{E@`$nWN22VRyAO{4D{A=Q97P;OMCHpHdauzRqTU~k?ZRAa)8m0u zsdu!6`OZsx=xf|`JD(OMvR!GKjIp-=04r=7UA?wA>A6&T*W=uu@K7I!(1@ebJ{WvH zwGa)_w6fk#K|RS?5PBY+kF9D&{{RIj`1JspEqoR5wld_5{hs}}I2hvOaCXwGkHO1U zpWKXop^TQ0SOD4cX{(_32)Qd@U}l%Xn-}+(>rBW|+wT0N(0B>QAkI zIr_i+6?^uww7ZfxFa87Db6?bm;5pPO=)^>+3h2>2UN39A<{3i-^&_C<&Qf6 z3PxC-oqc(%WAM%(8cjz|M~^BcmwUTsXaSpYV3Gz#Pd>H$(s)Py3fuc;>v|p4)!&Ic z1ss;rm`2|Y+mckO;B%i-`BvxrJ%9GqiC*4c7kF38kSe;L4?sZxaqs$~UNE(RWI8HP*V5BehQSi3GI40f)nc!e8#zmd|ga+a>5{{YF4 z=;y=V5B0z7nC)#e+nZS!5A|Lzp2|mH0V8s$ZkZi%#xdThc$>o7{k4pd$)R|f-FYQX zisiiwxygJp<2}z6_(`IF!BjtMQ)wyL z?Z*ITHS%7!`)zpt0AIMYxYYEI58X(28Cure<0qVF21Xh0jx+C6>_2Ev8fqxe%cbeR zWN>z{LK|r#E1rK*$2}|0cn3ymbkqC-Q%))~YX1N;;Exo?rN^q@{ew$rAcfz{jz_~H zo~frsbvGnASiUNZ_v5anmD`ap-Q@ zctsNI!xgGTcSPYZ2`jm_?$0DCC)|3R)n5r}n)UC)?+4Fy;oGF~?WLk=mq!M8t__;7 zj^$Won{;Zel5$v)jycr0@VAO}#?<4#zSJPoA&F#pVqs!2DLL9ONWoK_oL~+)ubYi$ zPWsEKLJHaHrPntHB zWJ2L{oRiLZJTa?xy(G1s8A~O~RzHnHA0pJkCWQ=yMI=HAd+IKxFSUQlh zYR83YJ}CIHtvg+6dX&p$9K&t3m9cXWanmGZ1{`Fs83Q%-C+xwa>2dhCRlD&XnW(0v z{hQ>Zk`r+?tgU$%ET|65l@1Pi0(w<>d@pv7X%r8>Ns9!QP2a;Jk`6dHa8j&BI{VkR zd^Grj@TX4I{6jU*iab4G;vHrOu!SJhqm`r%0=fYo1zFjfIOuwe*Ns|&l&;_6c2az) zD|YB(e#HL(8g<_v_&>zcUs}O`e_?$rD;ZF)5t3AB7v%+{9$c&$xGUv!Am+WV_HTyr zeKT0U_=l;*sQBYhyE>iij8PW1@cqn^0MvJHuMMP+r3z4yI7{`%%@y zDPTtRd}Q;RTK@pWy($j}_=`!KPxzC21n4dF`U;{c5L>qiEsU-@Ylp%8@;ib=Fe5PiZ+EV8Yi-LjHr$ah06>uQW>}n&jonX z_($U1O5WGPJ{L`Xe-dd&L%25jjn*Z)gutN^JBP_;lLUt?f>;u8NUxcEE%6sp(L57j z;fXvG;;|i^rLN+87%c6Y405pfw^GX=$`s(74Ww~_jH~|u7V6j9hNpj}{44S1mlmOL zlUvPceGCO9aK>PTPw`-qGI9fFpvGTSI@wbRC zuj5$LQb^!}Z9vH!i?O`8kN|SPuG|9H$2HO2>5*Pdq@Vatd_Y>_-Tav@t*rdpRfYqu@;iNI1tU0=f5hNIza160<$Vd7m^ zN4J6;NpYuXMeW^+G-$v;xpizQaEaQ;ZrCxT{Dkvf?H0X|ld zk0fLcm>`Zbe+*jD{zTHYx7z*Ap58gFFKz5Ed~|FNX`y8 z^*A%#IhGUsrQ!>DQrY~bj%^ksV!?up0fqzT4W2ML&lQJpn@fM4vYZ{KNt!didhJ z$O+q(KPM}koEoESn#QlAXu2k$@lo|JxA_emSGpFTYdk(sV;dJpkSSj*^y3GS&0f-2 zEu<1%#+pQOnd34}#veHqiY5pc$I2LEjD2ga(SP9`x7V+1A5*rw)1tDtyNoPQ%&#$) zAW?8jvwY-(*8t=Uiq>lOF{cFY+sNoX5LxRM7gjn*_>pz1L1}uE`4U)L#@=fLbrl%KF;~BT=^vKgw-@al;SYtM2mDKU<9lxzd@;V&5%jxQ zHCr{29^tcxS*_YwRAILUcU91IK@|H^vJO7eOAZ)_xwIWxBG#hQtu-6(IVX?GxVqEgnpc`N zVipM$mMGZ-@sJJ(CcJ~fmR=&#uHtz7Rjgdv+c%cUe$i^8IJb?!w5#T-L zA0GI}#Ma&gyNt!6oib~MDR_%2F>QdVh}(hpNXa}9eT{u2YgeJ~Mp~YBE|nV4lI1*~ zOVj)dV|tNA;kop^Sz$9Z<<-E}Ldplr5ROftF(d$6aQp$RPZs(`u(TlO=HPmDeP0E+x7 zr2%OcX1Vhr^d(Xgv~qg-6VsadruV|J_;+8>j9ORxC9y)jf17)y+zVM;A!d!)vB#E$ z`Vo>i#d-#XulQfae-QrHb8BmP4b(8=-$_XAQr+VXv^*pPnZ9RS5thK`pst#nM_!%1 z#Mb(!_-pzdG`M<=M&jx|YuQ>#-#sn(pPq;IL9v}K%6Ol{cX1>;ivu^v2^ev`7o7GP zJ$S`%`iJale|Z=7_lFOb~i?4!Hcx;8(6hM#~=_g+b{1dmt6ZmsW@lL1X4G+TdMLf#|!|GPeZ*r;=e%S<>47ulw4ZzpB z>l*jRe;as#bX%ViYFhrd6kA7d_tF?+9#WAUK5p#eC*{By70JcnKNV>@_3}%9Hm#}J zSWeC6YBI+Kj%4zrW*8vuU>hnv@3n6n!x4(6<5G+~(!cqg*nAEb3sN;9QhMK4Yd^sG zXW|#^o8jMw-X6Wy{9*Ag!JlE%{INURUdgK5A2EWfc~;?_hDhWN0jf~`!#*7GK9Qwr z(R^F@duL(d7-F8)jpMA$k|yQMa$%LQM+`CuAos7iG|vWUQ0iJ%mE!Yf<6Fb1*v+Wv z@XrDp>sb#Gt=7RHV8xY}X+JuEK3vz6*}c8qk)z(<>snuhV$@}g5)CnhPd%B;tXdeE^oD0{xmTYdEW?{j%ITK@CC+iUaE$m8@M*b>LWwyC2><9`j@2h8s@ z+KeUUZXzMh}{gAXlP%{_g5Hc`33T}bmTir`km&6IG>T}!M$l>iJkvAY>cw@VT zCm3F*uN^DU!)Dc5P?Z#)&Hks8S1YACb2-cJU*vImFYHO+yW8`n!SNGFyt~+5K4ne# z&j6Gloc0;6-9KWV23k+3*F)k|ofvGl2ky$NmAd=e-?zKa;V=IHXYYnS6ScIw zu)gtjr!J*8mX?;biE%pxAT)v@CjS60JZ{0{A4=1Y!XLBshLn=FmEv2=+kg+1cQR>G zD<_y|M~(Lru~0wCNMR!5xF0(=iK4c+yVPQvQfME{NTP(Qyg`877syJtQgXwP-S9bR{ozoK zb`{49ob$?)$gbbQ{yOm1mUSIBSJ3>eQtBZCJ`AwP?8E0w1AySK%3A|xTIH#qVkLJO zw{FQ6uvm3wpqH)R;&?a2Z`udOz8vwL{{Vq>4-I%`!%?>}G+Ks@YX+fhc^sHzbdpau z2WtRVaAhYAU$B3(w}rId7W^jgFN?fpV7b;jM%Nbe$NQ40G;&Rd+soxh2^rw>91=Oj zem(ql_8ZV2w}I=JU`&P;@5jmEpBP19 z+kb07qqkbscD{{VNB#d%+gSHxEK*Ozztvt8Q3Xt%peY(ruo%^y|B&rbgUcZ}AR zm%}Z0QPvV$TgzK(Wt_~#ZNO!P*CBUg06-*U=cRnzqdVSh{vYOjtQ(Hev3o%HjiK44 zlV}zmFV^N+0>vbDUR-h}@Io_6M;k*gJ#p_{i+on`8NPyTC&HR$kkfBv`+e%N{QH;Z zNDE|l>(mO&)BYC;RbbU6)aFYWh17RXI3F^xVHqQaLI^yJ{MG4t7s8JQ&1nR)=!p!A zG_$J4?wQP?@JKk@xNyBbU^;PC8kAzJ{{XMZx{kF?SM~XxSpNVK{vhib#kP@g;#&bMv9dVEag6itKK_9zh`p1I`Lu-WO8XjT#( z8E1Vy;Nm$K0LH_B4tdDq9^86Yp+c=Mu@(0t&aY0QTFIsAeua2m{{Y~7>k;UF1@L{8 z(3slR7Fi^?OL@RKP|kmQwZ_sr0DY_JPlvi~v@-a19Y?}?MXl@wOsyDgmvOSV^H68x zY;0l=e1I$D{{Vr08SzE3{5V}#K)i}gRb$m`t`wq)6_uK8>;d1rxCKd43l;=0=j)v& z>sQcj^&LM)v=C}@nQkoM)qdbk33z3V*pLAkL$@Gtis!DQ8^xGWPNkey{{YtKrrXb< z%W-?7$)iL4p>=s6u((7rBcw=$)SO{?Y;(ah_up%#*qtv*vzt}6^2^A}8!1O6ReF5G zmIMKxlo6a)ndm@vs4U4cR^; z{54%q&wm^EKEq7Y?l()FR^BB$pLlJd&&Z?$$3Vuo;_s!-u157p?#8&fJ=VMStuDyv z;Uw83s=DxsNmUrzkC@H6)G4r zg(XNiIq#Ay8q{K&vg%a|?n*q;JwxH2$Bz?h-Wt=OzwqaROge<|U%I87s06ZJDRMu1 zz+wR-xfwjySMdAecZ;0aITp>fWR~rgLiuu8j=*!C2{_{b6jv@L zPn{#WFiGq->&BXwfIc_)?#D&5((Ww0WpuiQn?f8&pg)z-YSC2SJkfVyd~i%WY?h!bQTGe zOSM}JaVOpx;Pn(yU6d80a%7aGujYE@neh`-)x0uv+x;U!xQk4+QxmjvGmo7IB*=Gk z@9Jvir*EWqkHRx(y03?{E1PQ%vzQ#fT1ZHYC@AbnQ-h3*8YrwAYAu&2u5FK^KV|;_ z8*67$@Xv*=+TT;t(p#GV_aUa2%85fq^AmzWBxHlZuf4T@D(ha0NtWItX+5&YkN7Ch#vdJN9uoMA;B7loTVD@T>wc6`U3q8l zUp@-qfSkBK0^3ep=sK#yQjy07uphkF>rD61>@!8j;6h? zZwTn)OigVT<}3K&*vzsJBn@oR65jdlF~?dcuT4QYJFbZNN_x|jp7%Ka0EvGPJY6rr zzX9CKtj{61y}Yw1Rd$Hsg3Z@}I{fUUoaI=G^S_2MMtpH$WXK*JdT8RCJ&)cJt~d?| z;E;at2gcw`JMRcl67IP zPthKyYkKW><6RP1qMpY`ywXLwv0< zM~masd_kmmkHnrK(d=cqvz|EK6lJ!!4;f@^I=h{$bAf?h75Iz7Iv0cNygPfOTDiK` zX4PbsrIDKsE@X*WKp-g#&)1qLturbxgd0&y++94-vYN81Ueay*%=I4zTPBxo!p3Xs zZ#LutQs!hhDU*_{HynY>Wc$-EFD^9QQ^z-YbFGe{aca@E%xbG4Unxi##yBoWI+BjsEazsk$GcXDOQ<4WzdI~76hPmY{>UsEyxcfUZ!7Ozv@jk9r>QpA# zi6G#XZ!<0Icn6GhuAjiZD)AD;#(cpvQaUS>oE{H39YCUr^rZx_^&PeU0D^R7&Ad$| z=KlaBezEGl6VSCi8saJJqDa~@2t`ryvU0qGz#I=s%Frw#f;}Q#^2;dGVT7}gONg13 zSe$ZupHoE@^E3Dtsy_b!53HY~{SJ4;o-owBKF@JsZDIEWdmUV+(7TjiFz89gpg68# zZ;XB}w@9RgG=z4E0X|C}3uhd+y%bkg8k}VHLaiqqk3R7q#@~n$ylD=lZ*HZEjgbMg z0K@^G$MZF>;r{>|`2PUK8hyajynW*3hSl0MiYvW{0uUFB9OKrCE8O-`QMyZW!>wLv zuX+9F>}SC*hgvIqEbyh~t9`F{it^nfXyw1VY0P3l8x&E100u$_82|yA`ku?-Kg0Mx z(~pL<`-`Y;gvj!)@}$U#fPFTTMHS#?KJ2A##@IQz!Z&w3yWnd@sr*g&>EXRr$4b<6 zp9O2y7P?|RoCs~9mKA9u+=!)tB=Xrl_3gUdt&WG{ol`}z({$0N>2lloYG6T?mc~|P zZJ=asiCA|8x32{i=U1sNdTRdw)sE_P;{B$FoN5}~&xZV8;q4a2-%q%d9i|WH|}-2y%WYZ{ur>1*l9Wqww&m)Cz{tDb>-M`D={U9$had1 z-}hU(x(=loemx^h<+uH<4&RBcDRCV7-**N%5=!{l#m>A@}4_msjq49 vPP?hzU-;E;E@Zlj!rlqvjoFF@BLrhT4s(xcD6dKDLBH#BhI&+AFVO$lR~BuK literal 0 HcmV?d00001 diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst new file mode 100644 index 000000000..f39ec1492 --- /dev/null +++ b/runestone/hparsons/test/_sources/index.rst @@ -0,0 +1,36 @@ +=============================== +Testing: Short Answer Questions +=============================== + +.. Here is were you specify the content and order of your new book. + +.. Each section heading (e.g. "SECTION 1: A Random Section") will be + a heading in the table of contents. Source files that should be + generated and included in that section should be placed on individual + lines, with one line separating the first source filename and the + :maxdepth: line. + +.. Sources can also be included from subfolders of this directory. + (e.g. "DataStructures/queues.rst"). + + +Short Answer +------------ + +.. shortanswer:: test_short_answer_1 + + What are the colors in the rainbow? + + +.. shortanswer:: test_short_answer_2 + :optional: + :mathjax: + + What are the colors in the rainbow? + What is meaning of :math:`\pi r^2` + How about an image? + + .. image:: Figures/LutherBellPic.jpg + :width: 200 + + This is the famous Luther Bell! diff --git a/runestone/hparsons/test/conf.py b/runestone/hparsons/test/conf.py new file mode 100644 index 000000000..6b0cf9737 --- /dev/null +++ b/runestone/hparsons/test/conf.py @@ -0,0 +1,229 @@ +# -*- coding: utf-8 -*- +# +# Problem Solving with Algorithms and Data Structures documentation build configuration file, created by +# sphinx-quickstart on Thu Oct 27 08:17:45 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('../modules')) + +from runestone import runestone_static_dirs, runestone_extensions, setup +import pkg_resources + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ["sphinx.ext.mathjax"] + runestone_extensions() + +# ,'runestone.video','runestone.reveal','runestone.poll','runestone.tabbedStuff','runestone.disqus','runestone.codelens','runestone.activecode', 'runestone.assess', 'runestone.animation','runestone.meta', 'runestone.parsons', 'runestone.blockly', 'runestone.livecode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = [ + pkg_resources.resource_filename("runestone", "common/project_template/_templates") +] + + +# The suffix of source filenames. +source_suffix = ".rst" + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = "Runestone Interactive Overview" +copyright = "2015 yasiro01" + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = "0.0.1" +# The full version, including alpha/beta/rc tags. +release = "0.0" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# `rst_prolog `_: +# A string of reStructuredText that will be included at the beginning of every +# source file that is read. +rst_prolog = ( + # For fill-in-the-blank questions, provide a convenient means to indicate a blank. + """ + +.. |blank| replace:: :blank:`x` +""" +) + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "sphinx_bootstrap" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {'nosidebar': 'true'} +html_theme_options = { + # Navigation bar title. (Default: ``project`` value) + "navbar_title": "Runestone Default", + # Tab name for entire site. (Default: "Site") + "navbar_site_name": "Chapters", + # Global TOC depth for "site" navbar tab. (Default: 1) + # Switching to -1 shows all levels. + "globaltoc_depth": 1, + # Include hidden TOCs in Site navbar? + # + # Note: If this is "false", you cannot have mixed ``:hidden:`` and + # non-hidden ``toctree`` directives in the same page, or else the build + # will break. + # + # Values: "true" (default) or "false" + "globaltoc_includehidden": "true", + # HTML navbar class (Default: "navbar") to attach to
element. + # For black navbar, do "navbar navbar-inverse" + "navbar_class": "navbar", + # Fix navigation bar to top of page? + # Values: "true" (default) or "false" + "navbar_fixed_top": "true", + # Location of link to source. + # Options are "nav" (default), "footer" or anything else to exclude. + "source_link_position": "nav", + # Bootswatch (http://bootswatch.com/) theme. + # + # Options are nothing with "" (default) or the name of a valid theme + # such as "amelia" or "cosmo". + # + # Note that this is served off CDN, so won't be available offline. + #'bootswatch_theme': "slate", +} + +# html_style = "style.css" + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = [ + pkg_resources.resource_filename( + "runestone", "common/project_template/_templates/plugin_layouts" + ) +] + + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = "Runestone Interactive Overview" + +# A shorter title for the navigation bar. Default is the same as html_title. +html_short_title = "Runestone Interactive Overview" + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. + +# logo is included in layout file +# html_logo = "../source/_static/logo_small.png" + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". + +html_static_path = runestone_static_dirs() + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = "PythonCoursewareProjectdoc" diff --git a/runestone/hparsons/test/pavement.py b/runestone/hparsons/test/pavement.py new file mode 100644 index 000000000..4ccdbbc6b --- /dev/null +++ b/runestone/hparsons/test/pavement.py @@ -0,0 +1,50 @@ +import paver +from paver.easy import * +import paver.setuputils + +paver.setuputils.install_distutils_tasks() +import os, sys +from runestone.server import get_dburl +from sphinxcontrib import paverutils +import pkg_resources + +sys.path.append(os.getcwd()) + +home_dir = os.getcwd() +master_url = "http://127.0.0.1:8000" +master_app = "runestone" +serving_dir = "./build/sa_test" +dest = "../../static" + +options( + sphinx=Bunch(docroot="."), + build=Bunch( + builddir="./build/sa_test", + sourcedir="_sources", + outdir="./build/sa_test", + confdir=".", + project_name="sa_test", + template_args={ + "course_id": "sa_test", + "login_required": "false", + "appname": master_app, + "loglevel": 0, + "course_url": master_url, + "use_services": "false", + "python3": "false", + "dburl": "", + "downloads_enabled": "true", + "enable_chatcodes": "false", + "allow_pairs": "false", + "basecourse": "sa_test", + }, + ), +) + +version = pkg_resources.require("runestone")[0].version +options.build.template_args["runestone_version"] = version + +# If DBURL is in the environment override dburl +options.build.template_args["dburl"] = get_dburl(outer=locals()) + +from runestone import build # build is called implicitly by the paver driver. diff --git a/runestone/hparsons/test/test_shortanswer.py b/runestone/hparsons/test/test_shortanswer.py new file mode 100644 index 000000000..182705ff4 --- /dev/null +++ b/runestone/hparsons/test/test_shortanswer.py @@ -0,0 +1,61 @@ +""" +Test Short Answer question directive +""" + +__author__ = "yasinovskyy" + +DIV_ID = "test_short_answer_1" + + +def get_sa(selenium_utils): + selenium_utils.wait_until_ready(DIV_ID) + selenium_utils.scroll_to_top() + return selenium_utils.driver.find_element_by_id(DIV_ID) + + +def click_button(sa_element): + sa_element.find_element_by_tag_name("button").click() + + +def test_sa1(selenium_utils_get): + """No input. Button not clicked""" + t1 = get_sa(selenium_utils_get) + fb = t1.find_element_by_id(f"{DIV_ID}_feedback") + assert "alert-danger" in fb.get_attribute("class") + + +def test_sa2(selenium_utils_get): + """No input. Button clicked""" + t1 = get_sa(selenium_utils_get) + click_button(t1) + fb = t1.find_element_by_id(f"{DIV_ID}_feedback") + assert "alert-success" in fb.get_attribute("class") + + +def test_sa3(selenium_utils_get): + """Answer entered""" + t1 = get_sa(selenium_utils_get) + ta = t1.find_element_by_id(f"{DIV_ID}_solution") + ta.clear() + ta.send_keys("My answer") + + click_button(t1) + + fb = t1.find_element_by_id(f"{DIV_ID}_feedback") + assert fb is not None + assert "alert-success" in fb.get_attribute("class") + + +# TODO: this is the same as ``_test_sa3``. +def test_sa4(selenium_utils_get): + """Answer entered and cleared""" + t1 = get_sa(selenium_utils_get) + ta = t1.find_element_by_id(f"{DIV_ID}_solution") + ta.clear() + ta.send_keys("My answer") + + click_button(t1) + + fb = t1.find_element_by_id(f"{DIV_ID}_feedback") + assert fb is not None + assert "alert-success" in fb.get_attribute("class") diff --git a/runestone/hparsons/toctree.rst b/runestone/hparsons/toctree.rst new file mode 100644 index 000000000..0bac15fe7 --- /dev/null +++ b/runestone/hparsons/toctree.rst @@ -0,0 +1,11 @@ +************************************************* +Accessibility +************************************************* +.. toctree:: + :maxdepth: 1 + :glob: + + *.py + js/*.js + css/*.css + test/test_*.py From b160087d6ced83426e5f680d35ec5dee216d485d Mon Sep 17 00:00:00 2001 From: amy21206 Date: Tue, 15 Feb 2022 00:32:46 -0500 Subject: [PATCH 02/31] :construction: Adding horizontal parsons --- package-lock.json | 3706 ++++++++++++++++- package.json | 8 +- runestone/__init__.py | 2 + runestone/hparsons/__init__.py | 2 +- runestone/hparsons/css/hparsons.css | 2 +- runestone/hparsons/css/shortanswer.css | 6 - runestone/hparsons/hparsons.py | 144 + runestone/hparsons/js/hparsons.js | 390 +- runestone/hparsons/js/sa_template.html | 32 - runestone/hparsons/js/shortanswer.js | 295 -- runestone/hparsons/js/timed_shortanswer.js | 43 - runestone/hparsons/shortanswer.py | 131 - runestone/hparsons/test/__init__.py | 0 .../test/_sources/Figures/LutherBellPic.jpg | Bin 64480 -> 0 bytes runestone/hparsons/test/_sources/index.rst | 88 +- runestone/hparsons/test/conf.py | 242 +- runestone/hparsons/test/pavement.py | 81 +- runestone/hparsons/test/test_shortanswer.py | 61 - runestone/hparsons/toctree.rst | 1 - runestone/mchoice/js/mchoice.js | 2 + setup.py | 2 +- webpack.index.js | 1 + 22 files changed, 4103 insertions(+), 1136 deletions(-) delete mode 100755 runestone/hparsons/css/shortanswer.css create mode 100755 runestone/hparsons/hparsons.py delete mode 100644 runestone/hparsons/js/sa_template.html delete mode 100644 runestone/hparsons/js/shortanswer.js delete mode 100644 runestone/hparsons/js/timed_shortanswer.js delete mode 100755 runestone/hparsons/shortanswer.py delete mode 100644 runestone/hparsons/test/__init__.py delete mode 100644 runestone/hparsons/test/_sources/Figures/LutherBellPic.jpg delete mode 100644 runestone/hparsons/test/test_shortanswer.py diff --git a/package-lock.json b/package-lock.json index cc1ee3637..b0d8faa23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,8 @@ "mini-css-extract-plugin": "^2.0.0", "webpack": "^5.61.0", "webpack-bundle-analyzer": "^4.0.0", - "webpack-cli": "^4.0.0" + "webpack-cli": "^4.9.2", + "webpack-dev-server": "^4.7.4" } }, "node_modules/-": { @@ -109,6 +110,44 @@ "node": ">=10.13.0" } }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "7.28.2", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", @@ -134,12 +173,44 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==" }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.0.0.tgz", "integrity": "sha512-NZwaaynfs1oIoLAV1vg18e7QMVDvw+6SQrdJc8w3BwUaoroVSf6EBj/Sk4PBWGxsq0dzhA2drbsuMC1/6C6KgQ==", "dev": true }, + "node_modules/@types/http-proxy": { + "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -151,6 +222,12 @@ "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.33.tgz", "integrity": "sha512-qEWiQff6q2tA5gcJGWwzplQcXdJtm+0oy6IHGHzlOf3eFAkGE/FIPXZK9ofWgNSHVp8AFFI33PJJshS0ei3Gvw==" }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, "node_modules/@types/node": { "version": "16.11.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", @@ -165,6 +242,61 @@ "moment": ">=2.14.0" } }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "dev": true + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -312,9 +444,9 @@ } }, "node_modules/@webpack-cli/configtest": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", - "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", + "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", "dev": true, "peerDependencies": { "webpack": "4.x.x || 5.x.x", @@ -322,9 +454,9 @@ } }, "node_modules/@webpack-cli/info": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", - "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz", + "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==", "dev": true, "dependencies": { "envinfo": "^7.7.3" @@ -334,9 +466,9 @@ } }, "node_modules/@webpack-cli/serve": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", - "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", + "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==", "dev": true, "peerDependencies": { "webpack-cli": "4.x.x" @@ -366,6 +498,19 @@ "optional": true, "peer": true }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -375,6 +520,19 @@ "node": ">=0.4.0" } }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -445,6 +603,18 @@ "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "dev": true }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -467,6 +637,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -485,6 +668,12 @@ "readable-stream": "^2.0.6" } }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -494,12 +683,26 @@ "node": ">=8" } }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "optional": true, - "peer": true + "devOptional": true + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true }, "node_modules/bessel": { "version": "1.0.2", @@ -517,6 +720,15 @@ "node": "*" } }, + "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/bit-field": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/bit-field/-/bit-field-1.5.3.tgz", @@ -598,6 +810,50 @@ "node": ">=10" } }, + "node_modules/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "dev": true, + "dependencies": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -616,8 +872,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -664,6 +919,34 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/camel-case": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", @@ -746,6 +1029,33 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "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" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -767,6 +1077,15 @@ "node": ">= 10.0" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -880,6 +1199,36 @@ "node": ">= 12" } }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/compression-webpack-plugin": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-9.0.1.tgz", @@ -953,12 +1302,26 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true, - "peer": true + "devOptional": true + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } }, "node_modules/console-control-strings": { "version": "1.1.0", @@ -967,6 +1330,42 @@ "optional": true, "peer": true }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, "node_modules/copy-webpack-plugin": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz", @@ -1017,8 +1416,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "optional": true, - "peer": true + "devOptional": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -1462,6 +1860,21 @@ "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -1470,6 +1883,23 @@ "node": ">=0.10.0" } }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -1480,38 +1910,139 @@ "node": ">=4.0.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "optional": true, - "peer": true - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "optional": true, - "peer": true, - "bin": { - "detect-libc": "bin/detect-libc.js" + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" }, "engines": { - "node": ">=0.10" + "node": ">= 10" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, "engines": { "node": ">=8" } }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true, + "peer": true + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true, + "peer": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -1592,6 +2123,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, "node_modules/electron-to-chromium": { "version": "1.3.885", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz", @@ -1603,6 +2140,15 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1659,6 +2205,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1702,6 +2254,21 @@ "node": ">=4.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -1734,6 +2301,53 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1777,6 +2391,18 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1789,6 +2415,24 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1802,6 +2446,44 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -1815,12 +2497,31 @@ "node": ">=12" } }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "devOptional": true + }, + "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, - "peer": true + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/function-bind": { "version": "1.1.1", @@ -1900,6 +2601,20 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -1916,8 +2631,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2000,6 +2714,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, "node_modules/handsontable": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/handsontable/-/handsontable-7.2.2.tgz", @@ -2034,6 +2754,33 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -2059,6 +2806,24 @@ "tiny-emitter": "^2.1.0" } }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + }, "node_modules/html-loader": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-3.0.1.tgz", @@ -2196,6 +2961,72 @@ "entities": "^2.0.0" } }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", + "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz", + "integrity": "sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -2254,12 +3085,20 @@ "node": ">=8" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2269,8 +3108,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "optional": true, - "peer": true + "devOptional": true }, "node_modules/ini": { "version": "1.3.8", @@ -2301,6 +3139,21 @@ "node": ">=4" } }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/is-absolute-url": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", @@ -2310,6 +3163,34 @@ "node": ">=8" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-core-module": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", @@ -2321,6 +3202,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2359,6 +3270,36 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -2371,6 +3312,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", @@ -2389,12 +3346,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "optional": true, - "peer": true + "devOptional": true }, "node_modules/isexe": { "version": "2.0.0", @@ -2629,6 +3597,15 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mem": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", @@ -2642,6 +3619,24 @@ "node": ">=6" } }, + "node_modules/memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2657,6 +3652,15 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -2683,21 +3687,21 @@ } }, "node_modules/mime-db": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", - "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.33", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", - "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dev": true, "dependencies": { - "mime-db": "1.50.0" + "mime-db": "1.51.0" }, "engines": { "node": ">= 0.6" @@ -2783,12 +3787,17 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2800,8 +3809,19 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "optional": true, - "peer": true + "devOptional": true + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "devOptional": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } }, "node_modules/moment": { "version": "2.20.1", @@ -2812,11 +3832,29 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true, - "peer": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "devOptional": true + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true }, "node_modules/nan": { "version": "2.15.0", @@ -2832,9 +3870,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -2871,6 +3909,15 @@ "ms": "^2.1.1" } }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -2893,14 +3940,31 @@ } }, "node_modules/node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", + "dev": true, + "engines": { + "node": ">= 6.13.0" } }, "node_modules/node-pre-gyp": { @@ -2960,21 +4024,8 @@ "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "optional": true, "peer": true, - "dependencies": { - "minipass": "^2.9.0" - } - }, - "node_modules/node-pre-gyp/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "optional": true, - "peer": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "minipass": "^2.9.0" } }, "node_modules/node-pre-gyp/node_modules/rimraf": { @@ -3162,6 +4213,58 @@ "node": ">=0.10.0" } }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3193,6 +4296,23 @@ "sax": "^1.2.1" } }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -3420,6 +4540,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "dev": true, + "dependencies": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -3444,6 +4592,15 @@ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", @@ -3467,8 +4624,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "optional": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -3487,6 +4643,12 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -3534,6 +4696,29 @@ "node": ">=8" } }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/postcss": { "version": "8.3.11", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", @@ -4060,8 +5245,29 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "optional": true, - "peer": true + "devOptional": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } }, "node_modules/pump": { "version": "3.0.0", @@ -4081,6 +5287,18 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4110,6 +5328,39 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "dev": true, + "dependencies": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -4140,8 +5391,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -4156,8 +5406,19 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true, - "peer": true + "devOptional": true + }, + "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/rechoir": { "version": "0.7.1", @@ -4171,6 +5432,22 @@ "node": ">= 0.10" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -4215,6 +5492,12 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, "node_modules/resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -4248,6 +5531,15 @@ "node": ">=8" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4258,6 +5550,21 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4334,11 +5641,29 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, "node_modules/select2": { "version": "4.1.0-rc.0", "resolved": "https://registry.npmjs.org/select2/-/select2-4.1.0-rc.0.tgz", "integrity": "sha512-Hr9TdhyHCZUtwznEH2CBf7967mEM0idtJ5nMtjvk3Up5tPukOLXbHUNmh10oRfeNIhj+3GD3niu+g6sVK+gK0A==" }, + "node_modules/selfsigned": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "dev": true, + "dependencies": { + "node-forge": "^1.2.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -4354,6 +5679,42 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -4363,11 +5724,77 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -4429,34 +5856,135 @@ "node": ">=8" } }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, "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": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "node_modules/spdy-transport/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/source-map-support": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", - "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "node_modules/spdy/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, + "node_modules/spdy/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/sql.js": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.5.0.tgz", @@ -4468,12 +5996,20 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "optional": true, - "peer": true, + "devOptional": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -4482,8 +6018,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true, - "peer": true + "devOptional": true }, "node_modules/string-width": { "version": "4.2.3", @@ -4660,6 +6195,12 @@ "node": ">= 8" } }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, "node_modules/timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", @@ -4683,6 +6224,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/topojson-client": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", @@ -4726,6 +6276,19 @@ "resolved": "https://registry.npmjs.org/tspan/-/tspan-0.4.0.tgz", "integrity": "sha512-0ELL9tpLpTqLliFyQySaxgCO43buCML+j3TI4E1LuSI8wkzITGEVhZCyMvv/A+3ek9KpgALhhgnZESRLTbN+iw==" }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/uniqs": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", @@ -4740,6 +6303,15 @@ "node": ">= 10.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4761,6 +6333,33 @@ "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", "dev": true }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vega": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/vega/-/vega-4.4.0.tgz", @@ -5324,6 +6923,15 @@ "tspan": "^0.4.0" } }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -5421,15 +7029,15 @@ } }, "node_modules/webpack-cli": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", - "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz", + "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.1.0", - "@webpack-cli/info": "^1.4.0", - "@webpack-cli/serve": "^1.6.0", + "@webpack-cli/configtest": "^1.1.1", + "@webpack-cli/info": "^1.4.1", + "@webpack-cli/serve": "^1.6.1", "colorette": "^2.0.14", "commander": "^7.0.0", "execa": "^5.0.0", @@ -5458,20 +7066,249 @@ "webpack-bundle-analyzer": { "optional": true }, - "webpack-dev-server": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/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/webpack-dev-middleware": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz", + "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.1", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", + "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz", + "integrity": "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", + "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { "optional": true } } }, - "node_modules/webpack-cli/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/webpack-merge": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", @@ -5515,6 +7352,29 @@ "node": ">=10.13.0" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -5868,6 +7728,44 @@ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, "@types/eslint": { "version": "7.28.2", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", @@ -5893,12 +7791,44 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==" }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/html-minifier-terser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.0.0.tgz", "integrity": "sha512-NZwaaynfs1oIoLAV1vg18e7QMVDvw+6SQrdJc8w3BwUaoroVSf6EBj/Sk4PBWGxsq0dzhA2drbsuMC1/6C6KgQ==", "dev": true }, + "@types/http-proxy": { + "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -5910,6 +7840,12 @@ "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.33.tgz", "integrity": "sha512-qEWiQff6q2tA5gcJGWwzplQcXdJtm+0oy6IHGHzlOf3eFAkGE/FIPXZK9ofWgNSHVp8AFFI33PJJshS0ei3Gvw==" }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, "@types/node": { "version": "16.11.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", @@ -5924,6 +7860,61 @@ "moment": ">=2.14.0" } }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -6071,25 +8062,25 @@ } }, "@webpack-cli/configtest": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", - "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", + "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", "dev": true, "requires": {} }, "@webpack-cli/info": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", - "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz", + "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==", "dev": true, "requires": { "envinfo": "^7.7.3" } }, "@webpack-cli/serve": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", - "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", + "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==", "dev": true, "requires": {} }, @@ -6112,12 +8103,32 @@ "optional": true, "peer": true }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -6172,6 +8183,12 @@ "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "dev": true }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6185,6 +8202,16 @@ "color-convert": "^2.0.1" } }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -6203,18 +8230,38 @@ "readable-stream": "^2.0.6" } }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "optional": true, - "peer": true + "devOptional": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true }, "bessel": { "version": "1.0.2", @@ -6226,6 +8273,12 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==" }, + "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 + }, "bit-field": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/bit-field/-/bit-field-1.5.3.tgz", @@ -6288,6 +8341,46 @@ } } }, + "body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "dev": true, + "requires": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "dependencies": { + "bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -6303,8 +8396,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6338,6 +8430,28 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "camel-case": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", @@ -6402,6 +8516,22 @@ "supports-color": "^7.1.0" } }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "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" + } + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -6417,6 +8547,12 @@ "source-map": "~0.6.0" } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -6508,6 +8644,38 @@ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "compression-webpack-plugin": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-9.0.1.tgz", @@ -6563,8 +8731,13 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true, - "peer": true + "devOptional": true + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -6573,6 +8746,33 @@ "optional": true, "peer": true }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, "copy-webpack-plugin": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz", @@ -6607,8 +8807,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "optional": true, - "peer": true + "devOptional": true }, "cross-spawn": { "version": "7.0.3", @@ -6959,11 +9158,42 @@ "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -6971,6 +9201,46 @@ "optional": true, "peer": true }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + } + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -6978,6 +9248,18 @@ "optional": true, "peer": true }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -6985,6 +9267,12 @@ "optional": true, "peer": true }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -6994,6 +9282,31 @@ "path-type": "^4.0.0" } }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, "dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -7056,6 +9369,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, "electron-to-chromium": { "version": "1.3.885", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz", @@ -7067,6 +9386,12 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -7108,6 +9433,12 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -7141,6 +9472,18 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -7164,6 +9507,52 @@ "strip-final-newline": "^2.0.0" } }, + "express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + } + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7204,6 +9593,15 @@ "reusify": "^1.0.4" } }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -7213,6 +9611,21 @@ "to-regex-range": "^5.0.1" } }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -7223,6 +9636,24 @@ "path-exists": "^4.0.0" } }, + "follow-redirects": { + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -7233,12 +9664,24 @@ "universalify": "^2.0.0" } }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "optional": true, - "peer": true + "devOptional": 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 }, "function-bind": { "version": "1.1.1", @@ -7308,6 +9751,17 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -7318,8 +9772,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -7380,6 +9833,12 @@ "duplexer": "^0.1.2" } }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, "handsontable": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/handsontable/-/handsontable-7.2.2.tgz", @@ -7407,6 +9866,21 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -7429,6 +9903,24 @@ "tiny-emitter": "^2.1.0" } }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + }, "html-loader": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-3.0.1.tgz", @@ -7514,6 +10006,55 @@ "entities": "^2.0.0" } }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", + "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz", + "integrity": "sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -7555,12 +10096,17 @@ "resolve-cwd": "^3.0.0" } }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -7570,8 +10116,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "optional": true, - "peer": true + "devOptional": true }, "ini": { "version": "1.3.8", @@ -7596,12 +10141,43 @@ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true + }, "is-absolute-url": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", "dev": true }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "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-core-module": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", @@ -7610,6 +10186,21 @@ "has": "^1.0.3" } }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -7636,6 +10227,24 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -7645,6 +10254,16 @@ "isobject": "^3.0.1" } }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", @@ -7657,12 +10276,20 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "optional": true, - "peer": true + "devOptional": true }, "isexe": { "version": "2.0.0", @@ -7860,6 +10487,12 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, "mem": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", @@ -7870,6 +10503,21 @@ "p-is-promise": "^2.0.0" } }, + "memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7882,6 +10530,12 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -7899,18 +10553,18 @@ "dev": true }, "mime-db": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", - "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", "dev": true }, "mime-types": { - "version": "2.1.33", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", - "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dev": true, "requires": { - "mime-db": "1.50.0" + "mime-db": "1.51.0" } }, "mimic-fn": { @@ -7968,12 +10622,17 @@ } } }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7982,8 +10641,16 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "optional": true, - "peer": true + "devOptional": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "devOptional": true, + "requires": { + "minimist": "^1.2.5" + } }, "moment": { "version": "2.20.1", @@ -7991,11 +10658,26 @@ "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true, - "peer": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "devOptional": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true }, "nan": { "version": "2.15.0", @@ -8011,9 +10693,9 @@ "dev": true }, "nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true }, "needle": { @@ -8040,6 +10722,12 @@ } } }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -8062,13 +10750,19 @@ } }, "node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "requires": { "whatwg-url": "^5.0.0" } }, + "node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", + "dev": true + }, "node-pre-gyp": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", @@ -8126,16 +10820,6 @@ "minipass": "^2.9.0" } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "optional": true, - "peer": true, - "requires": { - "minimist": "^1.2.5" - } - }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -8287,6 +10971,43 @@ "optional": true, "peer": true }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8312,6 +11033,17 @@ "sax": "^1.2.1" } }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -8477,6 +11209,25 @@ } } }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -8498,6 +11249,12 @@ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, "pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", @@ -8518,8 +11275,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "optional": true, - "peer": true + "devOptional": true }, "path-key": { "version": "3.1.1", @@ -8532,6 +11288,12 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -8567,6 +11329,28 @@ "find-up": "^4.0.0" } }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, "postcss": { "version": "8.3.11", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", @@ -8902,13 +11686,30 @@ "renderkid": "^3.0.0" } }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "optional": true, - "peer": true - }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "devOptional": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -8924,6 +11725,12 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "dev": true + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8939,6 +11746,32 @@ "safe-buffer": "^5.1.0" } }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "dev": true, + "requires": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true + } + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -8965,8 +11798,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8981,11 +11813,19 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true, - "peer": true + "devOptional": 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" + } + }, "rechoir": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", @@ -8995,6 +11835,16 @@ "resolve": "^1.9.0" } }, + "regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -9030,6 +11880,12 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -9056,12 +11912,27 @@ } } }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -9103,11 +11974,26 @@ "ajv-keywords": "^3.5.2" } }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, "select2": { "version": "4.1.0-rc.0", "resolved": "https://registry.npmjs.org/select2/-/select2-4.1.0-rc.0.tgz", "integrity": "sha512-Hr9TdhyHCZUtwznEH2CBf7967mEM0idtJ5nMtjvk3Up5tPukOLXbHUNmh10oRfeNIhj+3GD3niu+g6sVK+gK0A==" }, + "selfsigned": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "dev": true, + "requires": { + "node-forge": "^1.2.0" + } + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -9117,6 +12003,35 @@ "lru-cache": "^6.0.0" } }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + } + } + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -9126,11 +12041,70 @@ "randombytes": "^2.1.0" } }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -9177,6 +12151,17 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9199,6 +12184,78 @@ "source-map": "^0.6.0" } }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "sql.js": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.5.0.tgz", @@ -9210,12 +12267,17 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "optional": true, - "peer": true, + "devOptional": true, "requires": { "safe-buffer": "~5.1.0" }, @@ -9224,8 +12286,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true, - "peer": true + "devOptional": true } } }, @@ -9347,6 +12408,12 @@ } } }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, "timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", @@ -9367,6 +12434,12 @@ "is-number": "^7.0.0" } }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, "topojson-client": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", @@ -9404,6 +12477,16 @@ "resolved": "https://registry.npmjs.org/tspan/-/tspan-0.4.0.tgz", "integrity": "sha512-0ELL9tpLpTqLliFyQySaxgCO43buCML+j3TI4E1LuSI8wkzITGEVhZCyMvv/A+3ek9KpgALhhgnZESRLTbN+iw==" }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, "uniqs": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", @@ -9415,6 +12498,12 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -9436,6 +12525,24 @@ "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", "dev": true }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, "vega": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/vega/-/vega-4.4.0.tgz", @@ -9947,6 +13054,15 @@ "tspan": "^0.4.0" } }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -10037,15 +13153,15 @@ } }, "webpack-cli": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", - "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz", + "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.1.0", - "@webpack-cli/info": "^1.4.0", - "@webpack-cli/serve": "^1.6.0", + "@webpack-cli/configtest": "^1.1.1", + "@webpack-cli/info": "^1.4.1", + "@webpack-cli/serve": "^1.6.1", "colorette": "^2.0.14", "commander": "^7.0.0", "execa": "^5.0.0", @@ -10064,6 +13180,161 @@ } } }, + "webpack-dev-middleware": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz", + "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.1", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", + "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz", + "integrity": "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "ajv": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", + "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "dev": true, + "requires": {} + } + } + }, "webpack-merge": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", @@ -10074,6 +13345,23 @@ "wildcard": "^2.0.0" } }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index a413a283a..ab2ae6fc2 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build": "webpack --mode=development", "buildj": "webpack --mode=development --profile --json > stats.json", "watch": "webpack --mode=development --watch", + "start": "webpack-dev-server --mode=development --open", "serve": "cd dist; python -m http.server 8080; cd ..", "dist": "webpack --mode=production", "distj": "webpack --mode=production --profile --json > stats.json", @@ -18,8 +19,8 @@ "author": "", "license": "ISC", "devDependencies": { - "copy-webpack-plugin": "^9.0.0", "compression-webpack-plugin": "^9.0.0", + "copy-webpack-plugin": "^9.0.0", "css-loader": "^6.0.0", "css-minimizer-webpack-plugin": "^3.0.0", "html-loader": "^3.0.0", @@ -27,7 +28,8 @@ "mini-css-extract-plugin": "^2.0.0", "webpack": "^5.61.0", "webpack-bundle-analyzer": "^4.0.0", - "webpack-cli": "^4.0.0" + "webpack-cli": "^4.9.2", + "webpack-dev-server": "^4.7.4" }, "dependencies": { "-": "0.0.1", @@ -41,4 +43,4 @@ "vega-embed": "3.14.0", "wavedrom": "^2.0.0" } -} \ No newline at end of file +} diff --git a/runestone/__init__.py b/runestone/__init__.py index dfb3e0f3e..d3f7d6432 100644 --- a/runestone/__init__.py +++ b/runestone/__init__.py @@ -13,6 +13,7 @@ from .dragndrop import DragNDrop from .fitb import FillInTheBlank from .groupsub import GroupSubmission +from .hparsons import HParsonsNode from .khanex import Khanex from .selectquestion import SelectQuestion from .matrixeq import MatrixEq @@ -246,6 +247,7 @@ def build(options): "disqus": DisqusDirective, "dragndrop": DragNDrop, "groupsub": GroupSubmission, + "hparsons": HParsonsNode, "parsonsprob": ParsonsProblem, "poll": Poll, "quizly": Quizly, diff --git a/runestone/hparsons/__init__.py b/runestone/hparsons/__init__.py index 6791e4a05..401a75358 100755 --- a/runestone/hparsons/__init__.py +++ b/runestone/hparsons/__init__.py @@ -1 +1 @@ -from .shortanswer import * +from .hparsons import * diff --git a/runestone/hparsons/css/hparsons.css b/runestone/hparsons/css/hparsons.css index c6c6f7036..0a404e260 100755 --- a/runestone/hparsons/css/hparsons.css +++ b/runestone/hparsons/css/hparsons.css @@ -1,4 +1,4 @@ -div.journal div.latexoutput { +div.hparsons div.latexoutput { background-color: #eeeeee; padding: 1em; margin-bottom: 10px; diff --git a/runestone/hparsons/css/shortanswer.css b/runestone/hparsons/css/shortanswer.css deleted file mode 100755 index c6c6f7036..000000000 --- a/runestone/hparsons/css/shortanswer.css +++ /dev/null @@ -1,6 +0,0 @@ -div.journal div.latexoutput { - background-color: #eeeeee; - padding: 1em; - margin-bottom: 10px; - border-radius: 5px; -} diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py new file mode 100755 index 000000000..55bfc8f38 --- /dev/null +++ b/runestone/hparsons/hparsons.py @@ -0,0 +1,144 @@ +# Copyright (C) 2011 Bradley N. Miller +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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 the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +__author__ = "ziwu" + +from docutils import nodes +from docutils.parsers.rst import directives +from runestone.mchoice import Assessment +from runestone.server.componentdb import ( + addQuestionToDB, + addHTMLToDB, + maybeAddToAssignment, +) +from runestone.common.runestonedirective import RunestoneDirective, RunestoneIdNode + + +def setup(app): + app.add_directive("hparsons", HParsonsDirective) + # adding the html + app.add_node(HParsonsNode, html=(visit_hparsons_node, depart_hparsons_node)) + # TODO: figure out what these means + # app.add_config_value("shortanswer_div_class", "journal alert alert-warning", "html") + # app.add_config_value( + # "shortanswer_optional_div_class", "journal alert alert-success", "html" + # ) + + +# TODO: what is the alert and alert-warnings? +TEMPLATE_START = """ +
+
+
+ """ + +TEMPLATE_END = """ +
+
+
+
+
+ """ + + +# seems to be the same for all +class HParsonsNode(nodes.General, nodes.Element, RunestoneIdNode): + def __init__(self, options, **kwargs): + super(HParsonsNode, self).__init__(**kwargs) + self.runestone_options = options + +# generate the first part of the html +def visit_hparsons_node(self, node): + div_id = node.runestone_options["divid"] + components = dict(node.runestone_options) + components.update({"divid": div_id}) + node.delimiter = "_start__{}_".format(node.runestone_options["divid"]) + self.body.append(node.delimiter) + res = TEMPLATE_START % components + self.body.append(res) + + +# generate the second part of the html +def depart_hparsons_node(self, node): + components = dict(node.runestone_options) + res = TEMPLATE_END % components + self.body.append(res) + addHTMLToDB( + node.runestone_options["divid"], + components["basecourse"], + "".join(self.body[self.body.index(node.delimiter) + 1 :]), + ) + self.body.remove(node.delimiter) + + +class HParsonsDirective(Assessment): + """ + .. hparsons:: uniqueid + + nothing makes sense at all. + + """ + + required_arguments = 0 # the div id + optional_arguments = 0 + final_argument_whitespace = True + has_content = False + option_spec = Assessment.option_spec.copy() + # seem to be defining the type of the options + # option_spec.update({"mathjax": directives.flag}) + + # just fill it with the name + node_class = HParsonsNode + + def run(self): + # same + super(HParsonsDirective, self).run() + addQuestionToDB(self) + # Raise an error if the directive does not have contents. + self.assert_has_content() + + # specifying default for option? + # TODO: ignoring for now + # self.options["mathjax"] = "data-mathjax" if "mathjax" in self.options else "" + + # same + hparsons_node = HParsonsNode(self.options, rawsource=self.block_text) + hparsons_node.source, hparsons_node.line = self.state_machine.get_source_and_line( + self.lineno + ) + + # exist in short answer and mchoice but not parsons + # For MChoice its better to insert the qnum into the content before further processing. + self.updateContent() + + # same as mchoice, different from parsons. i think it is for generating instructions. + self.state.nested_parse(self.content, self.content_offset, hparsons_node) + # parsons: + # self.state.nested_parse( + # self.options["instructions"], self.content_offset, parsons_node + # ) + + # adding classes outside of the div based on the options + env = self.state.document.settings.env + if self.options["optional"]: + self.options["divclass"] = env.config.shortanswer_optional_div_class + else: + self.options["divclass"] = env.config.shortanswer_div_class + + # same + maybeAddToAssignment(self) + + # same + return [hparsons_node] diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js index 49fd4ba34..5cc947aa9 100644 --- a/runestone/hparsons/js/hparsons.js +++ b/runestone/hparsons/js/hparsons.js @@ -11,284 +11,224 @@ import RunestoneBase from "../../common/js/runestonebase.js"; import "./../css/hparsons.css"; +import "./regex-element.js"; +import "../../activecode/js/skulpt.min.js"; +import "../../activecode/js/skulpt-stdlib.js"; export var hpList; // Dictionary that contains all instances of horizontal Parsons problem objects if (hpList === undefined) hpList = {}; -export default class ShortAnswer extends RunestoneBase { +export default class HParsons extends RunestoneBase { constructor(opts) { super(opts); + console.log('hparsons') if (opts) { - var orig = opts.orig; // entire

element that will be replaced by new HTML + // TODO: what is orig? + var orig = opts.orig; // entire

 element that will be replaced by new HTML
+            this.containerDiv = orig;
+            this.origElem = $(orig).find("pre.parsonsblocks")[0];
+            console.log(orig)
             this.useRunestoneServices =
                 opts.useRunestoneServices || eBookConfig.useRunestoneServices;
-            this.origElem = orig;
             this.divid = orig.id;
-            this.question = this.origElem.innerHTML;
-            this.optional = false;
-            if ($(this.origElem).is("[data-optional]")) {
-                this.optional = true;
-            }
-            if ($(this.origElem).is("[data-mathjax]")) {
-                this.mathjax = true;
-            }
+
+            // The element that is going to be replaced
+            // this.elem = $(orig).find(".hparsons")[0];
+            // Find the question text and store it in .question
+            this.question = $(orig).find(`.hparsons_question`)[0];
+            // TODO: idk what this is with shortanswer
+            // this.question = this.origElem.innerHTML;
+            // this.optional = false;
+            // if ($(this.origElem).is("[data-optional]")) {
+            //     this.optional = true;
+            // }
+            // if ($(this.origElem).is("[data-mathjax]")) {
+            //     this.mathjax = true;
+            // }
             this.renderHTML();
-            this.caption = "shortanswer";
-            this.addCaption("runestone");
-            this.checkServer("shortanswer", true);
-        }
-    }
+            this.caption = "hparsons";
+            // this.addCaption("runestone");
+            // this.checkServer("hparsons", true);
 
-    renderHTML() {
-        this.containerDiv = document.createElement("div");
-        this.containerDiv.id = this.divid;
-        $(this.containerDiv).addClass(this.origElem.getAttribute("class"));
-        this.newForm = document.createElement("form");
-        this.newForm.id = this.divid + "_journal";
-        this.newForm.name = this.newForm.id;
-        this.newForm.action = "";
-        this.containerDiv.appendChild(this.newForm);
-        this.fieldSet = document.createElement("fieldset");
-        this.newForm.appendChild(this.fieldSet);
-        this.legend = document.createElement("legend");
-        this.legend.innerHTML = "Short Answer";
-        this.fieldSet.appendChild(this.legend);
-        this.firstLegendDiv = document.createElement("div");
-        this.firstLegendDiv.innerHTML = this.question;
-        $(this.firstLegendDiv).addClass("journal-question");
-        this.fieldSet.appendChild(this.firstLegendDiv);
-        this.jInputDiv = document.createElement("div");
-        this.jInputDiv.id = this.divid + "_journal_input";
-        this.fieldSet.appendChild(this.jInputDiv);
-        this.jOptionsDiv = document.createElement("div");
-        $(this.jOptionsDiv).addClass("journal-options");
-        this.jInputDiv.appendChild(this.jOptionsDiv);
-        this.jLabel = document.createElement("label");
-        $(this.jLabel).addClass("radio-inline");
-        this.jOptionsDiv.appendChild(this.jLabel);
-        this.jTextArea = document.createElement("textarea");
-        let self = this;
-        this.jTextArea.onchange = function () {
-            self.isAnswered = true;
-        };
-        this.jTextArea.id = this.divid + "_solution";
-        $(this.jTextArea).attr("aria-label", "textarea");
-        $(this.jTextArea).css("display:inline, width:530px");
-        $(this.jTextArea).addClass("form-control");
-        this.jTextArea.rows = 4;
-        this.jTextArea.cols = 50;
-        this.jLabel.appendChild(this.jTextArea);
-        this.jTextArea.onchange = function () {
-            this.feedbackDiv.innerHTML = "Your answer has not been saved yet!";
-            $(this.feedbackDiv).removeClass("alert-success");
-            $(this.feedbackDiv).addClass("alert alert-danger");
-        }.bind(this);
-        this.fieldSet.appendChild(document.createElement("br"));
-        if (this.mathjax) {
-            this.renderedAnswer = document.createElement("div");
-            $(this.renderedAnswer).addClass("latexoutput");
-            this.fieldSet.appendChild(this.renderedAnswer);
-        }
-        this.buttonDiv = document.createElement("div");
-        this.fieldSet.appendChild(this.buttonDiv);
-        this.submitButton = document.createElement("button");
-        $(this.submitButton).addClass("btn btn-success");
-        this.submitButton.type = "button";
-        this.submitButton.textContent = "Save";
-        this.submitButton.onclick = function () {
-            this.checkCurrentAnswer();
-            this.logCurrentAnswer();
-            this.renderFeedback();
-        }.bind(this);
-        this.buttonDiv.appendChild(this.submitButton);
-        this.randomSpan = document.createElement("span");
-        this.randomSpan.innerHTML = "Instructor's Feedback";
-        this.fieldSet.appendChild(this.randomSpan);
-        this.otherOptionsDiv = document.createElement("div");
-        $(this.otherOptionsDiv).css("padding-left:20px");
-        $(this.otherOptionsDiv).addClass("journal-options");
-        this.fieldSet.appendChild(this.otherOptionsDiv);
-        // add a feedback div to give user feedback
-        this.feedbackDiv = document.createElement("div");
-        //$(this.feedbackDiv).addClass("bg-info form-control");
-        //$(this.feedbackDiv).css("width:530px, background-color:#eee, font-style:italic");
-        $(this.feedbackDiv).css("width:530px, font-style:italic");
-        this.feedbackDiv.id = this.divid + "_feedback";
-        this.feedbackDiv.innerHTML = "You have not answered this question yet.";
-        $(this.feedbackDiv).addClass("alert alert-danger");
-        //this.otherOptionsDiv.appendChild(this.feedbackDiv);
-        this.fieldSet.appendChild(this.feedbackDiv);
-        //this.fieldSet.appendChild(document.createElement("br"));
-        $(this.origElem).replaceWith(this.containerDiv);
-        // This is a stopgap measure for when MathJax is not loaded at all.  There is another
-        // more difficult case that when MathJax is loaded asynchronously we will get here
-        // before MathJax is loaded.  In that case we will need to implement something
-        // like `the solution described here `_
-        if (typeof MathJax !== "undefined") {
-            this.queueMathJax(this.containerDiv)
+            // Set the storageId (key for storing data)
+            var storageId = super.localStorageKey();
+            this.storageId = storageId;
+            this.children = this.origElem.childNodes; // this contains all of the child elements of the entire tag...
+            this.contentArray = [];
+            HParsons.counter++; //    Unique identifier
+            this.counterId = "hparsons-" + HParsons.counter;
         }
     }
 
-    renderMath(value) {
-        if (this.mathjax) {
-            value = value.replace(/\$\$(.*?)\$\$/g, "\\[ $1 \\]");
-            value = value.replace(/\$(.*?)\$/g, "\\( $1 \\)");
-            $(this.renderedAnswer).text(value);
-            this.queueMathJax(this.renderedAnswer)
-        }
+    renderHTML() {
+        console.log('renderhtml');
+        const div = document.createElement('regex-element');
+        div.setAttribute('input-type', 'parsons')
+        div.id = 'abcd';
+        $(this.origElem).replaceWith(div);
+        // $(this.origElem).replaceWith(document.createElement('regex-element'));
+        // $(this.elem).innerHTML = ``;
+        console.log(div)
     }
 
     checkCurrentAnswer() { }
 
     async logCurrentAnswer(sid) {
-        let value = $(document.getElementById(this.divid + "_solution")).val();
-        this.renderMath(value);
-        this.setLocalStorage({
-            answer: value,
-            timestamp: new Date(),
-        });
-        let data = {
-            event: "shortanswer",
-            act: value,
-            answer: value,
-            div_id: this.divid,
-        };
-        if (typeof sid !== "undefined") {
-            data.sid = sid;
-        }
-        await this.logBookEvent(data);
+        console.log('hparsons, logcurrentanswer')
+        // let value = $(document.getElementById(this.divid + "_solution")).val();
+        // this.renderMath(value);
+        // this.setLocalStorage({
+        //     answer: value,
+        //     timestamp: new Date(),
+        // });
+        // let data = {
+        //     event: "shortanswer",
+        //     act: value,
+        //     answer: value,
+        //     div_id: this.divid,
+        // };
+        // if (typeof sid !== "undefined") {
+        //     data.sid = sid;
+        // }
+        // await this.logBookEvent(data);
     }
 
     renderFeedback() {
-        this.feedbackDiv.innerHTML = "Your answer has been saved.";
-        $(this.feedbackDiv).removeClass("alert-danger");
-        $(this.feedbackDiv).addClass("alert alert-success");
+        console.log('hparsons, renderfeedback')
+        // this.feedbackDiv.innerHTML = "Your answer has been saved.";
+        // $(this.feedbackDiv).removeClass("alert-danger");
+        // $(this.feedbackDiv).addClass("alert alert-success");
     }
     setLocalStorage(data) {
-        if (!this.graderactive) {
-            let key = this.localStorageKey();
-            localStorage.setItem(key, JSON.stringify(data));
-        }
+        console.log('hparsons, setlocalstorage')
+        // if (!this.graderactive) {
+        //     let key = this.localStorageKey();
+        //     localStorage.setItem(key, JSON.stringify(data));
+        // }
     }
     checkLocalStorage() {
+        console.log('hparsons, checklocalstorage')
         // Repopulates the short answer text
         // which was stored into local storage.
-        var answer = "";
-        if (this.graderactive) {
-            return;
-        }
-        var len = localStorage.length;
-        if (len > 0) {
-            var ex = localStorage.getItem(this.localStorageKey());
-            if (ex !== null) {
-                try {
-                    var storedData = JSON.parse(ex);
-                    answer = storedData.answer;
-                } catch (err) {
-                    // error while parsing; likely due to bad value stored in storage
-                    console.log(err.message);
-                    localStorage.removeItem(this.localStorageKey());
-                    return;
-                }
-                let solution = $("#" + this.divid + "_solution");
-                solution.text(answer);
-                this.renderMath(answer);
-                this.feedbackDiv.innerHTML =
-                    "Your current saved answer is shown above.";
-                $(this.feedbackDiv).removeClass("alert-danger");
-                $(this.feedbackDiv).addClass("alert alert-success");
-            }
-        }
+        // var answer = "";
+        // if (this.graderactive) {
+        //     return;
+        // }
+        // var len = localStorage.length;
+        // if (len > 0) {
+        //     var ex = localStorage.getItem(this.localStorageKey());
+        //     if (ex !== null) {
+        //         try {
+        //             var storedData = JSON.parse(ex);
+        //             answer = storedData.answer;
+        //         } catch (err) {
+        //             // error while parsing; likely due to bad value stored in storage
+        //             console.log(err.message);
+        //             localStorage.removeItem(this.localStorageKey());
+        //             return;
+        //         }
+        //         let solution = $("#" + this.divid + "_solution");
+        //         solution.text(answer);
+        //         this.renderMath(answer);
+        //         this.feedbackDiv.innerHTML =
+        //             "Your current saved answer is shown above.";
+        //         $(this.feedbackDiv).removeClass("alert-danger");
+        //         $(this.feedbackDiv).addClass("alert alert-success");
+        //     }
+        // }
     }
     restoreAnswers(data) {
+        console.log('hparsons, restoreanswers')
         // Restore answers from storage retrieval done in RunestoneBase
         // sometimes data.answer can be null
-        if (!data.answer) {
-            data.answer = "";
-        }
-        this.answer = data.answer;
-        this.jTextArea.value = this.answer;
-        this.renderMath(this.answer);
+        // if (!data.answer) {
+        //     data.answer = "";
+        // }
+        // this.answer = data.answer;
+        // this.jTextArea.value = this.answer;
+        // this.renderMath(this.answer);
 
-        let p = document.createElement("p");
-        this.jInputDiv.appendChild(p);
-        var tsString = "";
-        if (data.timestamp) {
-            tsString = new Date(data.timestamp).toLocaleString();
-        } else {
-            tsString = "";
-        }
-        $(p).text(tsString);
-        if (data.last_answer) {
-            this.current_answer = "ontime";
-            let toggle_answer_button = document.createElement("button");
-            toggle_answer_button.type = "button";
-            $(toggle_answer_button).text("Show Late Answer");
-            $(toggle_answer_button).addClass("btn btn-warning");
-            $(toggle_answer_button).css("margin-left", "5px");
+        // let p = document.createElement("p");
+        // this.jInputDiv.appendChild(p);
+        // var tsString = "";
+        // if (data.timestamp) {
+        //     tsString = new Date(data.timestamp).toLocaleString();
+        // } else {
+        //     tsString = "";
+        // }
+        // $(p).text(tsString);
+        // if (data.last_answer) {
+        //     this.current_answer = "ontime";
+        //     let toggle_answer_button = document.createElement("button");
+        //     toggle_answer_button.type = "button";
+        //     $(toggle_answer_button).text("Show Late Answer");
+        //     $(toggle_answer_button).addClass("btn btn-warning");
+        //     $(toggle_answer_button).css("margin-left", "5px");
 
-            $(toggle_answer_button).click(
-                function () {
-                    var display_timestamp, button_text;
-                    if (this.current_answer === "ontime") {
-                        this.jTextArea.value = data.last_answer;
-                        this.answer = data.last_answer;
-                        display_timestamp = new Date(
-                            data.last_timestamp
-                        ).toLocaleString();
-                        button_text = "Show on-Time Answer";
-                        this.current_answer = "late";
-                    } else {
-                        this.jTextArea.value = data.answer;
-                        this.answer = data.answer;
-                        display_timestamp = tsString;
-                        button_text = "Show Late Answer";
-                        this.current_answer = "ontime";
-                    }
-                    this.renderMath(this.answer);
-                    $(p).text(`Submitted: ${display_timestamp}`);
-                    $(toggle_answer_button).text(button_text);
-                }.bind(this)
-            );
+        //     $(toggle_answer_button).click(
+        //         function () {
+        //             var display_timestamp, button_text;
+        //             if (this.current_answer === "ontime") {
+        //                 this.jTextArea.value = data.last_answer;
+        //                 this.answer = data.last_answer;
+        //                 display_timestamp = new Date(
+        //                     data.last_timestamp
+        //                 ).toLocaleString();
+        //                 button_text = "Show on-Time Answer";
+        //                 this.current_answer = "late";
+        //             } else {
+        //                 this.jTextArea.value = data.answer;
+        //                 this.answer = data.answer;
+        //                 display_timestamp = tsString;
+        //                 button_text = "Show Late Answer";
+        //                 this.current_answer = "ontime";
+        //             }
+        //             this.renderMath(this.answer);
+        //             $(p).text(`Submitted: ${display_timestamp}`);
+        //             $(toggle_answer_button).text(button_text);
+        //         }.bind(this)
+        //     );
 
-            this.buttonDiv.appendChild(toggle_answer_button);
-        }
-        let feedbackStr = "Your current saved answer is shown above.";
-        if (typeof data.score !== "undefined") {
-            feedbackStr = `Score: ${data.score}`;
-        }
-        if (data.comment) {
-            feedbackStr += ` -- ${data.comment}`;
-        }
-        this.feedbackDiv.innerHTML = feedbackStr;
+        //     this.buttonDiv.appendChild(toggle_answer_button);
+        // }
+        // let feedbackStr = "Your current saved answer is shown above.";
+        // if (typeof data.score !== "undefined") {
+        //     feedbackStr = `Score: ${data.score}`;
+        // }
+        // if (data.comment) {
+        //     feedbackStr += ` -- ${data.comment}`;
+        // }
+        // this.feedbackDiv.innerHTML = feedbackStr;
 
-        $(this.feedbackDiv).removeClass("alert-danger");
-        $(this.feedbackDiv).addClass("alert alert-success");
+        // $(this.feedbackDiv).removeClass("alert-danger");
+        // $(this.feedbackDiv).addClass("alert alert-success");
     }
 
     disableInteraction() {
-        this.jTextArea.disabled = true;
+        console.log('hparsons, disableinteraction')
+        // this.jTextArea.disabled = true;
     }
 }
-
+HParsons.counter = 0;
 /*=================================
 == Find the custom HTML tags and ==
 ==   execute our code on them    ==
 =================================*/
 $(document).bind("runestone:login-complete", function () {
-    $("[data-component=shortanswer]").each(function () {
+    $("[data-component=hparsons]").each(function () {
         if ($(this).closest("[data-component=timedAssessment]").length == 0) {
             // If this element exists within a timed component, don't render it here
-            try {
-                saList[this.id] = new ShortAnswer({
+            console.log('rendering hparsons')
+            // try {
+                hpList[this.id] = new HParsons({
                     orig: this,
                     useRunestoneServices: eBookConfig.useRunestoneServices,
                 });
-            } catch (err) {
-                console.log(`Error rendering ShortAnswer Problem ${this.id}
-                Details: ${err}`);
-            }
+            // } catch (err) {
+            //     console.log(`Error rendering ShortAnswer Problem ${this.id}
+            //     Details: ${err}`);
+            // }
         }
     });
 });
diff --git a/runestone/hparsons/js/sa_template.html b/runestone/hparsons/js/sa_template.html
deleted file mode 100644
index caaf4f8f2..000000000
--- a/runestone/hparsons/js/sa_template.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
- Short Answer -
-

- -

-
-
-
- -
-
-
-
- -
-
Your current saved answer is shown above.
-
-
-

shortanswer (question1)

-
-
\ No newline at end of file diff --git a/runestone/hparsons/js/shortanswer.js b/runestone/hparsons/js/shortanswer.js deleted file mode 100644 index 1836ea7c6..000000000 --- a/runestone/hparsons/js/shortanswer.js +++ /dev/null @@ -1,295 +0,0 @@ -/*========================================== -======= Master shortanswer.js ======== -============================================ -=== This file contains the JS for === -=== the Runestone shortanswer component. === -============================================ -=== Created by === -=== Isaiah Mayerchak === -=== 7/2/15 === -=== Brad Miller === -=== 2019 === -==========================================*/ - -import RunestoneBase from "../../common/js/runestonebase.js"; -import "./../css/shortanswer.css"; - -export var saList; -if (saList === undefined) saList = {}; // Dictionary that contains all instances of shortanswer objects - -export default class ShortAnswer extends RunestoneBase { - constructor(opts) { - super(opts); - if (opts) { - var orig = opts.orig; // entire

element that will be replaced by new HTML - this.useRunestoneServices = - opts.useRunestoneServices || eBookConfig.useRunestoneServices; - this.origElem = orig; - this.divid = orig.id; - this.question = this.origElem.innerHTML; - this.optional = false; - if ($(this.origElem).is("[data-optional]")) { - this.optional = true; - } - if ($(this.origElem).is("[data-mathjax]")) { - this.mathjax = true; - } - this.renderHTML(); - this.caption = "shortanswer"; - this.addCaption("runestone"); - this.checkServer("shortanswer", true); - } - } - - renderHTML() { - this.containerDiv = document.createElement("div"); - this.containerDiv.id = this.divid; - $(this.containerDiv).addClass(this.origElem.getAttribute("class")); - this.newForm = document.createElement("form"); - this.newForm.id = this.divid + "_journal"; - this.newForm.name = this.newForm.id; - this.newForm.action = ""; - this.containerDiv.appendChild(this.newForm); - this.fieldSet = document.createElement("fieldset"); - this.newForm.appendChild(this.fieldSet); - this.legend = document.createElement("legend"); - this.legend.innerHTML = "Short Answer"; - this.fieldSet.appendChild(this.legend); - this.firstLegendDiv = document.createElement("div"); - this.firstLegendDiv.innerHTML = this.question; - $(this.firstLegendDiv).addClass("journal-question"); - this.fieldSet.appendChild(this.firstLegendDiv); - this.jInputDiv = document.createElement("div"); - this.jInputDiv.id = this.divid + "_journal_input"; - this.fieldSet.appendChild(this.jInputDiv); - this.jOptionsDiv = document.createElement("div"); - $(this.jOptionsDiv).addClass("journal-options"); - this.jInputDiv.appendChild(this.jOptionsDiv); - this.jLabel = document.createElement("label"); - $(this.jLabel).addClass("radio-inline"); - this.jOptionsDiv.appendChild(this.jLabel); - this.jTextArea = document.createElement("textarea"); - let self = this; - this.jTextArea.onchange = function () { - self.isAnswered = true; - }; - this.jTextArea.id = this.divid + "_solution"; - $(this.jTextArea).attr("aria-label", "textarea"); - $(this.jTextArea).css("display:inline, width:530px"); - $(this.jTextArea).addClass("form-control"); - this.jTextArea.rows = 4; - this.jTextArea.cols = 50; - this.jLabel.appendChild(this.jTextArea); - this.jTextArea.onchange = function () { - this.feedbackDiv.innerHTML = "Your answer has not been saved yet!"; - $(this.feedbackDiv).removeClass("alert-success"); - $(this.feedbackDiv).addClass("alert alert-danger"); - }.bind(this); - this.fieldSet.appendChild(document.createElement("br")); - if (this.mathjax) { - this.renderedAnswer = document.createElement("div"); - $(this.renderedAnswer).addClass("latexoutput"); - this.fieldSet.appendChild(this.renderedAnswer); - } - this.buttonDiv = document.createElement("div"); - this.fieldSet.appendChild(this.buttonDiv); - this.submitButton = document.createElement("button"); - $(this.submitButton).addClass("btn btn-success"); - this.submitButton.type = "button"; - this.submitButton.textContent = "Save"; - this.submitButton.onclick = function () { - this.checkCurrentAnswer(); - this.logCurrentAnswer(); - this.renderFeedback(); - }.bind(this); - this.buttonDiv.appendChild(this.submitButton); - this.randomSpan = document.createElement("span"); - this.randomSpan.innerHTML = "Instructor's Feedback"; - this.fieldSet.appendChild(this.randomSpan); - this.otherOptionsDiv = document.createElement("div"); - $(this.otherOptionsDiv).css("padding-left:20px"); - $(this.otherOptionsDiv).addClass("journal-options"); - this.fieldSet.appendChild(this.otherOptionsDiv); - // add a feedback div to give user feedback - this.feedbackDiv = document.createElement("div"); - //$(this.feedbackDiv).addClass("bg-info form-control"); - //$(this.feedbackDiv).css("width:530px, background-color:#eee, font-style:italic"); - $(this.feedbackDiv).css("width:530px, font-style:italic"); - this.feedbackDiv.id = this.divid + "_feedback"; - this.feedbackDiv.innerHTML = "You have not answered this question yet."; - $(this.feedbackDiv).addClass("alert alert-danger"); - //this.otherOptionsDiv.appendChild(this.feedbackDiv); - this.fieldSet.appendChild(this.feedbackDiv); - //this.fieldSet.appendChild(document.createElement("br")); - $(this.origElem).replaceWith(this.containerDiv); - // This is a stopgap measure for when MathJax is not loaded at all. There is another - // more difficult case that when MathJax is loaded asynchronously we will get here - // before MathJax is loaded. In that case we will need to implement something - // like `the solution described here `_ - if (typeof MathJax !== "undefined") { - this.queueMathJax(this.containerDiv) - } - } - - renderMath(value) { - if (this.mathjax) { - value = value.replace(/\$\$(.*?)\$\$/g, "\\[ $1 \\]"); - value = value.replace(/\$(.*?)\$/g, "\\( $1 \\)"); - $(this.renderedAnswer).text(value); - this.queueMathJax(this.renderedAnswer) - } - } - - checkCurrentAnswer() { } - - async logCurrentAnswer(sid) { - let value = $(document.getElementById(this.divid + "_solution")).val(); - this.renderMath(value); - this.setLocalStorage({ - answer: value, - timestamp: new Date(), - }); - let data = { - event: "shortanswer", - act: value, - answer: value, - div_id: this.divid, - }; - if (typeof sid !== "undefined") { - data.sid = sid; - } - await this.logBookEvent(data); - } - - renderFeedback() { - this.feedbackDiv.innerHTML = "Your answer has been saved."; - $(this.feedbackDiv).removeClass("alert-danger"); - $(this.feedbackDiv).addClass("alert alert-success"); - } - setLocalStorage(data) { - if (!this.graderactive) { - let key = this.localStorageKey(); - localStorage.setItem(key, JSON.stringify(data)); - } - } - checkLocalStorage() { - // Repopulates the short answer text - // which was stored into local storage. - var answer = ""; - if (this.graderactive) { - return; - } - var len = localStorage.length; - if (len > 0) { - var ex = localStorage.getItem(this.localStorageKey()); - if (ex !== null) { - try { - var storedData = JSON.parse(ex); - answer = storedData.answer; - } catch (err) { - // error while parsing; likely due to bad value stored in storage - console.log(err.message); - localStorage.removeItem(this.localStorageKey()); - return; - } - let solution = $("#" + this.divid + "_solution"); - solution.text(answer); - this.renderMath(answer); - this.feedbackDiv.innerHTML = - "Your current saved answer is shown above."; - $(this.feedbackDiv).removeClass("alert-danger"); - $(this.feedbackDiv).addClass("alert alert-success"); - } - } - } - restoreAnswers(data) { - // Restore answers from storage retrieval done in RunestoneBase - // sometimes data.answer can be null - if (!data.answer) { - data.answer = ""; - } - this.answer = data.answer; - this.jTextArea.value = this.answer; - this.renderMath(this.answer); - - let p = document.createElement("p"); - this.jInputDiv.appendChild(p); - var tsString = ""; - if (data.timestamp) { - tsString = new Date(data.timestamp).toLocaleString(); - } else { - tsString = ""; - } - $(p).text(tsString); - if (data.last_answer) { - this.current_answer = "ontime"; - let toggle_answer_button = document.createElement("button"); - toggle_answer_button.type = "button"; - $(toggle_answer_button).text("Show Late Answer"); - $(toggle_answer_button).addClass("btn btn-warning"); - $(toggle_answer_button).css("margin-left", "5px"); - - $(toggle_answer_button).click( - function () { - var display_timestamp, button_text; - if (this.current_answer === "ontime") { - this.jTextArea.value = data.last_answer; - this.answer = data.last_answer; - display_timestamp = new Date( - data.last_timestamp - ).toLocaleString(); - button_text = "Show on-Time Answer"; - this.current_answer = "late"; - } else { - this.jTextArea.value = data.answer; - this.answer = data.answer; - display_timestamp = tsString; - button_text = "Show Late Answer"; - this.current_answer = "ontime"; - } - this.renderMath(this.answer); - $(p).text(`Submitted: ${display_timestamp}`); - $(toggle_answer_button).text(button_text); - }.bind(this) - ); - - this.buttonDiv.appendChild(toggle_answer_button); - } - let feedbackStr = "Your current saved answer is shown above."; - if (typeof data.score !== "undefined") { - feedbackStr = `Score: ${data.score}`; - } - if (data.comment) { - feedbackStr += ` -- ${data.comment}`; - } - this.feedbackDiv.innerHTML = feedbackStr; - - $(this.feedbackDiv).removeClass("alert-danger"); - $(this.feedbackDiv).addClass("alert alert-success"); - } - - disableInteraction() { - this.jTextArea.disabled = true; - } -} - -/*================================= -== Find the custom HTML tags and == -== execute our code on them == -=================================*/ -$(document).bind("runestone:login-complete", function () { - $("[data-component=shortanswer]").each(function () { - if ($(this).closest("[data-component=timedAssessment]").length == 0) { - // If this element exists within a timed component, don't render it here - try { - saList[this.id] = new ShortAnswer({ - orig: this, - useRunestoneServices: eBookConfig.useRunestoneServices, - }); - } catch (err) { - console.log(`Error rendering ShortAnswer Problem ${this.id} - Details: ${err}`); - } - } - }); -}); diff --git a/runestone/hparsons/js/timed_shortanswer.js b/runestone/hparsons/js/timed_shortanswer.js deleted file mode 100644 index 4ecfde956..000000000 --- a/runestone/hparsons/js/timed_shortanswer.js +++ /dev/null @@ -1,43 +0,0 @@ -import ShortAnswer from "./shortanswer.js"; - -export default class TimedShortAnswer extends ShortAnswer { - constructor(opts) { - super(opts); - this.renderTimedIcon(this.containerDiv); - this.hideButtons(); - } - hideButtons() { - $(this.submitButton).hide(); - } - renderTimedIcon(component) { - // renders the clock icon on timed components. The component parameter - // is the element that the icon should be appended to. - var timeIconDiv = document.createElement("div"); - var timeIcon = document.createElement("img"); - $(timeIcon).attr({ - src: "../_static/clock.png", - style: "width:15px;height:15px", - }); - timeIconDiv.className = "timeTip"; - timeIconDiv.title = ""; - timeIconDiv.appendChild(timeIcon); - $(component).prepend(timeIconDiv); - } - checkCorrectTimed() { - return "I"; // we ignore this in the grading - } - hideFeedback() { - $(this.feedbackDiv).hide(); - } -} - -if (typeof window.component_factory === "undefined") { - window.component_factory = {}; -} - -window.component_factory.shortanswer = function (opts) { - if (opts.timed) { - return new TimedShortAnswer(opts); - } - return new ShortAnswer(opts); -}; diff --git a/runestone/hparsons/shortanswer.py b/runestone/hparsons/shortanswer.py deleted file mode 100755 index 61d795dba..000000000 --- a/runestone/hparsons/shortanswer.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (C) 2011 Bradley N. Miller -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -__author__ = "" - -from docutils import nodes -from docutils.parsers.rst import directives -from runestone.mchoice import Assessment -from runestone.server.componentdb import ( - addQuestionToDB, - addHTMLToDB, - maybeAddToAssignment, -) -from runestone.common.runestonedirective import RunestoneDirective, RunestoneIdNode - - -def setup(app): - app.add_directive("shortanswer", JournalDirective) - app.add_node(JournalNode, html=(visit_journal_node, depart_journal_node)) - app.add_config_value("shortanswer_div_class", "journal alert alert-warning", "html") - app.add_config_value( - "shortanswer_optional_div_class", "journal alert alert-success", "html" - ) - - -TEXT_START = """ -

-
-""" - -TEXT_END = """ -
-
-""" - - -class JournalNode(nodes.General, nodes.Element, RunestoneIdNode): - def __init__(self, options, **kwargs): - super(JournalNode, self).__init__(**kwargs) - self.runestone_options = options - - -def visit_journal_node(self, node): - div_id = node.runestone_options["divid"] - components = dict(node.runestone_options) - components.update({"divid": div_id}) - - node.delimiter = "_start__{}_".format(node.runestone_options["divid"]) - self.body.append(node.delimiter) - - res = TEXT_START % components - - self.body.append(res) - - -def depart_journal_node(self, node): - - components = dict(node.runestone_options) - - res = TEXT_END % components - self.body.append(res) - - addHTMLToDB( - node.runestone_options["divid"], - components["basecourse"], - "".join(self.body[self.body.index(node.delimiter) + 1 :]), - ) - - self.body.remove(node.delimiter) - - -class JournalDirective(Assessment): - """ -.. shortanswer:: uniqueid - :optional: - - text of the question goes here - - -config values (conf.py): - -- shortanswer_div_class - custom CSS class of the component's outermost div - """ - - required_arguments = 1 # the div id - optional_arguments = 0 - final_argument_whitespace = True - has_content = True - option_spec = Assessment.option_spec.copy() - option_spec.update({"mathjax": directives.flag}) - - node_class = JournalNode - - def run(self): - super(JournalDirective, self).run() - addQuestionToDB(self) - # Raise an error if the directive does not have contents. - self.assert_has_content() - - self.options["mathjax"] = "data-mathjax" if "mathjax" in self.options else "" - - journal_node = JournalNode(self.options, rawsource=self.block_text) - journal_node.source, journal_node.line = self.state_machine.get_source_and_line( - self.lineno - ) - - self.updateContent() - - self.state.nested_parse(self.content, self.content_offset, journal_node) - - env = self.state.document.settings.env - if self.options["optional"]: - self.options["divclass"] = env.config.shortanswer_optional_div_class - else: - self.options["divclass"] = env.config.shortanswer_div_class - - maybeAddToAssignment(self) - - return [journal_node] diff --git a/runestone/hparsons/test/__init__.py b/runestone/hparsons/test/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/runestone/hparsons/test/_sources/Figures/LutherBellPic.jpg b/runestone/hparsons/test/_sources/Figures/LutherBellPic.jpg deleted file mode 100644 index c6469a3ba0f5f7ee053902add1188bf99b1e4478..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64480 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
bMv9dVEag6itKK_9zh`p1I`Lu-WO8XjT#( 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 diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index f39ec1492..fd252111e 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -1,6 +1,6 @@ -=============================== -Testing: Short Answer Questions -=============================== +===================== +This Is A New Project +===================== .. Here is were you specify the content and order of your new book. @@ -13,24 +13,80 @@ Testing: Short Answer Questions .. Sources can also be included from subfolders of this directory. (e.g. "DataStructures/queues.rst"). +SECTION 1: Introduction +::::::::::::::::::::::: -Short Answer ------------- +Congratulations! If you can see this file you have probably successfully run the ``runestone init`` command. If you are looking at this as a source file you should now run ``runestone build`` to generate html files. Once you have run the build command you can run ``runestone serve`` and then view this in your browser at ``http://localhost:8000`` -.. shortanswer:: test_short_answer_1 +This is just a sample of what you can do. The index.rst file is the table of contents for your entire project. You can put all of your writing in the index, or you can include additional rst files. Those files may even be in subdirectories that you can reference using a relative path. - What are the colors in the rainbow? +:: -.. shortanswer:: test_short_answer_2 - :optional: - :mathjax: - What are the colors in the rainbow? - What is meaning of :math:`\pi r^2` - How about an image? + .. toctree:: + :maxdepth: 2 - .. image:: Figures/LutherBellPic.jpg - :width: 200 + some/path/myfile.rst - This is the famous Luther Bell! + +Section 2: Links +:::::::::::::::: + +Runestone uses the ``restructuredText`` (rst) markup language. We chose this over markdown largely because rst is extensible. Nearly all of the basic markup tasks are already handled by restructuredText. You should check out the docs for the basics of restructuredText (link below). Our extensions are all for the interactive elements. One key hint about restructuredText: Its like **Python** -- *indentation matters!* + +* `restructuredText Docs `_ +* `Runestone Docs `_ +* Join the discussion on our `Google Group `_ +* Tell us about problems on `Github `_ + + + +SECTION 3: Sample Directives +:::::::::::::::::::::::::::: + +ActiveCode +---------- + +.. hparsons:: codeexample1 + + content content content + + + +Multiple Choice +--------------- + +.. mchoice:: question1_2 + :multiple_answers: + :correct: a,b,d + :answer_a: red + :answer_b: yellow + :answer_c: black + :answer_d: green + :feedback_a: Red is a definitely on of the colors. + :feedback_b: Yes, yellow is correct. + :feedback_c: Remember the acronym...ROY G BIV. B stands for blue. + :feedback_d: Yes, green is one of the colors. + + Which colors might be found in a rainbow? (choose all that are correct) + +These are just two of the many interactive components for writing online course materials. You can see examples of all of them `On our Example Page `_ + +Now feel free to modify this file to start creating your own interactive page. + + +Section 4: Theme +::::::::::::::::::: + +You can add your own CSS or JS files to every page of a book by modifying ``setup.custom_css_files`` or ``setup.custom_js_files`` in conf.py. + +If you want to do more significant changes to the theme, you should copy the files you wish to modify from +the runestone/common/project/template/sphinx_bootstrap to a directory like ``_templates/``. Then make sure +the ``templates_path`` points to them in your conf.py. + +conf.py: + +.. code:: + + templates_path = ["_templates"] \ No newline at end of file diff --git a/runestone/hparsons/test/conf.py b/runestone/hparsons/test/conf.py index 6b0cf9737..935052f94 100644 --- a/runestone/hparsons/test/conf.py +++ b/runestone/hparsons/test/conf.py @@ -1,4 +1,6 @@ -# -*- coding: utf-8 -*- +# ************************************************************* +# |docname| - Sphinx configuration file for a Runestone project +# ************************************************************* # # Problem Solving with Algorithms and Data Structures documentation build configuration file, created by # sphinx-quickstart on Thu Oct 27 08:17:45 2011. @@ -16,7 +18,7 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('../modules')) +#sys.path.insert(0, os.path.abspath('../modules')) from runestone import runestone_static_dirs, runestone_extensions, setup import pkg_resources @@ -24,106 +26,157 @@ # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' +#needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ["sphinx.ext.mathjax"] + runestone_extensions() - -# ,'runestone.video','runestone.reveal','runestone.poll','runestone.tabbedStuff','runestone.disqus','runestone.codelens','runestone.activecode', 'runestone.assess', 'runestone.animation','runestone.meta', 'runestone.parsons', 'runestone.blockly', 'runestone.livecode'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = [ - pkg_resources.resource_filename("runestone", "common/project_template/_templates") -] +extensions = ['sphinx.ext.mathjax'] + runestone_extensions() +#,'runestone.video','runestone.reveal','runestone.poll','runestone.tabbedStuff','runestone.disqus','runestone.codelens','runestone.activecode', 'runestone.assess', 'runestone.animation','runestone.meta', 'runestone.parsons', 'runestone.blockly', 'runestone.livecode','runestone.accessibility'] # The suffix of source filenames. -source_suffix = ".rst" +source_suffix = '.rst' # The encoding of source files. -# source_encoding = 'utf-8-sig' +#source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = "index" +master_doc = 'index' # General information about the project. -project = "Runestone Interactive Overview" -copyright = "2015 yasiro01" +project = 'Runestone Interactive Overview' +copyright = '2017 amy21206' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = "0.0.1" +version = '0.0.1' # The full version, including alpha/beta/rc tags. -release = "0.0" +release = '0.0' # The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None +# for a list of supported languages. https://www.sphinx-doc.org/en/master/usage/configuration.html +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -# today = '' +#today = '' # Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' +#today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. -# default_role = None +#default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True +#add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -# add_module_names = True +#add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -# show_authors = False +#show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = "sphinx" +pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] +#modindex_common_prefix = [] + +# `keep_warnings `_: +# If true, keep warnings as “system message” paragraphs in the built documents. +# Regardless of this setting, warnings are always written to the standard error +# stream when sphinx-build is run. +keep_warnings = True # `rst_prolog `_: # A string of reStructuredText that will be included at the beginning of every # source file that is read. rst_prolog = ( - # For fill-in-the-blank questions, provide a convenient means to indicate a blank. - """ +# For fill-in-the-blank questions, provide a convenient means to indicate a blank. +""" .. |blank| replace:: :blank:`x` """ + +# For literate programming files, provide a convenient way to refer to a source file's name. See `runestone.lp.lp._docname_role`. +""".. |docname| replace:: :docname:`name` +""" ) +# Select whether to use server-side grading where possible. Server-side grading +# requires **all** the following: +# +# - The use of Runestone services (``eBookConfig.useRunestoneServices === true``) +# - Logging enabled (``eBookConfig.logLevel > 0``) +# +# The first two conditions cause the ``RunestoneBase.logBookEvent`` in ``runestonebase.js`` to post a student response to the server. The last conditions ensures that ``hsblog`` in ``ajax.py`` on the server will return a response containing grading information. +runestone_server_side_grading = False + +# Extensions +# ========== +# CodeChat +# -------- +# **CodeChat note:** A dict of {glob_, lexer_alias}, which uses lexer_alias +# (e.g. a lexer's `short name `_) to analyze +# any file wihch matches the given `glob +# `_. +CodeChat_lexer_for_glob = { + # Otherwise, Pygments picks the wrong lexer for CSS... + '*.css': 'CSS', + # ... and for JavaScript. + '*.js': 'JavaScript', +} +# +# **CodeChat note::** This is a list of exclude_patterns_ which applies only to +# source documents; exclude_patterns_ will exclude the given files from all of +# Sphinx (for example, files here won't be included even if they're mentioned in +# html_static_path_. +CodeChat_excludes = [] +# +# Inline syntax highlight +# ----------------------- +# `inline_highlight_respect_highlight `_: +# Use the language specified by the ``highlight`` directive to syntax highlight ``code`` role contents. +inline_highlight_respect_highlight = True +inline_highlight_literals = False # -- Options for HTML output --------------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = "sphinx_bootstrap" +# The theme to use for HTML and HTML Help pages. sphinx_bootstrap is provided with +# Runestone. Other themes are built into sphinx: +# https://www.sphinx-doc.org/en/master/usage/theming.html?highlight=html_theme_path#using-a-theme +html_theme = 'sphinx_bootstrap' + +# If using a non-sphinx theme, the path to the theme folder must be in this list +html_theme_path = [pkg_resources.resource_filename('runestone', 'common/project_template/_templates/plugin_layouts')] + +# To override individual templates from the theme, you can make a directory and add its path +# relative to this file to the templates_path list. In it, place copies of any template files +# you wish to override - your template file(s) will be used instead of the default ones from the theme +#templates_path = ['_templates'] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -# html_theme_options = {'nosidebar': 'true'} html_theme_options = { # Navigation bar title. (Default: ``project`` value) - "navbar_title": "Runestone Default", + 'navbar_title': "hparsons-test", + # Tab name for entire site. (Default: "Site") - "navbar_site_name": "Chapters", + 'navbar_site_name': "Chapters", + # Global TOC depth for "site" navbar tab. (Default: 1) # Switching to -1 shows all levels. - "globaltoc_depth": 1, + 'globaltoc_depth': 1, + # Include hidden TOCs in Site navbar? # # Note: If this is "false", you cannot have mixed ``:hidden:`` and @@ -131,16 +184,20 @@ # will break. # # Values: "true" (default) or "false" - "globaltoc_includehidden": "true", + 'globaltoc_includehidden': "true", + # HTML navbar class (Default: "navbar") to attach to
element. # For black navbar, do "navbar navbar-inverse" - "navbar_class": "navbar", + 'navbar_class': "navbar", + # Fix navigation bar to top of page? # Values: "true" (default) or "false" - "navbar_fixed_top": "true", + 'navbar_fixed_top': "true", + # Location of link to source. # Options are "nav" (default), "footer" or anything else to exclude. - "source_link_position": "nav", + 'source_link_position': "nav", + # Bootswatch (http://bootswatch.com/) theme. # # Options are nothing with "" (default) or the name of a valid theme @@ -150,80 +207,107 @@ #'bootswatch_theme': "slate", } -# html_style = "style.css" +# Additional templates that should be rendered to pages, maps page names to +# template names. +# https://www.sphinx-doc.org/en/master/usage/configuration.html?highlight=html_sidebars#confval-html_additional_pages +#html_additional_pages = {} + +# A list of paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# Example: html_static_path = runestone_static_dirs() + ['_static', 'other'] +html_static_path = runestone_static_dirs() -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = [ - pkg_resources.resource_filename( - "runestone", "common/project_template/_templates/plugin_layouts" - ) -] +# List of extra stylesheets that should be added to all html pages +# Files must be on a path contained in html_static_path +#setup.custom_css_files = ["sample.css", "sample2.css"] +# List of extra js files that should be added to all html pages +# Items may be a file name or a dict with properties {"file":FILENAME, "key1", "value1", "key2, "value2"...} +# in which case file should have the file name and other key/value pairs are used as attrs +# on the script tag. The sample below will set sample2.js's script tag to have the defer attr +# Files must be on a path contained in html_static_path +#setup.custom_js_files = ["sample.css", {"file": "sample2.js", "defer": ""}] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = "Runestone Interactive Overview" +html_title = 'Runestone Interactive Overview' # A shorter title for the navigation bar. Default is the same as html_title. -html_short_title = "Runestone Interactive Overview" - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. +html_short_title ='Runestone Interactive Overview' -# logo is included in layout file -# html_logo = "../source/_static/logo_small.png" +# Logo is included at the top of the page +#html_logo = "../source/_static/logo_small.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". - -html_static_path = runestone_static_dirs() +#html_favicon = None # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' +#html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} +#html_use_smartypants = True # If false, no module index is generated. -# html_domain_indices = True +#html_domain_indices = True # If false, no index is generated. -# html_use_index = True +#html_use_index = True # If true, the index is split into individual pages for each letter. -# html_split_index = False +#html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True +#html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True +#html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -# html_use_opensearch = '' +#html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None +#html_file_suffix = None + +# It True, sets js files from Sphinx & Runestone to be loaded with defer attr +# substantially speeding up page rendering. May cause issues with books that +# have custom directives or raw html that assume jquery or another library +# is loaded before body is parsed. +html_defer_js = True # Output file base name for HTML help builder. -htmlhelp_basename = "PythonCoursewareProjectdoc" +htmlhelp_basename = 'PythonCoursewareProjectdoc' + +# .. _accessibility_style: +# +# accessibility_style +# ------------------- +# This config value is defined in the `../../accessibility/accessibility.py` extension. +# By this config value you can select what accessibility stylesheet +# you want to add (``normal``, ``light``, ``darkest`` or ``none``). +#accessibility_style = 'normal' + +# Config values for specific Runestone components +# +#activecode_div_class = 'runestone explainer ac_section alert alert-warning' +#activecode_hide_load_history = False +#mchoice_div_class = 'runestone alert alert-warning' +#clickable_div_class = 'runestone alert alert-warning' +#codelens_div_class = 'alert alert-warning cd_section' +#dragndrop_div_class = 'runestone' +#fitb_div_class = 'runestone' +#parsons_div_class = 'runestone' +#poll_div_class = 'alert alert-warning' +#shortanswer_div_class = 'journal alert alert-warning' +#shortanswer_optional_div_class = 'journal alert alert-success' +#showeval_div_class = 'runestone explainer alert alert-warning' +#tabbed_div_class = 'alert alert-warning' diff --git a/runestone/hparsons/test/pavement.py b/runestone/hparsons/test/pavement.py index 4ccdbbc6b..02be4cbfb 100644 --- a/runestone/hparsons/test/pavement.py +++ b/runestone/hparsons/test/pavement.py @@ -1,50 +1,67 @@ -import paver -from paver.easy import * +import os +import sys +import pkg_resources +from socket import gethostname + +from paver.easy import options, Bunch import paver.setuputils -paver.setuputils.install_distutils_tasks() -import os, sys +from runestone import get_master_url +from runestone import build # NOQA: F401 -- build is called implicitly by the paver driver. from runestone.server import get_dburl -from sphinxcontrib import paverutils -import pkg_resources +paver.setuputils.install_distutils_tasks() sys.path.append(os.getcwd()) -home_dir = os.getcwd() -master_url = "http://127.0.0.1:8000" -master_app = "runestone" -serving_dir = "./build/sa_test" +# The project name, for use below. +project_name = 'hparsons-test' + +master_url = 'http://127.0.0.1:8000' +if not master_url: + master_url = get_master_url() + +# The root directory for ``runestone serve``. +serving_dir = "./build/" + project_name +# The destination directory for ``runestone deploy``. dest = "../../static" options( - sphinx=Bunch(docroot="."), + sphinx=Bunch(docroot=".",), + build=Bunch( - builddir="./build/sa_test", + builddir=serving_dir, sourcedir="_sources", - outdir="./build/sa_test", + outdir=serving_dir, confdir=".", - project_name="sa_test", template_args={ - "course_id": "sa_test", - "login_required": "false", - "appname": master_app, - "loglevel": 0, - "course_url": master_url, - "use_services": "false", - "python3": "false", - "dburl": "", - "downloads_enabled": "true", - "enable_chatcodes": "false", - "allow_pairs": "false", - "basecourse": "sa_test", - }, - ), + 'login_required': 'false', + 'loglevel': 0, + 'course_title': project_name, + 'python3': 'false', + 'dburl': '', + 'default_ac_lang': 'python', + 'downloads_enabled': 'false', + 'enable_chatcodes': 'false', + 'allow_pairs': 'false', + 'dynamic_pages': False, + 'use_services': 'false', + 'basecourse': project_name, + 'course_id': project_name, + # These are used for non-dynamic books. + 'appname': 'runestone', + 'course_url': master_url, + } + ) ) +# if we are on runestone-deploy then use the proxy server not canterbury +if gethostname() == 'runestone-deploy': + del options.build.template_args['jobe_server'] + del options.build.template_args['proxy_uri_runs'] + del options.build.template_args['proxy_uri_files'] + version = pkg_resources.require("runestone")[0].version -options.build.template_args["runestone_version"] = version +options.build.template_args['runestone_version'] = version # If DBURL is in the environment override dburl -options.build.template_args["dburl"] = get_dburl(outer=locals()) - -from runestone import build # build is called implicitly by the paver driver. +options.build.template_args['dburl'] = get_dburl(outer=locals()) diff --git a/runestone/hparsons/test/test_shortanswer.py b/runestone/hparsons/test/test_shortanswer.py deleted file mode 100644 index 182705ff4..000000000 --- a/runestone/hparsons/test/test_shortanswer.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Test Short Answer question directive -""" - -__author__ = "yasinovskyy" - -DIV_ID = "test_short_answer_1" - - -def get_sa(selenium_utils): - selenium_utils.wait_until_ready(DIV_ID) - selenium_utils.scroll_to_top() - return selenium_utils.driver.find_element_by_id(DIV_ID) - - -def click_button(sa_element): - sa_element.find_element_by_tag_name("button").click() - - -def test_sa1(selenium_utils_get): - """No input. Button not clicked""" - t1 = get_sa(selenium_utils_get) - fb = t1.find_element_by_id(f"{DIV_ID}_feedback") - assert "alert-danger" in fb.get_attribute("class") - - -def test_sa2(selenium_utils_get): - """No input. Button clicked""" - t1 = get_sa(selenium_utils_get) - click_button(t1) - fb = t1.find_element_by_id(f"{DIV_ID}_feedback") - assert "alert-success" in fb.get_attribute("class") - - -def test_sa3(selenium_utils_get): - """Answer entered""" - t1 = get_sa(selenium_utils_get) - ta = t1.find_element_by_id(f"{DIV_ID}_solution") - ta.clear() - ta.send_keys("My answer") - - click_button(t1) - - fb = t1.find_element_by_id(f"{DIV_ID}_feedback") - assert fb is not None - assert "alert-success" in fb.get_attribute("class") - - -# TODO: this is the same as ``_test_sa3``. -def test_sa4(selenium_utils_get): - """Answer entered and cleared""" - t1 = get_sa(selenium_utils_get) - ta = t1.find_element_by_id(f"{DIV_ID}_solution") - ta.clear() - ta.send_keys("My answer") - - click_button(t1) - - fb = t1.find_element_by_id(f"{DIV_ID}_feedback") - assert fb is not None - assert "alert-success" in fb.get_attribute("class") diff --git a/runestone/hparsons/toctree.rst b/runestone/hparsons/toctree.rst index 0bac15fe7..cc2bcbc2b 100644 --- a/runestone/hparsons/toctree.rst +++ b/runestone/hparsons/toctree.rst @@ -8,4 +8,3 @@ Accessibility *.py js/*.js css/*.css - test/test_*.py diff --git a/runestone/mchoice/js/mchoice.js b/runestone/mchoice/js/mchoice.js index 9d1c41cfe..1e26079d3 100644 --- a/runestone/mchoice/js/mchoice.js +++ b/runestone/mchoice/js/mchoice.js @@ -21,6 +21,7 @@ window.mcList = {}; // Multiple Choice dictionary export default class MultipleChoice extends RunestoneBase { constructor(opts) { super(opts); + console.log('mchoice') opts = opts || {}; var orig = opts.orig; // entire
    element this.origElem = orig; @@ -127,6 +128,7 @@ export default class MultipleChoice extends RunestoneBase { renderMCContainer() { this.containerDiv = document.createElement("div"); $(this.containerDiv).html(this.question); + $(this.containerDiv).addClass('mctesttest'); $(this.containerDiv).addClass(this.origElem.getAttribute("class")); this.containerDiv.id = this.divid; } diff --git a/setup.py b/setup.py index c74e62bac..e45f4fa6e 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ dependencies = [l.strip() for l in fh] -VERSION = "6.0.7" +VERSION = "6.0.8" # These pre-install hooks are useful to make sure any pre-requisite # programs that are not pip installable are in place. diff --git a/webpack.index.js b/webpack.index.js index 6c27ba631..f8646382c 100644 --- a/webpack.index.js +++ b/webpack.index.js @@ -70,6 +70,7 @@ const module_map = { khanex: () => import("./runestone/khanex/js/khanex.js"), lp_build: () => import("./runestone/lp/js/lp.js"), multiplechoice: () => import("./runestone/mchoice/js/timedmc.js"), + hparsons: () => import("./runestone/hparsons/js/hparsons.js"), parsons: () => import("./runestone/parsons/js/timedparsons.js"), poll: () => import("./runestone/poll/js/poll.js"), quizly: () => import("./runestone/quizly/js/quizly.js"), From 5ac48b5b4ea353633e446aff29148ddc3728a565 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Wed, 16 Feb 2022 00:29:47 -0500 Subject: [PATCH 03/31] :tada: A working version of the tool! --- runestone/hparsons/hparsons.py | 6 ++--- runestone/hparsons/js/hparsons.js | 30 +++++++++------------- runestone/hparsons/test/_sources/index.rst | 2 +- runestone/mchoice/js/mchoice.js | 3 ++- test.sh | 6 +++++ 5 files changed, 24 insertions(+), 23 deletions(-) create mode 100755 test.sh diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index 55bfc8f38..c8003c8e2 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -91,10 +91,10 @@ class HParsonsDirective(Assessment): """ - required_arguments = 0 # the div id - optional_arguments = 0 + required_arguments = 1 # the div id + optional_arguments = 0 final_argument_whitespace = True - has_content = False + has_content = True option_spec = Assessment.option_spec.copy() # seem to be defining the type of the options # option_spec.update({"mathjax": directives.flag}) diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js index 5cc947aa9..c694055b5 100644 --- a/runestone/hparsons/js/hparsons.js +++ b/runestone/hparsons/js/hparsons.js @@ -22,34 +22,26 @@ if (hpList === undefined) hpList = {}; export default class HParsons extends RunestoneBase { constructor(opts) { super(opts); - console.log('hparsons') if (opts) { // TODO: what is orig? var orig = opts.orig; // entire
     element that will be replaced by new HTML
                 this.containerDiv = orig;
    -            this.origElem = $(orig).find("pre.parsonsblocks")[0];
    +            this.origElem = $(orig).find(".hparsons")[0];
    +            console.log('hparsons')
                 console.log(orig)
    +            console.log(this.origElem)
                 this.useRunestoneServices =
                     opts.useRunestoneServices || eBookConfig.useRunestoneServices;
                 this.divid = orig.id;
     
                 // The element that is going to be replaced
    -            // this.elem = $(orig).find(".hparsons")[0];
                 // Find the question text and store it in .question
                 this.question = $(orig).find(`.hparsons_question`)[0];
                 // TODO: idk what this is with shortanswer
    -            // this.question = this.origElem.innerHTML;
    -            // this.optional = false;
    -            // if ($(this.origElem).is("[data-optional]")) {
    -            //     this.optional = true;
    -            // }
    -            // if ($(this.origElem).is("[data-mathjax]")) {
    -            //     this.mathjax = true;
    -            // }
                 this.renderHTML();
                 this.caption = "hparsons";
    -            // this.addCaption("runestone");
    -            // this.checkServer("hparsons", true);
    +            this.addCaption("runestone");
    +            this.checkServer("hparsons", true);
     
                 // Set the storageId (key for storing data)
                 var storageId = super.localStorageKey();
    @@ -63,13 +55,15 @@ export default class HParsons extends RunestoneBase {
     
         renderHTML() {
             console.log('renderhtml');
    -        const div = document.createElement('regex-element');
    -        div.setAttribute('input-type', 'parsons')
    -        div.id = 'abcd';
    -        $(this.origElem).replaceWith(div);
    +        // const div = document.createElement('regex-element');
    +        // div.setAttribute('input-type', 'parsons')
    +        // div.id = 'abcd';
    +        // console.log(this.origElem)
    +        $(this.origElem).html('');
    +        // console.log(this.origElem)
             // $(this.origElem).replaceWith(document.createElement('regex-element'));
             // $(this.elem).innerHTML = ``;
    -        console.log(div)
    +        // console.log(div)
         }
     
         checkCurrentAnswer() { }
    diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst
    index fd252111e..b2874c8eb 100644
    --- a/runestone/hparsons/test/_sources/index.rst
    +++ b/runestone/hparsons/test/_sources/index.rst
    @@ -50,7 +50,7 @@ ActiveCode
     
     .. hparsons:: codeexample1
     
    -   content content content
    +    content content content
     
     
     
    diff --git a/runestone/mchoice/js/mchoice.js b/runestone/mchoice/js/mchoice.js
    index 1e26079d3..5279b517b 100644
    --- a/runestone/mchoice/js/mchoice.js
    +++ b/runestone/mchoice/js/mchoice.js
    @@ -21,9 +21,10 @@ window.mcList = {}; // Multiple Choice dictionary
     export default class MultipleChoice extends RunestoneBase {
         constructor(opts) {
             super(opts);
    -        console.log('mchoice')
             opts = opts || {};
             var orig = opts.orig; // entire 
      element + console.log('mchoice') + console.log(orig) this.origElem = orig; this.useRunestoneServices = opts.useRunestoneServices; this.multipleanswers = false; diff --git a/test.sh b/test.sh new file mode 100755 index 000000000..3dbdecbb2 --- /dev/null +++ b/test.sh @@ -0,0 +1,6 @@ +#!/bin/bash +npm run build +pip install . +cd runestone/hparsons/test +runestone build +runestone serve \ No newline at end of file From e4a3e7cd4a6d2d1e1a2c546c4aca8f4ca7757600 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Wed, 16 Feb 2022 21:06:53 -0500 Subject: [PATCH 04/31] :sparkles: A working version with multiple instances of hparsons --- runestone/hparsons/js/regex-element.js | 118 ++++++++------------- runestone/hparsons/test/_sources/index.rst | 4 + test.sh | 4 + 3 files changed, 55 insertions(+), 71 deletions(-) diff --git a/runestone/hparsons/js/regex-element.js b/runestone/hparsons/js/regex-element.js index 9be7fc770..c508c6e65 100644 --- a/runestone/hparsons/js/regex-element.js +++ b/runestone/hparsons/js/regex-element.js @@ -3043,9 +3043,10 @@ class ParsonsInput { _dragSortable; parentElement; _prevPosition; - constructor() { + constructor(parentElement) { this.el = document.createElement('div'); - this.el.id = 'parsons-input'; + this.parentElement = parentElement; + this.el.id = 'regextool-' + this.parentElement.toolNumber + '-parsons-input'; this.el.append('Drag or click to select from the symbols below to form your regex'); this._dragArea = document.createElement('div'); this.el.appendChild(this._dragArea); @@ -3077,7 +3078,6 @@ class ParsonsInput { animation: 150 }); this._initSortable(); - this.parentElement = null; } getText = () => { let ret = ''; @@ -15506,20 +15506,18 @@ class TextInput { groups; parentElement; droppedText; - constructor() { + constructor(parentElement) { + this.parentElement = parentElement; this.el = document.createElement('div'); - this.el.id = 'regex-input'; + this.el.id = 'regextool-' + this.parentElement.toolNumber + '-regex-input'; this.el.classList.add('regex-input'); this.quill = null; this.groups = new Array(); - // this.el.classList.add('regex-textbox') - // this.el.setAttribute("rows", "1"); - this.parentElement = null; this.droppedText = false; } initQuill = () => { // initializing quill - this.quill = new Quill('#regex-input', { + this.quill = new Quill('#regextool-' + this.parentElement.toolNumber + '-regex-input', { modules: { toolbar: false }, @@ -15654,20 +15652,20 @@ class TestStringInput { parentElement; slotName; highlightMode; - constructor(slotName) { + constructor(slotName, parentElement) { + this.parentElement = parentElement; this.el = document.createElement('div'); this.slotName = slotName; - this.el.id = 'test-string-input' + slotName; + this.el.id = 'regextool-' + this.parentElement.toolNumber + '-test-string-input' + slotName; this.el.classList.add('regex-test-string'); this.quill = null; // console.log(Quill); this.droppedText = false; - this.parentElement = null; this.highlightMode = 're.finditer'; } initQuill = () => { // initializing quill - this.quill = new Quill('#test-string-input' + this.slotName, { + this.quill = new Quill('#regextool-' + this.parentElement.toolNumber + '-test-string-input' + this.slotName, { modules: { toolbar: false }, @@ -15799,27 +15797,19 @@ class StatusOutput { // The input element el; text; - constructor() { + parentElement; + constructor(parentElement) { + this.parentElement = parentElement; this.el = document.createElement('div'); this.text = document.createElement('textarea'); this.el.appendChild(this.text); - this.text.id = 'status-output'; + this.text.id = 'regextool-' + this.parentElement.toolNumber + '-status-output'; this.el.classList.add('regex-textbox'); // this.text.setAttribute("rows", "10"); this.text.value = 'initializing...\n'; } } -class TestButton { - // The input element - el; - constructor() { - this.el = document.createElement('button'); - this.el.id = 'test-button'; - this.el.innerText = 'match!'; - } -} - class RegexOptions { el; triggerButton; @@ -15827,7 +15817,9 @@ class RegexOptions { flags; buttons; selectedFlags; - constructor() { + parentElement; + constructor(parentElement) { + this.parentElement = parentElement; this.el = document.createElement('div'); this.el.classList.add('regex-options-dropdown'); this.triggerButton = document.createElement('button'); @@ -15910,14 +15902,16 @@ class UnitTestTable { // TODO: refactor this into strictgroup. noGroupsAllowed; latestResults; + latestStatus; columnsEnabled; parentElement; // for saving current index testcaseIndex; - constructor() { + constructor(parentElement) { + this.parentElement = parentElement; // init the element in HTML this.el = document.createElement('div'); - this.el.id = 'unittest-table'; + this.el.id = 'regextool-' + this.parentElement.toolNumber + '-unittest-table'; this.el.classList.add('regex-unittest'); // the element is hidden initially. this.el.classList.add('collapse'); @@ -15937,13 +15931,13 @@ class UnitTestTable { this.testCaseCount = 0; this.hintRevealed = []; this.latestResults = []; + this.latestStatus = ''; // not matching groups strictly by default this.strictGroup = false; // using strict match by default this.strictMatch = true; // allow groups by default this.noGroupsAllowed = false; - this.parentElement = null; this.testcaseIndex = 0; } // not used: Return value: 'Pass' if all pass, 'Error' if one error, 'Fail' if no error but at least one fail @@ -15985,7 +15979,7 @@ class UnitTestTable { } } const result = { success: true, match: matches, errorMessage: null }; - this._createRow(index, testCase, result); + this.latestStatus = this._createRow(index, testCase, result); }; builtinRead(x) { if (window.Sk.builtinFiles === undefined || window.Sk.builtinFiles["files"][x] === undefined) @@ -16011,7 +16005,7 @@ class UnitTestTable { read: this.builtinRead }); window.Sk.importMainWithBody("", false, pyCode, true); - return 'Pass'; + return this.latestStatus; }; // returns: 'Pass' if pass, 'Fail' if fail, 'Error' if error _createRow = (index, testCase, result) => { @@ -16021,6 +16015,7 @@ class UnitTestTable { // creating the status(the first) column const row = document.createElement('tr'); let status = result.success ? (JSON.stringify(result.match) === JSON.stringify(testCase.expect) ? 'Pass' : 'Fail') : 'Error'; + console.log(status); // if (status == 'Pass' && JSON.stringify(testCase.expect) != '[]' && this.noGroupsAllowed && window.pyodide.globals.unit_match_group_cnt != 1) { // status = 'Fail' // // fail because no group is allowed @@ -16115,7 +16110,9 @@ class RegexStatusTag { // The input element el; status; - constructor() { + parentElement; + constructor(parentElement) { + this.parentElement = parentElement; this.el = document.createElement('span'); this.el.classList.add('regex-status'); this.status = ''; @@ -16167,8 +16164,6 @@ class RegexElement extends HTMLElement { negativePrevText; // Python output statusOutput; - // The button to trigger matching - testButton; // *temporary: The checkbox to enable always check (will be integrated in options later) checkWhileTyping; positiveMatchResult; @@ -16187,9 +16182,11 @@ class RegexElement extends HTMLElement { _testStatusDiv; // highlights the result using findall. used for study 1 and 2. matchFindall; + static toolCount = 0; + toolNumber; outf(text) { - console.log('sk output'); - console.log(text); + // console.log('sk output') + // console.log(text) } builtinRead(x) { if (window.Sk.builtinFiles === undefined || window.Sk.builtinFiles["files"][x] === undefined) @@ -16261,6 +16258,9 @@ class RegexElement extends HTMLElement { }; constructor() { super(); + RegexElement.toolCount += 1; + console.log(RegexElement.toolCount); + this.toolNumber = RegexElement.toolCount; this.root = this.attachShadow({ mode: 'open' }); window.Sk.configure({ output: this.outf, @@ -16275,26 +16275,6 @@ class RegexElement extends HTMLElement { // unitTestButton.innerText = 'Run Unit Test'; // this.root.appendChild(unitTestButton); // unitTestButton.onclick = () => this.unitTestTable.check(this.regexInput.getText()); - // init elements: button for match - // TODO: disabled the button for study 0 and 1. - // this.root.appendChild(document.createElement('br')); - this.testButton = new TestButton(); - // this.root.appendChild(this.testButton.el); - // this.testButton.el.onclick = this.match; - // init elements: checkbox - // TODO[feature]: replace this with an option module - // const checkbox = document.createElement('input'); - // checkbox.setAttribute('type', 'checkbox'); - // checkbox.checked = false; - // this.root.appendChild(checkbox); - // this.root.append('always check on input'); - // this.checkWhileTyping = false; - // checkbox.addEventListener('change', () => { - // this.checkWhileTyping = checkbox.checked; - // if (this.checkWhileTyping) { - // this.match(); - // } - // }) // TODO: make this an option; for now always enabled the 'always check' for study 0 and 1. this.checkWhileTyping = true; // a div wrapping the input and the test case status @@ -16305,12 +16285,12 @@ class RegexElement extends HTMLElement { const inputDiv = document.createElement('div'); inputAndTestStatusDiv.appendChild(inputDiv); inputDiv.classList.add('regex-input-div'); - this.regexOptions = new RegexOptions(); + this.regexOptions = new RegexOptions(this); this.patternValidFlag = true; this._parsonsData = new Array(); this.parsonsExplanation = null; - this.regexStatus = new RegexStatusTag(); - this.regexInput = new ParsonsInput(); + this.regexStatus = new RegexStatusTag(this); + this.regexInput = new ParsonsInput(this); this.inputType = 'parsons'; // this.regexErrorMessage = document.createElement('div'); // this.regexErrorPosition = -1; @@ -16340,13 +16320,12 @@ class RegexElement extends HTMLElement { positiveTestStringDiv.appendChild(resetPositiveTestStringButton); resetPositiveTestStringButton.innerText = 'Reset'; resetPositiveTestStringButton.onclick = this.resetPositiveTestString; - this.positiveTestStringInput = new TestStringInput('positive'); + this.positiveTestStringInput = new TestStringInput('positive', this); this.positiveTestStringInput.slotName = 'positive'; this.appendChild(this.positiveTestStringInput.el); this.positiveTestStringInput.el.slot = 'positive-test-string-input'; this.positiveTestStringInput.initQuill(); this.positivePrevText = this.positiveTestStringInput.getText(); - this.positiveTestStringInput.parentElement = this; this.positiveTestStringInput.quill?.on('text-change', (delta, _, source) => { if (source == 'user') { const testStringInputEvent = { @@ -16384,13 +16363,12 @@ class RegexElement extends HTMLElement { negativeTestStringDiv.appendChild(resetNegativeTestStringButton); resetNegativeTestStringButton.innerText = 'Reset'; resetNegativeTestStringButton.onclick = this.resetNegativeTestString; - this.negativeTestStringInput = new TestStringInput('negative'); + this.negativeTestStringInput = new TestStringInput('negative', this); this.negativeTestStringInput.slotName = 'negative'; this.appendChild(this.negativeTestStringInput.el); this.negativeTestStringInput.el.slot = 'negative-test-string-input'; this.negativeTestStringInput.initQuill(); this.negativePrevText = this.negativeTestStringInput.getText(); - this.negativeTestStringInput.parentElement = this; this.negativeTestStringInput.quill?.on('text-change', (delta, _, source) => { if (source == 'user') { const testStringInputEvent = { @@ -16417,11 +16395,10 @@ class RegexElement extends HTMLElement { } }); // init element: unit test table - this.unitTestTable = new UnitTestTable(); - this.unitTestTable.parentElement = this; + this.unitTestTable = new UnitTestTable(this); this.root.appendChild(this.unitTestTable.el); // init element: python output - this.statusOutput = new StatusOutput(); + this.statusOutput = new StatusOutput(this); this.root.appendChild(this.statusOutput.el); // initialize the match result array this.positiveMatchResult = new Array(); @@ -16857,13 +16834,13 @@ class RegexElement extends HTMLElement { this._parsonsData = new Array(); this.parsonsExplanation = null; inputDiv.append('Your regular expression:'); - this.regexStatus = new RegexStatusTag(); + this.regexStatus = new RegexStatusTag(this); inputDiv.appendChild(this.regexStatus.el); inputDiv.appendChild(document.createElement('br')); // todo:(UI) fix the css for the input if (this.inputType == 'parsons') { // init elements: parsons regex input - this.regexInput = new ParsonsInput(); + this.regexInput = new ParsonsInput(this); inputDiv.appendChild(this.regexInput.el); this.regexInput.el.addEventListener('regexChanged', () => { this.regexInput.removeFormat(); @@ -16923,7 +16900,7 @@ class RegexElement extends HTMLElement { regex_slot.name = 'regex-input'; inputDiv.appendChild(regex_slot); // TODO: (refactor) rename RegexInput - this.regexInput = new TextInput(); + this.regexInput = new TextInput(this); this.appendChild(this.regexInput.el); this.regexInput.el.slot = 'regex-input'; this.regexInput.initQuill(); @@ -16985,13 +16962,12 @@ class RegexElement extends HTMLElement { } }); } - this.regexInput.parentElement = this; // this.regexErrorMessage = document.createElement('div'); // this.regexErrorMessage.classList.add('regex-error-message'); // inputDiv.appendChild(this.regexErrorMessage); // this.regexErrorPosition = -1; // init elements: regex options dropdown - this.regexOptions = new RegexOptions(); + this.regexOptions = new RegexOptions(this); // inputDiv.appendChild(this.regexOptions.el); } resetTool() { diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index b2874c8eb..b3107862c 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -53,6 +53,10 @@ ActiveCode content content content +.. hparsons:: codeexample2 + + content content content + Multiple Choice --------------- diff --git a/test.sh b/test.sh index 3dbdecbb2..2afab3851 100755 --- a/test.sh +++ b/test.sh @@ -1,4 +1,8 @@ #!/bin/bash +cd ~/code/regex +npm run build +cd ~/code/RunestoneComponents +cp ~/code/regex/packages/regex-element/regex-element.js ~/code/RunestoneComponents/runestone/hparsons/js/regex-element.js npm run build pip install . cd runestone/hparsons/test From 9777048e7f1aca8fe77390dce0b652174710ed07 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Thu, 17 Feb 2022 22:10:08 -0500 Subject: [PATCH 05/31] :sparkles: Specifying arguments for hparsons --- runestone/hparsons/hparsons.py | 137 +++++++++++++++++---- runestone/hparsons/js/hparsons.js | 45 ++++++- runestone/hparsons/js/regex-element.js | 18 +-- runestone/hparsons/test/_sources/index.rst | 119 ++++++++++-------- 4 files changed, 234 insertions(+), 85 deletions(-) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index c8003c8e2..6fd9941f9 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -15,6 +15,8 @@ # __author__ = "ziwu" +from cgi import test +import json from docutils import nodes from docutils.parsers.rst import directives from runestone.mchoice import Assessment @@ -39,14 +41,16 @@ def setup(app): # TODO: what is the alert and alert-warnings? TEMPLATE_START = """ -
      +
      -
      +
      """ TEMPLATE_END = """
      -
      +
      %(problem)s
      +
      +
      %(settings)s
      @@ -86,32 +90,131 @@ def depart_hparsons_node(self, node): class HParsonsDirective(Assessment): """ .. hparsons:: uniqueid - - nothing makes sense at all. - + :textentry: if you will use text entry instead of horizontal parsons + :hidetests: if the unittests will be hidden from learners + :nostrictmatch: if the answer is required to match the whole string. if not selected, the tool will add ^ and $ automatically to the answer to force matching the full string. This does not affect the test string area. + + --problem-- + Here is the problem description. + Make sure you use the correct delimitier for each section. + --blocks-- + block 1 + block 2 + --explanations-- + explanations for block 1 + explanations for block 2 + --positive test string-- + this is some positive test string. + it can be more than one line. + just ignore this section if you do not want to put anything in there. + --negative test string-- + this is some negative test string. + --test cases-- + input string 1 + ['expected match 1', 'expected match 2'] + input string 2 + [] + input string 3 + [] """ required_arguments = 1 # the div id - optional_arguments = 0 - final_argument_whitespace = True + optional_arguments = 1 + final_argument_whitespace = False has_content = True option_spec = Assessment.option_spec.copy() + option_spec.update( + { + "textentry": directives.flag, + "hidetests": directives.flag, + "nostrictmatch": directives.flag, + } + ) # seem to be defining the type of the options # option_spec.update({"mathjax": directives.flag}) # just fill it with the name - node_class = HParsonsNode + # node_class = HParsonsNode def run(self): # same super(HParsonsDirective, self).run() addQuestionToDB(self) # Raise an error if the directive does not have contents. + # env = self.state.document.settings.env + if "textentry" in self.options: + self.options['textentry'] = ' data-textentry="true"' + else: + self.options['textentry'] = '' + if "hidetests" in self.options: + self.options['hidetests'] = 'hidetests' + else: + self.options['hidetests'] = '' + if "nostrictmatch" in self.options: + self.options['nostrictmatch'] = ' data-nostrictmatch="true"' + else: + self.options['nostrictmatch'] = '' + self.assert_has_content() - # specifying default for option? - # TODO: ignoring for now - # self.options["mathjax"] = "data-mathjax" if "mathjax" in self.options else "" + # sepcifying the start end end for each section + delimitiers = ['--problem--', '--blocks--', '--explanations--', '--positive test string--', '--negative test string--', '--test cases--'] + delimitiers_index = [-1 for x in range(6)] + + has_content = False + for i in range(len(delimitiers)): + if delimitiers[i] in self.content: + has_content = True + delimitiers_index[i] = self.content.index(delimitiers[i]) + if has_content: + sorted_index, sorted_delimiters = [list(t) for t in zip(*[pair for pair in sorted(zip(delimitiers_index, delimitiers)) if pair[0] > 0])] + else: + sorted_index = [] + sorted_delimiters = [] + + content = self.content + + parsons_settings = {} + + if '--problem--' in sorted_delimiters: + index = sorted_delimiters.index('--problem--') + self.options['problem'] = '\n'.join(content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) + else: + self.options['problem'] = 'empty problem' + + if '--blocks--' in sorted_delimiters: + index = sorted_delimiters.index('--blocks--') + parsons_settings['blocks'] = list(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) + else: + parsons_settings['blocks'] = [] + + if '--explanations--' in sorted_delimiters: + index = sorted_delimiters.index('--explanations--') + parsons_settings['explanations'] = list(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) + + if '--positive test string--' in sorted_delimiters: + index = sorted_delimiters.index('--positive test string--') + parsons_settings['positivetest'] = '\n'.join(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) + + if '--negative test string--' in sorted_delimiters: + index = sorted_delimiters.index('--negative test string--') + parsons_settings['negativetest'] = '\n'.join(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) + + if '--test cases--' in sorted_delimiters: + index = sorted_index[sorted_delimiters.index('--test cases--')] + 1 + end_index = sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content) + parsons_settings['testcases'] = [] + while index < end_index: + testcase = {} + testcase['input'] = content[index] + testcase['expect'] = content[index + 1] if index + 1 < end_index else [] + parsons_settings['testcases'].append(testcase) + index += 2 + + self.options['settings'] = json.dumps(parsons_settings) + + # same + maybeAddToAssignment(self) # same hparsons_node = HParsonsNode(self.options, rawsource=self.block_text) @@ -124,21 +227,11 @@ def run(self): self.updateContent() # same as mchoice, different from parsons. i think it is for generating instructions. - self.state.nested_parse(self.content, self.content_offset, hparsons_node) # parsons: # self.state.nested_parse( # self.options["instructions"], self.content_offset, parsons_node # ) - # adding classes outside of the div based on the options - env = self.state.document.settings.env - if self.options["optional"]: - self.options["divclass"] = env.config.shortanswer_optional_div_class - else: - self.options["divclass"] = env.config.shortanswer_div_class - - # same - maybeAddToAssignment(self) # same return [hparsons_node] diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js index c694055b5..5a43deab3 100644 --- a/runestone/hparsons/js/hparsons.js +++ b/runestone/hparsons/js/hparsons.js @@ -27,8 +27,6 @@ export default class HParsons extends RunestoneBase { var orig = opts.orig; // entire
       element that will be replaced by new HTML
                   this.containerDiv = orig;
                   this.origElem = $(orig).find(".hparsons")[0];
      -            console.log('hparsons')
      -            console.log(orig)
                   console.log(this.origElem)
                   this.useRunestoneServices =
                       opts.useRunestoneServices || eBookConfig.useRunestoneServices;
      @@ -54,12 +52,50 @@ export default class HParsons extends RunestoneBase {
           }
       
           renderHTML() {
      -        console.log('renderhtml');
               // const div = document.createElement('regex-element');
               // div.setAttribute('input-type', 'parsons')
               // div.id = 'abcd';
               // console.log(this.origElem)
      -        $(this.origElem).html('');
      +        let attributes = '';
      +        console.log($(this.origElem).data("textentry"))
      +        let settings = JSON.parse($(this.origElem).children()[0].innerText)
      +        attributes += ' input-type=' + ($(this.origElem).data("textentry") ? 'text' : 'parsons' );
      +        $(this.origElem).html('');
      +        let regexElement = $(this.origElem).children()[0];
      +        if ($(this.origElem).data("nostrictmatch")) {
      +            regexElement.unitTestTable.strictMatch = false;
      +        } else {
      +            regexElement.unitTestTable.strictMatch = true;
      +        }
      +        if ($(this.origElem).data("hidetests")) {
      +            regexElement.hidetests = false;
      +        } else {
      +            regexElement.unitTestTable.strictMatch = true;
      +        }
      +
      +        if (settings.blocks) {
      +            regexElement.parsonsData = settings.blocks;
      +        }
      +        if (settings.explanations) {
      +            regexElement.parsonsExplanation = settings.explanations;
      +        }
      +        if (settings.positivetest) {
      +            regexElement.setPositiveInitialTestString(settings.positivetest);
      +        }
      +        if (settings.negativetest) {
      +            regexElement.setNegativeInitialTestString(settings.negativetest);
      +        }
      +        if (settings.testcases) {
      +            regexElement.setTestCases(settings.testcases);
      +        }
      +
      +        // tool.parsonsExplanation = toolConfig.parsonsExplanation;
      +        // tool.parsonsData = toolConfig.parsonsData;
      +        regexElement.resetTool();
      +        // tool.setTestCases(toolConfig.testCases);
      +        // tool.setPositiveInitialTestString(toolConfig.positiveInitialTestString);
      +        // tool.setNegativeInitialTestString(toolConfig.negativeInitialTestString);
      +
               // console.log(this.origElem)
               // $(this.origElem).replaceWith(document.createElement('regex-element'));
               // $(this.elem).innerHTML = ``;
      @@ -213,7 +249,6 @@ $(document).bind("runestone:login-complete", function () {
           $("[data-component=hparsons]").each(function () {
               if ($(this).closest("[data-component=timedAssessment]").length == 0) {
                   // If this element exists within a timed component, don't render it here
      -            console.log('rendering hparsons')
                   // try {
                       hpList[this.id] = new HParsons({
                           orig: this,
      diff --git a/runestone/hparsons/js/regex-element.js b/runestone/hparsons/js/regex-element.js
      index c508c6e65..65781dbdc 100644
      --- a/runestone/hparsons/js/regex-element.js
      +++ b/runestone/hparsons/js/regex-element.js
      @@ -16232,9 +16232,9 @@ class RegexElement extends HTMLElement {
               let match;
               while ((match = regex.exec(str)) != null) {
                   // console.log(match)
      -            console.log("Matched '" + match[0] + "' at position " + match.index +
      -                "; next search at " + regex.lastIndex);
      -            console.log("match: " + JSON.stringify(match));
      +            // console.log("Matched '" + match[0] + "' at position " + match.index +
      +            //      "; next search at " + regex.lastIndex);
      +            // console.log("match: " + JSON.stringify(match));
                   pos_result.push({ st: match.index, ed: regex.lastIndex });
                   if (match.length < 2) {
                       match_result.push(new window.Sk.builtin.str(match[0]));
      @@ -16259,7 +16259,7 @@ class RegexElement extends HTMLElement {
           constructor() {
               super();
               RegexElement.toolCount += 1;
      -        console.log(RegexElement.toolCount);
      +        // console.log(RegexElement.toolCount);
               this.toolNumber = RegexElement.toolCount;
               this.root = this.attachShadow({ mode: 'open' });
               window.Sk.configure({
      @@ -16413,7 +16413,7 @@ class RegexElement extends HTMLElement {
                       enabled: false
                   };
                   this.logEvent(visibilityStatusEvent);
      -            console.log("visibility not working");
      +            // console.log("visibility not working");
               }
               else {
                   const visibilityStatusEvent = {
      @@ -16421,7 +16421,7 @@ class RegexElement extends HTMLElement {
                       enabled: true
                   };
                   this.logEvent(visibilityStatusEvent);
      -            console.log("add visibility change");
      +            // console.log("add visibility change");
                   document.addEventListener("visibilitychange", (event) => {
                       let pageStatusEvent;
                       if (document.hidden) {
      @@ -16461,8 +16461,8 @@ class RegexElement extends HTMLElement {
               // stub for student and problem id
               this.studentId = this._getStudentIdFromURL();
               this.problemId = this.getAttribute('problem-id') || '';
      -        console.log(this.studentId);
      -        console.log(this.problemId);
      +        // console.log(this.studentId);
      +        // console.log(this.problemId);
               this.temporaryInputEvent = null;
               this.matchFindall = true;
           }
      @@ -16522,7 +16522,7 @@ class RegexElement extends HTMLElement {
               // sheet.innerHTML += '.regex-unittest > table, .regex-unittest td {border: 1px solid black; padding: 3px; text-align: center;}\n'
               // sheet.innerHTML += '.regex-unittest.collapse{display:none;}\n'
               // for study 0: hide the table
      -        // sheet.innerHTML += '.regex-unittest{display:none;}\n'
      +        sheet.innerHTML += '.hidetests .regex-unittest{display:none;}\n';
               document.body.appendChild(sheet);
               this.root.appendChild(sheet);
               const global_sheet = document.createElement('style');
      diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst
      index b3107862c..ed2f96067 100644
      --- a/runestone/hparsons/test/_sources/index.rst
      +++ b/runestone/hparsons/test/_sources/index.rst
      @@ -1,63 +1,84 @@
      -=====================
      -This Is A New Project
      -=====================
      +=========================
      +Horizontal Parsons Test
      +=========================
       
      -.. Here is were you specify the content and order of your new book.
      +.. Testing horizontal Parsons problems.
       
      -.. Each section heading (e.g. "SECTION 1: A Random Section") will be
      -   a heading in the table of contents. Source files that should be
      -   generated and included in that section should be placed on individual
      -   lines, with one line separating the first source filename and the
      -   :maxdepth: line.
      -
      -.. Sources can also be included from subfolders of this directory.
      -   (e.g. "DataStructures/queues.rst").
      -
      -SECTION 1: Introduction
      -:::::::::::::::::::::::
      -
      -Congratulations!   If you can see this file you have probably successfully run the ``runestone init`` command.  If you are looking at this as a source file you should now run ``runestone build``  to generate html files.   Once you have run the build command you can run ``runestone serve`` and then view this in your browser at ``http://localhost:8000``
      -
      -This is just a sample of what you can do.  The index.rst file is the table of contents for your entire project.  You can put all of your writing in the index, or  you can include additional rst files.  Those files may even be in subdirectories that you can reference using a relative path.
      -
      -
      -::
      -
      -
      -   .. toctree::
      -      :maxdepth: 2
      -
      -      some/path/myfile.rst
      -
      -
      -Section 2: Links
      -::::::::::::::::
      -
      -Runestone uses the ``restructuredText`` (rst) markup language.  We chose this over markdown largely because rst is extensible.  Nearly all of the basic markup tasks are already handled by restructuredText.  You should check out the docs for the basics of restructuredText (link below). Our extensions are all for the interactive elements.  One key hint about restructuredText:  Its like **Python** -- *indentation matters!*
      -
      -* `restructuredText Docs `_
      -* `Runestone Docs `_
      -* Join the discussion on our `Google Group `_
      -* Tell us about problems on `Github `_
      -
      -
      -
      -SECTION 3: Sample Directives
      -::::::::::::::::::::::::::::
      -
      -ActiveCode
      -----------
      +Horizontal Parsons + Regex
      +---------------------------
       
       .. hparsons:: codeexample1
      -
      -    content content content
      +    :hidetests:
      +    :nostrictmatch:
      +
      +    --problem--
      +    Capture words that start with a vowel letter(aeiou), but ends with a non-vowel letter. There can be 0 or more letters in between. Also, it is not allowed to have other characters besides letter in between. e.g. your regular expression should match unicorn, element, but should not match: banana, apple. All letters are lowercase.
      +    --blocks--
      +    [a-z]
      +    [aeiou]
      +    [^aeiou]
      +    +
      +    *
      +    --explanations--
      +    letter a to z
      +    letter a, e, i, o, u
      +    characters other than letter a, e, i, o, u
      +    repeat one or more times
      +    repeat zero or more times
      +    --positive test string--
      +    unicorn
      +    element
      +    --negative test string--
      +    apple
      +    banana
      +    --test cases--
      +    unicorn
      +    ['unicorn']
      +    element
      +    ['element']
      +    banana
      +    []
      +    bananu
      +    []
      +    apple
      +    []
      +    baby
      +    []
      +    az
      +    ['az']
      +    abcdefghizsdz
      +    ['abcdefghizsdz']
      +    abc9defghizsdz'
      +    []
       
       
       .. hparsons:: codeexample2
      +    :textentry:
       
           content content content
       
       
      +Other Problems for reference
      +-----------------------------
      +
      +.. parsonsprob:: test_parsons_1
      +    :adaptive:
      +    :order: 0 1 2 3 4
      +
      +    need some text ?
      +    -----
      +    def fib(num):
      +    =====
      +       if num == 0:
      +           return 0:
      +    =====
      +       if num == 1:
      +           return 1:
      +    =====
      +       return fib(num - 1) + fib(num - 2)
      +    =====
      +       return fib(num - 1) * fib(num - 2) #paired
      +
       Multiple Choice
       ---------------
       
      
      From b7cfe16b1c78e9665e513b60134a1438385218ad Mon Sep 17 00:00:00 2001
      From: amy21206 
      Date: Thu, 17 Feb 2022 23:03:38 -0500
      Subject: [PATCH 06/31] :sparkles: Fix hiding test cases
      
      ---
       runestone/hparsons/hparsons.py             | 19 ++++++++++---------
       runestone/hparsons/js/hparsons.js          |  1 +
       runestone/hparsons/js/regex-element.js     | 20 +++++++++++++++++---
       runestone/hparsons/test/_sources/index.rst |  7 +++++--
       4 files changed, 33 insertions(+), 14 deletions(-)
      
      diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py
      index 6fd9941f9..e6544dba3 100755
      --- a/runestone/hparsons/hparsons.py
      +++ b/runestone/hparsons/hparsons.py
      @@ -43,13 +43,13 @@ def setup(app):
       TEMPLATE_START = """
               
      -
      +
      """ TEMPLATE_END = """
      -
      %(problem)s
      -
      +
      %(instructions)s
      +
      %(settings)s
      @@ -147,7 +147,7 @@ def run(self): else: self.options['textentry'] = '' if "hidetests" in self.options: - self.options['hidetests'] = 'hidetests' + self.options['hidetests'] = ' data-hidetests="true"' else: self.options['hidetests'] = '' if "nostrictmatch" in self.options: @@ -167,7 +167,7 @@ def run(self): has_content = True delimitiers_index[i] = self.content.index(delimitiers[i]) if has_content: - sorted_index, sorted_delimiters = [list(t) for t in zip(*[pair for pair in sorted(zip(delimitiers_index, delimitiers)) if pair[0] > 0])] + sorted_index, sorted_delimiters = [list(t) for t in zip(*[pair for pair in sorted(zip(delimitiers_index, delimitiers)) if pair[0] >= 0])] else: sorted_index = [] sorted_delimiters = [] @@ -178,9 +178,9 @@ def run(self): if '--problem--' in sorted_delimiters: index = sorted_delimiters.index('--problem--') - self.options['problem'] = '\n'.join(content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) + self.options['instructions'] = '\n'.join(content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) else: - self.options['problem'] = 'empty problem' + self.options['instructions'] = 'empty problem' if '--blocks--' in sorted_delimiters: index = sorted_delimiters.index('--blocks--') @@ -224,12 +224,13 @@ def run(self): # exist in short answer and mchoice but not parsons # For MChoice its better to insert the qnum into the content before further processing. - self.updateContent() + # self.updateContent() + # TODO: fix the nested parse # same as mchoice, different from parsons. i think it is for generating instructions. # parsons: # self.state.nested_parse( - # self.options["instructions"], self.content_offset, parsons_node + # self.options['instructions'], self.content_offset, hparsons_node # ) diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js index 5a43deab3..08916ac20 100644 --- a/runestone/hparsons/js/hparsons.js +++ b/runestone/hparsons/js/hparsons.js @@ -60,6 +60,7 @@ export default class HParsons extends RunestoneBase { console.log($(this.origElem).data("textentry")) let settings = JSON.parse($(this.origElem).children()[0].innerText) attributes += ' input-type=' + ($(this.origElem).data("textentry") ? 'text' : 'parsons' ); + attributes += $(this.origElem).data("hidetests") ? ' hidetests="true"': ''; $(this.origElem).html(''); let regexElement = $(this.origElem).children()[0]; if ($(this.origElem).data("nostrictmatch")) { diff --git a/runestone/hparsons/js/regex-element.js b/runestone/hparsons/js/regex-element.js index 65781dbdc..2d363954d 100644 --- a/runestone/hparsons/js/regex-element.js +++ b/runestone/hparsons/js/regex-element.js @@ -15913,8 +15913,14 @@ class UnitTestTable { this.el = document.createElement('div'); this.el.id = 'regextool-' + this.parentElement.toolNumber + '-unittest-table'; this.el.classList.add('regex-unittest'); + if (this.parentElement.getAttribute('hidetests')) { + this.el.style.display = 'none'; + } + else { + this.el.style.display = 'block'; + } // the element is hidden initially. - this.el.classList.add('collapse'); + // this.el.classList.add('collapse'); // columns enabled besides the status column // TODO: only enabled notes for study 0 and 1 this.columnsEnabled = ['actualOutput', 'expectedOutput', 'input', 'notes']; @@ -16015,7 +16021,7 @@ class UnitTestTable { // creating the status(the first) column const row = document.createElement('tr'); let status = result.success ? (JSON.stringify(result.match) === JSON.stringify(testCase.expect) ? 'Pass' : 'Fail') : 'Error'; - console.log(status); + // console.log(status) // if (status == 'Pass' && JSON.stringify(testCase.expect) != '[]' && this.noGroupsAllowed && window.pyodide.globals.unit_match_group_cnt != 1) { // status = 'Fail' // // fail because no group is allowed @@ -16813,7 +16819,7 @@ class RegexElement extends HTMLElement { }; this.logEvent(problemFinished); }; - static get observedAttributes() { return ['input-type', 'problem-id']; } + static get observedAttributes() { return ['input-type', 'problem-id', 'hidetests']; } attributeChangedCallback(name, oldValue, newValue) { switch (name) { case 'input-type': { @@ -16824,6 +16830,14 @@ class RegexElement extends HTMLElement { this.problemId = newValue; break; } + case 'hidetests': { + if (newValue) { + this.unitTestTable.el.style.display = 'none'; + } + else { + this.unitTestTable.el.style.display = 'block'; + } + } } } initRegexInput(inputDiv) { diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index ed2f96067..d386b7ec2 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -8,11 +8,13 @@ Horizontal Parsons + Regex --------------------------- .. hparsons:: codeexample1 - :hidetests: :nostrictmatch: --problem-- - Capture words that start with a vowel letter(aeiou), but ends with a non-vowel letter. There can be 0 or more letters in between. Also, it is not allowed to have other characters besides letter in between. e.g. your regular expression should match unicorn, element, but should not match: banana, apple. All letters are lowercase. + Capture words that start with a vowel letter(aeiou), but ends with a non-vowel letter. + There can be 0 or more letters in between. + Also, it is not allowed to have other characters besides letter in between. + e.g. your regular expression should match unicorn, element, but should not match: banana, apple. All letters are lowercase. --blocks-- [a-z] [aeiou] @@ -54,6 +56,7 @@ Horizontal Parsons + Regex .. hparsons:: codeexample2 :textentry: + :hidetests: content content content From df7beb3b00fef730544a5e668320a9f338f63ad2 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Fri, 18 Feb 2022 18:07:10 -0500 Subject: [PATCH 07/31] :bug: Fix unittest --- runestone/hparsons/hparsons.py | 16 ++++--- runestone/hparsons/js/hparsons.js | 51 +++++++++------------- runestone/hparsons/js/regex-element.js | 1 - runestone/hparsons/test/_sources/index.rst | 22 +++++++++- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index e6544dba3..4a2ee2eb4 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -48,8 +48,7 @@ def setup(app): TEMPLATE_END = """
      -
      %(instructions)s
      -
      +
      %(settings)s
      @@ -178,9 +177,11 @@ def run(self): if '--problem--' in sorted_delimiters: index = sorted_delimiters.index('--problem--') - self.options['instructions'] = '\n'.join(content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) + self.options['instructions'] = content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))] + self.options['problem'] = '\n'.join(content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) else: - self.options['instructions'] = 'empty problem' + self.options['instructions'] = ['empty problem'] + self.options['problem'] = 'empty problem' if '--blocks--' in sorted_delimiters: index = sorted_delimiters.index('--blocks--') @@ -212,6 +213,7 @@ def run(self): index += 2 self.options['settings'] = json.dumps(parsons_settings) + self.options['type'] = type(self.options['instructions']) # same maybeAddToAssignment(self) @@ -229,9 +231,9 @@ def run(self): # TODO: fix the nested parse # same as mchoice, different from parsons. i think it is for generating instructions. # parsons: - # self.state.nested_parse( - # self.options['instructions'], self.content_offset, hparsons_node - # ) + self.state.nested_parse( + self.options['instructions'], self.content_offset, hparsons_node + ) # same diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js index 08916ac20..6e840e9a1 100644 --- a/runestone/hparsons/js/hparsons.js +++ b/runestone/hparsons/js/hparsons.js @@ -87,6 +87,10 @@ export default class HParsons extends RunestoneBase { regexElement.setNegativeInitialTestString(settings.negativetest); } if (settings.testcases) { + for (let index in settings.testcases) { + settings.testcases[index].expect = settings.testcases[index].expect.length < 4 ? [] : settings.testcases[index].expect.slice(2, -2).split(', '); + console.log(settings.testcases[index]) + } regexElement.setTestCases(settings.testcases); } @@ -126,13 +130,13 @@ export default class HParsons extends RunestoneBase { } renderFeedback() { - console.log('hparsons, renderfeedback') + // console.log('hparsons, renderfeedback') // this.feedbackDiv.innerHTML = "Your answer has been saved."; // $(this.feedbackDiv).removeClass("alert-danger"); // $(this.feedbackDiv).addClass("alert alert-success"); } setLocalStorage(data) { - console.log('hparsons, setlocalstorage') + // console.log('hparsons, setlocalstorage') // if (!this.graderactive) { // let key = this.localStorageKey(); // localStorage.setItem(key, JSON.stringify(data)); @@ -140,34 +144,21 @@ export default class HParsons extends RunestoneBase { } checkLocalStorage() { console.log('hparsons, checklocalstorage') - // Repopulates the short answer text - // which was stored into local storage. - // var answer = ""; - // if (this.graderactive) { - // return; - // } - // var len = localStorage.length; - // if (len > 0) { - // var ex = localStorage.getItem(this.localStorageKey()); - // if (ex !== null) { - // try { - // var storedData = JSON.parse(ex); - // answer = storedData.answer; - // } catch (err) { - // // error while parsing; likely due to bad value stored in storage - // console.log(err.message); - // localStorage.removeItem(this.localStorageKey()); - // return; - // } - // let solution = $("#" + this.divid + "_solution"); - // solution.text(answer); - // this.renderMath(answer); - // this.feedbackDiv.innerHTML = - // "Your current saved answer is shown above."; - // $(this.feedbackDiv).removeClass("alert-danger"); - // $(this.feedbackDiv).addClass("alert alert-success"); - // } - // } + var toStore; + if (data == undefined) { + toStore = { + source: this.sourceHash(), + answer: this.answerHash(), + timestamp: new Date(), + }; + var adaptiveHash = this.adaptiveHash(); + if (adaptiveHash.length > 0) { + toStore.adaptive = adaptiveHash; + } + } else { + toStore = data; + } + localStorage.setItem(this.storageId, JSON.stringify(toStore)); } restoreAnswers(data) { console.log('hparsons, restoreanswers') diff --git a/runestone/hparsons/js/regex-element.js b/runestone/hparsons/js/regex-element.js index 2d363954d..e31e2902b 100644 --- a/runestone/hparsons/js/regex-element.js +++ b/runestone/hparsons/js/regex-element.js @@ -16078,7 +16078,6 @@ class UnitTestTable { row.innerHTML += this._getRevealedRow(this.testCases[index], this.latestResults[index]); }; setTestCases = (testCases) => { - console.log('unittest 242'); this.testCases = testCases; // TODO: make this an option. Set all revealed for study 0 and 1. this.hintRevealed = Array(testCases.length).fill(true); diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index d386b7ec2..7434dad1f 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -14,6 +14,15 @@ Horizontal Parsons + Regex Capture words that start with a vowel letter(aeiou), but ends with a non-vowel letter. There can be 0 or more letters in between. Also, it is not allowed to have other characters besides letter in between. + + .. code-block:: python + + sum = 0 + for i in range(10): + sum = sum + i + + print(sum) + e.g. your regular expression should match unicorn, element, but should not match: banana, apple. All letters are lowercase. --blocks-- [a-z] @@ -50,7 +59,7 @@ Horizontal Parsons + Regex ['az'] abcdefghizsdz ['abcdefghizsdz'] - abc9defghizsdz' + abc9defghizsdz [] @@ -58,7 +67,18 @@ Horizontal Parsons + Regex :textentry: :hidetests: + --problem-- + Capture words that start with a vowel letter(aeiou), but ends with a non-vowel letter. + There can be 0 or more letters in between. + Also, it is not allowed to have other characters besides letter in between. + e.g. your regular expression should match unicorn, element, but should not match: banana, apple. All letters are lowercase. content content content + --positive test string-- + unicorn + element + --negative test string-- + apple + banana Other Problems for reference From 5ae7af9daa9e6e379d2ca0d6d779caa4dbb7c0b0 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Sun, 20 Feb 2022 11:09:40 -0500 Subject: [PATCH 08/31] :sparkles: Allow saving previous student answer --- runestone/hparsons/hparsons.py | 3 - runestone/hparsons/js/hparsons.js | 98 +++++++++++----------- runestone/hparsons/js/regex-element.js | 89 ++++++++++++++++---- runestone/hparsons/test/_sources/index.rst | 11 +-- runestone/parsons/js/parsons.js | 10 +++ 5 files changed, 132 insertions(+), 79 deletions(-) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index 4a2ee2eb4..77da5276e 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -228,9 +228,6 @@ def run(self): # For MChoice its better to insert the qnum into the content before further processing. # self.updateContent() - # TODO: fix the nested parse - # same as mchoice, different from parsons. i think it is for generating instructions. - # parsons: self.state.nested_parse( self.options['instructions'], self.content_offset, hparsons_node ) diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js index 6e840e9a1..388b6e428 100644 --- a/runestone/hparsons/js/hparsons.js +++ b/runestone/hparsons/js/hparsons.js @@ -22,33 +22,31 @@ if (hpList === undefined) hpList = {}; export default class HParsons extends RunestoneBase { constructor(opts) { super(opts); - if (opts) { - // TODO: what is orig? - var orig = opts.orig; // entire
       element that will be replaced by new HTML
      -            this.containerDiv = orig;
      -            this.origElem = $(orig).find(".hparsons")[0];
      -            console.log(this.origElem)
      -            this.useRunestoneServices =
      -                opts.useRunestoneServices || eBookConfig.useRunestoneServices;
      -            this.divid = orig.id;
      -
      -            // The element that is going to be replaced
      -            // Find the question text and store it in .question
      -            this.question = $(orig).find(`.hparsons_question`)[0];
      -            // TODO: idk what this is with shortanswer
      -            this.renderHTML();
      -            this.caption = "hparsons";
      -            this.addCaption("runestone");
      -            this.checkServer("hparsons", true);
      -
      -            // Set the storageId (key for storing data)
      -            var storageId = super.localStorageKey();
      -            this.storageId = storageId;
      -            this.children = this.origElem.childNodes; // this contains all of the child elements of the entire tag...
      -            this.contentArray = [];
      -            HParsons.counter++; //    Unique identifier
      -            this.counterId = "hparsons-" + HParsons.counter;
      -        }
      +        var orig = opts.orig;
      +        this.containerDiv = orig;
      +        // the div element that will contain regex-element
      +        this.origElem = $(orig).find(".hparsons")[0];
      +        this.useRunestoneServices =
      +            opts.useRunestoneServices || eBookConfig.useRunestoneServices;
      +        this.divid = orig.id;
      +
      +
      +        // Set the storageId (key for storing data)
      +        var storageId = super.localStorageKey();
      +        this.storageId = storageId;
      +        // this.setLocalStorage({test: 'test1'})
      +        this.children = this.origElem.childNodes; // this contains all of the child elements of the entire tag...
      +        this.contentArray = [];
      +        HParsons.counter++; //    Unique identifier
      +        this.counterId = "hparsons-" + HParsons.counter;
      +
      +        // The element that is going to be replaced
      +        // Find the question text and store it in .question
      +        this.renderHTML();
      +        this.origElem.addEventListener('regex-element', (e) => {this.handleRegexElementEvent(e)});
      +        this.caption = "hparsons";
      +        this.addCaption("runestone");
      +        this.checkServer("hparsons", true);
           }
       
           renderHTML() {
      @@ -57,12 +55,12 @@ export default class HParsons extends RunestoneBase {
               // div.id = 'abcd';
               // console.log(this.origElem)
               let attributes = '';
      -        console.log($(this.origElem).data("textentry"))
               let settings = JSON.parse($(this.origElem).children()[0].innerText)
               attributes += ' input-type=' + ($(this.origElem).data("textentry") ? 'text' : 'parsons' );
               attributes += $(this.origElem).data("hidetests") ? ' hidetests="true"': '';
               $(this.origElem).html('');
               let regexElement = $(this.origElem).children()[0];
      +        this.regexElement = regexElement;
               if ($(this.origElem).data("nostrictmatch")) {
                   regexElement.unitTestTable.strictMatch = false;
               } else {
      @@ -89,11 +87,11 @@ export default class HParsons extends RunestoneBase {
               if (settings.testcases) {
                   for (let index in settings.testcases) {
                       settings.testcases[index].expect = settings.testcases[index].expect.length < 4 ? [] : settings.testcases[index].expect.slice(2, -2).split(', ');
      -                console.log(settings.testcases[index])
                   }
                   regexElement.setTestCases(settings.testcases);
               }
       
      +
               // tool.parsonsExplanation = toolConfig.parsonsExplanation;
               // tool.parsonsData = toolConfig.parsonsData;
               regexElement.resetTool();
      @@ -105,6 +103,7 @@ export default class HParsons extends RunestoneBase {
               // $(this.origElem).replaceWith(document.createElement('regex-element'));
               // $(this.elem).innerHTML = ``;
               // console.log(div)
      +        // localStorage.setItem(this.storageId, JSON.stringify({test: 'test'}));
           }
       
           checkCurrentAnswer() { }
      @@ -136,31 +135,26 @@ export default class HParsons extends RunestoneBase {
               // $(this.feedbackDiv).addClass("alert alert-success");
           }
           setLocalStorage(data) {
      -        // console.log('hparsons, setlocalstorage')
      -        // if (!this.graderactive) {
      -        //     let key = this.localStorageKey();
      -        //     localStorage.setItem(key, JSON.stringify(data));
      -        // }
      +        localStorage.setItem(this.storageId, JSON.stringify(data));
           }
           checkLocalStorage() {
      -        console.log('hparsons, checklocalstorage')
      -        var toStore;
      -        if (data == undefined) {
      -            toStore = {
      -                source: this.sourceHash(),
      -                answer: this.answerHash(),
      -                timestamp: new Date(),
      -            };
      -            var adaptiveHash = this.adaptiveHash();
      -            if (adaptiveHash.length > 0) {
      -                toStore.adaptive = adaptiveHash;
      +        // Return what is stored in local storage
      +        var data = localStorage.getItem(this.storageId);
      +        if (data !== null) {
      +            if (data.charAt(0) == "{") {
      +                data = JSON.parse(data);
      +            } else {
      +                data = {};
                   }
               } else {
      -            toStore = data;
      +            data = {};
      +        }
      +        if (data.type != undefined && data.answer != undefined) {
      +            this.regexElement.restoreAnswer(data.type, data.answer)
               }
      -        localStorage.setItem(this.storageId, JSON.stringify(toStore));
           }
      -    restoreAnswers(data) {
      +    // called when server has data
      +    restoreAnswers(serverData) {
               console.log('hparsons, restoreanswers')
               // Restore answers from storage retrieval done in RunestoneBase
               // sometimes data.answer can be null
      @@ -231,6 +225,14 @@ export default class HParsons extends RunestoneBase {
               console.log('hparsons, disableinteraction')
               // this.jTextArea.disabled = true;
           }
      +
      +    handleRegexElementEvent(event) {
      +        if (event.detail['event-type'] == 'parsons-input' || event.detail['event-type'] == 'text-input') {
      +            this.setLocalStorage({'type': event.detail['event-type'].slice(0, -6), 'answer': event.detail['answer']}) 
      +            // this.setLocalStorage({'event-type': event.detail['event-type'], 'answer': event.detail['answer'][0]}) 
      +            // this.setLocalStorage({tes: 'tes'}) 
      +        }
      +    }
       }
       HParsons.counter = 0;
       /*=================================
      diff --git a/runestone/hparsons/js/regex-element.js b/runestone/hparsons/js/regex-element.js
      index e31e2902b..745c066f3 100644
      --- a/runestone/hparsons/js/regex-element.js
      +++ b/runestone/hparsons/js/regex-element.js
      @@ -3163,7 +3163,7 @@ class ParsonsInput {
                                   newBlock.parentNode?.removeChild(newBlock);
                                   if (this.parentElement) {
                                       this.parentElement.temporaryInputEvent = {
      -                                    'event-type': 'parsons',
      +                                    'event-type': 'parsons-input',
                                           action: RegexEvent.ParsonsInputAction.REMOVE,
                                           position: [endPosition, -1],
                                           answer: this._getTextArray()
      @@ -3177,7 +3177,7 @@ class ParsonsInput {
                           }
                           if (this.parentElement && firstBlock) {
                               this.parentElement.temporaryInputEvent = {
      -                            'event-type': 'parsons',
      +                            'event-type': 'parsons-input',
                                   action: RegexEvent.ParsonsInputAction.ADD,
                                   position: [-1, this._getBlockPosition(firstBlock)],
                                   answer: this._getTextArray(),
      @@ -3244,7 +3244,7 @@ class ParsonsInput {
                               newBlockCopy.parentNode?.removeChild(newBlockCopy);
                               if (this.parentElement) {
                                   this.parentElement.temporaryInputEvent = {
      -                                'event-type': 'parsons',
      +                                'event-type': 'parsons-input',
                                       action: RegexEvent.ParsonsInputAction.REMOVE,
                                       position: [endPosition, -1],
                                       answer: this._getTextArray()
      @@ -3254,7 +3254,7 @@ class ParsonsInput {
                           };
                           if (this.parentElement) {
                               this.parentElement.temporaryInputEvent = {
      -                            'event-type': 'parsons',
      +                            'event-type': 'parsons-input',
                                   action: RegexEvent.ParsonsInputAction.ADD,
                                   position: [-1, this._getBlockPosition(newBlockCopy)],
                                   answer: this._getTextArray(),
      @@ -3271,7 +3271,7 @@ class ParsonsInput {
                           newBlock.parentNode?.removeChild(newBlock);
                           if (this.parentElement) {
                               this.parentElement.temporaryInputEvent = {
      -                            'event-type': 'parsons',
      +                            'event-type': 'parsons-input',
                                   action: RegexEvent.ParsonsInputAction.REMOVE,
                                   position: [endPosition, -1],
                                   answer: this._getTextArray(),
      @@ -3317,7 +3317,7 @@ class ParsonsInput {
                                           newBlock.parentNode?.removeChild(newBlock);
                                           if (this.parentElement) {
                                               this.parentElement.temporaryInputEvent = {
      -                                            'event-type': 'parsons',
      +                                            'event-type': 'parsons-input',
                                                   action: RegexEvent.ParsonsInputAction.REMOVE,
                                                   position: [endPosition, -1],
                                                   answer: this._getTextArray(),
      @@ -3331,7 +3331,7 @@ class ParsonsInput {
                                   }
                                   if (this.parentElement && firstBlock) {
                                       this.parentElement.temporaryInputEvent = {
      -                                    'event-type': 'parsons',
      +                                    'event-type': 'parsons-input',
                                           action: RegexEvent.ParsonsInputAction.ADD,
                                           position: [-1, this._getBlockPosition(firstBlock)],
                                           answer: this._getTextArray(),
      @@ -3357,7 +3357,7 @@ class ParsonsInput {
                                       newBlockCopy.parentNode?.removeChild(newBlockCopy);
                                       if (this.parentElement) {
                                           this.parentElement.temporaryInputEvent = {
      -                                        'event-type': 'parsons',
      +                                        'event-type': 'parsons-input',
                                               action: RegexEvent.ParsonsInputAction.REMOVE,
                                               position: [endPosition, -1],
                                               answer: this._getTextArray(),
      @@ -3367,7 +3367,7 @@ class ParsonsInput {
                                   };
                                   if (this.parentElement) {
                                       this.parentElement.temporaryInputEvent = {
      -                                    'event-type': 'parsons',
      +                                    'event-type': 'parsons-input',
                                           action: RegexEvent.ParsonsInputAction.ADD,
                                           position: [-1, this._getBlockPosition(newBlockCopy)],
                                           answer: this._getTextArray(),
      @@ -3385,7 +3385,7 @@ class ParsonsInput {
                                   newBlock.parentNode?.removeChild(newBlock);
                                   if (this.parentElement) {
                                       this.parentElement.temporaryInputEvent = {
      -                                    'event-type': 'parsons',
      +                                    'event-type': 'parsons-input',
                                           action: RegexEvent.ParsonsInputAction.REMOVE,
                                           position: [endPosition, -1],
                                           answer: this._getTextArray(),
      @@ -3408,7 +3408,7 @@ class ParsonsInput {
                       // console.log(isExpandable);
                       if (this.parentElement) {
                           this.parentElement.temporaryInputEvent = {
      -                        'event-type': 'parsons',
      +                        'event-type': 'parsons-input',
                               action: RegexEvent.ParsonsInputAction.ADD,
                               position: [-1, this._getBlockPosition(event.item)],
                               answer: this._getTextArray(),
      @@ -3441,7 +3441,7 @@ class ParsonsInput {
                                   newBlock.parentNode?.removeChild(newBlock);
                                   if (this.parentElement) {
                                       this.parentElement.temporaryInputEvent = {
      -                                    'event-type': 'parsons',
      +                                    'event-type': 'parsons-input',
                                           action: RegexEvent.ParsonsInputAction.REMOVE,
                                           position: [endPosition, -1],
                                           answer: this._getTextArray(),
      @@ -3480,7 +3480,7 @@ class ParsonsInput {
                       }
                       if (this.parentElement) {
                           this.parentElement.temporaryInputEvent = {
      -                        'event-type': 'parsons',
      +                        'event-type': 'parsons-input',
                               action: action,
                               position: [this._prevPosition, endposition],
                               answer: this._getTextArray(),
      @@ -3565,6 +3565,38 @@ class ParsonsInput {
                   }
               }
           };
      +    restoreAnswer(type, answer) {
      +        if (type != 'parsons' || !Array.isArray(answer)) {
      +            return;
      +        }
      +        console.log('parsons restore');
      +        console.log(answer);
      +        this._dropArea.innerHTML = '';
      +        for (let i = 0; i < answer.length; ++i) {
      +            if (typeof answer[i] === 'string') {
      +                const newBlock = document.createElement('div');
      +                this._dropArea.appendChild(newBlock);
      +                newBlock.innerText = answer[i];
      +                newBlock.style.display = 'inline-block';
      +                newBlock.classList.add('parsons-block');
      +                newBlock.onclick = () => {
      +                    // clicking the new block generated by clicking an extendable block to remove that block
      +                    // console.log('expandable new block onclick')
      +                    const endPosition = this._getBlockPosition(newBlock);
      +                    newBlock.parentNode?.removeChild(newBlock);
      +                    if (this.parentElement) {
      +                        this.parentElement.temporaryInputEvent = {
      +                            'event-type': 'parsons-input',
      +                            action: RegexEvent.ParsonsInputAction.REMOVE,
      +                            position: [endPosition, -1],
      +                            answer: this._getTextArray()
      +                        };
      +                    }
      +                };
      +                this.el.dispatchEvent(new Event('regexChanged'));
      +            }
      +        }
      +    }
       }
       
       var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
      @@ -15527,7 +15559,7 @@ class TextInput {
                   shortKey: true,
               }, (range, context) => {
                   const freeKeyboardEvent = {
      -                'event-type': 'free-input-keyboard',
      +                'event-type': 'text-input-keyboard',
                       range: range,
                       keys: ['ctrl', 'c']
                   };
      @@ -15541,7 +15573,7 @@ class TextInput {
                   shortKey: true,
               }, (range, context) => {
                   const freeKeyboardEvent = {
      -                'event-type': 'free-input-keyboard',
      +                'event-type': 'text-input-keyboard',
                       range: range,
                       keys: ['ctrl', 'v']
                   };
      @@ -15625,6 +15657,16 @@ class TextInput {
                   'background': '#ff99b3'
               }, 'silent');
           };
      +    restoreAnswer(type, answer) {
      +        // TODO (misplaced): consider removing expandable blocks
      +        // TODO: add logging to restoring answer
      +        if (type != 'text' || typeof answer !== 'string') {
      +            return;
      +        }
      +        console.log('text restore');
      +        console.log(answer);
      +        this.quill?.setText(answer);
      +    }
       }
       
       // import {Quill} from '../types/Quill';
      @@ -16775,13 +16817,15 @@ class RegexElement extends HTMLElement {
               this.negativeTestStringInput.setText(this.negativeInitialTestString);
           };
           logEvent = (eventContent) => {
      -        ({
      +        const basicEvent = {
                   'student-id': window.regexStudentId || 'stub-id',
                   'course-id': window.regexCourseId || 'stub-course-id',
                   'problem-id': this.problemId,
                   'input-type': this.inputType,
                   'client-timestamp': this._getTimestamp()
      -        });
      +        };
      +        const ev = new CustomEvent('regex-element', { bubbles: true, detail: { ...basicEvent, ...eventContent } });
      +        this.dispatchEvent(ev);
               // console.log({...basicEvent, ...eventContent});
           };
           _getTimestamp = () => {
      @@ -16921,7 +16965,7 @@ class RegexElement extends HTMLElement {
                       this.regexInput.removeFormat();
                       // logging free input event
                       this.temporaryInputEvent = {
      -                    'event-type': 'free-input',
      +                    'event-type': 'text-input',
                           dropped: this.regexInput.droppedText,
                           delta: delta,
                           answer: this.regexInput.getText()
      @@ -16995,6 +17039,15 @@ class RegexElement extends HTMLElement {
               this._testStatusDiv.innerText = '';
               this.unitTestTable.setError();
           }
      +    // restore student answer from outside storage
      +    restoreAnswer(type, answer) {
      +        if (type == undefined || answer == undefined) {
      +            return;
      +        }
      +        console.log('regex restore');
      +        console.log(answer);
      +        this.regexInput.restoreAnswer(type, answer);
      +    }
       }
       customElements.define('regex-element', RegexElement);
       
      diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst
      index 7434dad1f..69b23a436 100644
      --- a/runestone/hparsons/test/_sources/index.rst
      +++ b/runestone/hparsons/test/_sources/index.rst
      @@ -13,16 +13,7 @@ Horizontal Parsons + Regex
           --problem--
           Capture words that start with a vowel letter(aeiou), but ends with a non-vowel letter. 
           There can be 0 or more letters in between. 
      -    Also, it is not allowed to have other characters besides letter in between.
      -
      -    .. code-block:: python
      -
      -        sum = 0
      -        for i in range(10):
      -            sum = sum + i
      -    
      -        print(sum)
      -    
      +    Also, it is not allowed to have other characters besides letter in between
           e.g. your regular expression should match unicorn, element, but should not match: banana, apple. All letters are lowercase.
           --blocks--
           [a-z]
      diff --git a/runestone/parsons/js/parsons.js b/runestone/parsons/js/parsons.js
      index 2cf064837..80116f05c 100644
      --- a/runestone/parsons/js/parsons.js
      +++ b/runestone/parsons/js/parsons.js
      @@ -64,6 +64,8 @@ export default class Parsons extends RunestoneBase {
               // Set the storageId (key for storing data)
               var storageId = super.localStorageKey();
               this.storageId = storageId;
      +        console.log('parsons, first getting storageid')
      +        console.log(this.storageId)
               this.children = this.origElem.childNodes; // this contains all of the child elements of the entire tag...
               this.contentArray = [];
               Parsons.counter++; //    Unique identifier
      @@ -673,6 +675,10 @@ export default class Parsons extends RunestoneBase {
           // Return what is stored in local storage
           localData() {
               var data = localStorage.getItem(this.storageId);
      +        console.log('parsons, load')
      +        console.log(data)
      +        console.log('parsons, storageid')
      +        console.log(this.storageId)
               if (data !== null) {
                   if (data.charAt(0) == "{") {
                       data = JSON.parse(data);
      @@ -697,6 +703,8 @@ export default class Parsons extends RunestoneBase {
           }
           // RunestoneBase: Set the state of the problem in local storage
           setLocalStorage(data) {
      +        console.log('parsons, id')
      +        console.log(this.storageId)
               var toStore;
               if (data == undefined) {
                   toStore = {
      @@ -711,6 +719,8 @@ export default class Parsons extends RunestoneBase {
               } else {
                   toStore = data;
               }
      +        console.log('parsons, save')
      +        console.log(toStore)
               localStorage.setItem(this.storageId, JSON.stringify(toStore));
           }
           /* =====================================================================
      
      From 99a60c727dcebdfd19e10fd42579efd23cc4852e Mon Sep 17 00:00:00 2001
      From: amy21206 
      Date: Tue, 22 Feb 2022 00:41:07 -0500
      Subject: [PATCH 09/31] :mute: Removing logs
      
      ---
       runestone/hparsons/js/regex-element.js | 33 ++++++++++++++++----------
       runestone/parsons/js/parsons.js        |  4 ----
       2 files changed, 20 insertions(+), 17 deletions(-)
      
      diff --git a/runestone/hparsons/js/regex-element.js b/runestone/hparsons/js/regex-element.js
      index 745c066f3..0ee308d3f 100644
      --- a/runestone/hparsons/js/regex-element.js
      +++ b/runestone/hparsons/js/regex-element.js
      @@ -3569,8 +3569,6 @@ class ParsonsInput {
               if (type != 'parsons' || !Array.isArray(answer)) {
                   return;
               }
      -        console.log('parsons restore');
      -        console.log(answer);
               this._dropArea.innerHTML = '';
               for (let i = 0; i < answer.length; ++i) {
                   if (typeof answer[i] === 'string') {
      @@ -15663,8 +15661,6 @@ class TextInput {
               if (type != 'text' || typeof answer !== 'string') {
                   return;
               }
      -        console.log('text restore');
      -        console.log(answer);
               this.quill?.setText(answer);
           }
       }
      @@ -15825,9 +15821,18 @@ class TestStringInput {
           };
           // TODO: (structure) move this function to the main element after adding highlight to input
           generateColor = (colors, cnt) => {
      -        const newcolors = randomColor.randomColor({ count: 10, luminosity: 'light' });
      -        for (let i = 0; i < cnt; ++i) {
      -            colors.push(newcolors[i]);
      +        // const newcolors = randomColor({count: 10, luminosity: 'light'});
      +        // for(let i = 0; i < cnt; ++ i) {
      +        //     colors.push(newcolors[i]);
      +        // }
      +        let len = colors.length;
      +        for (let i = len; i < len + cnt; ++i) {
      +            if (i % 2 == 0) {
      +                colors.push('#b0d4a9');
      +            }
      +            else {
      +                colors.push('#98d18c');
      +            }
               }
           };
           setText(text) {
      @@ -15991,6 +15996,10 @@ class UnitTestTable {
           // not used: Return value: 'Pass' if all pass, 'Error' if one error, 'Fail' if no error but at least one fail
           // return number of test cases passed
           check = (regex) => {
      +        if (regex == '') {
      +            this.table.innerHTML = this.table.rows[0].innerHTML;
      +            return 0;
      +        }
               let passCount = 0;
               if (this.el.classList.contains('collapse')) {
                   this.el.classList.remove('collapse');
      @@ -16057,8 +16066,6 @@ class UnitTestTable {
           };
           // returns: 'Pass' if pass, 'Fail' if fail, 'Error' if error
           _createRow = (index, testCase, result) => {
      -        // console.log(testCase);
      -        // console.log(result);
               this.latestResults.push(result);
               // creating the status(the first) column
               const row = document.createElement('tr');
      @@ -16935,10 +16942,12 @@ class RegexElement extends HTMLElement {
                               // }
                               if (this.regexInput.getText() == '') {
                                   this.regexStatus.updateStatus('');
      +                            this.unitTestTable.check('');
                               }
                               else {
                                   this.regexStatus.updateStatus('error');
                                   this.regexInput.updateTestStatus('Error');
      +                            this.unitTestTable.setError();
                                   // this.regexInput.highlightError(this.regexErrorPosition);
                                   // console.log('highlight error: ');
                                   // console.log(this.regexErrorPosition);
      @@ -16946,7 +16955,6 @@ class RegexElement extends HTMLElement {
                               this.positiveTestStringInput.quill?.removeFormat(0, this.positiveTestStringInput.quill.getLength() - 1, 'silent');
                               this.negativeTestStringInput.quill?.removeFormat(0, this.negativeTestStringInput.quill.getLength() - 1, 'silent');
                               this._testStatusDiv.innerText = 'Test cases passed: 0/' + this.unitTestTable.testCaseCount;
      -                        this.unitTestTable.setError();
                           }
                       }
                   }, false);
      @@ -17004,16 +17012,17 @@ class RegexElement extends HTMLElement {
                               if (this.regexInput.getText() == '') {
                                   // it means the regex is actually empty
                                   this.regexStatus.updateStatus('');
      +                            this.unitTestTable.check('');
                               }
                               else {
                                   this.regexStatus.updateStatus('error');
                                   this.regexInput.updateTestStatus('Error');
      +                            this.unitTestTable.setError();
                                   // this.regexInput.highlightError(this.regexErrorPosition);
                               }
                               this.positiveTestStringInput.quill?.removeFormat(0, this.positiveTestStringInput.quill.getLength() - 1, 'silent');
                               this.negativeTestStringInput.quill?.removeFormat(0, this.negativeTestStringInput.quill.getLength() - 1, 'silent');
                               this._testStatusDiv.innerText = 'Test cases passed: 0/' + this.unitTestTable.testCaseCount;
      -                        this.unitTestTable.setError();
                           }
                           // check and update the background color of the parsons input based on the unit test results
                       }
      @@ -17044,8 +17053,6 @@ class RegexElement extends HTMLElement {
               if (type == undefined || answer == undefined) {
                   return;
               }
      -        console.log('regex restore');
      -        console.log(answer);
               this.regexInput.restoreAnswer(type, answer);
           }
       }
      diff --git a/runestone/parsons/js/parsons.js b/runestone/parsons/js/parsons.js
      index 80116f05c..77475394b 100644
      --- a/runestone/parsons/js/parsons.js
      +++ b/runestone/parsons/js/parsons.js
      @@ -675,10 +675,6 @@ export default class Parsons extends RunestoneBase {
           // Return what is stored in local storage
           localData() {
               var data = localStorage.getItem(this.storageId);
      -        console.log('parsons, load')
      -        console.log(data)
      -        console.log('parsons, storageid')
      -        console.log(this.storageId)
               if (data !== null) {
                   if (data.charAt(0) == "{") {
                       data = JSON.parse(data);
      
      From 88d03c2241d3df50aa09f525b740c5d5a548238a Mon Sep 17 00:00:00 2001
      From: amy21206 
      Date: Tue, 1 Mar 2022 22:40:11 -0500
      Subject: [PATCH 10/31] :alembic: Experimenting to switch to SQL
      
      ---
       runestone/hparsons/hparsons.py        |  18 +-
       runestone/hparsons/js/hparsons-sql.js | 380 ++++++++++++++++++++++++++
       2 files changed, 395 insertions(+), 3 deletions(-)
       create mode 100644 runestone/hparsons/js/hparsons-sql.js
      
      diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py
      index 77da5276e..32f22df05 100755
      --- a/runestone/hparsons/hparsons.py
      +++ b/runestone/hparsons/hparsons.py
      @@ -92,6 +92,7 @@ class HParsonsDirective(Assessment):
               :textentry: if you will use text entry instead of horizontal parsons
               :hidetests: if the unittests will be hidden from learners
               :nostrictmatch: if the answer is required to match the whole string. if not selected, the tool will add ^ and $ automatically to the answer to force matching the full string. This does not affect the test string area.
      +        :language: SQL or regex
       
               --problem--
               Here is the problem description. 
      @@ -102,13 +103,13 @@ class HParsonsDirective(Assessment):
               --explanations--
               explanations for block 1
               explanations for block 2
      -        --positive test string--
      +        --positive test string-- (this is only for regex)
               this is some positive test string.
               it can be more than one line.
               just ignore this section if you do not want to put anything in there.
      -        --negative test string--
      +        --negative test string-- (this is only for regex)
               this is some negative test string.
      -        --test cases--
      +        --test cases-- (this is only for regex)
               input string 1
               ['expected match 1', 'expected match 2']
               input string 2
      @@ -154,6 +155,17 @@ def run(self):
               else:
                   self.options['nostrictmatch'] = ''
       
      +        # SQL Options copied from activecode
      +        if "dburl" in self.options:
      +            self.options["dburl"] = "data-dburl='{}'".format(self.options["dburl"])
      +        else:
      +            self.options["dburl"] = ""
      +
      +        if "showlastsql" in self.options:
      +            self.options["showlastsql"] = 'data-showlastsql="true"'
      +        else:
      +            self.options["showlastsql"] = ""
      +        
               self.assert_has_content()
       
               # sepcifying the start end end for each section
      diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js
      new file mode 100644
      index 000000000..d692500b3
      --- /dev/null
      +++ b/runestone/hparsons/js/hparsons-sql.js
      @@ -0,0 +1,380 @@
      +import { ActiveCode } from "./activecode.js";
      +import Handsontable from "handsontable";
      +import 'handsontable/dist/handsontable.full.css';
      +import initSqlJs from "sql.js/dist/sql-wasm.js";
      +
      +var allDburls = {};
      +
      +export default class SQLActiveCode extends ActiveCode {
      +    constructor(opts) {
      +        super(opts);
      +        //  fnprefix sets the path to load the sql-wasm.wasm file
      +        var bookprefix;
      +        var fnprefix;
      +        if (eBookConfig.useRunestoneServices) {
      +            bookprefix = `${eBookConfig.app}/books/published/${eBookConfig.basecourse}`;
      +            fnprefix = bookprefix + "/_static";
      +        } else {
      +            bookprefix = "";
      +            fnprefix = "/_static";
      +        }
      +        this.config = {
      +            locateFile: (filename) => `${fnprefix}/${filename}`,
      +        };
      +        this.showLast = $(this.origElem).data("showlastsql");
      +        var self = this;
      +        initSqlJs(this.config).then(function (SQL) {
      +            // set up call to load database asynchronously if given
      +            if (self.dburl) {
      +                if (self.dburl.startsWith("/_static")) {
      +                    self.dburl = `${bookprefix}${self.dburl}`;
      +                }
      +                $(self.runButton).attr("disabled", "disabled");
      +                let buttonText = $(self.runButton).text();
      +                $(self.runButton).text($.i18n("msg_activecode_load_db"));
      +                if (!(self.dburl in allDburls)) {
      +                    allDburls[self.dburl] = {
      +                        status: "loading",
      +                        xWaitFor: jQuery.Deferred(),
      +                    };
      +                } else {
      +                    if (allDburls[self.dburl].status == "loading") {
      +                        allDburls[self.dburl].xWaitFor.done(function () {
      +                            self.db = allDburls[self.dburl].dbObject;
      +                            $(self.runButton).removeAttr("disabled");
      +                            $(self.runButton).text(buttonText);
      +                        });
      +                        return;
      +                    }
      +                    self.db = allDburls[self.dburl].dbObject;
      +                    $(self.runButton).removeAttr("disabled");
      +                    $(self.runButton).text(buttonText);
      +                    return;
      +                }
      +                var xhr = new XMLHttpRequest();
      +                // For example: https://github.com/lerocha/chinook-database/raw/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite
      +                xhr.open("GET", self.dburl, true);
      +                xhr.responseType = "arraybuffer";
      +                xhr.onload = (e) => {
      +                    var uInt8Array = new Uint8Array(xhr.response);
      +                    self.db = new SQL.Database(uInt8Array);
      +                    allDburls[self.dburl].dbObject = self.db;
      +                    $(self.runButton).text(buttonText);
      +                    $(self.runButton).removeAttr("disabled");
      +                    allDburls[self.dburl].db = uInt8Array;
      +                    allDburls[self.dburl].status = "ready";
      +                    allDburls[self.dburl].xWaitFor.resolve();
      +                    // contents is now [{columns:['col1','col2',...], values:[[first row], [second row], ...]}]
      +                };
      +                xhr.send();
      +            } else {
      +                self.db = new SQL.Database();
      +            }
      +        });
      +    }
      +    async runProg(noUI, logResults) {
      +        if (typeof logResults === "undefined") {
      +            this.logResults = true;
      +        } else {
      +            this.logResults = logResults;
      +        }
      +        if (typeof noUI !== "boolean") {
      +            noUI = false;
      +        }
      +        // Clear any old results
      +        this.saveCode = "True";
      +        let divid = this.divid + "_sql_out";
      +        let respDiv = document.getElementById(divid);
      +        if (respDiv) {
      +            respDiv.parentElement.removeChild(respDiv);
      +        }
      +        $(this.output).text("");
      +        // Run this query
      +        let query = await this.buildProg(false); // false --> Do not include suffix
      +        if (!this.db) {
      +            $(this.output).text(
      +                `Error: Database not initialized! DBURL: ${this.dburl}`
      +            );
      +            return;
      +        }
      +
      +        let it = this.db.iterateStatements(query);
      +        this.results = [];
      +        try {
      +            for (let statement of it) {
      +                let columns = statement.getColumnNames();
      +                if (columns.length > 0) {
      +                    // data! probably a SELECT
      +                    let data = [];
      +                    while (statement.step()) {
      +                        data.push(statement.get());
      +                    }
      +                    this.results.push({
      +                        status: "success",
      +                        columns: columns,
      +                        values: data,
      +                        rowcount: data.length,
      +                    });
      +                } else {
      +                    let nsql = statement.getNormalizedSQL();
      +                    let prefix = nsql.substr(0, 6).toLowerCase();
      +                    statement.step(); // execute the query
      +                    // Try to detect INSERT/UPDATE/DELETE to give friendly feedback
      +                    // on rows modified - unfortunately, this won't catch such queries
      +                    // if they use CTEs.  There seems to be no reliable way of knowing
      +                    // when a SQLite query actually modified data.
      +                    if (
      +                        prefix === "insert" ||
      +                        prefix === "update" ||
      +                        prefix === "delete"
      +                    ) {
      +                        this.results.push({
      +                            status: "success",
      +                            operation: prefix,
      +                            rowcount: this.db.getRowsModified(),
      +                        });
      +                    } else {
      +                        this.results.push({ status: "success" });
      +                    }
      +                }
      +            }
      +        } catch (e) {
      +            this.results.push({
      +                status: "failure",
      +                message: e.toString(),
      +                sql: it.getRemainingSQL(),
      +            });
      +        }
      +
      +        if (this.results.length === 0) {
      +            this.results.push({
      +                status: "failure",
      +                message: "No queries submitted.",
      +            });
      +        }
      +
      +        try {
      +            this.saveCode = await this.manage_scrubber(this.saveCode);
      +            if (this.slideit) {
      +                $(this.historyScrubber).on(
      +                    "slidechange",
      +                    this.slideit.bind(this)
      +                );
      +            }
      +            $(this.historyScrubber).slider("enable");
      +        } catch (e) {
      +            console.log(`Failed to update scrubber ${e}`);
      +        }
      +
      +        respDiv = document.createElement("div");
      +        respDiv.id = divid;
      +        this.outDiv.appendChild(respDiv);
      +        $(this.outDiv).show();
      +        // Sometimes we don't want to show a bunch of intermediate results
      +        // like when we are including a bunch of previous statements from
      +        // other activecodes In that case the showlastsql flag can be set
      +        // so we only show the last result
      +        let resultArray = this.results;
      +        if (this.showLast) {
      +            resultArray = this.results.slice(-1);
      +        }
      +        for (let r of resultArray) {
      +            let section = document.createElement("div");
      +            section.setAttribute("class", "ac_sql_result");
      +            respDiv.appendChild(section);
      +            if (r.status === "success") {
      +                if (r.columns) {
      +                    let tableDiv = document.createElement("div");
      +                    section.appendChild(tableDiv);
      +                    let maxHeight = 350;
      +                    if (resultArray.length > 1) maxHeight = 200; // max height smaller if lots of results
      +                    createTable(r, tableDiv, maxHeight);
      +                    let messageBox = document.createElement("pre");
      +                    let rmsg = r.rowcount !== 1 ? " rows " : " row ";
      +                    let msg = "" + r.rowcount + rmsg + "returned";
      +                    if (r.rowcount > 100) {
      +                        msg = msg + " (only first 100 rows displayed)";
      +                    }
      +                    msg = msg + ".";
      +                    messageBox.textContent = msg;
      +                    messageBox.setAttribute("class", "ac_sql_result_success");
      +                    section.appendChild(messageBox);
      +                } else if (r.rowcount) {
      +                    let messageBox = document.createElement("pre");
      +                    let op = r.operation;
      +                    op = op + (op.charAt(op.length - 1) === "e" ? "d." : "ed.");
      +                    let rmsg = r.rowcount !== 1 ? " rows " : " row ";
      +                    messageBox.textContent = "" + r.rowcount + rmsg + op;
      +                    messageBox.setAttribute("class", "ac_sql_result_success");
      +                    section.appendChild(messageBox);
      +                } else {
      +                    let messageBox = document.createElement("pre");
      +                    messageBox.textContent = "Operation succeeded.";
      +                    messageBox.setAttribute("class", "ac_sql_result_success");
      +                    section.appendChild(messageBox);
      +                }
      +            } else {
      +                let messageBox = document.createElement("pre");
      +                messageBox.textContent = r.message;
      +                messageBox.setAttribute("class", "ac_sql_result_failure");
      +                section.appendChild(messageBox);
      +            }
      +        }
      +
      +        // Now handle autograding
      +        if (this.suffix) {
      +            this.testResult = this.autograde(
      +                this.results[this.results.length - 1]
      +            );
      +        } else {
      +            $(this.output).css("visibility", "hidden");
      +        }
      +
      +        return Promise.resolve("done");
      +    }
      +
      +    async logCurrentAnswer(sid) {
      +        let data = {
      +            div_id: this.divid,
      +            code: this.editor.getValue(),
      +            language: this.language,
      +            errinfo: this.results[this.results.length - 1].status,
      +            to_save: this.saveCode,
      +            prefix: this.pretext,
      +            suffix: this.suffix,
      +            partner: this.partner,
      +        }; // Log the run event
      +        if (typeof sid !== "undefined") {
      +            data.sid = sid;
      +        }
      +        await this.logRunEvent(data);
      +
      +        if (this.unit_results) {
      +            let unitData = {
      +                event: "unittest",
      +                div_id: this.divid,
      +                course: eBookConfig.course,
      +                act: this.unit_results,
      +            };
      +            if (typeof sid !== "undefined") {
      +                unitData.sid = sid;
      +            }
      +            await this.logBookEvent(unitData);
      +        }
      +    }
      +
      +    renderFeedback() {
      +        if (this.testResult) {
      +            $(this.output).text(this.testResult);
      +            $(this.output).css("visibility", "visible");
      +        }
      +    }
      +
      +    autograde(result_table) {
      +        var tests = this.suffix.split(/\n/);
      +        this.passed = 0;
      +        this.failed = 0;
      +        // Tests should be of the form
      +        // assert row,col oper value for example
      +        // assert 4,4 == 3
      +        var result = "";
      +        tests = tests.filter(function (s) {
      +            return s.indexOf("assert") > -1;
      +        });
      +        for (let test of tests) {
      +            let wlist = test.split(/\s+/);
      +            wlist.shift();
      +            let loc = wlist.shift();
      +            let oper = wlist.shift();
      +            let expected = wlist.join(" ");
      +            let [row, col] = loc.split(",");
      +            result += this.testOneAssert(
      +                row,
      +                col,
      +                oper,
      +                expected,
      +                result_table
      +            );
      +            result += "\n";
      +        }
      +        let pct = (100 * this.passed) / (this.passed + this.failed);
      +        pct = pct.toLocaleString(undefined, { maximumFractionDigits: 2 });
      +        result += `You passed ${this.passed} out of ${this.passed + this.failed
      +            } tests for ${pct}%`;
      +        this.unit_results = `percent:${pct}:passed:${this.passed}:failed:${this.failed}`;
      +        return result;
      +    }
      +    testOneAssert(row, col, oper, expected, result_table) {
      +        // make sure row and col are in bounds
      +        let actual;
      +        let output = "";
      +        try {
      +            actual = result_table.values[row][col];
      +        } catch (e) {
      +            output = `Failed Not enough data to check row ${row} or column ${col}`;
      +            return output;
      +        }
      +        const operators = {
      +            "==": function (operand1, operand2) {
      +                return operand1 == operand2;
      +            },
      +            "!=": function (operand1, operand2) {
      +                return operand1 != operand2;
      +            },
      +            ">": function (operand1, operand2) {
      +                return operand1 > operand2;
      +            },
      +            "<": function (operand1, operand2) {
      +                return operand1 > operand2;
      +            },
      +        };
      +        let res = operators[oper](actual, expected);
      +        if (res) {
      +            output = `Pass: ${actual} ${oper} ${expected} in row ${row} column ${result_table.columns[col]}`;
      +            this.passed++;
      +        } else {
      +            output = `Failed ${actual} ${oper} ${expected} in row ${row} column ${result_table.columns[col]}`;
      +            this.failed++;
      +        }
      +        return output;
      +    }
      +}
      +
      +function createTable(tableData, container, maxHeight) {
      +    let data = tableData.values;
      +    let trimRows = undefined;
      +    if (data.length === 0) {
      +        // kludge: no column headers will show up unless we do this
      +        data = [tableData.columns.map((e) => null)];
      +        trimRows = [0];
      +    }
      +
      +    var hot = new Handsontable(container, {
      +        data: data,
      +        trimRows: trimRows,
      +        width: "100%",
      +        height: maxHeight,
      +        autoRowSize: true,
      +        autoColumnSize: { useHeaders: true },
      +        rowHeaders: false,
      +        colHeaders: tableData.columns,
      +        editor: false,
      +        maxRows: 100,
      +        filters: false,
      +        dropdownMenu: false,
      +        licenseKey: "non-commercial-and-evaluation",
      +    });
      +
      +    // calculate actual height and resize
      +    let actualHeight = 40; // header height + small margin
      +    if (tableData.values.length > 0) {
      +        for (let i = 0; i < data.length; i++) {
      +            actualHeight = actualHeight + hot.getRowHeight(i);
      +            if (actualHeight > maxHeight) break;
      +        }
      +    }
      +
      +    hot.updateSettings({ height: actualHeight });
      +
      +    return hot;
      +}
      \ No newline at end of file
      
      From 8353958a5f9fe3dd7412d6228f6c83ddff8b9d54 Mon Sep 17 00:00:00 2001
      From: Zihan Wu 
      Date: Thu, 3 Mar 2022 18:14:36 -0500
      Subject: [PATCH 11/31] :alembic: Migrating activecode-sql fully to hparsons
      
      ---
       runestone/hparsons/hparsons.py                | 525 ++++++++++++------
       runestone/hparsons/js/hparsons-sql.js         | 322 ++++++++++-
       runestone/hparsons/test.rst                   |  23 +
       .../hparsons/test/_sources/_static/test.db    | Bin 0 -> 3072 bytes
       runestone/hparsons/test/_sources/index.rst    | 125 ++---
       runestone/hparsons/textfield.py               |  61 ++
       6 files changed, 820 insertions(+), 236 deletions(-)
       create mode 100644 runestone/hparsons/test.rst
       create mode 100644 runestone/hparsons/test/_sources/_static/test.db
       create mode 100644 runestone/hparsons/textfield.py
      
      diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py
      index 32f22df05..3d862a180 100755
      --- a/runestone/hparsons/hparsons.py
      +++ b/runestone/hparsons/hparsons.py
      @@ -1,3 +1,6 @@
      +# *********
      +# |docname|
      +# *********
       # Copyright (C) 2011  Bradley N. Miller
       #
       # This program is free software: you can redistribute it and/or modify
      @@ -13,149 +16,336 @@
       # You should have received a copy of the GNU General Public License
       # along with this program.  If not, see .
       #
      -__author__ = "ziwu"
      +from __future__ import print_function
      +
      +__author__ = "bmiller"
       
      -from cgi import test
      -import json
       from docutils import nodes
       from docutils.parsers.rst import directives
      -from runestone.mchoice import Assessment
      +from .textfield import textfield_role
      +from sqlalchemy import Table
       from runestone.server.componentdb import (
           addQuestionToDB,
           addHTMLToDB,
      +    get_engine_meta,
           maybeAddToAssignment,
       )
      -from runestone.common.runestonedirective import RunestoneDirective, RunestoneIdNode
      +from runestone.common.runestonedirective import (
      +    RunestoneIdDirective,
      +    RunestoneIdNode,
      +)
      +
      +try:
      +    from html import escape  # py3
      +except ImportError:
      +    from cgi import escape  # py2
       
       
       def setup(app):
           app.add_directive("hparsons", HParsonsDirective)
      -    # adding the html
      -    app.add_node(HParsonsNode, html=(visit_hparsons_node, depart_hparsons_node))
      -    # TODO: figure out what these means
      -    # app.add_config_value("shortanswer_div_class", "journal alert alert-warning", "html")
      -    # app.add_config_value(
      -    #     "shortanswer_optional_div_class", "journal alert alert-success", "html"
      -    # )
      +    app.add_node(HParsonsNode, html=(visit_ac_node, depart_ac_node))
       
       
      -# TODO: what is the alert and alert-warnings?
       TEMPLATE_START = """
      -        
      -
      -
      - """ +
      +
      +
      +""" TEMPLATE_END = """ -
      -
      -
      %(settings)s
      -
      -
      -
      - """ +
      + +
      +
      +""" -# seems to be the same for all class HParsonsNode(nodes.General, nodes.Element, RunestoneIdNode): def __init__(self, options, **kwargs): super(HParsonsNode, self).__init__(**kwargs) self.runestone_options = options -# generate the first part of the html -def visit_hparsons_node(self, node): - div_id = node.runestone_options["divid"] - components = dict(node.runestone_options) - components.update({"divid": div_id}) + +# self for these functions is an instance of the writer class. For example +# in html, self is sphinx.writers.html.SmartyPantsHTMLTranslator +# The node that is passed as a parameter is an instance of our node class. +def visit_ac_node(self, node): + # print self.settings.env.activecodecounter + + # todo: handle above in node.runestone_options + # todo handle 'hidecode' not in node.runestone_options: + # todo: handle if 'gradebutton' in node.runestone_options: res += GRADES + node.delimiter = "_start__{}_".format(node.runestone_options["divid"]) + self.body.append(node.delimiter) - res = TEMPLATE_START % components + + res = TEMPLATE_START % node.runestone_options self.body.append(res) -# generate the second part of the html -def depart_hparsons_node(self, node): - components = dict(node.runestone_options) - res = TEMPLATE_END % components +def depart_ac_node(self, node): + """This is called at the start of processing an activecode node. If activecode had recursive nodes + etc and did not want to do all of the processing in visit_ac_node any finishing touches could be + added here. + """ + res = TEMPLATE_END % node.runestone_options self.body.append(res) + addHTMLToDB( node.runestone_options["divid"], - components["basecourse"], + node.runestone_options["basecourse"], "".join(self.body[self.body.index(node.delimiter) + 1 :]), ) + self.body.remove(node.delimiter) -class HParsonsDirective(Assessment): +def process_activcode_nodes(app, env, docname): + pass + + +def purge_activecodes(app, env, docname): + pass + + +class HParsonsDirective(RunestoneIdDirective): """ - .. hparsons:: uniqueid - :textentry: if you will use text entry instead of horizontal parsons - :hidetests: if the unittests will be hidden from learners - :nostrictmatch: if the answer is required to match the whole string. if not selected, the tool will add ^ and $ automatically to the answer to force matching the full string. This does not affect the test string area. - :language: SQL or regex - - --problem-- - Here is the problem description. - Make sure you use the correct delimitier for each section. - --blocks-- - block 1 - block 2 - --explanations-- - explanations for block 1 - explanations for block 2 - --positive test string-- (this is only for regex) - this is some positive test string. - it can be more than one line. - just ignore this section if you do not want to put anything in there. - --negative test string-- (this is only for regex) - this is some negative test string. - --test cases-- (this is only for regex) - input string 1 - ['expected match 1', 'expected match 2'] - input string 2 - [] - input string 3 - [] + .. activecode:: uniqueid + :nocanvas: -- do not create a canvas + :autograde: unittest + :nopre: -- do not create an output component + :above: -- put the canvas above the code + :autorun: -- run this activecode as soon as the page is loaded + :caption: this is the caption + :include: div1,div2 -- invisibly include code from another activecode + :hidecode: -- Don't show the editor initially + :nocodelens: -- Do not show the codelens button + :timelimit: -- set the time limit for this program in seconds + :language: python, html, javascript, java, python2, python3 + :chatcodes: -- Enable users to talk about this code snippet with others + :tour_1: audio tour track + :tour_2: audio tour track + :tour_3: audio tour track + :tour_4: audio tour track + :tour_5: audio tour track + :stdin: : A file to simulate stdin (java, python2, python3) + :datafile: : A datafile for the program to read (java, python2, python3) + :sourcefile: : source files (java, python2, python3) + :available_files: : other additional files (java, python2, python3) + :enabledownload: -- allow textfield contents to be downloaded as *.py file + :nopair: -- disable pair programming features + :dburl: url to load database for sql mode + :showlastsql: -- Only show the last sql result in output + + If this is a homework problem instead of an example in the text + then the assignment text should go here. The assignment text ends with + the line containing four tilde ~ + ~~~~ + print("Hidden code before students code - good for scaffolding") + ^^^^ + print("hello world") + ==== + print("Hidden code, such as unit tests come after the four = signs") + + config values (conf.py): + + - activecode_div_class - custom CSS class of the component's outermost div + - activecode_hide_load_history - if True, hide the load history button + - wasm_uri - Path or Full URL to folder containing WASM files for SQL. /_static is default """ - required_arguments = 1 # the div id + required_arguments = 1 optional_arguments = 1 - final_argument_whitespace = False - has_content = True - option_spec = Assessment.option_spec.copy() + has_content = True + option_spec = RunestoneIdDirective.option_spec.copy() option_spec.update( { - "textentry": directives.flag, - "hidetests": directives.flag, - "nostrictmatch": directives.flag, + "nocanvas": directives.flag, + "nopre": directives.flag, + "above": directives.flag, # put the canvas above the code + "autorun": directives.flag, + "caption": directives.unchanged, + "include": directives.unchanged, + "hidecode": directives.flag, + "language": directives.unchanged, + "chatcodes": directives.flag, + "tour_1": directives.unchanged, + "tour_2": directives.unchanged, + "tour_3": directives.unchanged, + "tour_4": directives.unchanged, + "tour_5": directives.unchanged, + "nocodelens": directives.flag, + "coach": directives.flag, + "gradebutton": directives.flag, + "timelimit": directives.unchanged, + "stdin": directives.unchanged, + "datafile": directives.unchanged, + "sourcefile": directives.unchanged, + "available_files": directives.unchanged, + "enabledownload": directives.flag, + "compileargs": directives.unchanged, + "linkargs": directives.unchanged, + "interpreterargs": directives.unchanged, + "runargs": directives.unchanged, + "tie": directives.unchanged, + "nopair": directives.flag, + "dburl": directives.unchanged, + "showlastsql": directives.flag, } ) - # seem to be defining the type of the options - # option_spec.update({"mathjax": directives.flag}) - - # just fill it with the name - # node_class = HParsonsNode def run(self): - # same super(HParsonsDirective, self).run() + + env = self.state.document.settings.env + # keep track of how many activecodes we have.... + # could be used to automatically make a unique id for them. + if not hasattr(env, "activecodecounter"): + env.activecodecounter = 0 + env.activecodecounter += 1 + self.options["name"] = self.arguments[0].strip() + + explain_text = None + if self.content: + if "~~~~" in self.content: + idx = self.content.index("~~~~") + explain_text = self.content[:idx] + self.content = self.content[idx + 1 :] + source = "\n".join(self.content) + else: + source = "\n" + + self.explain_text = explain_text or ["Not an Exercise"] addQuestionToDB(self) - # Raise an error if the directive does not have contents. - # env = self.state.document.settings.env - if "textentry" in self.options: - self.options['textentry'] = ' data-textentry="true"' + + self.options["initialcode"] = source + str = source.replace("\n", "*nline*") + str0 = str.replace('"', "*doubleq*") + str1 = str0.replace("(", "*open*") + str2 = str1.replace(")", "*close*") + str3 = str2.replace("'", "*singleq*") + self.options["argu"] = str3 + + # TODO: This is BAD -- using '_' as a key for audio tour stuff is wrong. + complete = "" + no_of_buttons = 0 + okeys = list(self.options.keys()) + for k in okeys: + if "tour_" in k: + x, label = k.split("_") + no_of_buttons = no_of_buttons + 1 + complete = complete + self.options[k] + "*atype*" + + newcomplete = complete.replace('"', "*doubleq*") + self.options["ctext"] = newcomplete + self.options["no_of_buttons"] = no_of_buttons + + if "caption" not in self.options: + self.options["caption"] = "" + else: + self.options["caption"] = "data-caption='%s'" % self.options["caption"] + + if "include" not in self.options: + self.options["include"] = "" + else: + lst = self.options["include"].split(",") + lst = [x.strip() for x in lst] + self.options["include"] = 'data-include="' + " ".join(lst) + '"' + + if "hidecode" in self.options: + self.options["hidecode"] = 'data-hidecode="true"' else: - self.options['textentry'] = '' - if "hidetests" in self.options: - self.options['hidetests'] = ' data-hidetests="true"' + self.options["hidecode"] = "" + + if "enabledownload" in self.options: + self.options["enabledownload"] = 'data-enabledownload="true"' + else: + self.options["enabledownload"] = "" + + if "chatcodes" in self.options: + self.options["chatcodes"] = 'data-chatcodes="true"' + else: + self.options["chatcodes"] = "" + + if "language" not in self.options: + self.options["language"] = "python" + + if self.options["language"] == "html": + self.options["language"] = "htmlmixed" + self.options["initialcode"] = escape(self.options["initialcode"]) + + if "nocodelens" in self.options or self.options["language"] not in [ + "python", + "java", + "c", + "cpp", + ]: + self.options["codelens"] = "" + else: + self.options["codelens"] = 'data-codelens="true"' + + if "nopair" in self.options: + self.options["nopair"] = 'data-nopair="true"' + else: + self.options["nopair"] = "" + + if "timelimit" not in self.options: + self.options["timelimit"] = "data-timelimit=25000" + else: + self.options["timelimit"] = "data-timelimit=%s" % self.options["timelimit"] + + if "autorun" not in self.options: + self.options["autorun"] = "" + else: + self.options["autorun"] = 'data-autorun="true"' + + if "coach" in self.options: + self.options["coach"] = 'data-coach="true"' + else: + self.options["coach"] = "" + + # livecode options + if "stdin" in self.options: + self.options["stdin"] = "data-stdin='%s'" % self.options["stdin"] + else: + self.options["stdin"] = "" + + if "datafile" not in self.options: + self.options["datafile"] = "" else: - self.options['hidetests'] = '' - if "nostrictmatch" in self.options: - self.options['nostrictmatch'] = ' data-nostrictmatch="true"' + self.options["datafile"] = "data-datafile='%s'" % self.options["datafile"] + + if "sourcefile" not in self.options: + self.options["sourcefile"] = "" else: - self.options['nostrictmatch'] = '' + self.options["sourcefile"] = ( + "data-sourcefile='%s'" % self.options["sourcefile"] + ) - # SQL Options copied from activecode + if "tie" in self.options: + self.options["tie"] = "data-tie='{}'".format(self.options["tie"]) + else: + self.options["tie"] = "" + + for opt, tp in [ + ("compileargs", "cargs"), + ("linkargs", "largs"), + ("runargs", "rargs"), + ("interpreterargs", "iargs"), + ]: + if opt in self.options: + self.options[tp] = 'data-{}="{}"'.format(opt, escape(self.options[opt])) + else: + self.options[tp] = "" + + # SQL Options if "dburl" in self.options: self.options["dburl"] = "data-dburl='{}'".format(self.options["dburl"]) else: @@ -165,85 +355,86 @@ def run(self): self.options["showlastsql"] = 'data-showlastsql="true"' else: self.options["showlastsql"] = "" - - self.assert_has_content() - - # sepcifying the start end end for each section - delimitiers = ['--problem--', '--blocks--', '--explanations--', '--positive test string--', '--negative test string--', '--test cases--'] - delimitiers_index = [-1 for x in range(6)] - - has_content = False - for i in range(len(delimitiers)): - if delimitiers[i] in self.content: - has_content = True - delimitiers_index[i] = self.content.index(delimitiers[i]) - if has_content: - sorted_index, sorted_delimiters = [list(t) for t in zip(*[pair for pair in sorted(zip(delimitiers_index, delimitiers)) if pair[0] >= 0])] - else: - sorted_index = [] - sorted_delimiters = [] - - content = self.content - - parsons_settings = {} - - if '--problem--' in sorted_delimiters: - index = sorted_delimiters.index('--problem--') - self.options['instructions'] = content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))] - self.options['problem'] = '\n'.join(content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) - else: - self.options['instructions'] = ['empty problem'] - self.options['problem'] = 'empty problem' - - if '--blocks--' in sorted_delimiters: - index = sorted_delimiters.index('--blocks--') - parsons_settings['blocks'] = list(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) - else: - parsons_settings['blocks'] = [] - - if '--explanations--' in sorted_delimiters: - index = sorted_delimiters.index('--explanations--') - parsons_settings['explanations'] = list(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) - - if '--positive test string--' in sorted_delimiters: - index = sorted_delimiters.index('--positive test string--') - parsons_settings['positivetest'] = '\n'.join(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) - - if '--negative test string--' in sorted_delimiters: - index = sorted_delimiters.index('--negative test string--') - parsons_settings['negativetest'] = '\n'.join(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))]) - - if '--test cases--' in sorted_delimiters: - index = sorted_index[sorted_delimiters.index('--test cases--')] + 1 - end_index = sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content) - parsons_settings['testcases'] = [] - while index < end_index: - testcase = {} - testcase['input'] = content[index] - testcase['expect'] = content[index + 1] if index + 1 < end_index else [] - parsons_settings['testcases'].append(testcase) - index += 2 - - self.options['settings'] = json.dumps(parsons_settings) - self.options['type'] = type(self.options['instructions']) - - # same - maybeAddToAssignment(self) - # same - hparsons_node = HParsonsNode(self.options, rawsource=self.block_text) - hparsons_node.source, hparsons_node.line = self.state_machine.get_source_and_line( - self.lineno - ) + # other options - # exist in short answer and mchoice but not parsons - # For MChoice its better to insert the qnum into the content before further processing. - # self.updateContent() + if "gradebutton" not in self.options: + self.options["gradebutton"] = "" + else: + self.options["gradebutton"] = "data-gradebutton=true" - self.state.nested_parse( - self.options['instructions'], self.content_offset, hparsons_node - ) + self.options["divclass"] = env.config.activecode_div_class + if env.config.activecode_hide_load_history: + self.options["hidehistory"] = "data-hidehistory=true" + else: + self.options["hidehistory"] = "" + + if env.config.wasm_uri: + self.options["wasmuri"] = f"data-wasm={env.config.wasm_uri}" + else: + self.options["wasmuri"] = "" + + if self.content: + if "^^^^" in self.content: + idx = self.content.index("^^^^") + prefix = "\n".join(self.content[:idx]) + if "====" in self.content: + idx = self.content.index("====") + source = "\n".join(self.content[:idx]) + suffix = "\n".join(self.content[idx + 1 :]) + else: + source = "\n".join(self.content) + suffix = "\n" + else: + source = "\n" + suffix = "\n" + + course_name = env.config.html_context["course_id"] + divid = self.options["divid"] + + engine, meta, sess = get_engine_meta() + + if engine: + Source_code = Table( + "source_code", meta, autoload=True, autoload_with=engine + ) + engine.execute( + Source_code.delete() + .where(Source_code.c.acid == divid) + .where(Source_code.c.course_id == course_name) + ) + engine.execute( + Source_code.insert().values( + acid=divid, + course_id=course_name, + main_code=source, + suffix_code=suffix, + includes=self.options["include"], + available_files=self.options.get("available_files", ""), + ) + ) + else: + if ( + not hasattr(env, "dberr_activecode_reported") + or not env.dberr_activecode_reported + ): + env.dberr_activecode_reported = True + print( + "Unable to save to source_code table in activecode.py. Possible problems:" + ) + print(" 1. dburl or course_id are not set in conf.py for your book") + print(" 2. unable to connect to the database using dburl") + print("") + print( + "This should only affect the grading interface. Everything else should be fine." + ) + + acnode = HParsonsNode(self.options, rawsource=self.block_text) + acnode.source, acnode.line = self.state_machine.get_source_and_line(self.lineno) + self.add_name(acnode) # make this divid available as a target for :ref: + maybeAddToAssignment(self) + if explain_text: + self.state.nested_parse(explain_text, self.content_offset, acnode) - # same - return [hparsons_node] + return [acnode] diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index d692500b3..e5fb99881 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -1,13 +1,123 @@ -import { ActiveCode } from "./activecode.js"; import Handsontable from "handsontable"; import 'handsontable/dist/handsontable.full.css'; import initSqlJs from "sql.js/dist/sql-wasm.js"; +import RunestoneBase from "../../common/js/runestonebase.js"; + +// temporary code mirror stuff copied from activecode +import CodeMirror from "codemirror"; +import "codemirror/mode/python/python.js"; +import "codemirror/mode/css/css.js"; +import "codemirror/mode/htmlmixed/htmlmixed.js"; +import "codemirror/mode/xml/xml.js"; +import "codemirror/mode/javascript/javascript.js"; +import "codemirror/mode/sql/sql.js"; +import "codemirror/mode/clike/clike.js"; +import "codemirror/mode/octave/octave.js"; +import "./../css/activecode.css"; +import "codemirror/lib/codemirror.css"; + +// copied from activecode +// Used by Skulpt. +import embed from "vega-embed"; +// Adapt for use outside webpack -- see https://github.com/vega/vega-embed. +window.vegaEmbed = embed; var allDburls = {}; -export default class SQLActiveCode extends ActiveCode { +export default class SQLHParons extends RunestoneBase { constructor(opts) { super(opts); + // copied from activecode + var suffStart; + var orig = $(opts.orig).find("textarea")[0]; + this.containerDiv = opts.orig; + this.useRunestoneServices = opts.useRunestoneServices; + this.python3 = opts.python3; + this.alignVertical = opts.vertical; + this.origElem = orig; + this.origText = this.origElem.textContent; + this.divid = opts.orig.id; + this.code = $(orig).text() || "\n\n\n\n\n"; + this.language = $(orig).data("lang"); + this.timelimit = $(orig).data("timelimit"); + this.includes = $(orig).data("include"); + this.hidecode = $(orig).data("hidecode"); + this.chatcodes = $(orig).data("chatcodes"); + this.hidehistory = $(orig).data("hidehistory"); + this.question = $(opts.orig).find(`#${this.divid}_question`)[0]; + this.tie = $(orig).data("tie"); + this.dburl = $(orig).data("dburl"); + this.runButton = null; + this.enabledownload = $(orig).data("enabledownload"); + this.downloadButton = null; + this.saveButton = null; + this.loadButton = null; + this.outerDiv = null; + this.partner = ""; + this.logResults = true; + if (!eBookConfig.allow_pairs || $(orig).data("nopair")) { + this.enablePartner = false; + } else { + this.enablePartner = true; + } + this.output = null; // create pre for output + this.graphics = null; // create div for turtle graphics + this.codecoach = null; + this.codelens = null; + this.controlDiv = null; + this.historyScrubber = null; + this.timestamps = ["Original"]; + this.autorun = $(orig).data("autorun"); + if (this.chatcodes && eBookConfig.enable_chatcodes) { + if (!socket) { + socket = new WebSocket("wss://" + chatcodesServer); + } + if (!connection) { + connection = new window.sharedb.Connection(socket); + } + if (!doc) { + doc = connection.get("chatcodes", "channels"); + } + } + if (this.graderactive || this.isTimed) { + this.hidecode = false; + } + if (this.includes) { + this.includes = this.includes.split(/\s+/); + } + let prefixEnd = this.code.indexOf("^^^^"); + if (prefixEnd > -1) { + this.prefix = this.code.substring(0, prefixEnd); + this.code = this.code.substring(prefixEnd + 5); + } + suffStart = this.code.indexOf("===="); + if (suffStart > -1) { + this.suffix = this.code.substring(suffStart + 5); + this.code = this.code.substring(0, suffStart); + } + this.history = [this.code]; + this.createEditor(); + this.createOutput(); + this.createControls(); + if ($(orig).data("caption")) { + this.caption = $(orig).data("caption"); + } else { + this.caption = "ActiveCode"; + } + this.addCaption("runestone"); + setTimeout( + function () { + this.editor.refresh(); + }.bind(this), + 1000 + ); + if (this.autorun) { + // Simulate pressing the run button, since this will also prevent the user from clicking it until the initial run is complete, and also help the user understand why they're waiting. + $(this.runButtonHandler.bind(this)); + } + this.indicate_component_ready(); + + // copied from activecode-sql // fnprefix sets the path to load the sql-wasm.wasm file var bookprefix; var fnprefix; @@ -72,6 +182,190 @@ export default class SQLActiveCode extends ActiveCode { } }); } + // copied from activecode + createEditor(index) { + this.outerDiv = document.createElement("div"); + var linkdiv = document.createElement("div"); + linkdiv.id = this.divid.replace(/_/g, "-").toLowerCase(); // :ref: changes _ to - so add this as a target + $(this.outerDiv).addClass("ac_section alert alert-warning"); + var codeDiv = document.createElement("div"); + $(codeDiv).addClass("ac_code_div col-md-12"); + this.codeDiv = codeDiv; + this.outerDiv.lang = this.language; + $(this.origElem).replaceWith(this.outerDiv); + if (linkdiv.id !== this.divid) { + // Don't want the 'extra' target if they match. + this.outerDiv.appendChild(linkdiv); + } + this.outerDiv.appendChild(codeDiv); + var edmode = this.outerDiv.lang; + if (edmode === "sql") { + edmode = "text/x-sql"; + } else if (edmode === "java") { + edmode = "text/x-java"; + } else if (edmode === "cpp") { + edmode = "text/x-c++src"; + } else if (edmode === "c") { + edmode = "text/x-csrc"; + } else if (edmode === "python3") { + edmode = "python"; + } else if (edmode === "octave" || edmode === "MATLAB") { + edmode = "text/x-octave"; + } + var editor = CodeMirror(codeDiv, { + value: this.code, + lineNumbers: true, + mode: edmode, + indentUnit: 4, + matchBrackets: true, + autoMatchParens: true, + extraKeys: { + Tab: "indentMore", + "Shift-Tab": "indentLess", + }, + }); + // Make the editor resizable + $(editor.getWrapperElement()).resizable({ + resize: function () { + editor.setSize($(this).width(), $(this).height()); + editor.refresh(); + }, + }); + // give the user a visual cue that they have changed but not saved + editor.on( + "change", + function (ev) { + if ( + editor.acEditEvent == false || + editor.acEditEvent === undefined + ) { + // change events can come before any real changes for various reasons, some unknown + // this avoids unneccsary log events and updates to the activity counter + if (this.origText === editor.getValue()) { + return; + } + $(editor.getWrapperElement()).css( + "border-top", + "2px solid #b43232" + ); + $(editor.getWrapperElement()).css( + "border-bottom", + "2px solid #b43232" + ); + this.isAnswered = true; + this.logBookEvent({ + event: "activecode", + act: "edit", + div_id: this.divid, + }); + } + editor.acEditEvent = true; + }.bind(this) + ); // use bind to preserve *this* inside the on handler. + //Solving Keyboard Trap of ActiveCode: If user use tab for navigation outside of ActiveCode, then change tab behavior in ActiveCode to enable tab user to tab out of the textarea + $(window).keydown(function (e) { + var code = e.keyCode ? e.keyCode : e.which; + if (code == 9 && $("textarea:focus").length === 0) { + editor.setOption("extraKeys", { + Tab: function (cm) { + $(document.activeElement) + .closest(".tab-content") + .nextSibling.focus(); + }, + "Shift-Tab": function (cm) { + $(document.activeElement) + .closest(".tab-content") + .previousSibling.focus(); + }, + }); + } + }); + this.editor = editor; + if (this.hidecode) { + $(this.codeDiv).css("display", "none"); + } + } + + // copied from activecode + createOutput() { + // Create a parent div with two elements: pre for standard output and a div + // to hold turtle graphics output. We use a div in case the turtle changes from + // using a canvas to using some other element like svg in the future. + var outDiv = document.createElement("div"); + $(outDiv).addClass("ac_output col-md-12"); + this.outDiv = outDiv; + this.output = document.createElement("pre"); + this.output.id = this.divid + "_stdout"; + $(this.output).css("visibility", "hidden"); + this.graphics = document.createElement("div"); + this.graphics.id = this.divid + "_graphics"; + $(this.graphics).addClass("ac-canvas"); + // This bit of magic adds an event which waits for a canvas child to be created on our + // newly created div. When a canvas child is added we add a new class so that the visible + // canvas can be styled in CSS. Which a the moment means just adding a border. + $(this.graphics).on( + "DOMNodeInserted", + "canvas", + function () { + $(this.graphics).addClass("visible-ac-canvas"); + }.bind(this) + ); + var clearDiv = document.createElement("div"); + $(clearDiv).css("clear", "both"); // needed to make parent div resize properly + this.outerDiv.appendChild(clearDiv); + outDiv.appendChild(this.output); + outDiv.appendChild(this.graphics); + this.outerDiv.appendChild(outDiv); + var lensDiv = document.createElement("div"); + lensDiv.id = `${this.divid}_codelens`; + $(lensDiv).addClass("col-md-12"); + $(lensDiv).css("display", "none"); + this.codelens = lensDiv; + this.outerDiv.appendChild(lensDiv); + var coachDiv = document.createElement("div"); + $(coachDiv).addClass("col-md-12"); + $(coachDiv).css("display", "none"); + this.codecoach = coachDiv; + this.outerDiv.appendChild(coachDiv); + clearDiv = document.createElement("div"); + $(clearDiv).css("clear", "both"); // needed to make parent div resize properly + this.outerDiv.appendChild(clearDiv); + } + + // copied from activecode + createControls() { + var ctrlDiv = document.createElement("div"); + var butt; + $(ctrlDiv).addClass("ac_actions"); + $(ctrlDiv).addClass("col-md-12"); + // Run + butt = document.createElement("button"); + $(butt).text($.i18n("msg_activecode_run_code")); + $(butt).addClass("btn btn-success run-button"); + ctrlDiv.appendChild(butt); + this.runButton = butt; + console.log("adding click function for run"); + this.runButton.onclick = this.runButtonHandler.bind(this); + $(butt).attr("type", "button"); + + if (!this.hidecode && !this.hidehistory) { + this.addHistoryButton(ctrlDiv); + } + if ($(this.origElem).data("gradebutton") && !this.graderactive) { + this.addFeedbackButton(ctrlDiv); + } + + $(this.outerDiv).prepend(ctrlDiv); + if (this.question) { + if ($(this.question).html().match(/^\s+$/)) { + $(this.question).remove(); + } else { + $(this.outerDiv).prepend(this.question); + } + } + this.controlDiv = ctrlDiv; + } + async runProg(noUI, logResults) { if (typeof logResults === "undefined") { this.logResults = true; @@ -377,4 +671,26 @@ function createTable(tableData, container, maxHeight) { hot.updateSettings({ height: actualHeight }); return hot; -} \ No newline at end of file +} + + +/*================================= +== Find the custom HTML tags and == +== execute our code on them == +=================================*/ +$(document).bind("runestone:login-complete", function () { + $("[data-component=sqlhparsons]").each(function () { + if ($(this).closest("[data-component=timedAssessment]").length == 0) { + // If this element exists within a timed component, don't render it here + // try { + hpList[this.id] = new SQLHParons({ + orig: this, + useRunestoneServices: eBookConfig.useRunestoneServices, + }); + // } catch (err) { + // console.log(`Error rendering ShortAnswer Problem ${this.id} + // Details: ${err}`); + // } + } + }); +}); diff --git a/runestone/hparsons/test.rst b/runestone/hparsons/test.rst new file mode 100644 index 000000000..8728a3602 --- /dev/null +++ b/runestone/hparsons/test.rst @@ -0,0 +1,23 @@ + .. hparsons:: unqiue_problem_id_here + :maxdist: + :order: + :language: + :noindent: + :adaptive: + :numbered: + + Solve my really cool horizontal Parsons problem...if you can. + ----- + def findmax(alist): + ===== + if len(alist) == 0: + return None + ===== + curmax = alist[0] + for item in alist: + ===== + if item > curmax: + ===== + curmax = item + ===== + return curmax diff --git a/runestone/hparsons/test/_sources/_static/test.db b/runestone/hparsons/test/_sources/_static/test.db new file mode 100644 index 0000000000000000000000000000000000000000..6ea96554ccedab8bcb1e2e99675a0b524385e10d GIT binary patch literal 3072 zcmeH{ze+D?qnCWdx>qN7jJv@HvTEy`yM`$xH+qf zE-t!sFcAtaeE>BF&X?~T4uSmOf7{`=hiibsmp&tnRUjm0ziW?1@SbdrqYev>5KR8aB?L@Vk`>jOzj#^K9 zDjiyNQKSQ1jMcdrZ>nxSw8k~Bbdpg@* z{cek@6N!6<54_?eG0aOF6MjYk%CNHvHM2O

      . +# +__author__ = "bmiller" + +from docutils import nodes +from docutils.parsers.rst import directives +from docutils.parsers.rst import Directive +import json +import random + +# setup is called in activecode.py + + +def textfield_role(name, rawtext, text, lineno, inliner, options={}, content=[]): + """ + Usage: + In your document you can write :textfield:`myid:myvalue:width` + This will translate to: + + + where width can be specified in pixels or percentage of page width (standard CSS syntax). + Width can also be specified using relative sizes: + mini, small, medium, large, xlarge, and xxlarge + """ + iid, value, width = text.split(":") + + if "mini" in width: + width = "60px" + elif "small" in width: + width = "90px" + elif "medium" in width: + width = "150px" + elif "large" in width: + width = "210px" + elif "xlarge" in width: + width = "270px" + elif "xxlarge" in width: + width = "530px" + + res = ( + """""" + % (iid, width, value) + ) + + return [nodes.raw("", res, format="html")], [] From fa6d7d26d47910959c94cf968dcfa2166b01272e Mon Sep 17 00:00:00 2001 From: Zihan Wu Date: Thu, 3 Mar 2022 22:34:45 -0500 Subject: [PATCH 12/31] :fire: Removing code that is not useful for horizontal parsons + sql --- runestone/hparsons/hparsons.py | 206 +-------------------- runestone/hparsons/js/hparsons-sql.js | 61 +----- runestone/hparsons/test/_sources/index.rst | 2 +- 3 files changed, 7 insertions(+), 262 deletions(-) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index 3d862a180..b08a3eafe 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -47,17 +47,16 @@ def setup(app): TEMPLATE_START = """ -

      +
      """ TEMPLATE_END = """
      - @@ -79,8 +78,6 @@ def visit_ac_node(self, node): # print self.settings.env.activecodecounter # todo: handle above in node.runestone_options - # todo handle 'hidecode' not in node.runestone_options: - # todo: handle if 'gradebutton' in node.runestone_options: res += GRADES node.delimiter = "_start__{}_".format(node.runestone_options["divid"]) @@ -116,31 +113,11 @@ def purge_activecodes(app, env, docname): class HParsonsDirective(RunestoneIdDirective): + # only keep: language, autograde, dburl """ .. activecode:: uniqueid - :nocanvas: -- do not create a canvas :autograde: unittest - :nopre: -- do not create an output component - :above: -- put the canvas above the code - :autorun: -- run this activecode as soon as the page is loaded - :caption: this is the caption - :include: div1,div2 -- invisibly include code from another activecode - :hidecode: -- Don't show the editor initially - :nocodelens: -- Do not show the codelens button - :timelimit: -- set the time limit for this program in seconds :language: python, html, javascript, java, python2, python3 - :chatcodes: -- Enable users to talk about this code snippet with others - :tour_1: audio tour track - :tour_2: audio tour track - :tour_3: audio tour track - :tour_4: audio tour track - :tour_5: audio tour track - :stdin: : A file to simulate stdin (java, python2, python3) - :datafile: : A datafile for the program to read (java, python2, python3) - :sourcefile: : source files (java, python2, python3) - :available_files: : other additional files (java, python2, python3) - :enabledownload: -- allow textfield contents to be downloaded as *.py file - :nopair: -- disable pair programming features :dburl: url to load database for sql mode :showlastsql: -- Only show the last sql result in output @@ -158,7 +135,6 @@ class HParsonsDirective(RunestoneIdDirective): - activecode_div_class - custom CSS class of the component's outermost div - activecode_hide_load_history - if True, hide the load history button - - wasm_uri - Path or Full URL to folder containing WASM files for SQL. /_static is default """ required_arguments = 1 @@ -167,35 +143,7 @@ class HParsonsDirective(RunestoneIdDirective): option_spec = RunestoneIdDirective.option_spec.copy() option_spec.update( { - "nocanvas": directives.flag, - "nopre": directives.flag, - "above": directives.flag, # put the canvas above the code - "autorun": directives.flag, - "caption": directives.unchanged, - "include": directives.unchanged, - "hidecode": directives.flag, "language": directives.unchanged, - "chatcodes": directives.flag, - "tour_1": directives.unchanged, - "tour_2": directives.unchanged, - "tour_3": directives.unchanged, - "tour_4": directives.unchanged, - "tour_5": directives.unchanged, - "nocodelens": directives.flag, - "coach": directives.flag, - "gradebutton": directives.flag, - "timelimit": directives.unchanged, - "stdin": directives.unchanged, - "datafile": directives.unchanged, - "sourcefile": directives.unchanged, - "available_files": directives.unchanged, - "enabledownload": directives.flag, - "compileargs": directives.unchanged, - "linkargs": directives.unchanged, - "interpreterargs": directives.unchanged, - "runargs": directives.unchanged, - "tie": directives.unchanged, - "nopair": directives.flag, "dburl": directives.unchanged, "showlastsql": directives.flag, } @@ -233,118 +181,9 @@ def run(self): str3 = str2.replace("'", "*singleq*") self.options["argu"] = str3 - # TODO: This is BAD -- using '_' as a key for audio tour stuff is wrong. - complete = "" - no_of_buttons = 0 - okeys = list(self.options.keys()) - for k in okeys: - if "tour_" in k: - x, label = k.split("_") - no_of_buttons = no_of_buttons + 1 - complete = complete + self.options[k] + "*atype*" - - newcomplete = complete.replace('"', "*doubleq*") - self.options["ctext"] = newcomplete - self.options["no_of_buttons"] = no_of_buttons - - if "caption" not in self.options: - self.options["caption"] = "" - else: - self.options["caption"] = "data-caption='%s'" % self.options["caption"] - - if "include" not in self.options: - self.options["include"] = "" - else: - lst = self.options["include"].split(",") - lst = [x.strip() for x in lst] - self.options["include"] = 'data-include="' + " ".join(lst) + '"' - - if "hidecode" in self.options: - self.options["hidecode"] = 'data-hidecode="true"' - else: - self.options["hidecode"] = "" - - if "enabledownload" in self.options: - self.options["enabledownload"] = 'data-enabledownload="true"' - else: - self.options["enabledownload"] = "" - - if "chatcodes" in self.options: - self.options["chatcodes"] = 'data-chatcodes="true"' - else: - self.options["chatcodes"] = "" - if "language" not in self.options: self.options["language"] = "python" - if self.options["language"] == "html": - self.options["language"] = "htmlmixed" - self.options["initialcode"] = escape(self.options["initialcode"]) - - if "nocodelens" in self.options or self.options["language"] not in [ - "python", - "java", - "c", - "cpp", - ]: - self.options["codelens"] = "" - else: - self.options["codelens"] = 'data-codelens="true"' - - if "nopair" in self.options: - self.options["nopair"] = 'data-nopair="true"' - else: - self.options["nopair"] = "" - - if "timelimit" not in self.options: - self.options["timelimit"] = "data-timelimit=25000" - else: - self.options["timelimit"] = "data-timelimit=%s" % self.options["timelimit"] - - if "autorun" not in self.options: - self.options["autorun"] = "" - else: - self.options["autorun"] = 'data-autorun="true"' - - if "coach" in self.options: - self.options["coach"] = 'data-coach="true"' - else: - self.options["coach"] = "" - - # livecode options - if "stdin" in self.options: - self.options["stdin"] = "data-stdin='%s'" % self.options["stdin"] - else: - self.options["stdin"] = "" - - if "datafile" not in self.options: - self.options["datafile"] = "" - else: - self.options["datafile"] = "data-datafile='%s'" % self.options["datafile"] - - if "sourcefile" not in self.options: - self.options["sourcefile"] = "" - else: - self.options["sourcefile"] = ( - "data-sourcefile='%s'" % self.options["sourcefile"] - ) - - if "tie" in self.options: - self.options["tie"] = "data-tie='{}'".format(self.options["tie"]) - else: - self.options["tie"] = "" - - for opt, tp in [ - ("compileargs", "cargs"), - ("linkargs", "largs"), - ("runargs", "rargs"), - ("interpreterargs", "iargs"), - ]: - if opt in self.options: - self.options[tp] = 'data-{}="{}"'.format(opt, escape(self.options[opt])) - else: - self.options[tp] = "" - # SQL Options if "dburl" in self.options: self.options["dburl"] = "data-dburl='{}'".format(self.options["dburl"]) @@ -356,39 +195,6 @@ def run(self): else: self.options["showlastsql"] = "" - # other options - - if "gradebutton" not in self.options: - self.options["gradebutton"] = "" - else: - self.options["gradebutton"] = "data-gradebutton=true" - - self.options["divclass"] = env.config.activecode_div_class - if env.config.activecode_hide_load_history: - self.options["hidehistory"] = "data-hidehistory=true" - else: - self.options["hidehistory"] = "" - - if env.config.wasm_uri: - self.options["wasmuri"] = f"data-wasm={env.config.wasm_uri}" - else: - self.options["wasmuri"] = "" - - if self.content: - if "^^^^" in self.content: - idx = self.content.index("^^^^") - prefix = "\n".join(self.content[:idx]) - if "====" in self.content: - idx = self.content.index("====") - source = "\n".join(self.content[:idx]) - suffix = "\n".join(self.content[idx + 1 :]) - else: - source = "\n".join(self.content) - suffix = "\n" - else: - source = "\n" - suffix = "\n" - course_name = env.config.html_context["course_id"] divid = self.options["divid"] @@ -409,8 +215,6 @@ def run(self): course_id=course_name, main_code=source, suffix_code=suffix, - includes=self.options["include"], - available_files=self.options.get("available_files", ""), ) ) else: diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index e5fb99881..8664b4015 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -39,49 +39,22 @@ export default class SQLHParons extends RunestoneBase { this.divid = opts.orig.id; this.code = $(orig).text() || "\n\n\n\n\n"; this.language = $(orig).data("lang"); - this.timelimit = $(orig).data("timelimit"); this.includes = $(orig).data("include"); - this.hidecode = $(orig).data("hidecode"); - this.chatcodes = $(orig).data("chatcodes"); this.hidehistory = $(orig).data("hidehistory"); this.question = $(opts.orig).find(`#${this.divid}_question`)[0]; this.tie = $(orig).data("tie"); this.dburl = $(orig).data("dburl"); this.runButton = null; - this.enabledownload = $(orig).data("enabledownload"); - this.downloadButton = null; this.saveButton = null; this.loadButton = null; this.outerDiv = null; this.partner = ""; this.logResults = true; - if (!eBookConfig.allow_pairs || $(orig).data("nopair")) { - this.enablePartner = false; - } else { - this.enablePartner = true; - } this.output = null; // create pre for output - this.graphics = null; // create div for turtle graphics - this.codecoach = null; - this.codelens = null; this.controlDiv = null; this.historyScrubber = null; this.timestamps = ["Original"]; this.autorun = $(orig).data("autorun"); - if (this.chatcodes && eBookConfig.enable_chatcodes) { - if (!socket) { - socket = new WebSocket("wss://" + chatcodesServer); - } - if (!connection) { - connection = new window.sharedb.Connection(socket); - } - if (!doc) { - doc = connection.get("chatcodes", "channels"); - } - } - if (this.graderactive || this.isTimed) { - this.hidecode = false; - } if (this.includes) { this.includes = this.includes.split(/\s+/); } @@ -281,52 +254,23 @@ export default class SQLHParons extends RunestoneBase { } }); this.editor = editor; - if (this.hidecode) { - $(this.codeDiv).css("display", "none"); - } } // copied from activecode createOutput() { // Create a parent div with two elements: pre for standard output and a div // to hold turtle graphics output. We use a div in case the turtle changes from - // using a canvas to using some other element like svg in the future. var outDiv = document.createElement("div"); $(outDiv).addClass("ac_output col-md-12"); this.outDiv = outDiv; this.output = document.createElement("pre"); this.output.id = this.divid + "_stdout"; $(this.output).css("visibility", "hidden"); - this.graphics = document.createElement("div"); - this.graphics.id = this.divid + "_graphics"; - $(this.graphics).addClass("ac-canvas"); - // This bit of magic adds an event which waits for a canvas child to be created on our - // newly created div. When a canvas child is added we add a new class so that the visible - // canvas can be styled in CSS. Which a the moment means just adding a border. - $(this.graphics).on( - "DOMNodeInserted", - "canvas", - function () { - $(this.graphics).addClass("visible-ac-canvas"); - }.bind(this) - ); var clearDiv = document.createElement("div"); $(clearDiv).css("clear", "both"); // needed to make parent div resize properly this.outerDiv.appendChild(clearDiv); outDiv.appendChild(this.output); - outDiv.appendChild(this.graphics); this.outerDiv.appendChild(outDiv); - var lensDiv = document.createElement("div"); - lensDiv.id = `${this.divid}_codelens`; - $(lensDiv).addClass("col-md-12"); - $(lensDiv).css("display", "none"); - this.codelens = lensDiv; - this.outerDiv.appendChild(lensDiv); - var coachDiv = document.createElement("div"); - $(coachDiv).addClass("col-md-12"); - $(coachDiv).css("display", "none"); - this.codecoach = coachDiv; - this.outerDiv.appendChild(coachDiv); clearDiv = document.createElement("div"); $(clearDiv).css("clear", "both"); // needed to make parent div resize properly this.outerDiv.appendChild(clearDiv); @@ -348,12 +292,9 @@ export default class SQLHParons extends RunestoneBase { this.runButton.onclick = this.runButtonHandler.bind(this); $(butt).attr("type", "button"); - if (!this.hidecode && !this.hidehistory) { + if (!this.hidehistory) { this.addHistoryButton(ctrlDiv); } - if ($(this.origElem).data("gradebutton") && !this.graderactive) { - this.addFeedbackButton(ctrlDiv); - } $(this.outerDiv).prepend(ctrlDiv); if (this.question) { diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index 140961b86..b77c99d2a 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -4,7 +4,7 @@ Horizontal Parsons Test .. Testing horizontal Parsons problems. -Horizontal Parsons + SQL +Horizontal Parsons + SQL -------------------------------------- .. hparsons:: test_activecode_6 :language: sql From 68e3db178bdaca6d71c64789bbe6c144ced878fc Mon Sep 17 00:00:00 2001 From: Zihan Wu Date: Sat, 5 Mar 2022 07:13:12 -0500 Subject: [PATCH 13/31] :beers: Oh GOD now it is truly migrated --- runestone/hparsons/hparsons.py | 2 +- runestone/hparsons/js/hparsons-sql.js | 196 +++++++++++++++++++++++++- webpack.index.js | 2 +- 3 files changed, 192 insertions(+), 8 deletions(-) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index 3d862a180..22665a4da 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -48,7 +48,7 @@ def setup(app): TEMPLATE_START = """
      -
      +
      """ diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index e5fb99881..c420487b5 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -1,5 +1,5 @@ import Handsontable from "handsontable"; -import 'handsontable/dist/handsontable.full.css'; +// import 'handsontable/dist/handsontable.full.css'; import initSqlJs from "sql.js/dist/sql-wasm.js"; import RunestoneBase from "../../common/js/runestonebase.js"; @@ -13,8 +13,8 @@ import "codemirror/mode/javascript/javascript.js"; import "codemirror/mode/sql/sql.js"; import "codemirror/mode/clike/clike.js"; import "codemirror/mode/octave/octave.js"; -import "./../css/activecode.css"; -import "codemirror/lib/codemirror.css"; +// import "./../../activecode/css/activecode.css"; +// import "codemirror/lib/codemirror.css"; // copied from activecode // Used by Skulpt. @@ -24,9 +24,14 @@ window.vegaEmbed = embed; var allDburls = {}; +export var hpList; +// Dictionary that contains all instances of horizontal Parsons problem objects +if (hpList === undefined) hpList = {}; + export default class SQLHParons extends RunestoneBase { constructor(opts) { super(opts); + console.log('hparsons sql constructor') // copied from activecode var suffStart; var orig = $(opts.orig).find("textarea")[0]; @@ -182,6 +187,155 @@ export default class SQLHParons extends RunestoneBase { } }); } + + // copied from activecode + async runButtonHandler() { + // Disable the run button until the run is finished. + this.runButton.disabled = true; + try { + await this.runProg(); + } catch (e) { + console.log(`there was an error ${e} running the code`); + } + if (this.logResults) { + this.logCurrentAnswer(); + } + this.renderFeedback(); + // The run is finished; re-enable the button. + this.runButton.disabled = false; + } + + // copied from activecode + addHistoryButton(ctrlDiv) { + let butt = document.createElement("button"); + $(butt).text($.i18n("msg_activecode_load_history")); + $(butt).addClass("btn btn-default"); + $(butt).attr("type", "button"); + ctrlDiv.appendChild(butt); + this.histButton = butt; + $(butt).click(this.addHistoryScrubber.bind(this)); + if (this.graderactive) { + this.addHistoryScrubber(true); + } + } + + // copied from activecode + // _`addHistoryScrubber` + // --------------------- + // Activecode -- If the code has not changed wrt the scrubber position value then don't save the code or reposition the scrubber + // -- still call runlog, but add a parameter to not save the code + // add an initial load history button + // if there is no edit then there is no append to_save (True/False) + async addHistoryScrubber(pos_last) { + let response; + var reqData = { + acid: this.divid, + }; + if (this.sid !== undefined) { + reqData["sid"] = this.sid; + } + console.log("before get hist"); + if ( + eBookConfig.practice_mode || + (this.isTimed && !this.assessmentTaken) + ) { + // If this is timed and already taken we should restore history info + this.renderScrubber(); + } else { + let request = new Request(`${eBookConfig.new_server_prefix}/assessment/gethist`, { + method: "POST", + headers: this.jsonHeaders, + body: JSON.stringify(reqData), + }); + try { + response = await fetch(request); + let data = await response.json(); + if (!response.ok) { + throw new Error(`Failed to get the history data: ${data.detail}`); + } + data = data.detail; + if (data.history !== undefined) { + this.history = this.history.concat(data.history); + for (let t in data.timestamps) { + this.timestamps.push( + new Date(data.timestamps[t]).toLocaleString() + ); + } + } + } catch (e) { + console.log(`unable to fetch history: ${e}`); + } + this.renderScrubber(pos_last); + } + return "success"; + } + + renderScrubber(pos_last) { + console.log("making a new scrubber"); + var scrubberDiv = document.createElement("div"); + $(scrubberDiv).css("display", "inline-block"); + $(scrubberDiv).css("margin-left", "10px"); + $(scrubberDiv).css("margin-right", "10px"); + $(scrubberDiv).css({ + "min-width": "200px", + "max-width": "300px", + }); + var scrubber = document.createElement("div"); + this.timestampP = document.createElement("span"); + this.slideit = function () { + this.editor.setValue(this.history[$(scrubber).slider("value")]); + var curVal = this.timestamps[$(scrubber).slider("value")]; + let pos = $(scrubber).slider("value"); + let outOf = this.history.length; + $(this.timestampP).text(`${curVal} - ${pos + 1} of ${outOf}`); + this.logBookEvent({ + event: "activecode", + act: "slide:" + curVal, + div_id: this.divid, + }); + }; + $(scrubber).slider({ + max: this.history.length - 1, + value: this.history.length - 1, + }); + $(scrubber).css("margin", "10px"); + $(scrubber).on("slide", this.slideit.bind(this)); + $(scrubber).on("slidechange", this.slideit.bind(this)); + scrubberDiv.appendChild(scrubber); + scrubberDiv.appendChild(this.timestampP); + // If there is a deadline set then position the scrubber at the last submission + // prior to the deadline + if (this.deadline) { + let i = 0; + let done = false; + while (i < this.history.length && !done) { + if (new Date(this.timestamps[i]) > this.deadline) { + done = true; + } else { + i += 1; + } + } + i = i - 1; + scrubber.value = Math.max(i, 0); + this.editor.setValue(this.history[scrubber.value]); + $(scrubber).slider("value", scrubber.value); + } else if (pos_last) { + scrubber.value = this.history.length - 1; + this.editor.setValue(this.history[scrubber.value]); + } else { + scrubber.value = 0; + } + let pos = $(scrubber).slider("value"); + let outOf = this.history.length; + let ts = this.timestamps[$(scrubber).slider("value")]; + $(this.timestampP).text(`${ts} - ${pos + 1} of ${outOf}`); + $(this.histButton).remove(); + this.histButton = null; + this.historyScrubber = scrubber; + $(scrubberDiv).insertAfter(this.runButton); + } // end definition of helper + + // copied from activecode createEditor(index) { this.outerDiv = document.createElement("div"); @@ -344,7 +498,8 @@ export default class SQLHParons extends RunestoneBase { $(butt).addClass("btn btn-success run-button"); ctrlDiv.appendChild(butt); this.runButton = butt; - console.log("adding click function for run"); + console.log(butt); + console.log("adding click function for run in sql"); this.runButton.onclick = this.runButtonHandler.bind(this); $(butt).attr("type", "button"); @@ -527,12 +682,41 @@ export default class SQLHParons extends RunestoneBase { return Promise.resolve("done"); } + // copied from anctivecode + async buildProg(useSuffix) { + // assemble code from prefix, suffix, and editor for running. + var pretext; + var prog = this.editor.getValue() + "\n"; + if (this.prefix) { + prog = this.prefix + prog; + } + this.pretext = ""; + this.pretextLines = 0; + this.progLines = prog.match(/\n/g).length + 1; + if (this.includes) { + // iterate over the includes, in-order prepending to prog + pretext = ""; + for (var x = 0; x < this.includes.length; x++) { + let iCode = await this.getIncludedCode(this.includes[x]); + pretext = pretext + iCode + "\n"; + } + this.pretext = pretext; + if (this.pretext) { + this.pretextLines = (this.pretext.match(/\n/g) || "").length; + } + prog = pretext + prog; + } + if (useSuffix && this.suffix) { + prog = prog + this.suffix; + } + return Promise.resolve(prog); + } async logCurrentAnswer(sid) { let data = { div_id: this.divid, code: this.editor.getValue(), language: this.language, - errinfo: this.results[this.results.length - 1].status, + // errinfo: this.results[this.results.length - 1].status, to_save: this.saveCode, prefix: this.pretext, suffix: this.suffix, @@ -679,7 +863,7 @@ function createTable(tableData, container, maxHeight) { == execute our code on them == =================================*/ $(document).bind("runestone:login-complete", function () { - $("[data-component=sqlhparsons]").each(function () { + $("[data-component=hparsons]").each(function () { if ($(this).closest("[data-component=timedAssessment]").length == 0) { // If this element exists within a timed component, don't render it here // try { diff --git a/webpack.index.js b/webpack.index.js index f8646382c..685217fff 100644 --- a/webpack.index.js +++ b/webpack.index.js @@ -70,7 +70,7 @@ const module_map = { khanex: () => import("./runestone/khanex/js/khanex.js"), lp_build: () => import("./runestone/lp/js/lp.js"), multiplechoice: () => import("./runestone/mchoice/js/timedmc.js"), - hparsons: () => import("./runestone/hparsons/js/hparsons.js"), + hparsons: () => import("./runestone/hparsons/js/hparsons-sql.js"), parsons: () => import("./runestone/parsons/js/timedparsons.js"), poll: () => import("./runestone/poll/js/poll.js"), quizly: () => import("./runestone/quizly/js/quizly.js"), From 243a5289ef0bbdb7b70d260446f7e1d456008666 Mon Sep 17 00:00:00 2001 From: Zihan Wu Date: Mon, 7 Mar 2022 11:15:00 -0500 Subject: [PATCH 14/31] :fire: Initial cleanup of unused attributes --- runestone/hparsons/hparsons.py | 1 - runestone/hparsons/js/hparsons-sql.js | 151 --------------------- runestone/hparsons/test/_sources/index.rst | 2 +- runestone/hparsons/textfield.py | 61 --------- test.sh | 2 +- 5 files changed, 2 insertions(+), 215 deletions(-) delete mode 100644 runestone/hparsons/textfield.py diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index be8d564d5..c42f207bb 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -22,7 +22,6 @@ from docutils import nodes from docutils.parsers.rst import directives -from .textfield import textfield_role from sqlalchemy import Table from runestone.server.componentdb import ( addQuestionToDB, diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 6297ce131..22d60585c 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -44,7 +44,6 @@ export default class SQLHParons extends RunestoneBase { this.code = $(orig).text() || "\n\n\n\n\n"; this.language = $(orig).data("lang"); this.includes = $(orig).data("include"); - this.hidehistory = $(orig).data("hidehistory"); this.question = $(opts.orig).find(`#${this.divid}_question`)[0]; this.tie = $(orig).data("tie"); this.dburl = $(orig).data("dburl"); @@ -56,7 +55,6 @@ export default class SQLHParons extends RunestoneBase { this.logResults = true; this.output = null; // create pre for output this.controlDiv = null; - this.historyScrubber = null; this.timestamps = ["Original"]; this.autorun = $(orig).data("autorun"); if (this.includes) { @@ -72,7 +70,6 @@ export default class SQLHParons extends RunestoneBase { this.suffix = this.code.substring(suffStart + 5); this.code = this.code.substring(0, suffStart); } - this.history = [this.code]; this.createEditor(); this.createOutput(); this.createControls(); @@ -177,137 +174,6 @@ export default class SQLHParons extends RunestoneBase { this.runButton.disabled = false; } - // copied from activecode - addHistoryButton(ctrlDiv) { - let butt = document.createElement("button"); - $(butt).text($.i18n("msg_activecode_load_history")); - $(butt).addClass("btn btn-default"); - $(butt).attr("type", "button"); - ctrlDiv.appendChild(butt); - this.histButton = butt; - $(butt).click(this.addHistoryScrubber.bind(this)); - if (this.graderactive) { - this.addHistoryScrubber(true); - } - } - - // copied from activecode - // _`addHistoryScrubber` - // --------------------- - // Activecode -- If the code has not changed wrt the scrubber position value then don't save the code or reposition the scrubber - // -- still call runlog, but add a parameter to not save the code - // add an initial load history button - // if there is no edit then there is no append to_save (True/False) - async addHistoryScrubber(pos_last) { - let response; - var reqData = { - acid: this.divid, - }; - if (this.sid !== undefined) { - reqData["sid"] = this.sid; - } - console.log("before get hist"); - if ( - eBookConfig.practice_mode || - (this.isTimed && !this.assessmentTaken) - ) { - // If this is timed and already taken we should restore history info - this.renderScrubber(); - } else { - let request = new Request(`${eBookConfig.new_server_prefix}/assessment/gethist`, { - method: "POST", - headers: this.jsonHeaders, - body: JSON.stringify(reqData), - }); - try { - response = await fetch(request); - let data = await response.json(); - if (!response.ok) { - throw new Error(`Failed to get the history data: ${data.detail}`); - } - data = data.detail; - if (data.history !== undefined) { - this.history = this.history.concat(data.history); - for (let t in data.timestamps) { - this.timestamps.push( - new Date(data.timestamps[t]).toLocaleString() - ); - } - } - } catch (e) { - console.log(`unable to fetch history: ${e}`); - } - this.renderScrubber(pos_last); - } - return "success"; - } - - renderScrubber(pos_last) { - console.log("making a new scrubber"); - var scrubberDiv = document.createElement("div"); - $(scrubberDiv).css("display", "inline-block"); - $(scrubberDiv).css("margin-left", "10px"); - $(scrubberDiv).css("margin-right", "10px"); - $(scrubberDiv).css({ - "min-width": "200px", - "max-width": "300px", - }); - var scrubber = document.createElement("div"); - this.timestampP = document.createElement("span"); - this.slideit = function () { - this.editor.setValue(this.history[$(scrubber).slider("value")]); - var curVal = this.timestamps[$(scrubber).slider("value")]; - let pos = $(scrubber).slider("value"); - let outOf = this.history.length; - $(this.timestampP).text(`${curVal} - ${pos + 1} of ${outOf}`); - this.logBookEvent({ - event: "activecode", - act: "slide:" + curVal, - div_id: this.divid, - }); - }; - $(scrubber).slider({ - max: this.history.length - 1, - value: this.history.length - 1, - }); - $(scrubber).css("margin", "10px"); - $(scrubber).on("slide", this.slideit.bind(this)); - $(scrubber).on("slidechange", this.slideit.bind(this)); - scrubberDiv.appendChild(scrubber); - scrubberDiv.appendChild(this.timestampP); - // If there is a deadline set then position the scrubber at the last submission - // prior to the deadline - if (this.deadline) { - let i = 0; - let done = false; - while (i < this.history.length && !done) { - if (new Date(this.timestamps[i]) > this.deadline) { - done = true; - } else { - i += 1; - } - } - i = i - 1; - scrubber.value = Math.max(i, 0); - this.editor.setValue(this.history[scrubber.value]); - $(scrubber).slider("value", scrubber.value); - } else if (pos_last) { - scrubber.value = this.history.length - 1; - this.editor.setValue(this.history[scrubber.value]); - } else { - scrubber.value = 0; - } - let pos = $(scrubber).slider("value"); - let outOf = this.history.length; - let ts = this.timestamps[$(scrubber).slider("value")]; - $(this.timestampP).text(`${ts} - ${pos + 1} of ${outOf}`); - $(this.histButton).remove(); - this.histButton = null; - this.historyScrubber = scrubber; - $(scrubberDiv).insertAfter(this.runButton); - } // end definition of helper - - // copied from activecode createEditor(index) { this.outerDiv = document.createElement("div"); @@ -444,10 +310,6 @@ export default class SQLHParons extends RunestoneBase { this.runButton.onclick = this.runButtonHandler.bind(this); $(butt).attr("type", "button"); - if (!this.hidehistory) { - this.addHistoryButton(ctrlDiv); - } - $(this.outerDiv).prepend(ctrlDiv); if (this.question) { if ($(this.question).html().match(/^\s+$/)) { @@ -540,19 +402,6 @@ export default class SQLHParons extends RunestoneBase { }); } - try { - this.saveCode = await this.manage_scrubber(this.saveCode); - if (this.slideit) { - $(this.historyScrubber).on( - "slidechange", - this.slideit.bind(this) - ); - } - $(this.historyScrubber).slider("enable"); - } catch (e) { - console.log(`Failed to update scrubber ${e}`); - } - respDiv = document.createElement("div"); respDiv.id = divid; this.outDiv.appendChild(respDiv); diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index b77c99d2a..140961b86 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -4,7 +4,7 @@ Horizontal Parsons Test .. Testing horizontal Parsons problems. -Horizontal Parsons + SQL +Horizontal Parsons + SQL -------------------------------------- .. hparsons:: test_activecode_6 :language: sql diff --git a/runestone/hparsons/textfield.py b/runestone/hparsons/textfield.py deleted file mode 100644 index 71087692c..000000000 --- a/runestone/hparsons/textfield.py +++ /dev/null @@ -1,61 +0,0 @@ -# ********* -# |docname| -# ********* -# Copyright (C) 2011 Bradley N. Miller -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -__author__ = "bmiller" - -from docutils import nodes -from docutils.parsers.rst import directives -from docutils.parsers.rst import Directive -import json -import random - -# setup is called in activecode.py - - -def textfield_role(name, rawtext, text, lineno, inliner, options={}, content=[]): - """ - Usage: - In your document you can write :textfield:`myid:myvalue:width` - This will translate to: - - - where width can be specified in pixels or percentage of page width (standard CSS syntax). - Width can also be specified using relative sizes: - mini, small, medium, large, xlarge, and xxlarge - """ - iid, value, width = text.split(":") - - if "mini" in width: - width = "60px" - elif "small" in width: - width = "90px" - elif "medium" in width: - width = "150px" - elif "large" in width: - width = "210px" - elif "xlarge" in width: - width = "270px" - elif "xxlarge" in width: - width = "530px" - - res = ( - """""" - % (iid, width, value) - ) - - return [nodes.raw("", res, format="html")], [] diff --git a/test.sh b/test.sh index 2afab3851..311f76658 100755 --- a/test.sh +++ b/test.sh @@ -2,7 +2,7 @@ cd ~/code/regex npm run build cd ~/code/RunestoneComponents -cp ~/code/regex/packages/regex-element/regex-element.js ~/code/RunestoneComponents/runestone/hparsons/js/regex-element.js +cp ~/code/regex/packages/horizontal-parsons/horizontal-parsons.js ~/code/RunestoneComponents/runestone/hparsons/js/horizontal-parsons.js npm run build pip install . cd runestone/hparsons/test From 60145efa13630439a11deefbc3ade927ac2049d4 Mon Sep 17 00:00:00 2001 From: Zihan Wu Date: Mon, 7 Mar 2022 11:42:02 -0500 Subject: [PATCH 15/31] :sparkles: Initial demo version for hparsons - sql. -messy code warning- --- runestone/hparsons/hparsons.py | 1 + ...regex-element.js => horizontal-parsons.js} | 1322 +---------------- runestone/hparsons/js/hparsons-sql.js | 171 ++- test.sh | 8 +- 4 files changed, 157 insertions(+), 1345 deletions(-) rename runestone/hparsons/js/{regex-element.js => horizontal-parsons.js} (91%) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index c42f207bb..cb6211d09 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -53,6 +53,7 @@ def setup(app): TEMPLATE_END = """
      +
      @@ -190,11 +190,6 @@ def run(self): else: self.options["dburl"] = "" - if "showlastsql" in self.options: - self.options["showlastsql"] = 'data-showlastsql="true"' - else: - self.options["showlastsql"] = "" - course_name = env.config.html_context["course_id"] divid = self.options["divid"] diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 38f14aef9..6b9f1210c 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -3,25 +3,6 @@ import Handsontable from "handsontable"; import initSqlJs from "sql.js/dist/sql-wasm.js"; import RunestoneBase from "../../common/js/runestonebase.js"; -// temporary code mirror stuff copied from activecode -import CodeMirror from "codemirror"; -import "codemirror/mode/python/python.js"; -import "codemirror/mode/css/css.js"; -import "codemirror/mode/htmlmixed/htmlmixed.js"; -import "codemirror/mode/xml/xml.js"; -import "codemirror/mode/javascript/javascript.js"; -import "codemirror/mode/sql/sql.js"; -import "codemirror/mode/clike/clike.js"; -import "codemirror/mode/octave/octave.js"; -// import "./../../activecode/css/activecode.css"; -// import "codemirror/lib/codemirror.css"; - -// copied from activecode -// Used by Skulpt. -import embed from "vega-embed"; -// Adapt for use outside webpack -- see https://github.com/vega/vega-embed. -window.vegaEmbed = embed; - var allDburls = {}; export var hpList; @@ -40,29 +21,18 @@ export default class SQLHParons extends RunestoneBase { this.divid = opts.orig.id; this.containerDiv = opts.orig; this.useRunestoneServices = opts.useRunestoneServices; - this.python3 = opts.python3; - this.alignVertical = opts.vertical; this.origElem = orig; this.origText = this.origElem.textContent; this.code = $(orig).text() || "\n\n\n\n\n"; - this.language = $(orig).data("lang"); - this.includes = $(orig).data("include"); this.question = $(opts.orig).find(`#${this.divid}_question`)[0]; - this.tie = $(orig).data("tie"); this.dburl = $(orig).data("dburl"); this.runButton = null; this.saveButton = null; this.loadButton = null; this.outerDiv = null; - this.partner = ""; this.logResults = true; this.output = null; // create pre for output this.controlDiv = null; - this.timestamps = ["Original"]; - this.autorun = $(orig).data("autorun"); - if (this.includes) { - this.includes = this.includes.split(/\s+/); - } let prefixEnd = this.code.indexOf("^^^^"); if (prefixEnd > -1) { this.prefix = this.code.substring(0, prefixEnd); @@ -82,16 +52,6 @@ export default class SQLHParons extends RunestoneBase { this.caption = "ActiveCode"; } this.addCaption("runestone"); - // setTimeout( - // function () { - // // this.editor.refresh(); - // }.bind(this), - // 1000 - // ); - if (this.autorun) { - // Simulate pressing the run button, since this will also prevent the user from clicking it until the initial run is complete, and also help the user understand why they're waiting. - $(this.runButtonHandler.bind(this)); - } this.indicate_component_ready(); // copied from activecode-sql @@ -108,7 +68,6 @@ export default class SQLHParons extends RunestoneBase { this.config = { locateFile: (filename) => `${fnprefix}/${filename}`, }; - this.showLast = $(this.origElem).data("showlastsql"); var self = this; initSqlJs(this.config).then(function (SQL) { // set up call to load database asynchronously if given @@ -177,104 +136,18 @@ export default class SQLHParons extends RunestoneBase { this.runButton.disabled = false; } - // copied from activecode - createEditor(index) { + // copied from activecode, already modified to add parsons + createEditor() { this.outerDiv = document.createElement("div"); - var linkdiv = document.createElement("div"); - linkdiv.id = this.divid.replace(/_/g, "-").toLowerCase(); // :ref: changes _ to - so add this as a target $(this.outerDiv).addClass("ac_section alert alert-warning"); - this.outerDiv.lang = this.language; $(this.origElem).replaceWith(this.outerDiv); this.outerDiv.innerHTML = ``; this.hparsons = $(this.outerDiv).find("horizontal-parsons")[0]; this.hparsons.parsonsData = ['select', '*', 'from', 'test', ';']; - // this.outerDiv.appendChild(codeDiv); - var edmode = this.outerDiv.lang; - if (edmode === "sql") { - edmode = "text/x-sql"; - } else if (edmode === "java") { - edmode = "text/x-java"; - } else if (edmode === "cpp") { - edmode = "text/x-c++src"; - } else if (edmode === "c") { - edmode = "text/x-csrc"; - } else if (edmode === "python3") { - edmode = "python"; - } else if (edmode === "octave" || edmode === "MATLAB") { - edmode = "text/x-octave"; - } - // var editor = CodeMirror(codeDiv, { - // value: this.code, - // lineNumbers: true, - // mode: edmode, - // indentUnit: 4, - // matchBrackets: true, - // autoMatchParens: true, - // extraKeys: { - // Tab: "indentMore", - // "Shift-Tab": "indentLess", - // }, - // }); - // Make the editor resizable - // $(editor.getWrapperElement()).resizable({ - // resize: function () { - // editor.setSize($(this).width(), $(this).height()); - // editor.refresh(); - // }, - // }); - // give the user a visual cue that they have changed but not saved - // editor.on( - // "change", - // function (ev) { - // if ( - // editor.acEditEvent == false || - // editor.acEditEvent === undefined - // ) { - // // change events can come before any real changes for various reasons, some unknown - // // this avoids unneccsary log events and updates to the activity counter - // if (this.origText === editor.getValue()) { - // return; - // } - // $(editor.getWrapperElement()).css( - // "border-top", - // "2px solid #b43232" - // ); - // $(editor.getWrapperElement()).css( - // "border-bottom", - // "2px solid #b43232" - // ); - // this.isAnswered = true; - // this.logBookEvent({ - // event: "activecode", - // act: "edit", - // div_id: this.divid, - // }); - // } - // editor.acEditEvent = true; - // }.bind(this) - // ); // use bind to preserve *this* inside the on handler. - // //Solving Keyboard Trap of ActiveCode: If user use tab for navigation outside of ActiveCode, then change tab behavior in ActiveCode to enable tab user to tab out of the textarea - // $(window).keydown(function (e) { - // var code = e.keyCode ? e.keyCode : e.which; - // if (code == 9 && $("textarea:focus").length === 0) { - // editor.setOption("extraKeys", { - // Tab: function (cm) { - // $(document.activeElement) - // .closest(".tab-content") - // .nextSibling.focus(); - // }, - // "Shift-Tab": function (cm) { - // $(document.activeElement) - // .closest(".tab-content") - // .previousSibling.focus(); - // }, - // }); - // } - // }); - // this.editor = editor; } // copied from activecode + // seems pretty clear, and do not need to modify createOutput() { // Create a parent div with two elements: pre for standard output and a div // to hold turtle graphics output. We use a div in case the turtle changes from @@ -309,6 +182,7 @@ export default class SQLHParons extends RunestoneBase { this.runButton.onclick = this.runButtonHandler.bind(this); $(butt).attr("type", "button"); + // TODO: maybe remove the question part $(this.outerDiv).prepend(ctrlDiv); if (this.question) { if ($(this.question).html().match(/^\s+$/)) { @@ -320,6 +194,7 @@ export default class SQLHParons extends RunestoneBase { this.controlDiv = ctrlDiv; } + // copied from activecode-sql async runProg(noUI, logResults) { if (typeof logResults === "undefined") { this.logResults = true; @@ -410,9 +285,6 @@ export default class SQLHParons extends RunestoneBase { // other activecodes In that case the showlastsql flag can be set // so we only show the last result let resultArray = this.results; - if (this.showLast) { - resultArray = this.results.slice(-1); - } for (let r of resultArray) { let section = document.createElement("div"); section.setAttribute("class", "ac_sql_result"); @@ -477,34 +349,22 @@ export default class SQLHParons extends RunestoneBase { this.pretext = ""; this.pretextLines = 0; this.progLines = prog.match(/\n/g).length + 1; - if (this.includes) { - // iterate over the includes, in-order prepending to prog - pretext = ""; - for (var x = 0; x < this.includes.length; x++) { - let iCode = await this.getIncludedCode(this.includes[x]); - pretext = pretext + iCode + "\n"; - } - this.pretext = pretext; - if (this.pretext) { - this.pretextLines = (this.pretext.match(/\n/g) || "").length; - } - prog = pretext + prog; - } if (useSuffix && this.suffix) { prog = prog + this.suffix; } return Promise.resolve(prog); } + + // copied from activecode-sql async logCurrentAnswer(sid) { let data = { div_id: this.divid, // code: this.editor.getValue(), - language: this.language, + language: "sql", // errinfo: this.results[this.results.length - 1].status, to_save: this.saveCode, prefix: this.pretext, suffix: this.suffix, - partner: this.partner, }; // Log the run event if (typeof sid !== "undefined") { data.sid = sid; diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index 140961b86..b77c99d2a 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -4,7 +4,7 @@ Horizontal Parsons Test .. Testing horizontal Parsons problems. -Horizontal Parsons + SQL +Horizontal Parsons + SQL -------------------------------------- .. hparsons:: test_activecode_6 :language: sql From 0bdd7d28b749f77ae8ec5644946892d66610e453 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Wed, 9 Mar 2022 16:50:30 -0500 Subject: [PATCH 17/31] :lipstick: Final fix for css --- runestone/hparsons/css/hparsons.css | 17 ++-- runestone/hparsons/js/horizontal-parsons.js | 92 ++++++--------------- runestone/hparsons/js/hparsons-sql.js | 2 +- runestone/hparsons/test/_sources/index.rst | 2 +- test.sh | 8 +- 5 files changed, 44 insertions(+), 77 deletions(-) diff --git a/runestone/hparsons/css/hparsons.css b/runestone/hparsons/css/hparsons.css index 0a404e260..0fbdf0634 100755 --- a/runestone/hparsons/css/hparsons.css +++ b/runestone/hparsons/css/hparsons.css @@ -1,6 +1,13 @@ -div.hparsons div.latexoutput { - background-color: #eeeeee; - padding: 1em; - margin-bottom: 10px; - border-radius: 5px; +.hparsons_section { + position: relative; + margin-right: auto; + margin-left: auto; + max-width: 800px; + clear: both; } + +.hparsons_section > *:not(.hparsons_section) { + max-width: 500pt; + margin-left: auto; + margin-right: auto; +} \ No newline at end of file diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index cdd8067f5..a777adbfa 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3047,25 +3047,27 @@ class ParsonsInput { this.el = document.createElement('div'); this.parentElement = parentElement; this.el.id = 'regextool-' + this.parentElement.toolNumber + '-parsons-input'; - this.el.append('Drag or click to select from the symbols below to form your code'); + const dragTip = document.createElement('div'); + dragTip.innerText = 'Drag or click to select from the symbols below to form your code'; + dragTip.classList.add('hparsons-tip'); + this.el.append(dragTip); this._dragArea = document.createElement('div'); this.el.appendChild(this._dragArea); this._dragArea.classList.add('drag-area'); - this._dragArea.style.height = '20px'; - this._dragArea.style.backgroundColor = '#fffcc4'; - this.el.append('Your Code:'); + const dropTip = document.createElement('div'); + dropTip.innerText = 'Your code:'; + dropTip.classList.add('hparsons-tip'); + this.el.append(dropTip); this._dropArea = document.createElement('div'); this.el.appendChild(this._dropArea); this._dropArea.classList.add('drop-area'); - this._dropArea.style.height = '20px'; - // this._dropArea.style.backgroundColor = '#bcebd7'; this._prevPosition = -1; this.expandableBlocks = []; this.expandableBlockTooltips = null; this._dragSortable = new Sortable(this._dragArea, { group: { name: 'shared', - pull: 'clone', + pull: true, put: false }, sort: false, @@ -15682,29 +15684,26 @@ class HParsonsElement extends HTMLElement { static toolCount = 0; toolNumber; temporaryInputEvent; + inputDiv; constructor() { super(); HParsonsElement.toolCount += 1; // console.log(RegexElement.toolCount); this.toolNumber = HParsonsElement.toolCount; this.root = this.attachShadow({ mode: 'open' }); - this.hparsonsInput = new ParsonsInput(this); - // add style this.addStyle(); + this.inputDiv = document.createElement('div'); + this.inputDiv.classList.add('hparsons-input'); + this.root.append(this.inputDiv); + this.hparsonsInput = new ParsonsInput(this); // a div wrapping the input and the test case status - const inputAndTestStatusDiv = document.createElement('div'); - this.root.append(inputAndTestStatusDiv); - inputAndTestStatusDiv.classList.add('regex-input-and-test-status'); // init regex input based on the input type - const inputDiv = document.createElement('div'); - inputAndTestStatusDiv.appendChild(inputDiv); - inputDiv.classList.add('regex-input-div'); this._parsonsData = new Array(); this.parsonsExplanation = null; this.inputType = 'parsons'; // this.regexErrorMessage = document.createElement('div'); // this.regexErrorPosition = -1; - this.initRegexInput(inputDiv); + this.initRegexInput(); this.temporaryInputEvent = {}; } set parsonsData(data) { @@ -15719,60 +15718,22 @@ class HParsonsElement extends HTMLElement { // TODO[refactor]: put stylesheet in a separate css/scss file addStyle = () => { const sheet = document.createElement('style'); - sheet.innerHTML += '.regex-textbox {width: 100%; display:none;}\n'; - sheet.innerHTML += '.regex-input-and-test-status {display: flex; flex-wrap: nowrap;}\n'; - sheet.innerHTML += '.regex-test-status {font-family: monospace; font-size: 15px; color:black; padding:20px 0 0 10px;height: fit-content;}\n'; - sheet.innerHTML += '.regex-test-status.Fail {font-family: monospace; font-size: 15px; color:#ebd071;}\n'; - sheet.innerHTML += '.regex-test-status.Pass {font-family: monospace; font-size: 15px; color:green;}\n'; - // regex status tag - sheet.innerHTML += '.regex-status { border-radius: 4px; visibility: collapse; font-family: monospace; padding: 3px 6px; margin: 2px 10px; color: #fff; }\n'; - sheet.innerHTML += '.regex-status.error { visibility: visible; background-color: red; }\n'; - sheet.innerHTML += '.regex-status.valid { visibility: visible; background-color: green; }\n'; - sheet.innerHTML += '.parsons-selected {background-color: red;}\n'; - // regex error message - sheet.innerHTML += '.regex-error-message {color: red; font-family: monospace; font-size: 15px;}\n'; - sheet.innerHTML += '.regex-error-message.hidden {visibility: collapse;}\n'; // parsons block - sheet.innerHTML += '.parsons-block {display: inline-block; font-family: monospace; font-size: large; background-color: white; padding: 1px 2px; border: 1px solid; border-color:gray; margin: 0 1px; border-radius: 2px; position: relative;}\n'; - sheet.innerHTML += '.parsons-block:hover, .parsons-block:focus { border-color: black; padding: 0 6px; border: 2px solid;}\n'; - sheet.innerHTML += '.drop-area { background-color: #b1dafa; }\n'; - sheet.innerHTML += '.drop-area.Pass { background-color: #bcebd7; }\n'; - sheet.innerHTML += '.drop-area.Fail { background-color: #ebd071; }\n'; - sheet.innerHTML += '.drop-area.Error { background-color: #ff99b3; }\n'; + sheet.innerHTML += '.hparsons-input {padding: 15px;}\n'; + sheet.innerHTML += '.hparsons-tip { font-style: italic; }\n'; + sheet.innerHTML += '.parsons-block {display: inline-block; font-family: monospace; border-color:gray; margin: 0 1px; position: relative; border-radius: 10px; background-color: #efefef; border: 1px solid #d3d3d3; padding: 5px 10px; margin-top: 5px;}\n'; + sheet.innerHTML += '.parsons-block:hover, .parsons-block:focus { border-color: black;}\n'; + sheet.innerHTML += '.drop-area { background-color: #ffa; padding: 0 5px; height: 42px; }\n'; // TODO:(UI) move the tooltip to the top of the line sheet.innerHTML += '.parsons-block .tooltip { visibility: hidden; width: 200px; background-color: black; color: #fff; text-align: center; padding: 5px 0; border-radius: 6px; position: absolute; z-index: 1; margin: 0 10px; bottom: 120%; margin-left: -100px;}\n'; sheet.innerHTML += '.parsons-block .tooltip::after {content: " ";position: absolute; top: 100%;left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: black transparent transparent transparent;}\n'; sheet.innerHTML += '.drag-area .parsons-block:hover .tooltip { visibility: visible;}\n'; - sheet.innerHTML += '.drag-area{ width: 510px;}\n'; - sheet.innerHTML += '.regex-test-string-container {display:flex;}\n'; - sheet.innerHTML += '.regex-test-string-div, .regex-input-div { margin: 8px 0; height: fit-content; }\n'; - sheet.innerHTML += '.regex-test-string-div { flex: 1; }\n'; - sheet.innerHTML += '.regex-input-div { width: 80%; }\n'; - sheet.innerHTML += '.regex-input-div > div {display:inline-block;}\n'; - // the dropdown menu for regex options - sheet.innerHTML += '.regex-options-dropdown-btn { background-color: #3498DB; color: white; padding: 10px; font-size: 16px; border: none; cursor: pointer;}\n'; - sheet.innerHTML += '.regex-options-dropdown-btn:hover, .regex-options-dropdown-btn:focus { background-color: #2980B9;}\n'; - sheet.innerHTML += '.regex-options-dropdown { position: relative; display: inline-block; }\n'; - sheet.innerHTML += '.regex-options-container { display: none; position: absolute; background-color: #f1f1f1; min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);z-index: 1;}\n'; - sheet.innerHTML += '.regex-options-container > button{ color: black; padding: 12px 16px; text-decoration: none; display: block; width: -webkit-fill-available;}\n'; - sheet.innerHTML += '.regex-options-container .selected{ background-color: pink;}\n'; - sheet.innerHTML += '.show {display:block;}\n'; + sheet.innerHTML += '.drag-area { background-color: #efefff; padding: 0 5px; height: 42px; }\n'; // unittest - sheet.innerHTML += '.regex-unittest > table, .regex-unittest td {border: 1px solid black; padding: 3px; text-align: center; border-collapse: collapse;}\n'; - // TODO: showing the table for now - // sheet.innerHTML += '.regex-unittest > table, .regex-unittest td {border: 1px solid black; padding: 3px; text-align: center;}\n' - // sheet.innerHTML += '.regex-unittest.collapse{display:none;}\n' - // for study 0: hide the table - sheet.innerHTML += '.hidetests .regex-unittest{display:none;}\n'; - document.body.appendChild(sheet); this.root.appendChild(sheet); const global_sheet = document.createElement('style'); - global_sheet.innerHTML += '.regex-test-string .ql-editor, .regex-input .ql-editor { padding: 5px; border: 1px solid; border-radius: 3px; font-family: monospace; font-size: 14px; box-shadow: inset 0 1px 2px rgb(0 0 0 / 10%); line-height: 18px; letter-spacing: 0.5px;}\n'; global_sheet.innerHTML += '.regex-input .ql-editor {height: fit-content;}\n'; global_sheet.innerHTML += '.ql-editor { box-shadow: 0 0 2px 5px #b1dafa; margin: 5px; }\n'; - global_sheet.innerHTML += '.Pass .ql-editor { box-shadow: 0 0 2px 5px #bcebd7; margin: 5px; }\n'; - global_sheet.innerHTML += '.Fail .ql-editor { box-shadow: 0 0 2px 5px #ebd071; margin: 5px; }\n'; - global_sheet.innerHTML += '.Error .ql-editor { box-shadow: 0 0 2px 5px #ff99b3; margin: 5px; }\n'; this.appendChild(global_sheet); }; logEvent = (eventContent) => { @@ -15808,29 +15769,28 @@ class HParsonsElement extends HTMLElement { attributeChangedCallback(name, oldValue, newValue) { switch (name) { case 'input-type': { - this.initRegexInput(this.root.querySelector('.regex-input-div')); + this.initRegexInput(); break; } } } - initRegexInput(inputDiv) { - inputDiv.innerHTML = ''; + initRegexInput() { + this.inputDiv.innerHTML = ''; let inputType = this.getAttribute('input-type'); this.inputType = inputType == 'parsons' ? 'parsons' : 'text'; this._parsonsData = new Array(); this.parsonsExplanation = null; - inputDiv.appendChild(document.createElement('br')); // todo:(UI) fix the css for the input if (this.inputType == 'parsons') { // init elements: parsons regex input this.hparsonsInput = new ParsonsInput(this); - inputDiv.appendChild(this.hparsonsInput.el); + this.inputDiv.appendChild(this.hparsonsInput.el); } else { // (this.inputType == 'text') const regex_slot = document.createElement('slot'); regex_slot.name = 'regex-input'; - inputDiv.appendChild(regex_slot); + this.inputDiv.appendChild(regex_slot); // TODO: (refactor) rename RegexInput this.hparsonsInput = new TextInput(this); this.appendChild(this.hparsonsInput.el); diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 6b9f1210c..e182c3837 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -139,7 +139,7 @@ export default class SQLHParons extends RunestoneBase { // copied from activecode, already modified to add parsons createEditor() { this.outerDiv = document.createElement("div"); - $(this.outerDiv).addClass("ac_section alert alert-warning"); + $(this.outerDiv).addClass("hparsons_section alert alert-warning"); $(this.origElem).replaceWith(this.outerDiv); this.outerDiv.innerHTML = ``; this.hparsons = $(this.outerDiv).find("horizontal-parsons")[0]; diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index b77c99d2a..494041145 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -4,7 +4,7 @@ Horizontal Parsons Test .. Testing horizontal Parsons problems. -Horizontal Parsons + SQL +Horizontal Parsons + SQL aaa -------------------------------------- .. hparsons:: test_activecode_6 :language: sql diff --git a/test.sh b/test.sh index 27a49d80c..9cc7d82ab 100755 --- a/test.sh +++ b/test.sh @@ -1,8 +1,8 @@ #!/bin/bash -# cd ~/code/regex -# npm run build -# cp ~/code/regex/packages/horizontal-parsons/horizontal-parsons.js ~/code/RunestoneComponents/runestone/hparsons/js/horizontal-parsons.js -cd ~/RunestoneComponents +cd ~/code/regex +npm run build +cp ~/code/regex/packages/horizontal-parsons/horizontal-parsons.js ~/code/RunestoneComponents/runestone/hparsons/js/horizontal-parsons.js +cd ~/code/RunestoneComponents npm run build pip install . cd runestone/hparsons/test From 62a6adc87fa9ba4e4a0031bef48102103e3a1c8a Mon Sep 17 00:00:00 2001 From: amy21206 Date: Wed, 9 Mar 2022 17:43:24 -0500 Subject: [PATCH 18/31] :sparkles: Allow adding blocks from ReST --- runestone/hparsons/css/hparsons.css | 8 +- runestone/hparsons/hparsons.py | 98 ++++++++------------- runestone/hparsons/js/horizontal-parsons.js | 4 +- runestone/hparsons/js/hparsons-sql.js | 23 ++--- runestone/hparsons/test/_sources/index.rst | 13 ++- 5 files changed, 65 insertions(+), 81 deletions(-) diff --git a/runestone/hparsons/css/hparsons.css b/runestone/hparsons/css/hparsons.css index 0fbdf0634..fc8933fae 100755 --- a/runestone/hparsons/css/hparsons.css +++ b/runestone/hparsons/css/hparsons.css @@ -10,4 +10,10 @@ max-width: 500pt; margin-left: auto; margin-right: auto; -} \ No newline at end of file +} + +.hp_question { + padding-left: 10px; + padding-top: 10px; + margin: 5px; +} diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index 441385104..f24f3dbb1 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -16,8 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from __future__ import print_function - __author__ = "bmiller" from docutils import nodes @@ -34,31 +32,26 @@ RunestoneIdNode, ) -try: - from html import escape # py3 -except ImportError: - from cgi import escape # py2 - - def setup(app): app.add_directive("hparsons", HParsonsDirective) - app.add_node(HParsonsNode, html=(visit_ac_node, depart_ac_node)) + app.add_node(HParsonsNode, html=(visit_hp_node, depart_hp_node)) TEMPLATE_START = """
      -
      -
      +
      +
      """ TEMPLATE_END = """
      -
      @@ -74,10 +67,7 @@ def __init__(self, options, **kwargs): # self for these functions is an instance of the writer class. For example # in html, self is sphinx.writers.html.SmartyPantsHTMLTranslator # The node that is passed as a parameter is an instance of our node class. -def visit_ac_node(self, node): - # print self.settings.env.activecodecounter - - # todo: handle above in node.runestone_options +def visit_hp_node(self, node): node.delimiter = "_start__{}_".format(node.runestone_options["divid"]) @@ -87,11 +77,7 @@ def visit_ac_node(self, node): self.body.append(res) -def depart_ac_node(self, node): - """This is called at the start of processing an activecode node. If activecode had recursive nodes - etc and did not want to do all of the processing in visit_ac_node any finishing touches could be - added here. - """ +def depart_hp_node(self, node): res = TEMPLATE_END % node.runestone_options self.body.append(res) @@ -104,37 +90,28 @@ def depart_ac_node(self, node): self.body.remove(node.delimiter) -def process_activcode_nodes(app, env, docname): - pass - - -def purge_activecodes(app, env, docname): - pass - - class HParsonsDirective(RunestoneIdDirective): # only keep: language, autograde, dburl """ - .. activecode:: uniqueid - :autograde: unittest - :language: python, html, javascript, java, python2, python3 - :dburl: url to load database for sql mode - :showlastsql: -- Only show the last sql result in output - - If this is a homework problem instead of an example in the text - then the assignment text should go here. The assignment text ends with - the line containing four tilde ~ - ~~~~ - print("Hidden code before students code - good for scaffolding") - ^^^^ - print("hello world") - ==== - print("Hidden code, such as unit tests come after the four = signs") + .. hparsons:: uniqueid + :language: sql, regex + :dburl: only for sql -- url to load database - config values (conf.py): + :textentry: if you will use text entry instead of horizontal parsons - - activecode_div_class - custom CSS class of the component's outermost div - - activecode_hide_load_history - if True, hide the load history button + Here is the problem description. It must ends with the tildes. + Make sure you use the correct delimitier for each section below. + ~~~~ + --blocks-- + block 1 + block 2 + --explanations-- + explanations for block 1 + explanations for block 2 + --unittest-- + assert 1,1 == world + assert 0,1 == hello + assert 2,1 == 42 """ required_arguments = 1 @@ -143,9 +120,9 @@ class HParsonsDirective(RunestoneIdDirective): option_spec = RunestoneIdDirective.option_spec.copy() option_spec.update( { - "language": directives.unchanged, "dburl": directives.unchanged, - "showlastsql": directives.flag, + "language": directives.unchanged, + "textentry": directives.flag, } ) @@ -153,12 +130,11 @@ def run(self): super(HParsonsDirective, self).run() env = self.state.document.settings.env - # keep track of how many activecodes we have.... - # could be used to automatically make a unique id for them. - if not hasattr(env, "activecodecounter"): - env.activecodecounter = 0 - env.activecodecounter += 1 - self.options["name"] = self.arguments[0].strip() + + if "textentry" in self.options: + self.options['textentry'] = ' data-textentry="true"' + else: + self.options['textentry'] = '' explain_text = None if self.content: @@ -173,14 +149,9 @@ def run(self): self.explain_text = explain_text or ["Not an Exercise"] addQuestionToDB(self) - self.options["initialcode"] = source - str = source.replace("\n", "*nline*") - str0 = str.replace('"', "*doubleq*") - str1 = str0.replace("(", "*open*") - str2 = str1.replace(")", "*close*") - str3 = str2.replace("'", "*singleq*") - self.options["argu"] = str3 + self.options["initialsetting"] = source + # TODO: change this if "language" not in self.options: self.options["language"] = "python" @@ -234,6 +205,7 @@ def run(self): maybeAddToAssignment(self) if explain_text: + self.updateContent() self.state.nested_parse(explain_text, self.content_offset, acnode) return [acnode] diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index a777adbfa..5bfc87e19 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -15723,12 +15723,12 @@ class HParsonsElement extends HTMLElement { sheet.innerHTML += '.hparsons-tip { font-style: italic; }\n'; sheet.innerHTML += '.parsons-block {display: inline-block; font-family: monospace; border-color:gray; margin: 0 1px; position: relative; border-radius: 10px; background-color: #efefef; border: 1px solid #d3d3d3; padding: 5px 10px; margin-top: 5px;}\n'; sheet.innerHTML += '.parsons-block:hover, .parsons-block:focus { border-color: black;}\n'; - sheet.innerHTML += '.drop-area { background-color: #ffa; padding: 0 5px; height: 42px; }\n'; + sheet.innerHTML += '.drop-area { background-color: #ffa; padding: 0 5px; height: 42px; margin: 2px 0;}\n'; // TODO:(UI) move the tooltip to the top of the line sheet.innerHTML += '.parsons-block .tooltip { visibility: hidden; width: 200px; background-color: black; color: #fff; text-align: center; padding: 5px 0; border-radius: 6px; position: absolute; z-index: 1; margin: 0 10px; bottom: 120%; margin-left: -100px;}\n'; sheet.innerHTML += '.parsons-block .tooltip::after {content: " ";position: absolute; top: 100%;left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: black transparent transparent transparent;}\n'; sheet.innerHTML += '.drag-area .parsons-block:hover .tooltip { visibility: visible;}\n'; - sheet.innerHTML += '.drag-area { background-color: #efefff; padding: 0 5px; height: 42px; }\n'; + sheet.innerHTML += '.drag-area { background-color: #efefff; padding: 0 5px; height: 42px; margin: 2px 0; }\n'; // unittest this.root.appendChild(sheet); const global_sheet = document.createElement('style'); diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index e182c3837..5feb42310 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -2,6 +2,7 @@ import Handsontable from "handsontable"; // import 'handsontable/dist/handsontable.full.css'; import initSqlJs from "sql.js/dist/sql-wasm.js"; import RunestoneBase from "../../common/js/runestonebase.js"; +import "../css/hparsons.css"; var allDburls = {}; @@ -24,7 +25,6 @@ export default class SQLHParons extends RunestoneBase { this.origElem = orig; this.origText = this.origElem.textContent; this.code = $(orig).text() || "\n\n\n\n\n"; - this.question = $(opts.orig).find(`#${this.divid}_question`)[0]; this.dburl = $(orig).data("dburl"); this.runButton = null; this.saveButton = null; @@ -38,7 +38,7 @@ export default class SQLHParons extends RunestoneBase { this.prefix = this.code.substring(0, prefixEnd); this.code = this.code.substring(prefixEnd + 5); } - suffStart = this.code.indexOf("===="); + suffStart = this.code.indexOf("--unittest--"); if (suffStart > -1) { this.suffix = this.code.substring(suffStart + 5); this.code = this.code.substring(0, suffStart); @@ -139,11 +139,19 @@ export default class SQLHParons extends RunestoneBase { // copied from activecode, already modified to add parsons createEditor() { this.outerDiv = document.createElement("div"); - $(this.outerDiv).addClass("hparsons_section alert alert-warning"); $(this.origElem).replaceWith(this.outerDiv); this.outerDiv.innerHTML = ``; + console.log(this.code); + let blocks = []; + let blockIndex = this.code.indexOf('--blocks--'); + if (blockIndex > -1) { + let blocksString = this.code.substring(blockIndex + 10); + let endIndex = blocksString.indexOf('\n--'); + blocksString = endIndex > -1 ? blocksString.substring(0, endIndex) : blocksString; + blocks = blocksString.split('\n'); + } this.hparsons = $(this.outerDiv).find("horizontal-parsons")[0]; - this.hparsons.parsonsData = ['select', '*', 'from', 'test', ';']; + this.hparsons.parsonsData = blocks.slice(1, -1); } // copied from activecode @@ -184,13 +192,6 @@ export default class SQLHParons extends RunestoneBase { // TODO: maybe remove the question part $(this.outerDiv).prepend(ctrlDiv); - if (this.question) { - if ($(this.question).html().match(/^\s+$/)) { - $(this.question).remove(); - } else { - $(this.outerDiv).prepend(this.question); - } - } this.controlDiv = ctrlDiv; } diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index 494041145..24aaa717f 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -4,16 +4,21 @@ Horizontal Parsons Test .. Testing horizontal Parsons problems. -Horizontal Parsons + SQL aaa +Horizontal Parsons + SQL -------------------------------------- .. hparsons:: test_activecode_6 :language: sql - :autograde: unittest :dburl: /_static/test.db - select * from test; - ===== + this is a cute horizontal parsons problem! + ~~~~ + --blocks-- + select + * + from + test + --unittest-- assert 1,1 == world assert 0,1 == hello assert 2,1 == 42 From b58cd7959a32ac4e5c4780143729d891ca640264 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Wed, 9 Mar 2022 17:54:31 -0500 Subject: [PATCH 19/31] :bulb: Adding todo for fixing text entry --- runestone/hparsons/hparsons.py | 2 +- runestone/hparsons/js/hparsons-sql.js | 16 ++++++++++++---- runestone/hparsons/test/_sources/index.rst | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index f24f3dbb1..ea69cb900 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -96,7 +96,7 @@ class HParsonsDirective(RunestoneIdDirective): .. hparsons:: uniqueid :language: sql, regex :dburl: only for sql -- url to load database - + TODO: fix textentry :textentry: if you will use text entry instead of horizontal parsons Here is the problem description. It must ends with the tildes. diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 5feb42310..55955a0f8 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -19,6 +19,7 @@ export default class SQLHParons extends RunestoneBase { // copied from activecode var suffStart; var orig = $(opts.orig).find("textarea")[0]; + this.textentry = $(orig).data('textentry') ? true : false; this.divid = opts.orig.id; this.containerDiv = opts.orig; this.useRunestoneServices = opts.useRunestoneServices; @@ -140,8 +141,11 @@ export default class SQLHParons extends RunestoneBase { createEditor() { this.outerDiv = document.createElement("div"); $(this.origElem).replaceWith(this.outerDiv); - this.outerDiv.innerHTML = ``; - console.log(this.code); + if (this.textentry) { + this.outerDiv.innerHTML = ``; + } else { + this.outerDiv.innerHTML = ``; + } let blocks = []; let blockIndex = this.code.indexOf('--blocks--'); if (blockIndex > -1) { @@ -345,8 +349,12 @@ export default class SQLHParons extends RunestoneBase { // changed to getting parsons async buildProg(useSuffix) { // assemble code from prefix, suffix, and editor for running. - var pretext; - var prog = this.hparsons.getParsonsTextArray().join(' ') + "\n"; + var prog; + if (this.textentry) { + prog = this.hparsons.getCurrentInput(); + } else { + prog = this.hparsons.getParsonsTextArray().join(' ') + "\n"; + } this.pretext = ""; this.pretextLines = 0; this.progLines = prog.match(/\n/g).length + 1; diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index 24aaa717f..8bab72bf3 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -25,6 +25,27 @@ Horizontal Parsons + SQL .. :dburl: http://localhost:8000/_static/test.db + +.. hparsons:: teasfasfas + :language: sql + :dburl: /_static/test.db + + + this is a cute horizontal parsons problem! + ~~~~ + --blocks-- + select + * + from + test + --unittest-- + assert 1,1 == world + assert 0,1 == hello + assert 2,1 == 42 + +.. :dburl: http://localhost:8000/_static/test.db + + .. activecode:: abcde :language: sql :autograde: unittest From 41a951d42266477991762ea00650f1ecb7031f10 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Sat, 19 Mar 2022 00:11:24 -0400 Subject: [PATCH 20/31] :sparkles: Finishing horizontal parsons problems with sql --- runestone/hparsons/hparsons.py | 8 + runestone/hparsons/js/horizontal-parsons.js | 579 ++++++++------------ runestone/hparsons/js/hparsons-sql.js | 13 +- runestone/hparsons/test/_sources/index.rst | 3 +- 4 files changed, 244 insertions(+), 359 deletions(-) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index ea69cb900..5dd19265f 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -50,6 +50,7 @@ def setup(app): %(optional)s %(dburl)s %(textentry)s + %(reuse)s style="visibility: hidden;"> %(initialsetting)s @@ -97,6 +98,7 @@ class HParsonsDirective(RunestoneIdDirective): :language: sql, regex :dburl: only for sql -- url to load database TODO: fix textentry + :reuse: only for parsons -- make the blocks reusable :textentry: if you will use text entry instead of horizontal parsons Here is the problem description. It must ends with the tildes. @@ -123,6 +125,7 @@ class HParsonsDirective(RunestoneIdDirective): "dburl": directives.unchanged, "language": directives.unchanged, "textentry": directives.flag, + "reuse": directives.flag, } ) @@ -136,6 +139,11 @@ def run(self): else: self.options['textentry'] = '' + if "reuse" in self.options: + self.options['reuse'] = ' data-reuse="true"' + else: + self.options['reuse'] = '' + explain_text = None if self.content: if "~~~~" in self.content: diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index 5bfc87e19..a0fd720da 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3043,7 +3043,8 @@ class ParsonsInput { _dragSortable; parentElement; _prevPosition; - constructor(parentElement) { + reusable; + constructor(parentElement, reusable) { this.el = document.createElement('div'); this.parentElement = parentElement; this.el.id = 'regextool-' + this.parentElement.toolNumber + '-parsons-input'; @@ -3064,13 +3065,9 @@ class ParsonsInput { this._prevPosition = -1; this.expandableBlocks = []; this.expandableBlockTooltips = null; + this.reusable = reusable; this._dragSortable = new Sortable(this._dragArea, { - group: { - name: 'shared', - pull: true, - put: false - }, - sort: false, + group: 'shared', direction: 'horizontal', animation: 150 }); @@ -3111,87 +3108,6 @@ class ParsonsInput { // clearing previous settings this._dragArea.innerHTML = ''; this._dropArea.innerHTML = ''; - // adding expandable blocks - for (let i = 0; i < this.expandableBlocks.length; ++i) { - const newBlock = document.createElement('div'); - this._dragArea.appendChild(newBlock); - newBlock.innerText = this.expandableBlocks[i]; - newBlock.style.display = 'inline-block'; - newBlock.classList.add('parsons-block'); - newBlock.classList.add('expandable-block'); - if (this.expandableBlockTooltips && this.expandableBlockTooltips.length > i) { - const tooltip = document.createElement('span'); - newBlock.appendChild(tooltip); - tooltip.innerText = this.expandableBlockTooltips[i]; - tooltip.classList.add('tooltip'); - newBlock.onmouseover = () => { - if (newBlock.parentNode.classList.contains('drag-area')) { - const parsonsTooltipEvent = { - 'event-type': 'parsons-tooltip', - block: this.expandableBlocks[i], - tooltip: tooltip.innerText, - start: true - }; - this.parentElement?.logEvent(parsonsTooltipEvent); - } - }; - newBlock.onmouseout = () => { - if (newBlock.parentNode.classList.contains('drag-area')) { - const parsonsTooltipEvent = { - 'event-type': 'parsons-tooltip', - block: this.expandableBlocks[i], - tooltip: tooltip.innerText, - start: false - }; - this.parentElement?.logEvent(parsonsTooltipEvent); - } - }; - } - newBlock.onclick = () => { - // console.log('expandable block onclick'); - if (newBlock.parentNode.classList.contains('drag-area')) { - const text = this.expandableBlocks[i]; - let firstBlock = null; - for (let i = 0; i < text.length; ++i) { - const newBlock = document.createElement('div'); - this._dropArea.appendChild(newBlock); - newBlock.innerText = text.charAt(i); - newBlock.style.display = 'inline-block'; - newBlock.classList.add('parsons-block'); - newBlock.onclick = () => { - // clicking the new block generated by clicking an extendable block to remove that block - // console.log('expandable new block onclick') - const endPosition = this._getBlockPosition(newBlock); - newBlock.parentNode?.removeChild(newBlock); - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.REMOVE, - position: [endPosition, -1], - answer: this._getTextArray() - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - }; - if (firstBlock == null) { - firstBlock = newBlock; - } - } - if (this.parentElement && firstBlock) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.ADD, - position: [-1, this._getBlockPosition(firstBlock)], - answer: this._getTextArray(), - 'add-block-cnt': text.length, - 'is-expandable': true, - 'add-by-click': true - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - } - }; - } // adding normal blocks for (let i = 0; i < data.length; ++i) { const newBlock = document.createElement('div'); @@ -3206,82 +3122,82 @@ class ParsonsInput { } newBlock.style.display = 'inline-block'; newBlock.classList.add('parsons-block'); - if (tooltips && tooltips.length > i) { - const tooltip = document.createElement('span'); - newBlock.appendChild(tooltip); - tooltip.innerText = tooltips[i]; - tooltip.classList.add('tooltip'); - newBlock.onmouseover = () => { - if (newBlock.parentNode.classList.contains('drag-area')) { - const parsonsTooltipEvent = { - 'event-type': 'parsons-tooltip', - block: data[i], - tooltip: tooltips[i], - start: true - }; - this.parentElement?.logEvent(parsonsTooltipEvent); - } - }; - newBlock.onmouseout = () => { - if (newBlock.parentNode.classList.contains('drag-area')) { - const parsonsTooltipEvent = { - 'event-type': 'parsons-tooltip', - block: data[i], - tooltip: tooltips[i], - start: false - }; - this.parentElement?.logEvent(parsonsTooltipEvent); - } - }; - } - newBlock.onclick = () => { - // console.log('normal block onclick'); - if (newBlock.parentNode.classList.contains('drag-area')) { - let newBlockCopy = newBlock.cloneNode(true); - this._dropArea.appendChild(newBlockCopy); - newBlockCopy.onclick = () => { - // console.log('normal new block onclick') - // clicking on the new block generated by a normal block to remove the new block - const endPosition = this._getBlockPosition(newBlockCopy); - newBlockCopy.parentNode?.removeChild(newBlockCopy); - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.REMOVE, - position: [endPosition, -1], - answer: this._getTextArray() - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - }; - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.ADD, - position: [-1, this._getBlockPosition(newBlockCopy)], - answer: this._getTextArray(), - 'add-block-cnt': 1, - 'is-expandable': false, - 'add-by-click': true - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - } - if (newBlock.parentNode.classList.contains('drop-area')) { - // clicking on the normal block dragged down to remove the normal block - const endPosition = this._getBlockPosition(newBlock); - newBlock.parentNode?.removeChild(newBlock); - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.REMOVE, - position: [endPosition, -1], - answer: this._getTextArray(), - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - } - }; + // if (tooltips && tooltips.length > i) { + // const tooltip = document.createElement('span'); + // newBlock.appendChild(tooltip); + // tooltip.innerText = tooltips[i]; + // tooltip.classList.add('tooltip'); + // newBlock.onmouseover = () => { + // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drag-area')) { + // const parsonsTooltipEvent: RegexEvent.ParsonsTooltipEvent = { + // 'event-type': 'parsons-tooltip', + // block: data[i], + // tooltip: tooltips[i], + // start: true + // } + // this.parentElement?.logEvent(parsonsTooltipEvent) + // } + // } + // newBlock.onmouseout = () => { + // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drag-area')) { + // const parsonsTooltipEvent: RegexEvent.ParsonsTooltipEvent = { + // 'event-type': 'parsons-tooltip', + // block: data[i], + // tooltip: tooltips[i], + // start: false + // } + // this.parentElement?.logEvent(parsonsTooltipEvent); + // } + // } + // } + // newBlock.onclick = () => { + // // console.log('normal block onclick'); + // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drag-area')) { + // let newBlockCopy = newBlock.cloneNode(true); + // this._dropArea.appendChild(newBlockCopy); + // // (newBlockCopy as HTMLDivElement).onclick = () => { + // // // console.log('normal new block onclick') + // // // clicking on the new block generated by a normal block to remove the new block + // // const endPosition = this._getBlockPosition(newBlockCopy as HTMLElement); + // // newBlockCopy.parentNode?.removeChild(newBlockCopy); + // // if (this.parentElement) { + // // this.parentElement.temporaryInputEvent = { + // // 'event-type': 'parsons-input', + // // action: RegexEvent.ParsonsInputAction.REMOVE, + // // position: [endPosition, -1], + // // answer: this._getTextArray() + // // }; + // // } + // // this.el.dispatchEvent(new Event('regexChanged')); + // // } + // if (this.parentElement) { + // this.parentElement.temporaryInputEvent = { + // 'event-type': 'parsons-input', + // action: RegexEvent.ParsonsInputAction.ADD, + // position: [-1, this._getBlockPosition(newBlockCopy as HTMLElement)], + // answer: this._getTextArray(), + // 'add-block-cnt': 1, + // 'is-expandable': false, + // 'add-by-click': true + // }; + // } + // this.el.dispatchEvent(new Event('regexChanged')); + // } + // // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drop-area')) { + // // // clicking on the normal block dragged down to remove the normal block + // // const endPosition = this._getBlockPosition(newBlock as HTMLElement); + // // newBlock.parentNode?.removeChild(newBlock); + // // if (this.parentElement) { + // // this.parentElement.temporaryInputEvent = { + // // 'event-type': 'parsons-input', + // // action: RegexEvent.ParsonsInputAction.REMOVE, + // // position: [endPosition, -1], + // // answer: this._getTextArray(), + // // }; + // // } + // // this.el.dispatchEvent(new Event('regexChanged')); + // // } + // } } this._initSortable(); }; @@ -3291,205 +3207,156 @@ class ParsonsInput { this._dragSortable = new Sortable(this._dragArea, { group: { name: 'shared', - pull: 'clone', - put: false + // pull: 'clone', + // put: false }, - sort: false, + // sort: false, direction: 'horizontal', animation: 150, draggable: '.parsons-block', - onClone: (event) => { - const newBlock = event.clone; - if (event.item.classList.contains('expandable-block')) { - newBlock.onclick = () => { - // console.log('expandable block onclick'); - if (newBlock.parentNode.classList.contains('drag-area')) { - const text = (newBlock.firstChild?.textContent) || ''; - let firstBlock = null; - for (let i = 0; i < text.length; ++i) { - const newBlock = document.createElement('div'); - this._dropArea.appendChild(newBlock); - newBlock.innerText = text.charAt(i); - newBlock.style.display = 'inline-block'; - newBlock.classList.add('parsons-block'); - newBlock.onclick = () => { - // console.log('expandable new block onclick') - // clicking on a new block generated by an expandable block to remove the new block - const endPosition = this._getBlockPosition(newBlock); - newBlock.parentNode?.removeChild(newBlock); - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.REMOVE, - position: [endPosition, -1], - answer: this._getTextArray(), - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - }; - if (firstBlock == null) { - firstBlock = newBlock; - } - } - if (this.parentElement && firstBlock) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.ADD, - position: [-1, this._getBlockPosition(firstBlock)], - answer: this._getTextArray(), - 'add-block-cnt': text.length, - 'is-expandable': true, - 'add-by-click': true - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - } - }; - } - else { - newBlock.onclick = () => { - // console.log('normal block onclick'); - if (newBlock.parentNode.classList.contains('drag-area')) { - let newBlockCopy = newBlock.cloneNode(true); - this._dropArea.appendChild(newBlockCopy); - newBlockCopy.onclick = () => { - // console.log('normal new block onclick') - // clicking on a new block added by clicking to remove the new block - const endPosition = this._getBlockPosition(newBlockCopy); - newBlockCopy.parentNode?.removeChild(newBlockCopy); - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.REMOVE, - position: [endPosition, -1], - answer: this._getTextArray(), - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - }; - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.ADD, - position: [-1, this._getBlockPosition(newBlockCopy)], - answer: this._getTextArray(), - 'add-block-cnt': 1, - 'is-expandable': false, - 'add-by-click': true - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - } - if (newBlock.parentNode.classList.contains('drop-area')) { - // console.log('359') - // (not sure) clicking on a normal block added by dragging to remove the new block - const endPosition = this._getBlockPosition(newBlock); - newBlock.parentNode?.removeChild(newBlock); - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.REMOVE, - position: [endPosition, -1], - answer: this._getTextArray(), - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - } - }; - } - } + // onClone: (event) => { + // const newBlock = event.clone; + // newBlock.onclick = () => { + // // console.log('normal block onclick'); + // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drag-area')) { + // let newBlockCopy = newBlock.cloneNode(true); + // this._dropArea.appendChild(newBlockCopy); + // (newBlockCopy as HTMLDivElement).onclick = () => { + // // console.log('normal new block onclick') + // // clicking on a new block added by clicking to remove the new block + // const endPosition = this._getBlockPosition(newBlockCopy as HTMLElement); + // newBlockCopy.parentNode?.removeChild(newBlockCopy); + // if (this.parentElement) { + // this.parentElement.temporaryInputEvent = { + // 'event-type': 'parsons-input', + // action: RegexEvent.ParsonsInputAction.REMOVE, + // position: [endPosition, -1], + // answer: this._getTextArray(), + // }; + // } + // this.el.dispatchEvent(new Event('regexChanged')); + // } + // if (this.parentElement) { + // this.parentElement.temporaryInputEvent = { + // 'event-type': 'parsons-input', + // action: RegexEvent.ParsonsInputAction.ADD, + // position: [-1, this._getBlockPosition(newBlockCopy as HTMLElement)], + // answer: this._getTextArray(), + // 'add-block-cnt': 1, + // 'is-expandable': false, + // 'add-by-click': true + // }; + // } + // this.el.dispatchEvent(new Event('regexChanged')); + // } + // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drop-area')) { + // // console.log('359') + // // (not sure) clicking on a normal block added by dragging to remove the new block + // const endPosition = this._getBlockPosition(newBlock as HTMLElement); + // newBlock.parentNode?.removeChild(newBlock); + // if (this.parentElement) { + // this.parentElement.temporaryInputEvent = { + // 'event-type': 'parsons-input', + // action: RegexEvent.ParsonsInputAction.REMOVE, + // position: [endPosition, -1], + // answer: this._getTextArray(), + // }; + // } + // this.el.dispatchEvent(new Event('regexChanged')); + // } + // } + // } + // } }); this._dropSortable = new Sortable(this._dropArea, { group: 'shared', direction: 'horizontal', animation: 150, draggable: '.parsons-block', - onAdd: (event) => { - // getting the position - const isExpandable = event.item.classList.contains('expandable-block'); - // console.log(isExpandable); - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.ADD, - position: [-1, this._getBlockPosition(event.item)], - answer: this._getTextArray(), - 'add-block-cnt': isExpandable ? event.item.innerText.length : 1, - 'is-expandable': isExpandable, - 'add-by-click': false - }; - } - if (isExpandable) { - const parentNode = event.item.parentNode; - const text = event.item.innerText; - this._getBlockPosition(event.item); - const nextSibling = event.item.nextSibling; - parentNode?.removeChild(event.item); - for (let i = 0; i < text.length; ++i) { - const newBlock = document.createElement('div'); - if (!nextSibling) { - this._dropArea.appendChild(newBlock); - } - else { - parentNode?.insertBefore(newBlock, nextSibling); - } - newBlock.innerText = text.charAt(i); - newBlock.style.display = 'inline-block'; - newBlock.classList.add('parsons-block'); - newBlock.onclick = () => { - // console.log('expandable new block onclick') - // clicking on a block added by clicking an expandable block to remove the new block - const endPosition = this._getBlockPosition(newBlock); - newBlock.parentNode?.removeChild(newBlock); - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: RegexEvent.ParsonsInputAction.REMOVE, - position: [endPosition, -1], - answer: this._getTextArray(), - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - }; - } - } - if (this.parentElement) { - this.parentElement.temporaryInputEvent.answer = this._getTextArray(); - } - this.el.dispatchEvent(new Event('regexChanged')); - }, - onStart: (event) => { - this._prevPosition = this._getBlockPosition(event.item); - }, - onEnd: (event) => { - // TODO: (bug) This is a workaround that only works in the demo. - // compare clientY with the position of item. - // console.log(event.item.onclick); - let endposition = 0; - let action = RegexEvent.ParsonsInputAction.MOVE; - const upperbound = this._dropArea.getBoundingClientRect().top; - const lowerbound = this._dropArea.getBoundingClientRect().bottom; - if (event.originalEvent.clientY > lowerbound || event.originalEvent.clientY < upperbound) { - const item = event.item; - if (item.parentNode) { - item.parentNode.removeChild(item); - } - endposition = -1; - action = RegexEvent.ParsonsInputAction.REMOVE; - } - else { - endposition = this._getBlockPosition(event.item); - } - if (this.parentElement) { - this.parentElement.temporaryInputEvent = { - 'event-type': 'parsons-input', - action: action, - position: [this._prevPosition, endposition], - answer: this._getTextArray(), - }; - } - this.el.dispatchEvent(new Event('regexChanged')); - }, + // onAdd: (event) => { + // // getting the position + // const isExpandable = event.item.classList.contains('expandable-block'); + // // console.log(isExpandable); + // if (this.parentElement) { + // this.parentElement.temporaryInputEvent = { + // 'event-type': 'parsons-input', + // action: RegexEvent.ParsonsInputAction.ADD, + // position: [-1, this._getBlockPosition(event.item)], + // answer: this._getTextArray(), + // 'add-block-cnt': isExpandable? event.item.innerText.length : 1, + // 'is-expandable': isExpandable, + // 'add-by-click': false + // }; + // } + // if (isExpandable) { + // const parentNode = event.item.parentNode; + // const text = event.item.innerText; + // const insertPosition = this._getBlockPosition(event.item); + // const nextSibling = event.item.nextSibling; + // parentNode?.removeChild(event.item); + // for (let i = 0; i < text.length; ++i) { + // const newBlock = document.createElement('div'); + // if (!nextSibling) { + // this._dropArea.appendChild(newBlock); + // } else { + // parentNode?.insertBefore(newBlock, nextSibling); + // } + // newBlock.innerText = text.charAt(i); + // newBlock.style.display = 'inline-block'; + // newBlock.classList.add('parsons-block'); + // (newBlock as HTMLDivElement).onclick = () => { + // // console.log('expandable new block onclick') + // // clicking on a block added by clicking an expandable block to remove the new block + // const endPosition = this._getBlockPosition(newBlock as HTMLElement); + // newBlock.parentNode?.removeChild(newBlock); + // if (this.parentElement) { + // this.parentElement.temporaryInputEvent = { + // 'event-type': 'parsons-input', + // action: RegexEvent.ParsonsInputAction.REMOVE, + // position: [endPosition, -1], + // answer: this._getTextArray(), + // }; + // } + // this.el.dispatchEvent(new Event('regexChanged')); + // } + // } + // } + // if (this.parentElement) { + // this.parentElement.temporaryInputEvent.answer = this._getTextArray(); + // } + // this.el.dispatchEvent(new Event('regexChanged')); + // }, + // onStart: (event) => { + // this._prevPosition = this._getBlockPosition(event.item); + // }, + // onEnd: (event) => { + // // TODO: (bug) This is a workaround that only works in the demo. + // // compare clientY with the position of item. + // // console.log(event.item.onclick); + // let endposition = 0; + // let action = RegexEvent.ParsonsInputAction.MOVE; + // const upperbound = this._dropArea.getBoundingClientRect().top; + // const lowerbound = this._dropArea.getBoundingClientRect().bottom; + // if ((event as any).originalEvent.clientY > lowerbound || (event as any).originalEvent.clientY < upperbound ) { + // const item = event.item as HTMLElement; + // if (item.parentNode) { + // item.parentNode.removeChild(item); + // } + // endposition = -1; + // action = RegexEvent.ParsonsInputAction.REMOVE; + // } else { + // endposition = this._getBlockPosition(event.item); + // } + // if (this.parentElement) { + // this.parentElement.temporaryInputEvent = { + // 'event-type': 'parsons-input', + // action: action, + // position: [this._prevPosition, endposition], + // answer: this._getTextArray(), + // }; + // } + // this.el.dispatchEvent(new Event('regexChanged')); + // }, }); }; updateTestStatus = (result) => { @@ -15695,7 +15562,8 @@ class HParsonsElement extends HTMLElement { this.inputDiv = document.createElement('div'); this.inputDiv.classList.add('hparsons-input'); this.root.append(this.inputDiv); - this.hparsonsInput = new ParsonsInput(this); + const reusable = this.getAttribute('reuse-blocks') ? true : false; + this.hparsonsInput = new ParsonsInput(this, reusable); // a div wrapping the input and the test case status // init regex input based on the input type this._parsonsData = new Array(); @@ -15783,7 +15651,8 @@ class HParsonsElement extends HTMLElement { // todo:(UI) fix the css for the input if (this.inputType == 'parsons') { // init elements: parsons regex input - this.hparsonsInput = new ParsonsInput(this); + const reusable = this.getAttribute('reuse-blocks') != null ? true : false; + this.hparsonsInput = new ParsonsInput(this, reusable); this.inputDiv.appendChild(this.hparsonsInput.el); } else { diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 55955a0f8..151870e7c 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -20,6 +20,7 @@ export default class SQLHParons extends RunestoneBase { var suffStart; var orig = $(opts.orig).find("textarea")[0]; this.textentry = $(orig).data('textentry') ? true : false; + this.reuse = $(orig).data('reuse') ? true : false; this.divid = opts.orig.id; this.containerDiv = opts.orig; this.useRunestoneServices = opts.useRunestoneServices; @@ -141,11 +142,17 @@ export default class SQLHParons extends RunestoneBase { createEditor() { this.outerDiv = document.createElement("div"); $(this.origElem).replaceWith(this.outerDiv); + let parsonsHTML = ``; + parsonsHTML += ` input-type='text' `; } else { - this.outerDiv.innerHTML = ``; + parsonsHTML += ` input-type='parsons' `; } + if (this.reuse) { + parsonsHTML += ` reuse-blocks`; + } + parsonsHTML += `>` + this.outerDiv.innerHTML = parsonsHTML; let blocks = []; let blockIndex = this.code.indexOf('--blocks--'); if (blockIndex > -1) { @@ -368,7 +375,7 @@ export default class SQLHParons extends RunestoneBase { async logCurrentAnswer(sid) { let data = { div_id: this.divid, - // code: this.editor.getValue(), + code: this.hparsons.getParsonsTextArray(), language: "sql", // errinfo: this.results[this.results.length - 1].status, to_save: this.saveCode, diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index 8bab72bf3..00226c0ca 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -4,11 +4,12 @@ Horizontal Parsons Test .. Testing horizontal Parsons problems. -Horizontal Parsons + SQL +Horizontal Parsons + SQL aaa -------------------------------------- .. hparsons:: test_activecode_6 :language: sql :dburl: /_static/test.db + :reuse: this is a cute horizontal parsons problem! From eef9d8731ea67ca0249e3a99d195def7e9cf13f1 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Sat, 19 Mar 2022 10:39:16 -0400 Subject: [PATCH 21/31] Update package.json according to upstream --- package.json | 6 ++---- runestone/hparsons/js/horizontal-parsons.js | 2 +- runestone/mchoice/js/mchoice.js | 1 - test.sh | 10 ---------- 4 files changed, 3 insertions(+), 16 deletions(-) delete mode 100755 test.sh diff --git a/package.json b/package.json index ab2ae6fc2..fc51c3d34 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "build": "webpack --mode=development", "buildj": "webpack --mode=development --profile --json > stats.json", "watch": "webpack --mode=development --watch", - "start": "webpack-dev-server --mode=development --open", "serve": "cd dist; python -m http.server 8080; cd ..", "dist": "webpack --mode=production", "distj": "webpack --mode=production --profile --json > stats.json", @@ -19,8 +18,8 @@ "author": "", "license": "ISC", "devDependencies": { - "compression-webpack-plugin": "^9.0.0", "copy-webpack-plugin": "^9.0.0", + "compression-webpack-plugin": "^9.0.0", "css-loader": "^6.0.0", "css-minimizer-webpack-plugin": "^3.0.0", "html-loader": "^3.0.0", @@ -28,8 +27,7 @@ "mini-css-extract-plugin": "^2.0.0", "webpack": "^5.61.0", "webpack-bundle-analyzer": "^4.0.0", - "webpack-cli": "^4.9.2", - "webpack-dev-server": "^4.7.4" + "webpack-cli": "^4.0.0" }, "dependencies": { "-": "0.0.1", diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index a0fd720da..3e5df9a83 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3049,7 +3049,7 @@ class ParsonsInput { this.parentElement = parentElement; this.el.id = 'regextool-' + this.parentElement.toolNumber + '-parsons-input'; const dragTip = document.createElement('div'); - dragTip.innerText = 'Drag or click to select from the symbols below to form your code'; + dragTip.innerText = 'Drag from the blocks below to form your code'; dragTip.classList.add('hparsons-tip'); this.el.append(dragTip); this._dragArea = document.createElement('div'); diff --git a/runestone/mchoice/js/mchoice.js b/runestone/mchoice/js/mchoice.js index 5b9e1c3cc..9d1c41cfe 100644 --- a/runestone/mchoice/js/mchoice.js +++ b/runestone/mchoice/js/mchoice.js @@ -127,7 +127,6 @@ export default class MultipleChoice extends RunestoneBase { renderMCContainer() { this.containerDiv = document.createElement("div"); $(this.containerDiv).html(this.question); - $(this.containerDiv).addClass('mctesttest'); $(this.containerDiv).addClass(this.origElem.getAttribute("class")); this.containerDiv.id = this.divid; } diff --git a/test.sh b/test.sh deleted file mode 100755 index 9cc7d82ab..000000000 --- a/test.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -cd ~/code/regex -npm run build -cp ~/code/regex/packages/horizontal-parsons/horizontal-parsons.js ~/code/RunestoneComponents/runestone/hparsons/js/horizontal-parsons.js -cd ~/code/RunestoneComponents -npm run build -pip install . -cd runestone/hparsons/test -runestone build -runestone serve \ No newline at end of file From f82e939277892a495a4a908cc07e2451ce28a3b6 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Sat, 19 Mar 2022 11:24:49 -0400 Subject: [PATCH 22/31] :loud_sound: Add logs for moving horizontal parsons blocks. --- runestone/hparsons/js/horizontal-parsons.js | 276 +++----------------- runestone/hparsons/js/hparsons-sql.js | 12 + 2 files changed, 45 insertions(+), 243 deletions(-) diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index 3e5df9a83..a5e2da5ed 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3082,14 +3082,6 @@ class ParsonsInput { let ret = ''; if (this._dropArea.hasChildNodes()) { let el = this._dropArea.firstChild; - // if (el.innerHTML == ' ') { - // console.log('here') - // ret += ' '; - // } else { - // console.log(el.innerText) - // ret += el.innerText; - // } - // dealing with   ret += el.innerText.replace(/\xA0/g, ' '); while (el.nextSibling) { el = el.nextSibling; @@ -3122,82 +3114,6 @@ class ParsonsInput { } newBlock.style.display = 'inline-block'; newBlock.classList.add('parsons-block'); - // if (tooltips && tooltips.length > i) { - // const tooltip = document.createElement('span'); - // newBlock.appendChild(tooltip); - // tooltip.innerText = tooltips[i]; - // tooltip.classList.add('tooltip'); - // newBlock.onmouseover = () => { - // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drag-area')) { - // const parsonsTooltipEvent: RegexEvent.ParsonsTooltipEvent = { - // 'event-type': 'parsons-tooltip', - // block: data[i], - // tooltip: tooltips[i], - // start: true - // } - // this.parentElement?.logEvent(parsonsTooltipEvent) - // } - // } - // newBlock.onmouseout = () => { - // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drag-area')) { - // const parsonsTooltipEvent: RegexEvent.ParsonsTooltipEvent = { - // 'event-type': 'parsons-tooltip', - // block: data[i], - // tooltip: tooltips[i], - // start: false - // } - // this.parentElement?.logEvent(parsonsTooltipEvent); - // } - // } - // } - // newBlock.onclick = () => { - // // console.log('normal block onclick'); - // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drag-area')) { - // let newBlockCopy = newBlock.cloneNode(true); - // this._dropArea.appendChild(newBlockCopy); - // // (newBlockCopy as HTMLDivElement).onclick = () => { - // // // console.log('normal new block onclick') - // // // clicking on the new block generated by a normal block to remove the new block - // // const endPosition = this._getBlockPosition(newBlockCopy as HTMLElement); - // // newBlockCopy.parentNode?.removeChild(newBlockCopy); - // // if (this.parentElement) { - // // this.parentElement.temporaryInputEvent = { - // // 'event-type': 'parsons-input', - // // action: RegexEvent.ParsonsInputAction.REMOVE, - // // position: [endPosition, -1], - // // answer: this._getTextArray() - // // }; - // // } - // // this.el.dispatchEvent(new Event('regexChanged')); - // // } - // if (this.parentElement) { - // this.parentElement.temporaryInputEvent = { - // 'event-type': 'parsons-input', - // action: RegexEvent.ParsonsInputAction.ADD, - // position: [-1, this._getBlockPosition(newBlockCopy as HTMLElement)], - // answer: this._getTextArray(), - // 'add-block-cnt': 1, - // 'is-expandable': false, - // 'add-by-click': true - // }; - // } - // this.el.dispatchEvent(new Event('regexChanged')); - // } - // // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drop-area')) { - // // // clicking on the normal block dragged down to remove the normal block - // // const endPosition = this._getBlockPosition(newBlock as HTMLElement); - // // newBlock.parentNode?.removeChild(newBlock); - // // if (this.parentElement) { - // // this.parentElement.temporaryInputEvent = { - // // 'event-type': 'parsons-input', - // // action: RegexEvent.ParsonsInputAction.REMOVE, - // // position: [endPosition, -1], - // // answer: this._getTextArray(), - // // }; - // // } - // // this.el.dispatchEvent(new Event('regexChanged')); - // // } - // } } this._initSortable(); }; @@ -3214,149 +3130,35 @@ class ParsonsInput { direction: 'horizontal', animation: 150, draggable: '.parsons-block', - // onClone: (event) => { - // const newBlock = event.clone; - // newBlock.onclick = () => { - // // console.log('normal block onclick'); - // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drag-area')) { - // let newBlockCopy = newBlock.cloneNode(true); - // this._dropArea.appendChild(newBlockCopy); - // (newBlockCopy as HTMLDivElement).onclick = () => { - // // console.log('normal new block onclick') - // // clicking on a new block added by clicking to remove the new block - // const endPosition = this._getBlockPosition(newBlockCopy as HTMLElement); - // newBlockCopy.parentNode?.removeChild(newBlockCopy); - // if (this.parentElement) { - // this.parentElement.temporaryInputEvent = { - // 'event-type': 'parsons-input', - // action: RegexEvent.ParsonsInputAction.REMOVE, - // position: [endPosition, -1], - // answer: this._getTextArray(), - // }; - // } - // this.el.dispatchEvent(new Event('regexChanged')); - // } - // if (this.parentElement) { - // this.parentElement.temporaryInputEvent = { - // 'event-type': 'parsons-input', - // action: RegexEvent.ParsonsInputAction.ADD, - // position: [-1, this._getBlockPosition(newBlockCopy as HTMLElement)], - // answer: this._getTextArray(), - // 'add-block-cnt': 1, - // 'is-expandable': false, - // 'add-by-click': true - // }; - // } - // this.el.dispatchEvent(new Event('regexChanged')); - // } - // if ((newBlock.parentNode as HTMLDivElement).classList.contains('drop-area')) { - // // console.log('359') - // // (not sure) clicking on a normal block added by dragging to remove the new block - // const endPosition = this._getBlockPosition(newBlock as HTMLElement); - // newBlock.parentNode?.removeChild(newBlock); - // if (this.parentElement) { - // this.parentElement.temporaryInputEvent = { - // 'event-type': 'parsons-input', - // action: RegexEvent.ParsonsInputAction.REMOVE, - // position: [endPosition, -1], - // answer: this._getTextArray(), - // }; - // } - // this.el.dispatchEvent(new Event('regexChanged')); - // } - // } - // } - // } }); this._dropSortable = new Sortable(this._dropArea, { group: 'shared', direction: 'horizontal', animation: 150, draggable: '.parsons-block', - // onAdd: (event) => { - // // getting the position - // const isExpandable = event.item.classList.contains('expandable-block'); - // // console.log(isExpandable); - // if (this.parentElement) { - // this.parentElement.temporaryInputEvent = { - // 'event-type': 'parsons-input', - // action: RegexEvent.ParsonsInputAction.ADD, - // position: [-1, this._getBlockPosition(event.item)], - // answer: this._getTextArray(), - // 'add-block-cnt': isExpandable? event.item.innerText.length : 1, - // 'is-expandable': isExpandable, - // 'add-by-click': false - // }; - // } - // if (isExpandable) { - // const parentNode = event.item.parentNode; - // const text = event.item.innerText; - // const insertPosition = this._getBlockPosition(event.item); - // const nextSibling = event.item.nextSibling; - // parentNode?.removeChild(event.item); - // for (let i = 0; i < text.length; ++i) { - // const newBlock = document.createElement('div'); - // if (!nextSibling) { - // this._dropArea.appendChild(newBlock); - // } else { - // parentNode?.insertBefore(newBlock, nextSibling); - // } - // newBlock.innerText = text.charAt(i); - // newBlock.style.display = 'inline-block'; - // newBlock.classList.add('parsons-block'); - // (newBlock as HTMLDivElement).onclick = () => { - // // console.log('expandable new block onclick') - // // clicking on a block added by clicking an expandable block to remove the new block - // const endPosition = this._getBlockPosition(newBlock as HTMLElement); - // newBlock.parentNode?.removeChild(newBlock); - // if (this.parentElement) { - // this.parentElement.temporaryInputEvent = { - // 'event-type': 'parsons-input', - // action: RegexEvent.ParsonsInputAction.REMOVE, - // position: [endPosition, -1], - // answer: this._getTextArray(), - // }; - // } - // this.el.dispatchEvent(new Event('regexChanged')); - // } - // } - // } - // if (this.parentElement) { - // this.parentElement.temporaryInputEvent.answer = this._getTextArray(); - // } - // this.el.dispatchEvent(new Event('regexChanged')); - // }, - // onStart: (event) => { - // this._prevPosition = this._getBlockPosition(event.item); - // }, - // onEnd: (event) => { - // // TODO: (bug) This is a workaround that only works in the demo. - // // compare clientY with the position of item. - // // console.log(event.item.onclick); - // let endposition = 0; - // let action = RegexEvent.ParsonsInputAction.MOVE; - // const upperbound = this._dropArea.getBoundingClientRect().top; - // const lowerbound = this._dropArea.getBoundingClientRect().bottom; - // if ((event as any).originalEvent.clientY > lowerbound || (event as any).originalEvent.clientY < upperbound ) { - // const item = event.item as HTMLElement; - // if (item.parentNode) { - // item.parentNode.removeChild(item); - // } - // endposition = -1; - // action = RegexEvent.ParsonsInputAction.REMOVE; - // } else { - // endposition = this._getBlockPosition(event.item); - // } - // if (this.parentElement) { - // this.parentElement.temporaryInputEvent = { - // 'event-type': 'parsons-input', - // action: action, - // position: [this._prevPosition, endposition], - // answer: this._getTextArray(), - // }; - // } - // this.el.dispatchEvent(new Event('regexChanged')); - // }, + onAdd: (event) => { + const inputEvent = { + 'event-type': 'parsons-input', + action: 'add', + position: [-1, this._getBlockPosition(event.item)], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + }, + onStart: (event) => { + this._prevPosition = this._getBlockPosition(event.item); + }, + onEnd: (event) => { + let endposition = this._getBlockPosition(event.item); + const action = endposition == -1 ? 'remove' : 'move'; + const inputEvent = { + 'event-type': 'parsons-input', + action: action, + position: [this._prevPosition, endposition], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + }, }); }; updateTestStatus = (result) => { @@ -3378,8 +3180,8 @@ class ParsonsInput { this.expandableBlocks = expandableBlocks; }; _getBlockPosition = (block) => { - let position = 0; - const parent = block.parentNode; + let position = -1; + const parent = this._dropArea; if (parent) { for (position = 0; position < parent.childNodes.length; ++position) { if (parent.childNodes[position] === block) { @@ -3460,7 +3262,7 @@ class ParsonsInput { }; } }; - this.el.dispatchEvent(new Event('regexChanged')); + this.el.dispatchEvent(new Event('codeChanged')); } } } @@ -15534,14 +15336,6 @@ class TextInput { } } -// declare global { -// interface Window { -// languagePluginUrl: string -// Sk: Skulpt -// regexStudentId: string -// regexCourseId: string -// } -// } class HParsonsElement extends HTMLElement { root; _parsonsData; @@ -15605,17 +15399,13 @@ class HParsonsElement extends HTMLElement { this.appendChild(global_sheet); }; logEvent = (eventContent) => { - console.log('hparsons, logevent'); - // const basicEvent: RegexEvent.BasicEvent = { - // 'student-id': window.regexStudentId || 'stub-id', - // 'course-id': window.regexCourseId || 'stub-course-id', - // 'problem-id': this.problemId, - // 'input-type': this.inputType, - // 'client-timestamp': this._getTimestamp() - // }; - // const ev = new CustomEvent('regex-element', {bubbles: true, detail: {...basicEvent, ...eventContent}}); - // this.dispatchEvent(ev); - // // console.log({...basicEvent, ...eventContent}); + // TODO: fix the logging scheme for horizontal parsons in general. + // Right now it only dispatches event of moving parsons blocks. + const basicEvent = { + 'input-type': this.inputType, + }; + const ev = new CustomEvent('horizontal-parsons', { bubbles: true, detail: { ...basicEvent, ...eventContent } }); + this.dispatchEvent(ev); }; // private _getTimestamp = (): string => { // const timestamp = new Date(); diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 151870e7c..47671e9ec 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -153,6 +153,7 @@ export default class SQLHParons extends RunestoneBase { } parsonsHTML += `>` this.outerDiv.innerHTML = parsonsHTML; + this.outerDiv.addEventListener('horizontal-parsons', (ev) => {this.logHorizontalParsonsEvent(ev.detail)}) let blocks = []; let blockIndex = this.code.indexOf('--blocks--'); if (blockIndex > -1) { @@ -401,6 +402,17 @@ export default class SQLHParons extends RunestoneBase { } } + logHorizontalParsonsEvent(hparsonsEvent) { + // TODO: might need to find another way to change "act". + // The event string is probably too long. + let ev = { + event: "hparsons", + div_id: this.divid, + act: JSON.stringify(hparsonsEvent), + }; + this.logBookEvent(ev); + } + renderFeedback() { if (this.testResult) { $(this.output).text(this.testResult); From 34e653fff76da2472241388078a7af49ba4bb69f Mon Sep 17 00:00:00 2001 From: amy21206 Date: Sat, 19 Mar 2022 15:25:35 -0400 Subject: [PATCH 23/31] :sparkles: Allow reusing blocks in horizontal parsons --- runestone/hparsons/js/horizontal-parsons.js | 138 ++++++++++++++------ runestone/hparsons/js/hparsons-sql.js | 2 +- 2 files changed, 99 insertions(+), 41 deletions(-) diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index a5e2da5ed..d0dced992 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3120,46 +3120,103 @@ class ParsonsInput { _initSortable = () => { this._dragSortable.destroy(); this._dropSortable.destroy(); - this._dragSortable = new Sortable(this._dragArea, { - group: { - name: 'shared', - // pull: 'clone', - // put: false - }, - // sort: false, - direction: 'horizontal', - animation: 150, - draggable: '.parsons-block', - }); - this._dropSortable = new Sortable(this._dropArea, { - group: 'shared', - direction: 'horizontal', - animation: 150, - draggable: '.parsons-block', - onAdd: (event) => { - const inputEvent = { - 'event-type': 'parsons-input', - action: 'add', - position: [-1, this._getBlockPosition(event.item)], - answer: this._getTextArray(), - }; - this.parentElement.logEvent(inputEvent); - }, - onStart: (event) => { - this._prevPosition = this._getBlockPosition(event.item); - }, - onEnd: (event) => { - let endposition = this._getBlockPosition(event.item); - const action = endposition == -1 ? 'remove' : 'move'; - const inputEvent = { - 'event-type': 'parsons-input', - action: action, - position: [this._prevPosition, endposition], - answer: this._getTextArray(), - }; - this.parentElement.logEvent(inputEvent); - }, - }); + if (this.reusable) { + this._dragSortable = new Sortable(this._dragArea, { + group: { + name: 'shared', + pull: 'clone', + put: false + }, + sort: false, + direction: 'horizontal', + animation: 150, + draggable: '.parsons-block', + }); + this._dropSortable = new Sortable(this._dropArea, { + group: 'shared', + direction: 'horizontal', + animation: 150, + draggable: '.parsons-block', + onAdd: (event) => { + const inputEvent = { + 'event-type': 'parsons-input', + action: 'add', + position: [-1, this._getBlockPosition(event.item)], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + }, + onStart: (event) => { + this._prevPosition = this._getBlockPosition(event.item); + }, + onEnd: (event) => { + let endposition; + let action = 'move'; + const upperbound = this._dropArea.getBoundingClientRect().top; + const lowerbound = this._dropArea.getBoundingClientRect().bottom; + if (event.originalEvent.clientY > lowerbound || event.originalEvent.clientY < upperbound) { + const item = event.item; + if (item.parentNode) { + item.parentNode.removeChild(item); + } + endposition = -1; + action = 'remove'; + } + else { + endposition = this._getBlockPosition(event.item); + } + const inputEvent = { + 'event-type': 'parsons-input', + action: action, + position: [this._prevPosition, endposition], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + }, + }); + } + else { + this._dragSortable = new Sortable(this._dragArea, { + group: { + name: 'shared', + // pull: 'clone', + // put: false + }, + // sort: false, + direction: 'horizontal', + animation: 150, + draggable: '.parsons-block', + }); + this._dropSortable = new Sortable(this._dropArea, { + group: 'shared', + direction: 'horizontal', + animation: 150, + draggable: '.parsons-block', + onAdd: (event) => { + const inputEvent = { + 'event-type': 'parsons-input', + action: 'add', + position: [-1, this._getBlockPosition(event.item)], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + }, + onStart: (event) => { + this._prevPosition = this._getBlockPosition(event.item); + }, + onEnd: (event) => { + let endposition = this._getBlockPosition(event.item); + const action = endposition == -1 ? 'remove' : 'move'; + const inputEvent = { + 'event-type': 'parsons-input', + action: action, + position: [this._prevPosition, endposition], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + }, + }); + } }; updateTestStatus = (result) => { if (this._dropArea.classList.contains(result)) { @@ -15358,6 +15415,7 @@ class HParsonsElement extends HTMLElement { this.root.append(this.inputDiv); const reusable = this.getAttribute('reuse-blocks') ? true : false; this.hparsonsInput = new ParsonsInput(this, reusable); + // console.log(reusable) // a div wrapping the input and the test case status // init regex input based on the input type this._parsonsData = new Array(); diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 47671e9ec..adcf46ffe 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -149,7 +149,7 @@ export default class SQLHParons extends RunestoneBase { parsonsHTML += ` input-type='parsons' `; } if (this.reuse) { - parsonsHTML += ` reuse-blocks`; + parsonsHTML += ` reuse-blocks="true"`; } parsonsHTML += `>` this.outerDiv.innerHTML = parsonsHTML; From 21c9090f338004a80c020bacb1853e42d78a5524 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Sat, 19 Mar 2022 15:52:42 -0400 Subject: [PATCH 24/31] :ok_hand: Update node according to new pr --- runestone/hparsons/hparsons.py | 37 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index 5dd19265f..0c05ccca2 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -34,7 +34,7 @@ def setup(app): app.add_directive("hparsons", HParsonsDirective) - app.add_node(HParsonsNode, html=(visit_hp_node, depart_hp_node)) + app.add_node(HParsonsNode, html=(visit_hp_html, depart_hp_html)) TEMPLATE_START = """ @@ -60,35 +60,33 @@ def setup(app): class HParsonsNode(nodes.General, nodes.Element, RunestoneIdNode): - def __init__(self, options, **kwargs): - super(HParsonsNode, self).__init__(**kwargs) - self.runestone_options = options + pass # self for these functions is an instance of the writer class. For example # in html, self is sphinx.writers.html.SmartyPantsHTMLTranslator # The node that is passed as a parameter is an instance of our node class. -def visit_hp_node(self, node): +def visit_hp_html(self, node): - node.delimiter = "_start__{}_".format(node.runestone_options["divid"]) + node["delimiter"] = "_start__{}_".format(node["runestone_options"]["divid"]) - self.body.append(node.delimiter) + self.body.append(node["delimiter"]) - res = TEMPLATE_START % node.runestone_options + res = TEMPLATE_START % node["runestone_options"] self.body.append(res) -def depart_hp_node(self, node): - res = TEMPLATE_END % node.runestone_options +def depart_hp_html(self, node): + res = TEMPLATE_END % node["runestone_options"] self.body.append(res) addHTMLToDB( - node.runestone_options["divid"], - node.runestone_options["basecourse"], - "".join(self.body[self.body.index(node.delimiter) + 1 :]), + node["runestone_options"]["divid"], + node["runestone_options"]["basecourse"], + "".join(self.body[self.body.index(node["delimiter"]) + 1 :]), ) - self.body.remove(node.delimiter) + self.body.remove(node["delimiter"]) class HParsonsDirective(RunestoneIdDirective): @@ -207,13 +205,14 @@ def run(self): "This should only affect the grading interface. Everything else should be fine." ) - acnode = HParsonsNode(self.options, rawsource=self.block_text) - acnode.source, acnode.line = self.state_machine.get_source_and_line(self.lineno) - self.add_name(acnode) # make this divid available as a target for :ref: + hpnode = HParsonsNode() + hpnode["runestone_options"] = self.options + hpnode["source"], hpnode["line"] = self.state_machine.get_source_and_line(self.lineno) + self.add_name(hpnode) # make this divid available as a target for :ref: maybeAddToAssignment(self) if explain_text: self.updateContent() - self.state.nested_parse(explain_text, self.content_offset, acnode) + self.state.nested_parse(explain_text, self.content_offset, hpnode) - return [acnode] + return [hpnode] From 22492d237b54eb35bbd8444a24c5b2acc2fcbf13 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Sat, 19 Mar 2022 21:58:53 -0400 Subject: [PATCH 25/31] :sparkles: Allow radomizing blocks --- runestone/hparsons/css/hparsons.css | 40 +++ runestone/hparsons/hparsons.py | 8 + runestone/hparsons/js/horizontal-parsons.js | 26 +- runestone/hparsons/js/hparsons-sql.js | 23 +- runestone/hparsons/js/hparsons.js | 257 -------------------- runestone/hparsons/test/_sources/index.rst | 114 +-------- 6 files changed, 94 insertions(+), 374 deletions(-) delete mode 100644 runestone/hparsons/js/hparsons.js diff --git a/runestone/hparsons/css/hparsons.css b/runestone/hparsons/css/hparsons.css index fc8933fae..72ae40591 100755 --- a/runestone/hparsons/css/hparsons.css +++ b/runestone/hparsons/css/hparsons.css @@ -17,3 +17,43 @@ padding-top: 10px; margin: 5px; } + +.hp_actions { + text-align: center; +} + +.hp_output { + display: none; + max-width: 450px; + background-color: inherit; +} +.hp_output pre { + background-color: lightgray; +} + +.hp_sql_result { + background-color: lightgrey; + padding: 10px; + border-radius: 6px; + margin-bottom: 10px; +} + +.hp_sql_result_success { + background-color: transparent; + color: green; + border: 0px; + padding: 0px; + margin-top: 10px; + margin-bottom: 10px; + min-height: 0px !important; +} + +.hp_sql_result_failure { + background-color: transparent; + color: red; + border: 0px; + padding: 0px; + margin-top: 10px; + margin-bottom: 10px; + min-height: 0px !important; +} diff --git a/runestone/hparsons/hparsons.py b/runestone/hparsons/hparsons.py index 0c05ccca2..4aec7fc3e 100755 --- a/runestone/hparsons/hparsons.py +++ b/runestone/hparsons/hparsons.py @@ -51,6 +51,7 @@ def setup(app): %(dburl)s %(textentry)s %(reuse)s + %(randomize)s style="visibility: hidden;"> %(initialsetting)s @@ -95,6 +96,7 @@ class HParsonsDirective(RunestoneIdDirective): .. hparsons:: uniqueid :language: sql, regex :dburl: only for sql -- url to load database + :randomize: randomize the order of horizontal parsons TODO: fix textentry :reuse: only for parsons -- make the blocks reusable :textentry: if you will use text entry instead of horizontal parsons @@ -124,6 +126,7 @@ class HParsonsDirective(RunestoneIdDirective): "language": directives.unchanged, "textentry": directives.flag, "reuse": directives.flag, + "randomize": directives.flag, } ) @@ -142,6 +145,11 @@ def run(self): else: self.options['reuse'] = '' + if "randomize" in self.options: + self.options['randomize'] = ' data-randomize="true"' + else: + self.options['randomize'] = '' + explain_text = None if self.content: if "~~~~" in self.content: diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index d0dced992..c10cb5efd 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3044,7 +3044,8 @@ class ParsonsInput { parentElement; _prevPosition; reusable; - constructor(parentElement, reusable) { + randomize; + constructor(parentElement, reusable, randomize) { this.el = document.createElement('div'); this.parentElement = parentElement; this.el.id = 'regextool-' + this.parentElement.toolNumber + '-parsons-input'; @@ -3066,6 +3067,7 @@ class ParsonsInput { this.expandableBlocks = []; this.expandableBlockTooltips = null; this.reusable = reusable; + this.randomize = randomize; this._dragSortable = new Sortable(this._dragArea, { group: 'shared', direction: 'horizontal', @@ -3093,6 +3095,15 @@ class ParsonsInput { return ret; } }; + // Durstenfeld shuffle + shuffleArray(array) { + for (let i = array.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * (i + 1)); + let temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + } setSourceBlocks = (data, tooltips) => { // reset // this._dragSortable.destroy(); @@ -3101,6 +3112,13 @@ class ParsonsInput { this._dragArea.innerHTML = ''; this._dropArea.innerHTML = ''; // adding normal blocks + if (this.randomize) { + let originalData = JSON.stringify(data); + this.shuffleArray(data); + while (JSON.stringify(data) == originalData) { + this.shuffleArray(data); + } + } for (let i = 0; i < data.length; ++i) { const newBlock = document.createElement('div'); this._dragArea.appendChild(newBlock); @@ -15414,7 +15432,8 @@ class HParsonsElement extends HTMLElement { this.inputDiv.classList.add('hparsons-input'); this.root.append(this.inputDiv); const reusable = this.getAttribute('reuse-blocks') ? true : false; - this.hparsonsInput = new ParsonsInput(this, reusable); + const randomize = this.getAttribute('randomize') ? true : false; + this.hparsonsInput = new ParsonsInput(this, reusable, randomize); // console.log(reusable) // a div wrapping the input and the test case status // init regex input based on the input type @@ -15500,7 +15519,8 @@ class HParsonsElement extends HTMLElement { if (this.inputType == 'parsons') { // init elements: parsons regex input const reusable = this.getAttribute('reuse-blocks') != null ? true : false; - this.hparsonsInput = new ParsonsInput(this, reusable); + const randomize = this.getAttribute('randomize') != null ? true : false; + this.hparsonsInput = new ParsonsInput(this, reusable, randomize); this.inputDiv.appendChild(this.hparsonsInput.el); } else { diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index adcf46ffe..69a35ff58 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -3,6 +3,7 @@ import Handsontable from "handsontable"; import initSqlJs from "sql.js/dist/sql-wasm.js"; import RunestoneBase from "../../common/js/runestonebase.js"; import "../css/hparsons.css"; +import 'handsontable/dist/handsontable.full.css'; var allDburls = {}; @@ -21,6 +22,7 @@ export default class SQLHParons extends RunestoneBase { var orig = $(opts.orig).find("textarea")[0]; this.textentry = $(orig).data('textentry') ? true : false; this.reuse = $(orig).data('reuse') ? true : false; + this.randomize = $(orig).data('randomize') ? true : false; this.divid = opts.orig.id; this.containerDiv = opts.orig; this.useRunestoneServices = opts.useRunestoneServices; @@ -51,7 +53,7 @@ export default class SQLHParons extends RunestoneBase { if ($(orig).data("caption")) { this.caption = $(orig).data("caption"); } else { - this.caption = "ActiveCode"; + this.caption = "HorizontalParsons"; } this.addCaption("runestone"); this.indicate_component_ready(); @@ -151,6 +153,9 @@ export default class SQLHParons extends RunestoneBase { if (this.reuse) { parsonsHTML += ` reuse-blocks="true"`; } + if (this.randomize) { + parsonsHTML += ` randomize="true"`; + } parsonsHTML += `>` this.outerDiv.innerHTML = parsonsHTML; this.outerDiv.addEventListener('horizontal-parsons', (ev) => {this.logHorizontalParsonsEvent(ev.detail)}) @@ -172,7 +177,7 @@ export default class SQLHParons extends RunestoneBase { // Create a parent div with two elements: pre for standard output and a div // to hold turtle graphics output. We use a div in case the turtle changes from var outDiv = document.createElement("div"); - $(outDiv).addClass("ac_output col-md-12"); + $(outDiv).addClass("hp_output col-md-12"); this.outDiv = outDiv; this.output = document.createElement("pre"); this.output.id = this.divid + "_stdout"; @@ -191,11 +196,11 @@ export default class SQLHParons extends RunestoneBase { createControls() { var ctrlDiv = document.createElement("div"); var butt; - $(ctrlDiv).addClass("ac_actions"); + $(ctrlDiv).addClass("hp_actions"); $(ctrlDiv).addClass("col-md-12"); // Run butt = document.createElement("button"); - $(butt).text($.i18n("msg_activecode_run_code")); + $(butt).text("Run"); $(butt).addClass("btn btn-success run-button"); ctrlDiv.appendChild(butt); this.runButton = butt; @@ -300,7 +305,7 @@ export default class SQLHParons extends RunestoneBase { let resultArray = this.results; for (let r of resultArray) { let section = document.createElement("div"); - section.setAttribute("class", "ac_sql_result"); + section.setAttribute("class", "hp_sql_result"); respDiv.appendChild(section); if (r.status === "success") { if (r.columns) { @@ -317,7 +322,7 @@ export default class SQLHParons extends RunestoneBase { } msg = msg + "."; messageBox.textContent = msg; - messageBox.setAttribute("class", "ac_sql_result_success"); + messageBox.setAttribute("class", "hp_sql_result_success"); section.appendChild(messageBox); } else if (r.rowcount) { let messageBox = document.createElement("pre"); @@ -325,18 +330,18 @@ export default class SQLHParons extends RunestoneBase { op = op + (op.charAt(op.length - 1) === "e" ? "d." : "ed."); let rmsg = r.rowcount !== 1 ? " rows " : " row "; messageBox.textContent = "" + r.rowcount + rmsg + op; - messageBox.setAttribute("class", "ac_sql_result_success"); + messageBox.setAttribute("class", "hp_sql_result_success"); section.appendChild(messageBox); } else { let messageBox = document.createElement("pre"); messageBox.textContent = "Operation succeeded."; - messageBox.setAttribute("class", "ac_sql_result_success"); + messageBox.setAttribute("class", "hp_sql_result_success"); section.appendChild(messageBox); } } else { let messageBox = document.createElement("pre"); messageBox.textContent = r.message; - messageBox.setAttribute("class", "ac_sql_result_failure"); + messageBox.setAttribute("class", "hp_sql_result_failure"); section.appendChild(messageBox); } } diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js deleted file mode 100644 index 388b6e428..000000000 --- a/runestone/hparsons/js/hparsons.js +++ /dev/null @@ -1,257 +0,0 @@ -/*========================================== -========= Master hanswers.js ========= -============================================ -=== This file contains the JS for === -=== the Runestone hparsons component. === -============================================ -=== Created by === -=== Zihan Wu === -=== 2022 === -==========================================*/ - -import RunestoneBase from "../../common/js/runestonebase.js"; -import "./../css/hparsons.css"; -import "./regex-element.js"; -import "../../activecode/js/skulpt.min.js"; -import "../../activecode/js/skulpt-stdlib.js"; - -export var hpList; -// Dictionary that contains all instances of horizontal Parsons problem objects -if (hpList === undefined) hpList = {}; - -export default class HParsons extends RunestoneBase { - constructor(opts) { - super(opts); - var orig = opts.orig; - this.containerDiv = orig; - // the div element that will contain regex-element - this.origElem = $(orig).find(".hparsons")[0]; - this.useRunestoneServices = - opts.useRunestoneServices || eBookConfig.useRunestoneServices; - this.divid = orig.id; - - - // Set the storageId (key for storing data) - var storageId = super.localStorageKey(); - this.storageId = storageId; - // this.setLocalStorage({test: 'test1'}) - this.children = this.origElem.childNodes; // this contains all of the child elements of the entire tag... - this.contentArray = []; - HParsons.counter++; // Unique identifier - this.counterId = "hparsons-" + HParsons.counter; - - // The element that is going to be replaced - // Find the question text and store it in .question - this.renderHTML(); - this.origElem.addEventListener('regex-element', (e) => {this.handleRegexElementEvent(e)}); - this.caption = "hparsons"; - this.addCaption("runestone"); - this.checkServer("hparsons", true); - } - - renderHTML() { - // const div = document.createElement('regex-element'); - // div.setAttribute('input-type', 'parsons') - // div.id = 'abcd'; - // console.log(this.origElem) - let attributes = ''; - let settings = JSON.parse($(this.origElem).children()[0].innerText) - attributes += ' input-type=' + ($(this.origElem).data("textentry") ? 'text' : 'parsons' ); - attributes += $(this.origElem).data("hidetests") ? ' hidetests="true"': ''; - $(this.origElem).html(''); - let regexElement = $(this.origElem).children()[0]; - this.regexElement = regexElement; - if ($(this.origElem).data("nostrictmatch")) { - regexElement.unitTestTable.strictMatch = false; - } else { - regexElement.unitTestTable.strictMatch = true; - } - if ($(this.origElem).data("hidetests")) { - regexElement.hidetests = false; - } else { - regexElement.unitTestTable.strictMatch = true; - } - - if (settings.blocks) { - regexElement.parsonsData = settings.blocks; - } - if (settings.explanations) { - regexElement.parsonsExplanation = settings.explanations; - } - if (settings.positivetest) { - regexElement.setPositiveInitialTestString(settings.positivetest); - } - if (settings.negativetest) { - regexElement.setNegativeInitialTestString(settings.negativetest); - } - if (settings.testcases) { - for (let index in settings.testcases) { - settings.testcases[index].expect = settings.testcases[index].expect.length < 4 ? [] : settings.testcases[index].expect.slice(2, -2).split(', '); - } - regexElement.setTestCases(settings.testcases); - } - - - // tool.parsonsExplanation = toolConfig.parsonsExplanation; - // tool.parsonsData = toolConfig.parsonsData; - regexElement.resetTool(); - // tool.setTestCases(toolConfig.testCases); - // tool.setPositiveInitialTestString(toolConfig.positiveInitialTestString); - // tool.setNegativeInitialTestString(toolConfig.negativeInitialTestString); - - // console.log(this.origElem) - // $(this.origElem).replaceWith(document.createElement('regex-element')); - // $(this.elem).innerHTML = ``; - // console.log(div) - // localStorage.setItem(this.storageId, JSON.stringify({test: 'test'})); - } - - checkCurrentAnswer() { } - - async logCurrentAnswer(sid) { - console.log('hparsons, logcurrentanswer') - // let value = $(document.getElementById(this.divid + "_solution")).val(); - // this.renderMath(value); - // this.setLocalStorage({ - // answer: value, - // timestamp: new Date(), - // }); - // let data = { - // event: "shortanswer", - // act: value, - // answer: value, - // div_id: this.divid, - // }; - // if (typeof sid !== "undefined") { - // data.sid = sid; - // } - // await this.logBookEvent(data); - } - - renderFeedback() { - // console.log('hparsons, renderfeedback') - // this.feedbackDiv.innerHTML = "Your answer has been saved."; - // $(this.feedbackDiv).removeClass("alert-danger"); - // $(this.feedbackDiv).addClass("alert alert-success"); - } - setLocalStorage(data) { - localStorage.setItem(this.storageId, JSON.stringify(data)); - } - checkLocalStorage() { - // Return what is stored in local storage - var data = localStorage.getItem(this.storageId); - if (data !== null) { - if (data.charAt(0) == "{") { - data = JSON.parse(data); - } else { - data = {}; - } - } else { - data = {}; - } - if (data.type != undefined && data.answer != undefined) { - this.regexElement.restoreAnswer(data.type, data.answer) - } - } - // called when server has data - restoreAnswers(serverData) { - console.log('hparsons, restoreanswers') - // Restore answers from storage retrieval done in RunestoneBase - // sometimes data.answer can be null - // if (!data.answer) { - // data.answer = ""; - // } - // this.answer = data.answer; - // this.jTextArea.value = this.answer; - // this.renderMath(this.answer); - - // let p = document.createElement("p"); - // this.jInputDiv.appendChild(p); - // var tsString = ""; - // if (data.timestamp) { - // tsString = new Date(data.timestamp).toLocaleString(); - // } else { - // tsString = ""; - // } - // $(p).text(tsString); - // if (data.last_answer) { - // this.current_answer = "ontime"; - // let toggle_answer_button = document.createElement("button"); - // toggle_answer_button.type = "button"; - // $(toggle_answer_button).text("Show Late Answer"); - // $(toggle_answer_button).addClass("btn btn-warning"); - // $(toggle_answer_button).css("margin-left", "5px"); - - // $(toggle_answer_button).click( - // function () { - // var display_timestamp, button_text; - // if (this.current_answer === "ontime") { - // this.jTextArea.value = data.last_answer; - // this.answer = data.last_answer; - // display_timestamp = new Date( - // data.last_timestamp - // ).toLocaleString(); - // button_text = "Show on-Time Answer"; - // this.current_answer = "late"; - // } else { - // this.jTextArea.value = data.answer; - // this.answer = data.answer; - // display_timestamp = tsString; - // button_text = "Show Late Answer"; - // this.current_answer = "ontime"; - // } - // this.renderMath(this.answer); - // $(p).text(`Submitted: ${display_timestamp}`); - // $(toggle_answer_button).text(button_text); - // }.bind(this) - // ); - - // this.buttonDiv.appendChild(toggle_answer_button); - // } - // let feedbackStr = "Your current saved answer is shown above."; - // if (typeof data.score !== "undefined") { - // feedbackStr = `Score: ${data.score}`; - // } - // if (data.comment) { - // feedbackStr += ` -- ${data.comment}`; - // } - // this.feedbackDiv.innerHTML = feedbackStr; - - // $(this.feedbackDiv).removeClass("alert-danger"); - // $(this.feedbackDiv).addClass("alert alert-success"); - } - - disableInteraction() { - console.log('hparsons, disableinteraction') - // this.jTextArea.disabled = true; - } - - handleRegexElementEvent(event) { - if (event.detail['event-type'] == 'parsons-input' || event.detail['event-type'] == 'text-input') { - this.setLocalStorage({'type': event.detail['event-type'].slice(0, -6), 'answer': event.detail['answer']}) - // this.setLocalStorage({'event-type': event.detail['event-type'], 'answer': event.detail['answer'][0]}) - // this.setLocalStorage({tes: 'tes'}) - } - } -} -HParsons.counter = 0; -/*================================= -== Find the custom HTML tags and == -== execute our code on them == -=================================*/ -$(document).bind("runestone:login-complete", function () { - $("[data-component=hparsons]").each(function () { - if ($(this).closest("[data-component=timedAssessment]").length == 0) { - // If this element exists within a timed component, don't render it here - // try { - hpList[this.id] = new HParsons({ - orig: this, - useRunestoneServices: eBookConfig.useRunestoneServices, - }); - // } catch (err) { - // console.log(`Error rendering ShortAnswer Problem ${this.id} - // Details: ${err}`); - // } - } - }); -}); diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index 00226c0ca..5d036c40e 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -4,15 +4,16 @@ Horizontal Parsons Test .. Testing horizontal Parsons problems. -Horizontal Parsons + SQL aaa +Horizontal Parsons + SQL -------------------------------------- .. hparsons:: test_activecode_6 :language: sql :dburl: /_static/test.db - :reuse: + :randomize: - this is a cute horizontal parsons problem! + This is a horizontal Parsons problem! Feedback is based on code execution. + The blocks are randomized, but cannot be reused ;) ~~~~ --blocks-- select @@ -30,9 +31,13 @@ Horizontal Parsons + SQL aaa .. hparsons:: teasfasfas :language: sql :dburl: /_static/test.db + :reuse: - this is a cute horizontal parsons problem! + This is a horizontal parsons problem! Feedback is base on code execution. + The blocks are set as the original order, and can be used multiple times. + To delete a block, simply drag out of the input area. + These features might not be so useful in the context of SQL, but might be useful in regex. ~~~~ --blocks-- select @@ -47,105 +52,4 @@ Horizontal Parsons + SQL aaa .. :dburl: http://localhost:8000/_static/test.db -.. activecode:: abcde - :language: sql - :autograde: unittest - :dburl: /_static/test.db - - select * from test; - - ===== - assert 1,1 == world - assert 0,1 == hello - assert 2,1 == 42 - -.. :dburl: http://localhost:8000/_static/test.db - - -.. hparsons:: test_activecode_7 - :language: sql - :autograde: unittest - :dburl: /_static/test.db - - select * from test; - -.. hparsons:: test_activecode_6b - :language: sql - :autograde: unittest - :dburl: /_static/test.db - - select * from created_table; - - ===== - assert 0,0 == itworks - -.. hparsons:: sql3 - :language: sql - - CREATE TABLE contacts ( - contact_id INTEGER PRIMARY KEY, - first_name TEXT NOT NULL, - last_name TEXT NOT NULL, - email TEXT NOT NULL UNIQUE, - phone TEXT NOT NULL UNIQUE - ); - - - -Other Problems for reference ------------------------------ - -.. parsonsprob:: test_parsons_1 - :adaptive: - :order: 0 1 2 3 4 - - need some text ? - ----- - def fib(num): - ===== - if num == 0: - return 0: - ===== - if num == 1: - return 1: - ===== - return fib(num - 1) + fib(num - 2) - ===== - return fib(num - 1) * fib(num - 2) #paired - -Multiple Choice ---------------- - -.. mchoice:: question1_2 - :multiple_answers: - :correct: a,b,d - :answer_a: red - :answer_b: yellow - :answer_c: black - :answer_d: green - :feedback_a: Red is a definitely on of the colors. - :feedback_b: Yes, yellow is correct. - :feedback_c: Remember the acronym...ROY G BIV. B stands for blue. - :feedback_d: Yes, green is one of the colors. - - Which colors might be found in a rainbow? (choose all that are correct) - -These are just two of the many interactive components for writing online course materials. You can see examples of all of them `On our Example Page `_ - -Now feel free to modify this file to start creating your own interactive page. - - -Section 4: Theme -::::::::::::::::::: - -You can add your own CSS or JS files to every page of a book by modifying ``setup.custom_css_files`` or ``setup.custom_js_files`` in conf.py. - -If you want to do more significant changes to the theme, you should copy the files you wish to modify from -the runestone/common/project/template/sphinx_bootstrap to a directory like ``_templates/``. Then make sure -the ``templates_path`` points to them in your conf.py. - -conf.py: - -.. code:: - templates_path = ["_templates"] \ No newline at end of file From 97e8ddbd52a8fad277aaab6b4989472687fccd64 Mon Sep 17 00:00:00 2001 From: Zihan Wu Date: Mon, 21 Mar 2022 10:52:18 -0400 Subject: [PATCH 26/31] :sparkles: Allow click to add to a new block --- runestone/hparsons/js/horizontal-parsons.js | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index c10cb5efd..631fb2255 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3132,6 +3132,28 @@ class ParsonsInput { } newBlock.style.display = 'inline-block'; newBlock.classList.add('parsons-block'); + newBlock.onclick = () => { + // adding the block to the input area + if (newBlock.parentElement == this._dragArea) { + let endPosition; + if (this.reusable) { + const newBlockCopy = newBlock.cloneNode(true); + this._dropArea.appendChild(newBlockCopy); + endPosition = this._getBlockPosition(newBlockCopy); + } + else { + this._dropArea.appendChild(newBlock); + endPosition = this._getBlockPosition(newBlock); + } + const inputEvent = { + 'event-type': 'parsons-input', + action: 'add', + position: [-1, endPosition], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + } + }; } this._initSortable(); }; @@ -3311,6 +3333,7 @@ class ParsonsInput { } } }; + // TODO: not used for now, not sure if is working correctly restoreAnswer(type, answer) { if (type != 'parsons' || !Array.isArray(answer)) { return; From 9211c6e48838f143d48c7ff882213ca7c3125f04 Mon Sep 17 00:00:00 2001 From: Zihan Wu Date: Mon, 21 Mar 2022 11:18:39 -0400 Subject: [PATCH 27/31] :sparkles: Allow click to remove blocks --- runestone/hparsons/js/horizontal-parsons.js | 67 ++++++++++++++------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index 631fb2255..1406c5705 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3050,14 +3050,14 @@ class ParsonsInput { this.parentElement = parentElement; this.el.id = 'regextool-' + this.parentElement.toolNumber + '-parsons-input'; const dragTip = document.createElement('div'); - dragTip.innerText = 'Drag from the blocks below to form your code'; + dragTip.innerText = 'Drag or click the blocks below to form your code:'; dragTip.classList.add('hparsons-tip'); this.el.append(dragTip); this._dragArea = document.createElement('div'); this.el.appendChild(this._dragArea); this._dragArea.classList.add('drag-area'); const dropTip = document.createElement('div'); - dropTip.innerText = 'Your code:'; + dropTip.innerText = 'Your code (click on a block to remove it):'; dropTip.classList.add('hparsons-tip'); this.el.append(dropTip); this._dropArea = document.createElement('div'); @@ -3133,30 +3133,49 @@ class ParsonsInput { newBlock.style.display = 'inline-block'; newBlock.classList.add('parsons-block'); newBlock.onclick = () => { - // adding the block to the input area - if (newBlock.parentElement == this._dragArea) { - let endPosition; - if (this.reusable) { - const newBlockCopy = newBlock.cloneNode(true); - this._dropArea.appendChild(newBlockCopy); - endPosition = this._getBlockPosition(newBlockCopy); - } - else { - this._dropArea.appendChild(newBlock); - endPosition = this._getBlockPosition(newBlock); - } - const inputEvent = { - 'event-type': 'parsons-input', - action: 'add', - position: [-1, endPosition], - answer: this._getTextArray(), - }; - this.parentElement.logEvent(inputEvent); - } + this._onBlockClicked(newBlock); }; } this._initSortable(); }; + _onBlockClicked = (block) => { + if (block.parentElement == this._dragArea) { + let endPosition; + if (this.reusable) { + const blockCopy = block.cloneNode(true); + blockCopy.onclick = () => this._onBlockClicked(blockCopy); + this._dropArea.appendChild(blockCopy); + endPosition = this._getBlockPosition(blockCopy); + } + else { + this._dropArea.appendChild(block); + endPosition = this._getBlockPosition(block); + } + const inputEvent = { + 'event-type': 'parsons-input', + action: 'add', + position: [-1, endPosition], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + } + else { + const startPosition = this._getBlockPosition(block); + if (this.reusable) { + this._dropArea.removeChild(block); + } + else { + this._dragArea.appendChild(block); + } + const inputEvent = { + 'event-type': 'parsons-input', + action: 'remove', + position: [startPosition, -1], + answer: this._getTextArray(), + }; + this.parentElement.logEvent(inputEvent); + } + }; _initSortable = () => { this._dragSortable.destroy(); this._dropSortable.destroy(); @@ -3171,6 +3190,10 @@ class ParsonsInput { direction: 'horizontal', animation: 150, draggable: '.parsons-block', + onClone: (event) => { + const newBlock = event.clone; + newBlock.onclick = () => this._onBlockClicked(newBlock); + } }); this._dropSortable = new Sortable(this._dropArea, { group: 'shared', From c7dda64f86070b67837fa1b796ddf9e0cf2ac585 Mon Sep 17 00:00:00 2001 From: Zihan Wu Date: Mon, 21 Mar 2022 11:49:36 -0400 Subject: [PATCH 28/31] :sparkles: Allow resetting the input --- runestone/hparsons/js/horizontal-parsons.js | 77 ++++++++++++--------- runestone/hparsons/js/hparsons-sql.js | 10 +++ 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/runestone/hparsons/js/horizontal-parsons.js b/runestone/hparsons/js/horizontal-parsons.js index 1406c5705..fa4d703a7 100644 --- a/runestone/hparsons/js/horizontal-parsons.js +++ b/runestone/hparsons/js/horizontal-parsons.js @@ -3034,9 +3034,6 @@ var RegexEvent; class ParsonsInput { // The input element el; - // TODO(refactor): make expandable blocks more easy to use - expandableBlocks; - expandableBlockTooltips; _dropArea; _dragArea; _dropSortable; @@ -3045,6 +3042,10 @@ class ParsonsInput { _prevPosition; reusable; randomize; + storedSourceBlocks; + storedSourceBlockExplanations; + // if the input has been initialized once + initialized; constructor(parentElement, reusable, randomize) { this.el = document.createElement('div'); this.parentElement = parentElement; @@ -3064,21 +3065,14 @@ class ParsonsInput { this.el.appendChild(this._dropArea); this._dropArea.classList.add('drop-area'); this._prevPosition = -1; - this.expandableBlocks = []; - this.expandableBlockTooltips = null; + this.storedSourceBlocks = []; + this.storedSourceBlockExplanations = null; this.reusable = reusable; this.randomize = randomize; - this._dragSortable = new Sortable(this._dragArea, { - group: 'shared', - direction: 'horizontal', - animation: 150 - }); - this._dropSortable = new Sortable(this._dropArea, { - group: 'shared', - direction: 'horizontal', - animation: 150 - }); + this._dragSortable = null; + this._dropSortable = null; this._initSortable(); + this.initialized = false; } getText = () => { let ret = ''; @@ -3105,13 +3099,7 @@ class ParsonsInput { } } setSourceBlocks = (data, tooltips) => { - // reset - // this._dragSortable.destroy(); - // this._dropSortable.destroy(); - // clearing previous settings - this._dragArea.innerHTML = ''; - this._dropArea.innerHTML = ''; - // adding normal blocks + // shuffle source blocks if randomize if (this.randomize) { let originalData = JSON.stringify(data); this.shuffleArray(data); @@ -3119,16 +3107,30 @@ class ParsonsInput { this.shuffleArray(data); } } - for (let i = 0; i < data.length; ++i) { + this.storedSourceBlocks = data; + this.storedSourceBlockExplanations = tooltips; + this._resetInput(); + }; + // TODO: not efficient enough. should not need to create new elements; simply sorting them should be good. + _resetInput = () => { + // clearing previous blocks + while (this._dragArea.firstChild) { + this._dragArea.removeChild(this._dragArea.firstChild); + } + while (this._dropArea.firstChild) { + this._dropArea.removeChild(this._dropArea.firstChild); + } + // add new blocks + for (let i = 0; i < this.storedSourceBlocks.length; ++i) { const newBlock = document.createElement('div'); this._dragArea.appendChild(newBlock); - if (data[i] === ' ') { + if (this.storedSourceBlocks[i] === ' ') { // console.log('here'); newBlock.innerHTML = ' '; } else { // console.log(data[i]); - newBlock.innerText = data[i]; + newBlock.innerText = this.storedSourceBlocks[i]; } newBlock.style.display = 'inline-block'; newBlock.classList.add('parsons-block'); @@ -3136,7 +3138,6 @@ class ParsonsInput { this._onBlockClicked(newBlock); }; } - this._initSortable(); }; _onBlockClicked = (block) => { if (block.parentElement == this._dragArea) { @@ -3177,8 +3178,6 @@ class ParsonsInput { } }; _initSortable = () => { - this._dragSortable.destroy(); - this._dropSortable.destroy(); if (this.reusable) { this._dragSortable = new Sortable(this._dragArea, { group: { @@ -3296,9 +3295,6 @@ class ParsonsInput { } this._dropArea.classList.add(result); }; - setExpandableBlocks = (expandableBlocks) => { - this.expandableBlocks = expandableBlocks; - }; _getBlockPosition = (block) => { let position = -1; const parent = this._dropArea; @@ -3356,6 +3352,16 @@ class ParsonsInput { } } }; + resetInput = () => { + if (this.reusable) { + while (this._dropArea.firstChild) { + this._dropArea.removeChild(this._dropArea.firstChild); + } + } + else { + this._resetInput(); + } + }; // TODO: not used for now, not sure if is working correctly restoreAnswer(type, answer) { if (type != 'parsons' || !Array.isArray(answer)) { @@ -15593,11 +15599,18 @@ class HParsonsElement extends HTMLElement { }); } } - resetTool() { + resetInput() { if (this.inputType != 'parsons') { const regexInput = this.hparsonsInput; regexInput.quill?.setText('', 'silent'); } + else if (this.inputType == 'parsons') { + this.hparsonsInput.resetInput(); + } + const resetEvent = { + 'event-type': 'reset', + }; + this.logEvent(resetEvent); } // restore student answer from outside storage restoreAnswer(type, answer) { diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 69a35ff58..4853a18dc 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -207,6 +207,16 @@ export default class SQLHParons extends RunestoneBase { this.runButton.onclick = this.runButtonHandler.bind(this); $(butt).attr("type", "button"); + // Reset button + var resetBtn; + resetBtn = document.createElement("button"); + $(resetBtn).text("Reset"); + $(resetBtn).addClass("btn btn-warning run-button"); + ctrlDiv.appendChild(resetBtn); + this.resetButton = resetBtn; + this.resetButton.onclick = () => this.hparsons.resetInput(); + $(resetBtn).attr("type", "button"); + // TODO: maybe remove the question part $(this.outerDiv).prepend(ctrlDiv); this.controlDiv = ctrlDiv; From ace790943ddf979041ae1ae8e8dcaba698184404 Mon Sep 17 00:00:00 2001 From: amy21206 Date: Wed, 23 Mar 2022 17:24:36 -0400 Subject: [PATCH 29/31] :sparkles: Allow checking for no data in unittest. --- runestone/hparsons/js/hparsons-sql.js | 10 +- .../hparsons/test/_sources/_static/hptest.db | Bin 0 -> 16384 bytes .../test/_sources/database_hparsons.rst | 197 ++++++++++++++++++ runestone/hparsons/test/_sources/index.rst | 5 +- 4 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 runestone/hparsons/test/_sources/_static/hptest.db create mode 100644 runestone/hparsons/test/_sources/database_hparsons.rst diff --git a/runestone/hparsons/js/hparsons-sql.js b/runestone/hparsons/js/hparsons-sql.js index 4853a18dc..b55651b91 100644 --- a/runestone/hparsons/js/hparsons-sql.js +++ b/runestone/hparsons/js/hparsons-sql.js @@ -476,8 +476,14 @@ export default class SQLHParons extends RunestoneBase { try { actual = result_table.values[row][col]; } catch (e) { - output = `Failed Not enough data to check row ${row} or column ${col}`; - return output; + if (expected == 'NO_DATA') { + this.passed++; + output = `Passed: No data in row ${row}, column ${col}`; + return output; + } else { + output = `Failed: Not enough data to check row ${row} or column ${col}`; + return output; + } } const operators = { "==": function (operand1, operand2) { diff --git a/runestone/hparsons/test/_sources/_static/hptest.db b/runestone/hparsons/test/_sources/_static/hptest.db new file mode 100644 index 0000000000000000000000000000000000000000..1425dbd5a328006f1d19759b6d1525b2e8524e7f GIT binary patch literal 16384 zcmeI2Pj4GV6u@WWP2;TNI1r)`MTHk5K^z>XAyps^E_K#hYU;#^*P&D)3Tu0uY_fmc z-Ep08i$MJfNO0gYaOA+3;Mzk!01_t<2T;XZuS4uK?WsK=y_Gz>v+vEDH$TlA&6~Bp zRWt>MhU?jefF(A@B#GSuV2s7^C*$wz3Z8rG(wX1|_ZZ9QU&RyOvB>CCmiTe>Y2xq& z+9VMT1Ii zt*AjLfmyQ-g^h~#saA%~a$!AJz7Kb``)P0tn?ptWq5|6+g{^HZ4IUSs$sh9n8l>Kj zT$%i2N@Au{=g0m73)AjBqb1x|4Xh^BvIVIfRbT*opUo~gO^Lb0G~2<@#AnN5DE9~d52J#OG< z#Ad6C=Fvsx2=Z(1#$+~=)y=PYNc6Vh3-D{6Ygu5MmSwt5;D=vS2HDK%5Dpczx+z>Q zNQ zUF`Q*fX$s&^7@d>77Nd+m73h~!8V)|FzPr+9J^sb1F5wD>V^}p*KL{ifQ&+?upg7((?O--Gu<7A7$<5$->njuGB9u=~-w{c%gZZFl@gp zV+8E07y+VK*wE&?=pDoztQkHY7^KqRn@;c$0;_Y|UCl&fHm!G4_Omb(VRqk%Fuij= zQ0j!7>e?R-$n5RfUDLy;fsM>ri*~!631W@cO{de$bozp|<|+|+GAgMC#+*DfAdg3f z*W9Wi$& Date: Fri, 25 Mar 2022 12:15:23 -0400 Subject: [PATCH 30/31] :white_check_mark: Adding unittest for horizontal parsons problems --- .../test/_sources/database_hparsons.rst | 197 ------------------ runestone/hparsons/test/_sources/index.rst | 37 ++-- runestone/hparsons/test/test_hparsons.py | 78 +++++++ 3 files changed, 92 insertions(+), 220 deletions(-) delete mode 100644 runestone/hparsons/test/_sources/database_hparsons.rst create mode 100644 runestone/hparsons/test/test_hparsons.py diff --git a/runestone/hparsons/test/_sources/database_hparsons.rst b/runestone/hparsons/test/_sources/database_hparsons.rst deleted file mode 100644 index 249eef2a6..000000000 --- a/runestone/hparsons/test/_sources/database_hparsons.rst +++ /dev/null @@ -1,197 +0,0 @@ -More Database Practice ------------------------- -.. parsonsprob:: db_create_and_insert_new_hptest - :adaptive: - :numbered: left - :order: 10, 5, 9, 0, 11, 6, 3, 4, 1, 7, 8, 2 - - Put the code blocks in order to import the needed module, create the connection, create the cursor, create the table if it does not exist, insert data into the table if it isn't already there, commit the changes, and then select the data and print it. Finally close the cursor. - ----- - import sqlite3 - ===== - conn = sqlite3.connect('music.sqlite') - ===== - cur = conn.cursor() - ===== - # create the table with a key and unique title - cur.execute('CREATE TABLE IF NOT EXISTS Tracks - (id INTEGER PRIMARY KEY, title TEXT UNIQUE, - plays INTEGER)') - ===== - # create the table with a key and unique title - cur.execute('CREATE TABLE Tracks - (id INTEGER PRIMARY KEY, title TEXT UNIQUE, - plays INTEGER)') #paired - ===== - # insert data into the Tracks table - cur.execute('INSERT OR IGNORE INTO Tracks - (title, plays) VALUES (?, ?)', - ('Thunderstruck', 20)) - cur.execute('INSERT OR IGNORE INTO Tracks - (title, plays) VALUES (?, ?)', - ('My Way', 15)) - ===== - # commit the changes - conn.commit() - ===== - print('Tracks:') - cur.execute('SELECT title, plays FROM Tracks') - ===== - print('Tracks:') - conn.execute('SELECT title, plays FROM Tracks') #paired - ===== - for row in cur: - ===== - print(row) - ===== - # close the cursor - cur.close() - - -.. fillintheblank:: db_create_table_if_not_exists_hptest - :casei: - - Fill in the three blanks required to create the table only if it doesn't exist already - - .. code-block:: python - - cur.execute(''' - CREATE TABLE [_____][_____][_____] Twitter - (name TEXT, retrieved INTEGER, friends INTEGER) - ''') - - - :IF: Correct! - :.*: Try again! - - :NOT: Correct! - :.*: Try again! - - :EXISTS: Correct! - :.*: Try again! - -.. hparsons:: db_limit_one_hptest - :language: sql - :dburl: /_static/hptest.db - :randomize: - - Arrange the blocks to select names from the table Twitter, and return just one row. - - ~~~~ - --blocks-- - SELECT - name - FROM - Twitter - LIMIT - 1 - WHERE - 2 - --unittest-- - assert 0,0 == Alice - assert 1,0 == NO_DATA - - -.. fillintheblank:: db_update_and_set_hptest - :casei: - - Fill in the two blanks to specify that you want to modify the row where name = acct and want to change the value of retrieved to 1. - - .. code-block:: python - - cur.execute('[_____] Twitter [_____] retrieved=1 WHERE name = ?', (acct, )) - - - :UPDATE: Correct! - :.*: Try again! - - :SET: Correct! - :.*: Try again! - -.. fillintheblank:: db_insert_into_twitter_fitb_hptest - :casei: - - Fill in the two blanks to add a new row of data to the Twitter table. - - .. code-block:: python - - cur.execute(''' - [_____] [_____] Twitter (name, retrieved, friends) - VALUES (?, 0, 1)''', (screenName, )) - - - :INSERT: Correct! - :.*: Try again! - - :INTO: Correct! - :.*: Try again! - - -.. fillintheblank:: db_id_primary_key_fitb_hptest - :casei: - - Fill in the two blanks to make id a primary key (each row will have a unique value for id). - - .. code-block:: python - - cur.execute(''' - CREATE TABLE People - (id INTEGER [_____] [_____], name TEXT UNIQUE, retrieved INTEGER) - ''') - - - :PRIMARY: Correct! - :.*: Try again! - - :KEY: Correct! - :.*: Try again! - - -.. fillintheblank:: db_id_insert_or_ignore_hptest - :casei: - - Fill in the two blanks to only execute the following insert if it does not violate any constraints, such as not allowing duplicate data. - - .. code-block:: python - - cur.execute(''' - INSERT [_____] [_____] INTO People (name, retrieved) - VALUES (?, 0)''', (friend, ) ) - ''') - - - :OR: Correct! - :.*: Try again! - - :IGNORE: Correct! - :.*: Try again! - - -.. fillintheblank:: db_unique_tuple_hptest - :casei: - - Fill in the two blanks to only allow tuples of ``from_id`` and ``to_id`` that are unique. - - .. code-block:: python - - cur.execute(''' - CREATE TABLE IF NOT EXISTS Follows - (from_id INTEGER, to_id INTEGER, [_____](from_id, [_____]))''') - - - :UNIQUE: Correct! - :.*: Try again! - - :to_id: Correct! - :.*: Try again! - - -.. hparsons:: db_select_retrieved_zero_fitb_hptest - :language: sql - :dburl: /_static/hptest.db - :randomize: - - Arrange the blocks to select ``id`` and ``name`` from the table ``People`` where that person's friends have not been retrieved yet. - ~~~~ - --blocks-- - SELECT - id,name - FROM - People - WHERE - retrieved - = 0 - name - ON - --unittest-- - assert 0,0 == 2 - assert 0,1 == Pear - assert 1,0 == 3 - assert 1,1 == Watermelon \ No newline at end of file diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index 7e94e256a..0d84a6ba2 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -1,58 +1,49 @@ -========================= -Horizontal Parsons Test -========================= +========================================== +Test: Horizontal Parsons Problems with SQL +========================================== -.. Testing horizontal Parsons problems. -Horizontal Parsons + SQL +Examples +======== +Randomized Block -------------------------------------- -.. hparsons:: test_activecode_6 +.. hparsons:: test_hparsons_sql_1 :language: sql :dburl: /_static/test.db :randomize: - This is a horizontal Parsons problem! Feedback is based on code execution. The blocks are randomized, but cannot be reused ;) ~~~~ --blocks-- - select + SELECT * - from + FROM test --unittest-- assert 1,1 == world assert 0,1 == hello assert 2,1 == 42 -.. :dburl: http://localhost:8000/_static/test.db - -.. hparsons:: teasfasfas +Reusable Block +-------------------------------------- +.. hparsons:: test_hparsons_sql_2 :language: sql :dburl: /_static/test.db :reuse: - This is a horizontal parsons problem! Feedback is base on code execution. The blocks are set as the original order, and can be used multiple times. To delete a block, simply drag out of the input area. These features might not be so useful in the context of SQL, but might be useful in regex. ~~~~ --blocks-- - select + SELECT * - from + FROM test --unittest-- assert 1,1 == world assert 0,1 == hello assert 2,1 == 42 - -.. :dburl: http://localhost:8000/_static/test.db - - -.. toctree:: - :maxdepth: 3 - - database_hparsons.rst diff --git a/runestone/hparsons/test/test_hparsons.py b/runestone/hparsons/test/test_hparsons.py new file mode 100644 index 000000000..5498b2104 --- /dev/null +++ b/runestone/hparsons/test/test_hparsons.py @@ -0,0 +1,78 @@ +""" +Test horizontal Parsons problems directive +""" + +__author__ = "zihan wu" + +import time +from selenium.webdriver import ActionChains +from selenium.webdriver.common.by import By + +def find_hp_question(selenium_utils, div_id): + selenium_utils.wait_until_ready(div_id) + return selenium_utils.driver.find_element(By.ID, div_id) + + +''' +Test if the blocks are properly randomized. +1. Assert the sequence of the blocks is not the same as set in .rst file. +''' +def test_randomize_block(selenium_utils_get): + div_id = "test_hparsons_sql_1" + hp = find_hp_question(selenium_utils_get, div_id).find_element(By.CSS_SELECTOR, 'horizontal-parsons') + original_sequence = ['SELECT','*','FROM','test'] + randomized_sequence = [] + for block in hp.shadow_root.find_element(By.CSS_SELECTOR,'.drag-area').find_elements(By.CSS_SELECTOR, '.parsons-block'): + randomized_sequence.append(block.text) + assert len(original_sequence) == len(randomized_sequence) + is_same_order = True + for i in range(len(original_sequence)): + if original_sequence[i] != randomized_sequence[i]: + is_same_order = False + break + assert not is_same_order + + +# # Test adding and removing blocks by clicking in non-duplicating blocks +# """ +# 1. Click on the first block and make sure it is moved to the bottom +# 2. Click on the first block in the bottom and make sure it is put back +# """ +# def test_add_and_remove_blocks(selenium_utils_get): +# div_id = "test_hparsons_sql_1" +# hp = find_hp_question(selenium_utils_get, div_id).find_element(By.CSS_SELECTOR, 'horizontal-parsons') +# drag_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drag-area') +# drop_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drop-area') + +# # 1. Click on the first block and make sure it is added to the bottom +# block1 = drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] +# block1.click() +# assert block1 not in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block') +# assert block1 == drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] + + +# # 2. Drag the remaining first block and make sure it is added to the bottom +# drag = [x.text for x in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')] +# print(drag) +# block2 = drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] +# # ActionChains(selenium_utils_get.driver).drag_and_drop( +# # block2, drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] +# # ).perform() +# ActionChains(selenium_utils_get.driver).drag_and_drop( +# block2, drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] +# ).perform() +# time.sleep(2) + +# drag = [x.text for x in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')] +# print(drag) + +# # assert block2 not in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block') +# # assert block2 == drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] +# assert block2 in drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block') + +# # 2. Click on the moved block and make sure it is returned to the top +# # block2 and block1 are the same object +# block2 = drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] +# block2.click() +# assert block2 not in drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block') +# assert block2 == drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] \ No newline at end of file From 519f86fc8a15f78b5b58bdca56785a91715062af Mon Sep 17 00:00:00 2001 From: amy21206 Date: Mon, 28 Mar 2022 17:16:39 -0400 Subject: [PATCH 31/31] :white_check_mark: Adding tests to hparsons directive --- runestone/hparsons/test/test_hparsons.py | 112 +++++++++++++++-------- 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/runestone/hparsons/test/test_hparsons.py b/runestone/hparsons/test/test_hparsons.py index 5498b2104..ec0c5b5a5 100644 --- a/runestone/hparsons/test/test_hparsons.py +++ b/runestone/hparsons/test/test_hparsons.py @@ -5,13 +5,17 @@ __author__ = "zihan wu" import time -from selenium.webdriver import ActionChains +from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By def find_hp_question(selenium_utils, div_id): selenium_utils.wait_until_ready(div_id) return selenium_utils.driver.find_element(By.ID, div_id) +def click_control(hp_question, button_name): + css = '.btn-success.run-button' if button_name == 'Run' else '.btn-warning.run-button' + btn = hp_question.find_element(By.CSS_SELECTOR, css) + btn.click() ''' Test if the blocks are properly randomized. @@ -33,46 +37,76 @@ def test_randomize_block(selenium_utils_get): assert not is_same_order -# # Test adding and removing blocks by clicking in non-duplicating blocks -# """ -# 1. Click on the first block and make sure it is moved to the bottom -# 2. Click on the first block in the bottom and make sure it is put back -# """ -# def test_add_and_remove_blocks(selenium_utils_get): -# div_id = "test_hparsons_sql_1" -# hp = find_hp_question(selenium_utils_get, div_id).find_element(By.CSS_SELECTOR, 'horizontal-parsons') -# drag_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drag-area') -# drop_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drop-area') +""" +Test adding and removing blocks by clicking in non-duplicating blocks +For not reused blocks: +1. Click on the first block and make sure it is moved to the bottom +2. Click on the first block in the bottom and make sure it is put back +For reuseable blocks: +1. Click on the first block and make sure it is copied to the bottom +2. Click on the first block in the bottom and make sure it disappears +""" +def test_add_and_remove_blocks(selenium_utils_get): + div_id = "test_hparsons_sql_1" + hp = find_hp_question(selenium_utils_get, div_id).find_element(By.CSS_SELECTOR, 'horizontal-parsons') + drag_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drag-area') + drop_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drop-area') -# # 1. Click on the first block and make sure it is added to the bottom -# block1 = drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] -# block1.click() -# assert block1 not in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block') -# assert block1 == drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] - + # 1. Click on the first block and make sure it is added to the bottom + block1 = drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] + block1.click() + assert block1 not in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block') + assert block1 == drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] -# # 2. Drag the remaining first block and make sure it is added to the bottom -# drag = [x.text for x in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')] -# print(drag) -# block2 = drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] -# # ActionChains(selenium_utils_get.driver).drag_and_drop( -# # block2, drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] -# # ).perform() -# ActionChains(selenium_utils_get.driver).drag_and_drop( -# block2, drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] -# ).perform() -# time.sleep(2) + # 2. Click on the moved block and make sure it is returned to the top + # block2 and block1 are the same object + block2 = drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] + block2.click() + assert block2 not in drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block') + assert block2 == drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] + + # For reusable blocks + div_id = "test_hparsons_sql_2" + hp = find_hp_question(selenium_utils_get, div_id).find_element(By.CSS_SELECTOR, 'horizontal-parsons') + drag_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drag-area') + drop_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drop-area') -# drag = [x.text for x in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')] -# print(drag) + # 1. Click on the first block and make sure it is copied to the bottom + block1 = drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] + block1.click() + assert len(drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')) == 4 + assert block1.text == drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1].text -# # assert block2 not in drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block') -# # assert block2 == drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] -# assert block2 in drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block') + # 2. Click on the moved block and make sure it is removed + block2 = drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] + block2.click() + assert block2 not in drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block') + assert len(drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')) == 4 + + +""" +Test SQL feedback is correct +1. Click on each of the four blocks in second problem to form a solution +2. Click on run button to run code and check the result +""" +def test_run_SQL(selenium_utils_get): + # For reusable blocks + div_id = "test_hparsons_sql_2" + hp_question = find_hp_question(selenium_utils_get, div_id) + time.sleep(2) + hp = hp_question.find_element(By.CSS_SELECTOR, 'horizontal-parsons') + drag_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drag-area') + drop_area = hp.shadow_root.find_element(By.CSS_SELECTOR, '.drop-area') -# # 2. Click on the moved block and make sure it is returned to the top -# # block2 and block1 are the same object -# block2 = drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[0] -# block2.click() -# assert block2 not in drop_area.find_elements(By.CSS_SELECTOR, '.parsons-block') -# assert block2 == drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block')[-1] \ No newline at end of file + # 1. Click on each of the four blocks in second problem to form a solution + blocks = drag_area.find_elements(By.CSS_SELECTOR, '.parsons-block') + for block in blocks: + block.click() + + # 2. Click on run button to run code and check the result + click_control(hp_question, 'Run') + selenium_utils_get.wait.until(EC.text_to_be_present_in_element((By.ID, f"{div_id}_stdout"), "You")) + res = selenium_utils_get.driver.find_element(By.ID, f"{div_id}_sql_out") + assert res + out = selenium_utils_get.driver.find_element(By.ID, f"{div_id}_stdout") + assert "You passed 2 out of 3 tests" in out.text \ No newline at end of file