@khanacademy/perseus-editor 37.0.2 → 37.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -26,7 +26,6 @@ var wonderBlocksLayout = require('@khanacademy/wonder-blocks-layout');
26
26
  var caretRight = require('@phosphor-icons/core/bold/caret-right-bold.svg');
27
27
  var _ = require('underscore');
28
28
  var IconButton = require('@khanacademy/wonder-blocks-icon-button');
29
- var $ = require('jquery');
30
29
  var Link = require('@khanacademy/wonder-blocks-link');
31
30
  var Tooltip = require('@khanacademy/wonder-blocks-tooltip');
32
31
  var questionIcon = require('@phosphor-icons/core/regular/question.svg');
@@ -101,7 +100,6 @@ var Switch__default = /*#__PURE__*/_interopDefaultCompat(Switch);
101
100
  var caretRight__default = /*#__PURE__*/_interopDefaultCompat(caretRight);
102
101
  var ___default = /*#__PURE__*/_interopDefaultCompat(_);
103
102
  var IconButton__default = /*#__PURE__*/_interopDefaultCompat(IconButton);
104
- var $__default = /*#__PURE__*/_interopDefaultCompat($);
105
103
  var Link__default = /*#__PURE__*/_interopDefaultCompat(Link);
106
104
  var Tooltip__default = /*#__PURE__*/_interopDefaultCompat(Tooltip);
107
105
  var questionIcon__default = /*#__PURE__*/_interopDefaultCompat(questionIcon);
@@ -131,7 +129,7 @@ var minusCircle__default = /*#__PURE__*/_interopDefaultCompat(minusCircle);
131
129
  var checkIcon__default = /*#__PURE__*/_interopDefaultCompat(checkIcon);
132
130
  var minusCircleIcon__default = /*#__PURE__*/_interopDefaultCompat(minusCircleIcon);
133
131
 
134
- const libName="@khanacademy/perseus-editor";const libVersion="37.0.2";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
132
+ const libName="@khanacademy/perseus-editor";const libVersion="37.1.1";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
135
133
 
136
134
  var jsxRuntime = {exports: {}};
137
135
 
@@ -1519,13 +1517,13 @@ function AlignmentSelect({supportedAlignments,widgetInfo,isEditingDisabled,onCha
1519
1517
 
1520
1518
  function WidgetEditorSettings(props){const{bestPractices,supportsStaticMode,isStatic,onStaticChange,supportsGradedToggle,isGraded,onGradedChange,supportedAlignments,widgetInfo,onAlignmentChange,isEditingDisabled}=props;const hasControls=supportsStaticMode||supportsGradedToggle||supportedAlignments.length>1;if(!bestPractices&&!hasControls){return null}return jsxRuntimeExports.jsxs(wonderBlocksCore.View,{className:"widget-editor-settings-container",children:[bestPractices&&jsxRuntimeExports.jsx(wonderBlocksCore.View,{className:"best-practices-container",children:jsxRuntimeExports.jsx(Link__default.default,{href:bestPractices.url,target:"_blank",children:bestPractices.label})}),hasControls&&jsxRuntimeExports.jsxs("div",{className:"perseus-widget-row",children:[supportsStaticMode&&jsxRuntimeExports.jsx(LabeledSwitch,{label:"Static",checked:isStatic,disabled:isEditingDisabled,onChange:onStaticChange,style:{marginBlockEnd:wonderBlocksTokens.sizing.size_060}}),supportsGradedToggle&&jsxRuntimeExports.jsx(LabeledSwitch,{label:"Interactive but ungraded",checked:!isGraded,disabled:isEditingDisabled,onChange:e=>{onGradedChange(!e);},style:{marginBlockEnd:wonderBlocksTokens.sizing.size_060}}),supportedAlignments.length>1&&jsxRuntimeExports.jsx(AlignmentSelect,{supportedAlignments:supportedAlignments,widgetInfo:widgetInfo,isEditingDisabled:isEditingDisabled,onChange:onAlignmentChange,style:{marginBlockEnd:wonderBlocksTokens.sizing.size_060}})]})]})}
1521
1519
 
1522
- const _upgradeWidgetInfo=props=>{const filteredProps=perseus.excludeDenylistKeys(props);filteredProps.graded=props.graded;return perseusCore.applyDefaultsToWidget(filteredProps)};class WidgetEditor extends React__namespace.Component{UNSAFE_componentWillReceiveProps(nextProps){this.setState({widgetInfo:_upgradeWidgetInfo(nextProps)});if(nextProps.widgetIsOpen!=null&&nextProps.widgetIsOpen!==this.props.widgetIsOpen){this.setState({showWidget:nextProps.widgetIsOpen});}}render(){const widgetInfo=this.state.widgetInfo;const isEditingDisabled=this.props.apiOptions.editingDisabled??false;const Ed=perseus.Widgets.getEditor(widgetInfo.type);let supportedAlignments;if(this.props.apiOptions.showAlignmentOptions){supportedAlignments=perseusCore.CoreWidgetRegistry.getSupportedAlignments(widgetInfo.type);}else {supportedAlignments=["default"];}const supportsStaticMode=perseus.Widgets.supportsStaticMode(widgetInfo.type);const supportsGradedToggle=perseus.Widgets.supportsUngraded(widgetInfo.type);return jsxRuntimeExports.jsxs("div",{className:"perseus-widget-editor",children:[jsxRuntimeExports.jsxs("div",{className:"perseus-widget-editor-title "+(this.state.showWidget?"open":"closed"),children:[jsxRuntimeExports.jsx("div",{className:"perseus-widget-editor-title-id",children:jsxRuntimeExports.jsxs(wonderBlocksCore.View,{style:{display:"flex",flexDirection:"row",alignItems:"center",gap:"0.25em"},onClick:this._toggleWidget,children:[jsxRuntimeExports.jsx(ToggleableCaret,{isExpanded:this.state.showWidget}),jsxRuntimeExports.jsx("span",{children:this.props.id})]})}),jsxRuntimeExports.jsx(SectionControlButton,{icon:trashIcon__default.default,disabled:isEditingDisabled,onClick:()=>{this.props.onRemove();},title:"Remove image widget"})]}),this.state.showWidget&&jsxRuntimeExports.jsx(WidgetEditorSettings,{bestPractices:Ed?.bestPractices,supportsStaticMode:!!supportsStaticMode,isStatic:!!widgetInfo.static,onStaticChange:this._setStatic,supportsGradedToggle:supportsGradedToggle,isGraded:widgetInfo.graded!==false,onGradedChange:this._setGraded,supportedAlignments:supportedAlignments,widgetInfo:widgetInfo,onAlignmentChange:this._handleAlignmentChange,isEditingDisabled:isEditingDisabled}),jsxRuntimeExports.jsx("div",{className:"perseus-widget-editor-content "+(this.state.showWidget?"enter":"leave"),children:Ed&&jsxRuntimeExports.jsx(Ed,{ref:this.widget,onChange:this._handleWidgetChange,static:widgetInfo.static,graded:widgetInfo.graded,apiOptions:this.props.apiOptions,...widgetInfo.options})})]})}constructor(props){super(props),this._toggleWidget=e=>{e.preventDefault();this.setState({showWidget:!this.state.showWidget});},this._handleWidgetChange=(newProps,cb,silent)=>{const newWidgetInfo={...this.state.widgetInfo,options:{...this.state.widgetInfo.options,...this.widget.current?.serialize()??{},...newProps}};this.props.onChange(newWidgetInfo,cb,silent);},this._setStatic=value=>{const newWidgetInfo={...this.state.widgetInfo,static:value,graded:true};this.props.onChange(newWidgetInfo);},this._setGraded=value=>{const newWidgetInfo={...this.state.widgetInfo,graded:value,static:false};this.props.onChange(newWidgetInfo);},this._handleAlignmentChange=newAlignment=>{this.props.onChange({...this.state.widgetInfo,alignment:newAlignment});},this.getSaveWarnings=()=>{const issuesFunc=this.widget.current?.getSaveWarnings;return issuesFunc?issuesFunc():[]},this.serialize=()=>{const widgetInfo=this.state.widgetInfo;return {type:widgetInfo.type,alignment:widgetInfo.alignment,static:widgetInfo.static,graded:widgetInfo.graded,options:this.widget.current?.serialize()??widgetInfo.options,version:widgetInfo.version}};this.state={showWidget:props.widgetIsOpen??true,widgetInfo:_upgradeWidgetInfo(props)};this.widget=React__namespace.createRef();}}
1520
+ function _upgradeWidgetInfo(widgetInfo){const filteredWidget=perseus.excludeDenylistKeys(widgetInfo);filteredWidget.graded=widgetInfo.graded;return perseusCore.applyDefaultsToWidget(filteredWidget)}class WidgetEditor extends React__namespace.Component{UNSAFE_componentWillReceiveProps(nextProps){this.setState({widgetInfo:_upgradeWidgetInfo(nextProps.widgetInfo)});if(nextProps.widgetIsOpen!=null&&nextProps.widgetIsOpen!==this.props.widgetIsOpen){this.setState({showWidget:nextProps.widgetIsOpen});}}render(){const widgetInfo=this.state.widgetInfo;const isEditingDisabled=this.props.apiOptions.editingDisabled??false;const Ed=perseus.Widgets.getEditor(widgetInfo.type);let supportedAlignments;if(this.props.apiOptions.showAlignmentOptions){supportedAlignments=perseusCore.CoreWidgetRegistry.getSupportedAlignments(widgetInfo.type);}else {supportedAlignments=["default"];}const supportsStaticMode=perseus.Widgets.supportsStaticMode(widgetInfo.type);const supportsGradedToggle=perseus.Widgets.supportsUngraded(widgetInfo.type);return jsxRuntimeExports.jsxs("div",{className:"perseus-widget-editor",children:[jsxRuntimeExports.jsxs("div",{className:"perseus-widget-editor-title "+(this.state.showWidget?"open":"closed"),children:[jsxRuntimeExports.jsx("div",{className:"perseus-widget-editor-title-id",children:jsxRuntimeExports.jsxs(wonderBlocksCore.View,{style:{display:"flex",flexDirection:"row",alignItems:"center",gap:"0.25em"},onClick:this._toggleWidget,children:[jsxRuntimeExports.jsx(ToggleableCaret,{isExpanded:this.state.showWidget}),jsxRuntimeExports.jsx("span",{children:this.props.id})]})}),jsxRuntimeExports.jsx(SectionControlButton,{icon:trashIcon__default.default,disabled:isEditingDisabled,onClick:()=>{this.props.onRemove();},title:"Remove image widget"})]}),this.state.showWidget&&jsxRuntimeExports.jsx(WidgetEditorSettings,{bestPractices:Ed?.bestPractices,supportsStaticMode:!!supportsStaticMode,isStatic:!!widgetInfo.static,onStaticChange:this._setStatic,supportsGradedToggle:supportsGradedToggle,isGraded:widgetInfo.graded!==false,onGradedChange:this._setGraded,supportedAlignments:supportedAlignments,widgetInfo:widgetInfo,onAlignmentChange:this._handleAlignmentChange,isEditingDisabled:isEditingDisabled}),jsxRuntimeExports.jsx("div",{className:"perseus-widget-editor-content "+(this.state.showWidget?"enter":"leave"),children:Ed&&jsxRuntimeExports.jsx(Ed,{ref:this.widget,onChange:this._handleWidgetChange,static:widgetInfo.static,graded:widgetInfo.graded,apiOptions:this.props.apiOptions,...widgetInfo.options})})]})}constructor(props){super(props),this._toggleWidget=e=>{e.preventDefault();this.setState({showWidget:!this.state.showWidget});},this._handleWidgetChange=(newOptions,cb,silent)=>{const newWidgetInfo={...this.state.widgetInfo,options:{...this.state.widgetInfo.options,...this.widget.current?.serialize()??{},...newOptions}};this.props.onChange(newWidgetInfo,cb,silent);},this._setStatic=value=>{const newWidgetInfo={...this.state.widgetInfo,static:value,graded:true};this.props.onChange(newWidgetInfo);},this._setGraded=value=>{const newWidgetInfo={...this.state.widgetInfo,graded:value,static:false};this.props.onChange(newWidgetInfo);},this._handleAlignmentChange=newAlignment=>{this.props.onChange({...this.state.widgetInfo,alignment:newAlignment});},this.getSaveWarnings=()=>{const issuesFunc=this.widget.current?.getSaveWarnings;return issuesFunc?issuesFunc():[]},this.serialize=()=>{const widgetInfo=this.state.widgetInfo;return {type:widgetInfo.type,alignment:widgetInfo.alignment,static:widgetInfo.static,graded:widgetInfo.graded,options:this.widget.current?.serialize()??widgetInfo.options,version:widgetInfo.version}};this.state={showWidget:props.widgetIsOpen??true,widgetInfo:_upgradeWidgetInfo(props.widgetInfo)};this.widget=React__namespace.createRef();}}
1523
1521
 
1524
1522
  class WidgetSelect extends React__namespace.Component{shouldComponentUpdate(){return false}render(){const widgets=perseus.Widgets.getPublicWidgets();const orderedWidgetNames=___default.default.sortBy(Object.keys(widgets),name=>{return widgets[name].displayName});const addWidgetString="Add a widget…";return jsxRuntimeExports.jsxs("select",{value:"",onChange:this.handleChange,"data-testid":"editor__widget-select",children:[jsxRuntimeExports.jsx("option",{value:"",children:addWidgetString}),jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),orderedWidgetNames.map(name=>{return jsxRuntimeExports.jsx("option",{value:name,children:widgets[name].displayName},name)})]})}constructor(...args){super(...args),this.handleChange=e=>{const widgetType=e.currentTarget.value;if(widgetType===""){return}if(this.props.onChange){this.props.onChange(widgetType);}};}}
1525
1523
 
1526
1524
  function setPerseusClipboardData({text,widgets}){const dummyElement=document.createElement("span");dummyElement.setAttribute("data-perseus-widgets",JSON.stringify(widgets));return navigator.clipboard.write([new ClipboardItem({"text/plain":text,"text/html":dummyElement.outerHTML})])}async function getPerseusClipboardData(){const clipboardItems=await navigator.clipboard.read();let widgets={};let text="";for(const item of clipboardItems){if(item.types.includes("text/html")){const dummyDiv=document.createElement("span");const htmlBlob=await item.getType("text/html");dummyDiv.innerHTML=await htmlBlob.text();const widgetsJson=dummyDiv.querySelector("[data-perseus-widgets]")?.getAttribute("data-perseus-widgets");if(widgetsJson){widgets=JSON.parse(widgetsJson);}}if(item.types.includes("text/plain")){const plainTextBlob=await item.getType("text/plain");text=await plainTextBlob.text();}}return {widgets,text}}
1527
1525
 
1528
- const widgetPlaceholder="[[☃ {id}]]";const widgetRegExp="(\\[\\[☃ {id}\\]\\])";const rWidgetSplit=new RegExp(widgetRegExp.replace("{id}","[a-z-]+ [0-9]+"),"g");const shortcutRegexp=/^\[\[([a-z-]+)$/;const ENDS_WITH_A_PARAGRAPH=/(?:\n{2,}|^\n*)$/;const TRAILING_NEWLINES=/(\n*)$/;const LEADING_NEWLINES=/^(\n*)/;const commafyInteger=n=>{let str=n.toString();if(str.length>=5){str=str.replace(/(\d)(?=(\d{3})+$)/g,"$1{,}");}return str};const makeEndWithAParagraphIfNecessary=content=>{if(!ENDS_WITH_A_PARAGRAPH.test(content)){const match=TRAILING_NEWLINES.exec(content);if(match){const newlines=match[1];return content+"\n\n".slice(0,2-newlines.length)}}return content};const makeStartWithAParagraphAlways=content=>{const match=LEADING_NEWLINES.exec(content);if(!match){return content}const newlines=match[1];return "\n\n".slice(0,2-newlines.length)+content};const IMAGE_REGEX$1=/!\[[^\]]*\]\(([^\s)]+)[^)]*\)/g;const allMatches=function(regex,str){const result=[];while(true){const match=regex.exec(str);if(!match){break}result.push(match);}return result};const imageUrlsFromContent=function(content){return allMatches(IMAGE_REGEX$1,content).map(capture=>capture[1])};class Editor extends React__namespace.Component{static getDerivedStateFromProps(props,state){return {textAreaValue:props.content,rememberedWidgetsForUndo:{...state.rememberedWidgetsForUndo,...props.widgets}}}componentDidMount(){this.lastUserValue=null;this._sizeImages(this.props);$__default.default(this.textarea.current).on("copy cut",this._maybeCopyWidgets).on("paste",this._maybePasteWidgets);}componentDidUpdate(prevProps){const textarea=this.textarea.current;if(this.lastUserValue!=null&&textarea){textarea.focus();textarea.value=this.lastUserValue;textarea.selectionStart=0;textarea.setSelectionRange(0,prevProps.content.length);document.execCommand("insertText",false,this.props.content);this.lastUserValue=null;}if(this._pendingCursorPos!=null&&textarea){perseus.Util.textarea.moveCursor(textarea,this._pendingCursorPos);this._pendingCursorPos=null;}if(this.props.content!==prevProps.content){this._sizeImages(this.props);}}getWidgetEditor(id,type){if(!perseus.Widgets.getEditor(type)){return}return React.createElement(WidgetEditor,{...this.props.widgets[id],ref:id,id:id,key:id,onChange:this._handleWidgetEditorChange.bind(this,id),onRemove:this._handleWidgetEditorRemove.bind(this,id),apiOptions:this.props.apiOptions,widgetIsOpen:this.props.widgetIsOpen})}getWidgetsReferencedIn(content){const referencedWidgetIds=perseusCore.getWidgetIdsFromContent(content);const allWidgets={...this.state.rememberedWidgetsForUndo,...this.props.widgets};const referencedWidgets={};for(const id of referencedWidgetIds){if(allWidgets[id]!=null){referencedWidgets[id]=allWidgets[id];}}return referencedWidgets}serialize(){const widgets={};const widgetIds=___default.default.intersection(this.widgetIds,Object.keys(this.refs));___default.default.each(widgetIds,id=>{widgets[id]=this.refs[id].serialize();});return {content:this.props.content,images:this.props.images,widgets}}render(){let pieces;let widgets;let underlayPieces;let templatesDropDown;let widgetsAndTemplates;let wordCountDisplay;if(this.props.showWordCount){const numChars=perseus.PerseusMarkdown.characterCount(this.props.content);const numWords=Math.floor(numChars/6);wordCountDisplay=jsxRuntimeExports.jsx("span",{className:"perseus-editor-word-count",title:"~"+commafyInteger(numWords)+" words ("+commafyInteger(numChars)+" characters)",children:commafyInteger(numWords)});}if(this.props.widgetEnabled){pieces=this.props.content.split(rWidgetSplit);widgets={};underlayPieces=[];for(let i=0;i<pieces.length;i++){if(i%2===0){underlayPieces.push(pieces[i]);}else {const match=perseus.Util.rWidgetParts.exec(pieces[i]);if(match!=null){const id=match[1];const type=match[2];const duplicate=id in widgets;widgets[id]=this.getWidgetEditor(id,type);const classes=(duplicate||!widgets[id]?"error ":"")+("");const key=duplicate?i:id;underlayPieces.push(jsxRuntimeExports.jsx("b",{className:classes,children:pieces[i]},key));}}}this.widgetIds=Object.keys(widgets);const insertTemplateString="Insert template…";templatesDropDown=jsxRuntimeExports.jsxs("select",{onChange:this.addTemplate,"data-testid":"editor__template-select",children:[jsxRuntimeExports.jsx("option",{value:"",children:insertTemplateString}),jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),jsxRuntimeExports.jsx("option",{value:"table",children:"Table"}),jsxRuntimeExports.jsx("option",{value:"titledTable",children:"Titled table"}),jsxRuntimeExports.jsx("option",{value:"alignment",children:"Aligned equations"}),jsxRuntimeExports.jsx("option",{value:"piecewise",children:"Piecewise function"}),Object.keys(this.props.additionalTemplates).length>0&&jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),Object.entries(this.props.additionalTemplates).map(([key])=>jsxRuntimeExports.jsx("option",{value:key,children:key},key)),jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),jsxRuntimeExports.jsx("option",{value:"allWidgets",children:"All widgets (for testing)"})]});const widgetNodes=Object.values(widgets);widgetsAndTemplates=jsxRuntimeExports.jsxs("div",{className:"perseus-editor-widgets",children:[jsxRuntimeExports.jsxs("div",{className:"perseus-editor-widgets-selectors",children:[jsxRuntimeExports.jsx(WidgetSelect,{onChange:this._addWidget}),templatesDropDown,wordCountDisplay]}),widgetNodes]});}else {underlayPieces=[this.props.content];}underlayPieces.push(jsxRuntimeExports.jsx("br",{},"end"));const completeTextarea=[jsxRuntimeExports.jsx("div",{className:"perseus-textarea-underlay",ref:this.underlay,children:underlayPieces},"underlay"),jsxRuntimeExports.jsx("textarea",{ref:this.textarea,"aria-label":"Markdown content",onChange:this.handleChange,onKeyDown:this._handleKeyDown,placeholder:this.props.placeholder,disabled:this.props.disabled,value:this.state.textAreaValue},"textarea")];let textareaWrapper;if(this.props.imageUploader){textareaWrapper=jsxRuntimeExports.jsx(DragTarget,{onDrop:this.handleDrop,className:"perseus-textarea-pair",children:completeTextarea});}else {textareaWrapper=jsxRuntimeExports.jsx("div",{className:"perseus-textarea-pair",children:completeTextarea});}const contentWithoutWidgets=this.props.content.replace(/\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/g,"");const noPrompt=contentWithoutWidgets.trim().length===0;const noWidgets=!/\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/g.test(this.props.content);const warningStyle={borderTop:"none",padding:4,backgroundColor:"pink"};const editingDisabled=this.props.apiOptions.editingDisabled;return jsxRuntimeExports.jsxs("div",{"data-testid":"perseus-single-editor",className:"perseus-single-editor "+(this.props.className||"")+(editingDisabled?" perseus-editor-disabled":""),children:[textareaWrapper,this.props.warnNoPrompt&&noPrompt&&jsxRuntimeExports.jsx("div",{style:warningStyle,children:"Graded Groups should contain a prompt"}),this.props.warnNoWidgets&&noWidgets&&jsxRuntimeExports.jsx("div",{style:warningStyle,children:"Graded Groups should contain at least one widget"}),widgetsAndTemplates]})}constructor(...args){super(...args),this._pendingCursorPos=null,this.underlay=React__namespace.createRef(),this.textarea=React__namespace.createRef(),this.state={textAreaValue:this.props.content,rememberedWidgetsForUndo:{}},this._handleWidgetEditorChange=(id,newWidgetInfo)=>{const widgets=Object.assign({},this.props.widgets);widgets[id]=Object.assign({},widgets[id],newWidgetInfo);this.props.onChange({widgets});},this._handleWidgetEditorRemove=id=>{if(!confirm("Are you sure you want to delete this item?")){return}const textarea=this.textarea.current;const re=new RegExp(widgetRegExp.replace("{id}",id),"gm");this.props.onChange({content:textarea?.value.replace(re,"")});},this._sizeImages=props=>{const imageUrls=imageUrlsFromContent(props.content);const images=___default.default.pick(props.images,imageUrls);const newImageUrls=___default.default.filter(imageUrls,url=>!images[url]);___default.default.each(newImageUrls,url=>{perseus.Util.getImageSize(url,(width,height)=>{images[url]={width:width,height:height};props.onChange({images:___default.default.clone(images)});});});},this.handleDrop=e=>{const{imageUploader}=this.props;let content=this.state.textAreaValue||"";const dataTransfer="dataTransfer"in e?e.dataTransfer:null;if(!dataTransfer||!imageUploader){return}const files=dataTransfer.files;if(files.length===0){const imageUrl=dataTransfer.getData("URL");if(imageUrl){const newContent=content+"\n\n![]("+imageUrl+")";this.lastUserValue=this.props.content;this.props.onChange({content:newContent});}return}const origContent=this.state.textAreaValue;___default.default(files).chain().map(function(file){if(!file.type.match("image.*")){return null}const sentinel="☃ "+___default.default.uniqueId("image_");content+="\n\n![]("+sentinel+")";return {file:file,sentinel:sentinel}}).reject(___default.default.isNull).tap(()=>{this.lastUserValue=origContent;this.props.onChange({content:content});}).each(fileAndSentinel=>{imageUploader(fileAndSentinel.file,url=>{this.lastUserValue=origContent;this.props.onChange({content:this.state.textAreaValue.replace(fileAndSentinel.sentinel,url)});});});},this.handleChange=e=>{const newValue=e.currentTarget.value;this.setState({textAreaValue:newValue});const widgets=this.getWidgetsReferencedIn(newValue);if(newValue!==this.props.content){this.props.onChange({content:newValue,widgets});}},this._handleKeyDown=e=>{e.stopPropagation();if(e.key==="Tab"){const textarea=this.textarea.current;const word=perseus.Util.textarea.getWordBeforeCursor(textarea);const matches=word.string.toLowerCase().match(shortcutRegexp);if(matches!=null){const text=matches[1];const widgets=perseus.Widgets.getAllWidgetTypes();const matchingWidgets=widgets.filter(name=>{return name.substring(0,text.length)===text});if(matchingWidgets.length===1){const widgetType=matchingWidgets[0];this._addWidgetToContent(this.props.content,[word.pos.start,word.pos.end+1],widgetType);}e.preventDefault();}}},this._maybeCopyWidgets=e=>{const textarea=e.currentTarget;const selectedText=textarea.value.substring(textarea.selectionStart,textarea.selectionEnd);const widgetIds=___default.default.map(selectedText.match(rWidgetSplit),syntax=>{return perseus.Util.rWidgetParts.exec(syntax)[1]});const widgetData=___default.default.pick(this.serialize().widgets,widgetIds);setPerseusClipboardData({text:selectedText,widgets:widgetData}).catch(err=>perseus.Log.error("failed to copy data to clipboard","Internal",{cause:err}));},this._maybePasteWidgets=async e=>{e.preventDefault();const{widgets,text:textToBePasted}=await getPerseusClipboardData();const safeWidgetMapping=this._safeWidgetNameMapping(widgets);const safeWidgetData={};for(const[key,data]of Object.entries(widgets)){safeWidgetData[safeWidgetMapping[key]]=data;}const safeText=textToBePasted.replace(rWidgetSplit,syntax=>{const match=perseus.Util.rWidgetParts.exec(syntax);const completeWidget=match[0];const widget=match[1];return completeWidget.replace(widget,safeWidgetMapping[widget])});const textarea=e.currentTarget;const selectionStart=textarea.selectionStart;const newContent=this.state.textAreaValue.substr(0,selectionStart)+safeText+this.state.textAreaValue.substr(textarea.selectionEnd);this.lastUserValue=this.state.textAreaValue;this._pendingCursorPos=selectionStart+safeText.length;this.props.onChange({content:newContent,widgets:{...safeWidgetData,...this.getWidgetsReferencedIn(newContent)}});},this._safeWidgetNameMapping=widgetData=>{const widgets=Object.keys(widgetData).map(name=>name.split(" "));const widgetTypes=___default.default.uniq(widgets.map(widget=>widget[0]));const existingWidgets=Object.keys(this.props.widgets).map(name=>name.split(" "));const safeWidgetNums={};___default.default.each(widgetTypes,type=>{safeWidgetNums[type]=___default.default.chain(existingWidgets).filter(existingWidget=>existingWidget[0]===type).map(existingWidget=>+existingWidget[1]+1).max().value();safeWidgetNums[type]=Math.max(safeWidgetNums[type],1);});const safeWidgetMapping={};___default.default.each(widgets,widget=>{const widgetName=widget.join(" ");const widgetType=widget[0];safeWidgetMapping[widgetName]=`${widgetType} ${safeWidgetNums[widgetType]}`;safeWidgetNums[widgetType]++;});return safeWidgetMapping},this._addWidgetToContent=(oldContent,cursorRange,widgetType)=>{const allWidgetIds=___default.default.map(oldContent.match(rWidgetSplit),syntax=>{const match=perseus.Util.rWidgetParts.exec(syntax);const type=match[2];const num=+match[3];return [type,num]});const widgetNum=___default.default.reduce(allWidgetIds,(currentNum,otherId)=>{const[otherType,otherNum]=otherId;if(otherType===widgetType){return Math.max(otherNum+1,currentNum)}return currentNum},1);const id=widgetType+" "+widgetNum;const widgetContent=widgetPlaceholder.replace("{id}",id);const isBlock=perseusCore.CoreWidgetRegistry.getDefaultAlignment(widgetType)==="block";const selectedText=oldContent.slice(cursorRange[0],cursorRange[1]);const prelude=oldContent.slice(0,cursorRange[0]);const postlude=oldContent.slice(cursorRange[1]);const newPrelude=isBlock?makeEndWithAParagraphIfNecessary(prelude):prelude;const newPostlude=isBlock?makeStartWithAParagraphAlways(postlude):postlude;const newContent=newPrelude+widgetContent+newPostlude;const newWidgets={...this.props.widgets};const widgetEditor=perseus.Widgets.getEditor(widgetType);const initializeWidgetOptionsParams={selectedText};const startWidgetOptions=widgetEditor?.initializeWidgetOptions?.(initializeWidgetOptionsParams);const defaultProps=widgetEditor?.defaultProps;newWidgets[id]={options:startWidgetOptions||defaultProps,type:widgetType,version:perseusCore.CoreWidgetRegistry.getCurrentVersion(widgetType)};this.lastUserValue=this.props.content;this._pendingCursorPos=newContent.length-postlude.length;this.props.onChange({content:newContent,widgets:newWidgets});},this._addWidget=widgetType=>{const textarea=this.textarea.current;if(!textarea){return}this._addWidgetToContent(this.props.content,[textarea.selectionStart,textarea.selectionEnd],widgetType);textarea.focus();},this.addTemplate=e=>{const templateType=e.currentTarget.value;if(templateType===""){return}e.currentTarget.value="";let oldContent=this.props.content;oldContent=oldContent.replace(/\n*$/,"\n\n");let template;if(templateType==="table"){template="header 1 | header 2 | header 3\n"+"- | - | -\n"+"data 1 | data 2 | data 3\n"+"data 4 | data 5 | data 6\n"+"data 7 | data 8 | data 9";}else if(templateType==="titledTable"){template="|| **Table title** ||\n"+"header 1 | header 2 | header 3\n"+"- | - | -\n"+"data 1 | data 2 | data 3\n"+"data 4 | data 5 | data 6\n"+"data 7 | data 8 | data 9";}else if(templateType==="alignment"){template="$\\begin{align} \n"+"\\\\\\\\\n"+"\\end{align}$";}else if(templateType==="piecewise"){template="$f(x) = \\begin{cases}\n"+"7 & \\text{if }x=1 \\\\\n"+"f(x-1)+5 & \\text{if }x > 1\n"+"\\end{cases}$";}else if(templateType==="allWidgets"){template=perseus.Widgets.getAllWidgetTypes().map(type=>`[[${perseus.Util.snowman} ${type} 1]]`).join("\n\n");}else if(templateType in this.props.additionalTemplates){template=this.props.additionalTemplates[templateType];}else {throw new perseusCore.PerseusError("Invalid template type: "+templateType,perseusCore.Errors.InvalidInput,{metadata:{templateType}})}const newContent=oldContent+template;this.lastUserValue=this.props.content;this.props.onChange({content:newContent});},this.getSaveWarnings=()=>{const widgetIds=___default.default.intersection(this.widgetIds,Object.keys(this.refs));const warnings=___default.default(widgetIds).chain().map(id=>{const issuesFunc=this.refs[id].getSaveWarnings;const issues=issuesFunc?issuesFunc():[];return ___default.default.map(issues,issue=>id+": "+issue)}).flatten(true).value();return warnings},this.focus=()=>{const textarea=this.textarea.current;if(textarea){textarea.focus();}},this.focusAndMoveToEnd=()=>{this.focus();const textarea=this.textarea.current;if(textarea){textarea.selectionStart=textarea.value.length;textarea.selectionEnd=textarea.value.length;}};}}Editor.defaultProps={content:"",placeholder:"",widgets:{},images:{},disabled:false,widgetEnabled:true,showWordCount:false,warnNoPrompt:false,warnNoWidgets:false,additionalTemplates:{},apiOptions:perseus.ApiOptions.defaults};
1526
+ const widgetPlaceholder="[[☃ {id}]]";const widgetRegExp="(\\[\\[☃ {id}\\]\\])";const rWidgetSplit=new RegExp(widgetRegExp.replace("{id}","[a-z-]+ [0-9]+"),"g");const shortcutRegexp=/^\[\[([a-z-]+)$/;const ENDS_WITH_A_PARAGRAPH=/(?:\n{2,}|^\n*)$/;const TRAILING_NEWLINES=/(\n*)$/;const LEADING_NEWLINES=/^(\n*)/;const commafyInteger=n=>{let str=n.toString();if(str.length>=5){str=str.replace(/(\d)(?=(\d{3})+$)/g,"$1{,}");}return str};const makeEndWithAParagraphIfNecessary=content=>{if(!ENDS_WITH_A_PARAGRAPH.test(content)){const match=TRAILING_NEWLINES.exec(content);if(match){const newlines=match[1];return content+"\n\n".slice(0,2-newlines.length)}}return content};const makeStartWithAParagraphAlways=content=>{const match=LEADING_NEWLINES.exec(content);if(!match){return content}const newlines=match[1];return "\n\n".slice(0,2-newlines.length)+content};const IMAGE_REGEX$1=/!\[[^\]]*\]\(([^\s)]+)[^)]*\)/g;const allMatches=function(regex,str){const result=[];while(true){const match=regex.exec(str);if(!match){break}result.push(match);}return result};const imageUrlsFromContent=function(content){return allMatches(IMAGE_REGEX$1,content).map(capture=>capture[1])};class Editor extends React__namespace.Component{static getDerivedStateFromProps(props,state){return {textAreaValue:props.content,rememberedWidgetsForUndo:{...state.rememberedWidgetsForUndo,...props.widgets}}}componentDidMount(){this.lastUserValue=null;this._sizeImages(this.props);}componentDidUpdate(prevProps){const textarea=this.textarea.current;if(this.lastUserValue!=null&&textarea){textarea.focus();textarea.value=this.lastUserValue;textarea.selectionStart=0;textarea.setSelectionRange(0,prevProps.content.length);document.execCommand("insertText",false,this.props.content);this.lastUserValue=null;}if(this._pendingCursorPos!=null&&textarea){perseus.Util.textarea.moveCursor(textarea,this._pendingCursorPos);this._pendingCursorPos=null;}if(this.props.content!==prevProps.content){this._sizeImages(this.props);}}getWidgetEditor(id,type){if(!perseus.Widgets.getEditor(type)){return}return jsxRuntimeExports.jsx(WidgetEditor,{widgetInfo:this.props.widgets[id],ref:id,id:id,onChange:this._handleWidgetEditorChange.bind(this,id),onRemove:this._handleWidgetEditorRemove.bind(this,id),apiOptions:this.props.apiOptions,widgetIsOpen:this.props.widgetIsOpen},id)}getWidgetsReferencedIn(content){const referencedWidgetIds=perseusCore.getWidgetIdsFromContent(content);const allWidgets={...this.state.rememberedWidgetsForUndo,...this.props.widgets};const referencedWidgets={};for(const id of referencedWidgetIds){if(allWidgets[id]!=null){referencedWidgets[id]=allWidgets[id];}}return referencedWidgets}serialize(){const widgets={};const widgetIds=___default.default.intersection(this.widgetIds,Object.keys(this.refs));___default.default.each(widgetIds,id=>{widgets[id]=this.refs[id].serialize();});return {content:this.props.content,images:this.props.images,widgets}}render(){let pieces;let widgets;let underlayPieces;let templatesDropDown;let widgetsAndTemplates;let wordCountDisplay;if(this.props.showWordCount){const numChars=perseus.PerseusMarkdown.characterCount(this.props.content);const numWords=Math.floor(numChars/6);wordCountDisplay=jsxRuntimeExports.jsx("span",{className:"perseus-editor-word-count",title:"~"+commafyInteger(numWords)+" words ("+commafyInteger(numChars)+" characters)",children:commafyInteger(numWords)});}if(this.props.widgetEnabled){pieces=this.props.content.split(rWidgetSplit);widgets={};underlayPieces=[];for(let i=0;i<pieces.length;i++){if(i%2===0){underlayPieces.push(pieces[i]);}else {const match=perseus.Util.rWidgetParts.exec(pieces[i]);if(match!=null){const id=match[1];const type=match[2];const duplicate=id in widgets;widgets[id]=this.getWidgetEditor(id,type);const classes=(duplicate||!widgets[id]?"error ":"")+("");const key=duplicate?i:id;underlayPieces.push(jsxRuntimeExports.jsx("b",{className:classes,children:pieces[i]},key));}}}this.widgetIds=Object.keys(widgets);const insertTemplateString="Insert template…";templatesDropDown=jsxRuntimeExports.jsxs("select",{onChange:this.addTemplate,"data-testid":"editor__template-select",children:[jsxRuntimeExports.jsx("option",{value:"",children:insertTemplateString}),jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),jsxRuntimeExports.jsx("option",{value:"table",children:"Table"}),jsxRuntimeExports.jsx("option",{value:"titledTable",children:"Titled table"}),jsxRuntimeExports.jsx("option",{value:"alignment",children:"Aligned equations"}),jsxRuntimeExports.jsx("option",{value:"piecewise",children:"Piecewise function"}),Object.keys(this.props.additionalTemplates).length>0&&jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),Object.entries(this.props.additionalTemplates).map(([key])=>jsxRuntimeExports.jsx("option",{value:key,children:key},key)),jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),jsxRuntimeExports.jsx("option",{value:"allWidgets",children:"All widgets (for testing)"})]});const widgetNodes=Object.values(widgets);widgetsAndTemplates=jsxRuntimeExports.jsxs("div",{className:"perseus-editor-widgets",children:[jsxRuntimeExports.jsxs("div",{className:"perseus-editor-widgets-selectors",children:[jsxRuntimeExports.jsx(WidgetSelect,{onChange:this._addWidget}),templatesDropDown,wordCountDisplay]}),widgetNodes]});}else {underlayPieces=[this.props.content];}underlayPieces.push(jsxRuntimeExports.jsx("br",{},"end"));const completeTextarea=[jsxRuntimeExports.jsx("div",{className:"perseus-textarea-underlay",ref:this.underlay,children:underlayPieces},"underlay"),jsxRuntimeExports.jsx("textarea",{ref:this.textarea,"aria-label":"Markdown content",onChange:this.handleChange,onKeyDown:this._handleKeyDown,onCopy:this._maybeCopyWidgets,onCut:this._maybeCopyWidgets,onPaste:this._maybePasteWidgets,placeholder:this.props.placeholder,disabled:this.props.disabled,value:this.state.textAreaValue},"textarea")];let textareaWrapper;if(this.props.imageUploader){textareaWrapper=jsxRuntimeExports.jsx(DragTarget,{onDrop:this.handleDrop,className:"perseus-textarea-pair",children:completeTextarea});}else {textareaWrapper=jsxRuntimeExports.jsx("div",{className:"perseus-textarea-pair",children:completeTextarea});}const contentWithoutWidgets=this.props.content.replace(/\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/g,"");const noPrompt=contentWithoutWidgets.trim().length===0;const noWidgets=!/\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/g.test(this.props.content);const warningStyle={borderTop:"none",padding:4,backgroundColor:"pink"};const editingDisabled=this.props.apiOptions.editingDisabled;return jsxRuntimeExports.jsxs("div",{"data-testid":"perseus-single-editor",className:"perseus-single-editor "+(this.props.className||"")+(editingDisabled?" perseus-editor-disabled":""),children:[textareaWrapper,this.props.warnNoPrompt&&noPrompt&&jsxRuntimeExports.jsx("div",{style:warningStyle,children:"Graded Groups should contain a prompt"}),this.props.warnNoWidgets&&noWidgets&&jsxRuntimeExports.jsx("div",{style:warningStyle,children:"Graded Groups should contain at least one widget"}),widgetsAndTemplates]})}constructor(...args){super(...args),this._pendingCursorPos=null,this.underlay=React__namespace.createRef(),this.textarea=React__namespace.createRef(),this.state={textAreaValue:this.props.content,rememberedWidgetsForUndo:{}},this._handleWidgetEditorChange=(id,newWidgetInfo)=>{const widgets=Object.assign({},this.props.widgets);widgets[id]=Object.assign({},widgets[id],newWidgetInfo);this.props.onChange({widgets});},this._handleWidgetEditorRemove=id=>{if(!confirm("Are you sure you want to delete this item?")){return}const textarea=this.textarea.current;const re=new RegExp(widgetRegExp.replace("{id}",id),"gm");this.props.onChange({content:textarea?.value.replace(re,"")});},this._sizeImages=props=>{const imageUrls=imageUrlsFromContent(props.content);const images=___default.default.pick(props.images,imageUrls);const newImageUrls=___default.default.filter(imageUrls,url=>!images[url]);___default.default.each(newImageUrls,url=>{perseus.Util.getImageSize(url,(width,height)=>{images[url]={width:width,height:height};props.onChange({images:___default.default.clone(images)});});});},this.handleDrop=e=>{const{imageUploader}=this.props;let content=this.state.textAreaValue||"";const dataTransfer="dataTransfer"in e?e.dataTransfer:null;if(!dataTransfer||!imageUploader){return}const files=dataTransfer.files;if(files.length===0){const imageUrl=dataTransfer.getData("URL");if(imageUrl){const newContent=content+"\n\n![]("+imageUrl+")";this.lastUserValue=this.props.content;this.props.onChange({content:newContent});}return}const origContent=this.state.textAreaValue;___default.default(files).chain().map(function(file){if(!file.type.match("image.*")){return null}const sentinel="☃ "+___default.default.uniqueId("image_");content+="\n\n![]("+sentinel+")";return {file:file,sentinel:sentinel}}).reject(___default.default.isNull).tap(()=>{this.lastUserValue=origContent;this.props.onChange({content:content});}).each(fileAndSentinel=>{imageUploader(fileAndSentinel.file,url=>{this.lastUserValue=origContent;this.props.onChange({content:this.state.textAreaValue.replace(fileAndSentinel.sentinel,url)});});});},this.handleChange=e=>{const newValue=e.currentTarget.value;this.setState({textAreaValue:newValue});const widgets=this.getWidgetsReferencedIn(newValue);if(newValue!==this.props.content){this.props.onChange({content:newValue,widgets});}},this._handleKeyDown=e=>{e.stopPropagation();if(e.key==="Tab"){const textarea=this.textarea.current;const word=perseus.Util.textarea.getWordBeforeCursor(textarea);const matches=word.string.toLowerCase().match(shortcutRegexp);if(matches!=null){const text=matches[1];const widgets=perseus.Widgets.getAllWidgetTypes();const matchingWidgets=widgets.filter(name=>{return name.substring(0,text.length)===text});if(matchingWidgets.length===1){const widgetType=matchingWidgets[0];this._addWidgetToContent(this.props.content,[word.pos.start,word.pos.end+1],widgetType);}e.preventDefault();}}},this._maybeCopyWidgets=e=>{const textarea=e.currentTarget;const selectedText=textarea.value.substring(textarea.selectionStart,textarea.selectionEnd);const widgetIds=___default.default.map(selectedText.match(rWidgetSplit),syntax=>{return perseus.Util.rWidgetParts.exec(syntax)[1]});const widgetData=___default.default.pick(this.serialize().widgets,widgetIds);setPerseusClipboardData({text:selectedText,widgets:widgetData}).catch(err=>perseus.Log.error("failed to copy data to clipboard","Internal",{cause:err}));},this._maybePasteWidgets=async e=>{e.preventDefault();const textarea=e.currentTarget;const{widgets,text:textToBePasted}=await getPerseusClipboardData();const safeWidgetMapping=this._safeWidgetNameMapping(widgets);const safeWidgetData={};for(const[key,data]of Object.entries(widgets)){safeWidgetData[safeWidgetMapping[key]]=data;}const safeText=textToBePasted.replace(rWidgetSplit,syntax=>{const match=perseus.Util.rWidgetParts.exec(syntax);const completeWidget=match[0];const widget=match[1];return completeWidget.replace(widget,safeWidgetMapping[widget])});const selectionStart=textarea.selectionStart;const newContent=this.state.textAreaValue.substr(0,selectionStart)+safeText+this.state.textAreaValue.substr(textarea.selectionEnd);this.lastUserValue=this.state.textAreaValue;this._pendingCursorPos=selectionStart+safeText.length;this.props.onChange({content:newContent,widgets:{...safeWidgetData,...this.getWidgetsReferencedIn(newContent)}});},this._safeWidgetNameMapping=widgetData=>{const widgets=Object.keys(widgetData).map(name=>name.split(" "));const widgetTypes=___default.default.uniq(widgets.map(widget=>widget[0]));const existingWidgets=Object.keys(this.props.widgets).map(name=>name.split(" "));const safeWidgetNums={};___default.default.each(widgetTypes,type=>{safeWidgetNums[type]=___default.default.chain(existingWidgets).filter(existingWidget=>existingWidget[0]===type).map(existingWidget=>+existingWidget[1]+1).max().value();safeWidgetNums[type]=Math.max(safeWidgetNums[type],1);});const safeWidgetMapping={};___default.default.each(widgets,widget=>{const widgetName=widget.join(" ");const widgetType=widget[0];safeWidgetMapping[widgetName]=`${widgetType} ${safeWidgetNums[widgetType]}`;safeWidgetNums[widgetType]++;});return safeWidgetMapping},this._addWidgetToContent=(oldContent,cursorRange,widgetType)=>{const allWidgetIds=___default.default.map(oldContent.match(rWidgetSplit),syntax=>{const match=perseus.Util.rWidgetParts.exec(syntax);const type=match[2];const num=+match[3];return [type,num]});const widgetNum=___default.default.reduce(allWidgetIds,(currentNum,otherId)=>{const[otherType,otherNum]=otherId;if(otherType===widgetType){return Math.max(otherNum+1,currentNum)}return currentNum},1);const id=widgetType+" "+widgetNum;const widgetContent=widgetPlaceholder.replace("{id}",id);const isBlock=perseusCore.CoreWidgetRegistry.getDefaultAlignment(widgetType)==="block";const selectedText=oldContent.slice(cursorRange[0],cursorRange[1]);const prelude=oldContent.slice(0,cursorRange[0]);const postlude=oldContent.slice(cursorRange[1]);const newPrelude=isBlock?makeEndWithAParagraphIfNecessary(prelude):prelude;const newPostlude=isBlock?makeStartWithAParagraphAlways(postlude):postlude;const newContent=newPrelude+widgetContent+newPostlude;const newWidgets={...this.props.widgets};const widgetEditor=perseus.Widgets.getEditor(widgetType);const initializeWidgetOptionsParams={selectedText};const startWidgetOptions=widgetEditor?.initializeWidgetOptions?.(initializeWidgetOptionsParams);const defaultProps=widgetEditor?.defaultProps;newWidgets[id]={options:startWidgetOptions||defaultProps,type:widgetType,version:perseusCore.CoreWidgetRegistry.getCurrentVersion(widgetType)};this.lastUserValue=this.props.content;this._pendingCursorPos=newContent.length-postlude.length;this.props.onChange({content:newContent,widgets:newWidgets});},this._addWidget=widgetType=>{const textarea=this.textarea.current;if(!textarea){return}this._addWidgetToContent(this.props.content,[textarea.selectionStart,textarea.selectionEnd],widgetType);textarea.focus();},this.addTemplate=e=>{const templateType=e.currentTarget.value;if(templateType===""){return}e.currentTarget.value="";let oldContent=this.props.content;oldContent=oldContent.replace(/\n*$/,"\n\n");let template;if(templateType==="table"){template="header 1 | header 2 | header 3\n"+"- | - | -\n"+"data 1 | data 2 | data 3\n"+"data 4 | data 5 | data 6\n"+"data 7 | data 8 | data 9";}else if(templateType==="titledTable"){template="|| **Table title** ||\n"+"header 1 | header 2 | header 3\n"+"- | - | -\n"+"data 1 | data 2 | data 3\n"+"data 4 | data 5 | data 6\n"+"data 7 | data 8 | data 9";}else if(templateType==="alignment"){template="$\\begin{align} \n"+"\\\\\\\\\n"+"\\end{align}$";}else if(templateType==="piecewise"){template="$f(x) = \\begin{cases}\n"+"7 & \\text{if }x=1 \\\\\n"+"f(x-1)+5 & \\text{if }x > 1\n"+"\\end{cases}$";}else if(templateType==="allWidgets"){template=perseus.Widgets.getAllWidgetTypes().map(type=>`[[${perseus.Util.snowman} ${type} 1]]`).join("\n\n");}else if(templateType in this.props.additionalTemplates){template=this.props.additionalTemplates[templateType];}else {throw new perseusCore.PerseusError("Invalid template type: "+templateType,perseusCore.Errors.InvalidInput,{metadata:{templateType}})}const newContent=oldContent+template;this.lastUserValue=this.props.content;this.props.onChange({content:newContent});},this.getSaveWarnings=()=>{const widgetIds=___default.default.intersection(this.widgetIds,Object.keys(this.refs));const warnings=___default.default(widgetIds).chain().map(id=>{const issuesFunc=this.refs[id].getSaveWarnings;const issues=issuesFunc?issuesFunc():[];return ___default.default.map(issues,issue=>id+": "+issue)}).flatten(true).value();return warnings},this.focus=()=>{const textarea=this.textarea.current;if(textarea){textarea.focus();}},this.focusAndMoveToEnd=()=>{this.focus();const textarea=this.textarea.current;if(textarea){textarea.selectionStart=textarea.value.length;textarea.selectionEnd=textarea.value.length;}};}}Editor.defaultProps={content:"",placeholder:"",widgets:{},images:{},disabled:false,widgetEnabled:true,showWordCount:false,warnNoPrompt:false,warnNoWidgets:false,additionalTemplates:{},apiOptions:perseus.ApiOptions.defaults};
1529
1527
 
1530
1528
  const SEVERITY_ERROR=1;const SEVERITY_WARNING=2;const SEVERITY_GUIDELINE=3;const SEVERITY_MAP={[SEVERITY_ERROR]:"high",[SEVERITY_WARNING]:"medium",[SEVERITY_GUIDELINE]:"low"};const WARNINGS={inaccessibleWidget:(widgetType,widgetId)=>({id:`${widgetId} inaccessible`,instanceId:`inaccessible-widget-${widgetId}`,description:`This ${widgetType} widget (${widgetId}) is inaccessible. Consider using an alternative to support all learners. Please check out the following documentation on compliant widget options.`,helpUrl:"https://khanacademy.atlassian.net/wiki/spaces/LC/pages/1909489691/Widget+Fundamentals",help:"Widget Fundamentals",impact:"medium",message:"Selecting inaccessible widgets for a practice item will result in this exercise being hidden from users with 'Hide visually dependant content' setting set to true. Please select another widget or create an alternative practice item."}),genericLinterWarning:(rule,message,severity)=>({id:rule,description:message,help:"Learn more about best practices for authoring items",helpUrl:"https://docs.google.com/document/d/1N13f4sY-7EXWDwQ04ivA9vJBVvPPd60qjBT73B4NHuM/edit?tab=t.0",impact:SEVERITY_MAP[severity??SEVERITY_GUIDELINE],message:message}),texError:(math,errorMessage,index)=>({id:`tex-debug-info-${index+1}`,description:"If your math doesn't display correctly, these errors might help you troubleshoot. Message #content-kitchen for help.",help:"Learn more about TeX syntax",helpUrl:"https://khan.github.io/KaTeX/",impact:"medium",message:`Error in: ${math}
1531
1529
 
@@ -1611,7 +1609,7 @@ const Categorizer=perseus.Categorizer.widget;class CategorizerEditor extends Rea
1611
1609
 
1612
1610
  class BlurInput extends React__namespace.Component{UNSAFE_componentWillReceiveProps(nextProps){this.setState({value:nextProps.value});}focus(){this.input.current?.focus();}render(){return jsxRuntimeExports.jsx("input",{ref:this.input,className:this.props.className,style:this.props.style,type:"text",value:this.state.value,onChange:this.handleChange,onBlur:this.handleBlur})}constructor(props){super(props),this.input=React__namespace.createRef(),this.handleChange=e=>{this.setState({value:e.target.value});},this.handleBlur=e=>{this.props.onChange(e.target.value);};this.state={value:this.props.value};}}
1613
1611
 
1614
- const DEFAULT_WIDTH=400;const DEFAULT_HEIGHT=400;let PairEditor$1 = class PairEditor extends React__namespace.Component{render(){return jsxRuntimeExports.jsxs("fieldset",{className:"pair-editor",children:[jsxRuntimeExports.jsxs("label",{children:["Name:"," ",jsxRuntimeExports.jsx(BlurInput,{value:this.props.name,onChange:this.change("name")})]}),jsxRuntimeExports.jsxs("label",{children:[" ","Value:"," ",jsxRuntimeExports.jsx(BlurInput,{value:this.props.value,onChange:this.change("value")})]})]})}constructor(...args){super(...args),this.change=(...args)=>{return perseus.Changeable.change.apply(this,args)};}};PairEditor$1.defaultProps={name:"",value:""};let PairsEditor$1 = class PairsEditor extends React__namespace.Component{render(){const editors=this.props.pairs.map((pair,i)=>{return jsxRuntimeExports.jsx(PairEditor$1,{name:pair.name,value:pair.value,onChange:this.handlePairChange.bind(this,i)},i)});return jsxRuntimeExports.jsx("div",{children:editors})}constructor(...args){super(...args),this.change=(...args)=>{return perseus.Changeable.change.apply(this,args)},this.handlePairChange=(pairIndex,pair)=>{const pairs=this.props.pairs.slice();pairs[pairIndex]=pair;const lastPair=pairs[pairs.length-1];if(lastPair.name&&lastPair.value){pairs.push({name:"",value:""});}this.change("pairs",pairs);};}};const KA_PROGRAM_URL=/khanacademy\.org\/computer-programming\/[^/]+\/(\d+)/;function isolateProgramID(programUrl){const match=KA_PROGRAM_URL.exec(programUrl);if(match){programUrl=match[1];}return programUrl}class CSProgramEditor extends React__namespace.Component{render(){return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsxs("label",{children:["Url or Program ID:"," ",jsxRuntimeExports.jsx(BlurInput,{value:this.props.programID,onChange:this._handleProgramIDChange})]}),jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsx(wonderBlocksForm.Checkbox,{label:"Show Editor",checked:this.props.showEditor,onChange:value=>{this.props.onChange({showEditor:value});}}),jsxRuntimeExports.jsx(InfoTip,{children:'If you show the editor, you should use the "full-width" alignment to make room for the width of the editor.'}),jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsx(wonderBlocksForm.Checkbox,{label:"Show Buttons",checked:this.props.showButtons,onChange:value=>{this.props.onChange({showButtons:value});}}),jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsxs("label",{children:["Settings:",jsxRuntimeExports.jsx(PairsEditor$1,{pairs:this.props.settings,onChange:this._handleSettingsChange}),jsxRuntimeExports.jsxs(InfoTip,{children:["Settings that you add here are available to the program as an object returned by ",jsxRuntimeExports.jsx("code",{children:"Program.settings()"})]})]})]})}constructor(...args){super(...args),this.change=(...args)=>{return perseus.Changeable.change.apply(this,args)},this._handleSettingsChange=settings=>{this.change({settings:settings.pairs});},this._handleProgramIDChange=programID=>{programID=isolateProgramID(programID);const{isDevServer,InitialRequestUrl}=perseus.Dependencies.getDependencies();const host=isDevServer?InitialRequestUrl.origin:"https://www.khanacademy.org";const baseUrl=`${host}/api/internal/scratchpads/${programID}`;$__default.default.getJSON(baseUrl).done(programInfo=>{const programType=programInfo.userAuthoredContentType;this.change({width:programInfo.width,height:programInfo.height,programID:programID,programType:programType});}).fail((jqxhr,textStatus,error)=>{perseus.Log.error("Error retrieving scratchpad info for program ID ",perseusCore.Errors.TransientService,{cause:error,loggedMetadata:{textStatus,programID}});this.change({width:DEFAULT_WIDTH,height:DEFAULT_HEIGHT,programID:programID,programType:null});});},this.serialize=()=>{return perseus.EditorJsonify.serialize.call(this)};}}CSProgramEditor.widgetName="cs-program";
1612
+ const DEFAULT_WIDTH=400;const DEFAULT_HEIGHT=400;let PairEditor$1 = class PairEditor extends React__namespace.Component{render(){return jsxRuntimeExports.jsxs("fieldset",{className:"pair-editor",children:[jsxRuntimeExports.jsxs("label",{children:["Name:"," ",jsxRuntimeExports.jsx(BlurInput,{value:this.props.name,onChange:this.change("name")})]}),jsxRuntimeExports.jsxs("label",{children:[" ","Value:"," ",jsxRuntimeExports.jsx(BlurInput,{value:this.props.value,onChange:this.change("value")})]})]})}constructor(...args){super(...args),this.change=(...args)=>{return perseus.Changeable.change.apply(this,args)};}};PairEditor$1.defaultProps={name:"",value:""};let PairsEditor$1 = class PairsEditor extends React__namespace.Component{render(){const editors=this.props.pairs.map((pair,i)=>{return jsxRuntimeExports.jsx(PairEditor$1,{name:pair.name,value:pair.value,onChange:this.handlePairChange.bind(this,i)},i)});return jsxRuntimeExports.jsx("div",{children:editors})}constructor(...args){super(...args),this.change=(...args)=>{return perseus.Changeable.change.apply(this,args)},this.handlePairChange=(pairIndex,pair)=>{const pairs=this.props.pairs.slice();pairs[pairIndex]=pair;const lastPair=pairs[pairs.length-1];if(lastPair.name&&lastPair.value){pairs.push({name:"",value:""});}this.change("pairs",pairs);};}};const KA_PROGRAM_URL=/khanacademy\.org\/computer-programming\/[^/]+\/(\d+)/;function isolateProgramID(programUrl){const match=KA_PROGRAM_URL.exec(programUrl);if(match){programUrl=match[1];}return programUrl}class CSProgramEditor extends React__namespace.Component{render(){return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsxs("label",{children:["Url or Program ID:"," ",jsxRuntimeExports.jsx(BlurInput,{value:this.props.programID,onChange:this._handleProgramIDChange})]}),jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsx(wonderBlocksForm.Checkbox,{label:"Show Editor",checked:this.props.showEditor,onChange:value=>{this.props.onChange({showEditor:value});}}),jsxRuntimeExports.jsx(InfoTip,{children:'If you show the editor, you should use the "full-width" alignment to make room for the width of the editor.'}),jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsx(wonderBlocksForm.Checkbox,{label:"Show Buttons",checked:this.props.showButtons,onChange:value=>{this.props.onChange({showButtons:value});}}),jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsxs("label",{children:["Settings:",jsxRuntimeExports.jsx(PairsEditor$1,{pairs:this.props.settings,onChange:this._handleSettingsChange}),jsxRuntimeExports.jsxs(InfoTip,{children:["Settings that you add here are available to the program as an object returned by ",jsxRuntimeExports.jsx("code",{children:"Program.settings()"})]})]})]})}constructor(...args){super(...args),this.change=(...args)=>{return perseus.Changeable.change.apply(this,args)},this._handleSettingsChange=settings=>{this.change({settings:settings.pairs});},this._handleProgramIDChange=programID=>{programID=isolateProgramID(programID);const{isDevServer,InitialRequestUrl}=perseus.Dependencies.getDependencies();const host=isDevServer?InitialRequestUrl.origin:"https://www.khanacademy.org";const baseUrl=`${host}/api/internal/scratchpads/${programID}`;const fetchProgramInfo=async()=>{const response=await fetch(baseUrl);if(!response.ok){throw new Error(`Request for scratchpad info failed with status ${response.status} ${response.statusText}`)}return response.json()};fetchProgramInfo().then(programInfo=>{const programType=programInfo.userAuthoredContentType;this.change({width:programInfo.width,height:programInfo.height,programID:programID,programType:programType});},error=>{perseus.Log.error("Error retrieving scratchpad info for program ID ",perseusCore.Errors.TransientService,{cause:error,loggedMetadata:{programID}});this.change({width:DEFAULT_WIDTH,height:DEFAULT_HEIGHT,programID:programID,programType:null});});},this.serialize=()=>{return perseus.EditorJsonify.serialize.call(this)};}}CSProgramEditor.widgetName="cs-program";
1615
1613
 
1616
1614
  const{TextInput: TextInput$5}=perseus.components;class DefinitionEditor extends React__namespace.Component{static initializeWidgetOptions(params){const defaultWidgetOptions={...perseusCore.definitionLogic.defaultWidgetOptions};if(params.selectedText){defaultWidgetOptions.togglePrompt=params.selectedText;}return defaultWidgetOptions}render(){return jsxRuntimeExports.jsxs("div",{className:"perseus-widget-definition-editor",children:[jsxRuntimeExports.jsx("a",{href:"https://docs.google.com/document/d/1udaPef4imOfTMhmLDlWq4SM0mxL0r3YHFZE-5J1uGfo",target:"_blank",rel:"noreferrer",children:"Definition style guide"}),jsxRuntimeExports.jsx("div",{className:"perseus-widget-row",children:jsxRuntimeExports.jsxs("label",{children:["Word to be defined:"," ",jsxRuntimeExports.jsx(TextInput$5,{value:this.props.togglePrompt,onChange:this.change("togglePrompt"),placeholder:"define me"})]})}),jsxRuntimeExports.jsx("div",{className:"perseus-widget-row",children:jsxRuntimeExports.jsx(Editor,{apiOptions:this.props.apiOptions,content:this.props.definition,widgetEnabled:false,placeholder:"definition goes here",onChange:props=>{const newProps={};if(___default.default.has(props,"content")){newProps.definition=props.content;}this.change(newProps);}})})]})}constructor(...args){super(...args),this.change=(...args)=>{return perseus.Changeable.change.apply(this,args)},this.serialize=()=>{return perseus.EditorJsonify.serialize.call(this)};}}DefinitionEditor.widgetName="definition";DefinitionEditor.defaultProps=perseusCore.definitionLogic.defaultWidgetOptions;
1617
1615